mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 19:11:33 +03:00
UBER-486: updated people avatars. (#3720)
Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
parent
d9d47846cf
commit
a07f88033f
@ -947,6 +947,7 @@ a.no-line {
|
|||||||
.content-color { color: var(--theme-content-color); }
|
.content-color { color: var(--theme-content-color); }
|
||||||
.caption-color { color: var(--theme-caption-color); }
|
.caption-color { color: var(--theme-caption-color); }
|
||||||
|
|
||||||
|
.content-accented-color { color: var(--accented-button-color); }
|
||||||
.red-color { color: var(--highlight-red); }
|
.red-color { color: var(--highlight-red); }
|
||||||
.error-color { color: var(--theme-error-color); }
|
.error-color { color: var(--theme-error-color); }
|
||||||
|
|
||||||
|
@ -194,7 +194,9 @@
|
|||||||
&.accented, &.brand, &.positive, &.negative {
|
&.accented, &.brand, &.positive, &.negative {
|
||||||
&:hover, &:active, &:focus {
|
&:hover, &:active, &:focus {
|
||||||
color: var(--accented-button-color);
|
color: var(--accented-button-color);
|
||||||
.btn-icon { color: var(--accented-button-color); }
|
|
||||||
|
.btn-icon,
|
||||||
|
.btn-right-icon { color: var(--accented-button-color); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.regular, &.ghost {
|
&.regular, &.ghost {
|
||||||
@ -208,7 +210,8 @@
|
|||||||
color: var(--accented-button-content-color);
|
color: var(--accented-button-content-color);
|
||||||
border-color: var(--accented-button-border);
|
border-color: var(--accented-button-border);
|
||||||
|
|
||||||
.btn-icon { color: var(--accented-button-content-color); }
|
.btn-icon,
|
||||||
|
.btn-right-icon { color: var(--accented-button-content-color); }
|
||||||
}
|
}
|
||||||
&.accented {
|
&.accented {
|
||||||
background-color: var(--accented-button-default);
|
background-color: var(--accented-button-default);
|
||||||
@ -257,7 +260,8 @@
|
|||||||
background-color: var(--theme-button-contrast-enabled);
|
background-color: var(--theme-button-contrast-enabled);
|
||||||
border-color: var(--theme-button-contrast-border);
|
border-color: var(--theme-button-contrast-border);
|
||||||
|
|
||||||
.btn-icon { color: var(--theme-button-contrast-color); }
|
.btn-icon,
|
||||||
|
.btn-right-icon { color: var(--theme-button-contrast-color); }
|
||||||
|
|
||||||
&:hover { background-color: var(--theme-button-contrast-hovered); }
|
&:hover { background-color: var(--theme-button-contrast-hovered); }
|
||||||
&:active { background-color: var(--theme-button-contrast-pressed); }
|
&:active { background-color: var(--theme-button-contrast-pressed); }
|
||||||
@ -298,7 +302,8 @@
|
|||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
|
|
||||||
.btn-icon { opacity: .5; }
|
.btn-icon,
|
||||||
|
.btn-right-icon { opacity: .5; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.resetIconSize { font-size: 16px; }
|
.resetIconSize { font-size: 16px; }
|
||||||
|
@ -93,7 +93,10 @@
|
|||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<svelte:fragment slot="iconRight">
|
<svelte:fragment slot="iconRight">
|
||||||
<DropdownIcon size={'small'} fill={'var(--theme-dark-color)'} />
|
<DropdownIcon
|
||||||
|
size={'small'}
|
||||||
|
fill={kind === 'accented' && !disabled ? 'var(--accented-button-content-color)' : 'var(--theme-dark-color)'}
|
||||||
|
/>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -191,7 +191,10 @@
|
|||||||
{#if showIcon}
|
{#if showIcon}
|
||||||
{#if withAvatar}
|
{#if withAvatar}
|
||||||
<div class="msgactivity-avatar">
|
<div class="msgactivity-avatar">
|
||||||
<Component is={contact.component.Avatar} props={{ avatar: person?.avatar, size: 'medium' }} />
|
<Component
|
||||||
|
is={contact.component.Avatar}
|
||||||
|
props={{ avatar: person?.avatar, size: 'medium', name: person?.name }}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="msgactivity-icon">
|
<div class="msgactivity-icon">
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
on:click={() => onClick(p)}
|
on:click={() => onClick(p)}
|
||||||
>
|
>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<Avatar size={'x-small'} avatar={p.avatar} />
|
<Avatar size={'x-small'} avatar={p.avatar} name={p.name} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex-nowrap">
|
<div class="flex-nowrap">
|
||||||
<div class="avatar"><Avatar size={'medium'} /></div>
|
<div class="avatar"><Avatar size={'medium'} avatar={user.avatar} name={user.name} /></div>
|
||||||
<div class="flex-col-stretch message">
|
<div class="flex-col-stretch message">
|
||||||
<div class="header">{getName(client.getHierarchy(), user)}<span>{message.createDate}</span></div>
|
<div class="header">{getName(client.getHierarchy(), user)}<span>{message.createDate}</span></div>
|
||||||
<div class="text">{message.text}</div>
|
<div class="text">{message.text}</div>
|
||||||
|
@ -74,7 +74,7 @@
|
|||||||
<div class="flex-row-top">
|
<div class="flex-row-top">
|
||||||
{#await getEmployee(value, $personByIdStore, $personAccountByIdStore) then employee}
|
{#await getEmployee(value, $personByIdStore, $personAccountByIdStore) then employee}
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<Avatar size={'medium'} avatar={employee?.avatar} />
|
<Avatar size={'medium'} avatar={employee?.avatar} name={employee?.name} />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-grow flex-col select-text">
|
<div class="flex-grow flex-col select-text">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
|
@ -204,7 +204,9 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="container clear-mins" class:highlighted={isHighlighted} id={message._id}>
|
<div class="container clear-mins" class:highlighted={isHighlighted} id={message._id}>
|
||||||
<div class="avatar"><Avatar size={'medium'} avatar={employee?.avatar} /></div>
|
<div class="avatar">
|
||||||
|
<Avatar size={'medium'} avatar={employee?.avatar} name={employee?.name} />
|
||||||
|
</div>
|
||||||
<div class="message clear-mins">
|
<div class="message clear-mins">
|
||||||
<div class="header clear-mins">
|
<div class="header clear-mins">
|
||||||
{#if employee}
|
{#if employee}
|
||||||
|
@ -52,7 +52,7 @@
|
|||||||
<div class="message">
|
<div class="message">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<Avatar size={'medium'} avatar={employee?.avatar} />
|
<Avatar size={'medium'} avatar={employee?.avatar} name={employee?.name} />
|
||||||
</div>
|
</div>
|
||||||
<span class="name">
|
<span class="name">
|
||||||
{employee ? getName(client.getHierarchy(), employee) : ''}
|
{employee ? getName(client.getHierarchy(), employee) : ''}
|
||||||
|
@ -59,7 +59,7 @@
|
|||||||
<div class="flex-row-center container cursor-pointer" on:click>
|
<div class="flex-row-center container cursor-pointer" on:click>
|
||||||
<div class="flex-row-center">
|
<div class="flex-row-center">
|
||||||
{#each showReplies as reply}
|
{#each showReplies as reply}
|
||||||
<div class="reply"><Avatar size={'x-small'} avatar={reply.avatar} /></div>
|
<div class="reply"><Avatar size={'x-small'} avatar={reply.avatar} name={reply.name} /></div>
|
||||||
{/each}
|
{/each}
|
||||||
{#if employees.size > shown}
|
{#if employees.size > shown}
|
||||||
<div class="reply"><span>+{employees.size - shown}</span></div>
|
<div class="reply"><span>+{employees.size - shown}</span></div>
|
||||||
|
@ -27,7 +27,13 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact, { AvatarProvider, AvatarType } from '@hcengineering/contact'
|
import contact, {
|
||||||
|
AvatarProvider,
|
||||||
|
AvatarType,
|
||||||
|
getAvatarColorForId,
|
||||||
|
getFirstName,
|
||||||
|
getLastName
|
||||||
|
} from '@hcengineering/contact'
|
||||||
import { Client, Ref } from '@hcengineering/core'
|
import { Client, Ref } from '@hcengineering/core'
|
||||||
import { Asset, getResource } from '@hcengineering/platform'
|
import { Asset, getResource } from '@hcengineering/platform'
|
||||||
import { getBlobURL, getClient } from '@hcengineering/presentation'
|
import { getBlobURL, getClient } from '@hcengineering/presentation'
|
||||||
@ -36,14 +42,21 @@
|
|||||||
import AvatarIcon from './icons/Avatar.svelte'
|
import AvatarIcon from './icons/Avatar.svelte'
|
||||||
|
|
||||||
export let avatar: string | null | undefined = undefined
|
export let avatar: string | null | undefined = undefined
|
||||||
|
export let name: string | null | undefined = undefined
|
||||||
export let direct: Blob | undefined = undefined
|
export let direct: Blob | undefined = undefined
|
||||||
export let size: IconSize
|
export let size: IconSize
|
||||||
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
||||||
|
|
||||||
let url: string[] | undefined
|
let url: string[] | undefined
|
||||||
let avatarProvider: AvatarProvider | undefined
|
let avatarProvider: AvatarProvider | undefined
|
||||||
|
let color: string | undefined = undefined
|
||||||
|
|
||||||
async function update (size: IconSize, avatar?: string | null, direct?: Blob) {
|
$: fname = getFirstName(name ?? '')
|
||||||
|
$: lname = getLastName(name ?? '')
|
||||||
|
$: displayName =
|
||||||
|
name != null ? (lname.length > 1 ? lname.trim()[0] : lname) + (fname.length > 1 ? fname.trim()[0] : fname) : ''
|
||||||
|
|
||||||
|
async function update (size: IconSize, avatar?: string | null, direct?: Blob, name?: string | null) {
|
||||||
if (direct !== undefined) {
|
if (direct !== undefined) {
|
||||||
getBlobURL(direct).then((blobURL) => {
|
getBlobURL(direct).then((blobURL) => {
|
||||||
url = [blobURL]
|
url = [blobURL]
|
||||||
@ -55,32 +68,46 @@
|
|||||||
|
|
||||||
if (!avatarProvider || avatarProvider.type === AvatarType.COLOR) {
|
if (!avatarProvider || avatarProvider.type === AvatarType.COLOR) {
|
||||||
url = undefined
|
url = undefined
|
||||||
|
color = avatar.split('://')[1]
|
||||||
} else if (avatarProvider?.type === AvatarType.IMAGE) {
|
} else if (avatarProvider?.type === AvatarType.IMAGE) {
|
||||||
url = (await getResource(avatarProvider.getUrl))(avatar, size)
|
url = (await getResource(avatarProvider.getUrl))(avatar, size)
|
||||||
} else {
|
} else {
|
||||||
const uri = avatar.split('://')[1]
|
const uri = avatar.split('://')[1]
|
||||||
url = (await getResource(avatarProvider.getUrl))(uri, size)
|
url = (await getResource(avatarProvider.getUrl))(uri, size)
|
||||||
}
|
}
|
||||||
|
} else if (name != null) {
|
||||||
|
color = getAvatarColorForId(name)
|
||||||
|
url = undefined
|
||||||
|
avatarProvider = undefined
|
||||||
} else {
|
} else {
|
||||||
url = undefined
|
url = undefined
|
||||||
avatarProvider = undefined
|
avatarProvider = undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$: update(size, avatar, direct)
|
$: update(size, avatar, direct, name)
|
||||||
|
|
||||||
let imageElement: HTMLImageElement | undefined = undefined
|
let imageElement: HTMLImageElement | undefined = undefined
|
||||||
|
|
||||||
$: srcset = url?.slice(1)?.join(', ')
|
$: srcset = url?.slice(1)?.join(', ')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="ava-{size} flex-center avatar-container" class:no-img={!url}>
|
<div
|
||||||
|
class="ava-{size} flex-center avatar-container"
|
||||||
|
class:no-img={!url && color}
|
||||||
|
class:bordered={!url && color === undefined}
|
||||||
|
style:background-color={url ? 'var(--theme-button-default)' : color}
|
||||||
|
>
|
||||||
{#if url}
|
{#if url}
|
||||||
{#if size === 'large' || size === 'x-large' || size === '2x-large'}
|
{#if size === 'large' || size === 'x-large' || size === '2x-large'}
|
||||||
<img class="ava-{size} ava-blur" src={url[0]} {srcset} alt={''} bind:this={imageElement} />
|
<img class="ava-{size} ava-blur" src={url[0]} {srcset} alt={''} bind:this={imageElement} />
|
||||||
{/if}
|
{/if}
|
||||||
<img class="ava-{size} ava-mask" src={url[0]} {srcset} alt={''} bind:this={imageElement} />
|
<img class="ava-{size} ava-mask" src={url[0]} {srcset} alt={''} bind:this={imageElement} />
|
||||||
|
{:else if name && displayName && displayName !== ''}
|
||||||
|
<div class="ava-text" data-name={displayName.toLocaleUpperCase()} />
|
||||||
{:else}
|
{:else}
|
||||||
<Icon icon={icon ?? AvatarIcon} size={size === 'card' ? 'x-small' : size} />
|
<div class="icon">
|
||||||
|
<Icon icon={icon ?? AvatarIcon} size={'full'} />
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -89,60 +116,139 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background-color: var(--avatar-bg-color);
|
background-color: var(--theme-button-default);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
||||||
|
&.no-img {
|
||||||
|
color: var(--accented-button-color);
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
&.bordered {
|
||||||
|
color: var(--theme-dark-color);
|
||||||
|
border: 1px solid var(--theme-button-border);
|
||||||
|
}
|
||||||
img {
|
img {
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
border: 1px solid var(--avatar-border-color);
|
border: 1px solid var(--avatar-border-color);
|
||||||
}
|
}
|
||||||
&.no-img {
|
.icon,
|
||||||
border-color: transparent;
|
.ava-text::after {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
}
|
||||||
|
.icon {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
color: inherit;
|
||||||
|
transform-origin: center;
|
||||||
|
transform: translate(-50%, -50%) scale(0.6);
|
||||||
|
}
|
||||||
|
.ava-text::after {
|
||||||
|
content: attr(data-name);
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ava-inline {
|
.ava-inline {
|
||||||
width: 0.875rem; // 24
|
width: 0.875rem; // 24
|
||||||
height: 0.875rem;
|
height: 0.875rem;
|
||||||
|
|
||||||
|
.ava-text {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.525rem;
|
||||||
|
letter-spacing: -0.05em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ava-tiny {
|
.ava-tiny {
|
||||||
width: 1.13rem; // ~18
|
width: 1.13rem; // ~18
|
||||||
height: 1.13rem;
|
height: 1.13rem;
|
||||||
|
|
||||||
|
.ava-text {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.625rem;
|
||||||
|
letter-spacing: -0.05em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ava-card {
|
.ava-card {
|
||||||
width: 1.25rem; // 20
|
width: 1.25rem; // 20
|
||||||
height: 1.25rem;
|
height: 1.25rem;
|
||||||
|
|
||||||
|
.ava-text {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.625rem;
|
||||||
|
letter-spacing: -0.05em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.ava-x-small {
|
.ava-x-small {
|
||||||
width: 1.5rem; // 24
|
width: 1.5rem; // 24
|
||||||
height: 1.5rem;
|
height: 1.5rem;
|
||||||
|
|
||||||
|
.ava-text {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
letter-spacing: -0.05em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.ava-smaller {
|
.ava-smaller {
|
||||||
width: 1.75rem; // 28
|
width: 1.75rem; // 28
|
||||||
height: 1.75rem;
|
height: 1.75rem;
|
||||||
|
|
||||||
|
.ava-text {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
letter-spacing: -0.05em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.ava-small {
|
.ava-small {
|
||||||
width: 2rem; // 32
|
width: 2rem; // 32
|
||||||
height: 2rem;
|
height: 2rem;
|
||||||
|
|
||||||
|
.ava-text {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
letter-spacing: -0.05em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.ava-medium {
|
.ava-medium {
|
||||||
width: 2.25rem; // 36
|
width: 2.25rem; // 36
|
||||||
height: 2.25rem;
|
height: 2.25rem;
|
||||||
|
|
||||||
|
.ava-text {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
letter-spacing: -0.05em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.ava-large {
|
.ava-large {
|
||||||
width: 4.5rem; // 72
|
width: 4.5rem; // 72
|
||||||
height: 4.5rem;
|
height: 4.5rem;
|
||||||
|
|
||||||
|
.ava-text {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.ava-x-large {
|
.ava-x-large {
|
||||||
width: 7.5rem; // 120
|
width: 7.5rem; // 120
|
||||||
height: 7.5rem;
|
height: 7.5rem;
|
||||||
|
|
||||||
|
.ava-text {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 3.5rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.ava-2x-large {
|
.ava-2x-large {
|
||||||
width: 10rem; // 120
|
width: 10rem; // 120
|
||||||
height: 10rem;
|
height: 10rem;
|
||||||
|
|
||||||
|
.ava-text {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 4.75rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ava-blur {
|
.ava-blur {
|
||||||
|
@ -56,7 +56,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{#each persons as person, i}
|
{#each persons as person, i}
|
||||||
<div class="combine-avatar {size}" data-over={getDataOver(persons.length === i + 1, items)}>
|
<div class="combine-avatar {size}" data-over={getDataOver(persons.length === i + 1, items)}>
|
||||||
<Avatar avatar={person.avatar} {size} />
|
<Avatar avatar={person.avatar} {size} name={person.name} />
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
@ -169,7 +169,13 @@
|
|||||||
<slot name="extraControls" />
|
<slot name="extraControls" />
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-4">
|
<div class="ml-4">
|
||||||
<EditableAvatar avatar={object.avatar} {email} {id} size={'large'} bind:this={avatarEditor} />
|
<EditableAvatar
|
||||||
|
avatar={object.avatar}
|
||||||
|
name={combineName(firstName, lastName)}
|
||||||
|
{email}
|
||||||
|
size={'large'}
|
||||||
|
bind:this={avatarEditor}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<svelte:fragment slot="pool">
|
<svelte:fragment slot="pool">
|
||||||
|
@ -115,7 +115,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-4">
|
<div class="ml-4">
|
||||||
<EditableAvatar avatar={object.avatar} {id} size={'large'} bind:this={avatarEditor} />
|
<EditableAvatar
|
||||||
|
avatar={object.avatar}
|
||||||
|
name={combineName(firstName, lastName)}
|
||||||
|
size={'large'}
|
||||||
|
bind:this={avatarEditor}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<svelte:fragment slot="pool">
|
<svelte:fragment slot="pool">
|
||||||
|
@ -110,13 +110,13 @@
|
|||||||
<EditableAvatar
|
<EditableAvatar
|
||||||
avatar={object.avatar}
|
avatar={object.avatar}
|
||||||
{email}
|
{email}
|
||||||
id={object._id}
|
|
||||||
size={'x-large'}
|
size={'x-large'}
|
||||||
|
name={object.name}
|
||||||
bind:this={avatarEditor}
|
bind:this={avatarEditor}
|
||||||
on:done={onAvatarDone}
|
on:done={onAvatarDone}
|
||||||
/>
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
<Avatar avatar={object.avatar} size={'x-large'} />
|
<Avatar avatar={object.avatar} size={'x-large'} name={object.name} />
|
||||||
{/if}
|
{/if}
|
||||||
{/key}
|
{/key}
|
||||||
</div>
|
</div>
|
||||||
|
@ -85,8 +85,8 @@
|
|||||||
{#key object}
|
{#key object}
|
||||||
<EditableAvatar
|
<EditableAvatar
|
||||||
avatar={object.avatar}
|
avatar={object.avatar}
|
||||||
id={object._id}
|
|
||||||
size={'x-large'}
|
size={'x-large'}
|
||||||
|
name={object.name}
|
||||||
bind:this={avatarEditor}
|
bind:this={avatarEditor}
|
||||||
on:done={onAvatarDone}
|
on:done={onAvatarDone}
|
||||||
/>
|
/>
|
||||||
@ -131,7 +131,7 @@
|
|||||||
.name {
|
.name {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 1.25rem;
|
font-size: 1.25rem;
|
||||||
color: var(--caption-color);
|
color: var(--theme-caption-color);
|
||||||
}
|
}
|
||||||
.location {
|
.location {
|
||||||
margin-top: 0.25rem;
|
margin-top: 0.25rem;
|
||||||
@ -141,6 +141,6 @@
|
|||||||
.separator {
|
.separator {
|
||||||
margin: 1rem 0;
|
margin: 1rem 0;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
background-color: var(--divider-color);
|
background-color: var(--theme-divider-color);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -16,24 +16,33 @@
|
|||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import attachment from '@hcengineering/attachment'
|
import attachment from '@hcengineering/attachment'
|
||||||
import { AnySvelteComponent, IconSize, showPopup } from '@hcengineering/ui'
|
import { AnySvelteComponent, IconSize, showPopup } from '@hcengineering/ui'
|
||||||
import { AvatarType } from '@hcengineering/contact'
|
import { AvatarType, getAvatarColorForId } from '@hcengineering/contact'
|
||||||
import { Asset, getResource } from '@hcengineering/platform'
|
import { Asset, getResource } from '@hcengineering/platform'
|
||||||
|
|
||||||
import AvatarComponent from './Avatar.svelte'
|
import AvatarComponent from './Avatar.svelte'
|
||||||
import SelectAvatarPopup from './SelectAvatarPopup.svelte'
|
import SelectAvatarPopup from './SelectAvatarPopup.svelte'
|
||||||
|
|
||||||
export let avatar: string | null | undefined
|
export let avatar: string | null | undefined
|
||||||
|
export let name: string | null | undefined = undefined
|
||||||
export let email: string | undefined = undefined
|
export let email: string | undefined = undefined
|
||||||
export let id: string
|
|
||||||
export let size: IconSize
|
export let size: IconSize
|
||||||
export let direct: Blob | undefined = undefined
|
export let direct: Blob | undefined = undefined
|
||||||
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
||||||
export let disabled: boolean = false
|
export let disabled: boolean = false
|
||||||
|
|
||||||
const [schema, uri] = avatar?.split('://') || []
|
$: [schema, uri] = avatar?.split('://') || []
|
||||||
|
|
||||||
let selectedAvatarType: AvatarType | undefined = avatar?.includes('://') ? (schema as AvatarType) : AvatarType.IMAGE
|
let selectedAvatarType: AvatarType | undefined
|
||||||
let selectedAvatar: string | null | undefined = selectedAvatarType === AvatarType.IMAGE ? avatar : uri
|
let selectedAvatar: string | null | undefined
|
||||||
|
$: selectedAvatarType = avatar?.includes('://')
|
||||||
|
? (schema as AvatarType)
|
||||||
|
: avatar === undefined
|
||||||
|
? AvatarType.COLOR
|
||||||
|
: AvatarType.IMAGE
|
||||||
|
$: selectedAvatar = selectedAvatarType === AvatarType.IMAGE ? avatar : uri
|
||||||
|
$: if (selectedAvatar === undefined && selectedAvatarType === AvatarType.COLOR) {
|
||||||
|
selectedAvatar = getAvatarColorForId(name)
|
||||||
|
}
|
||||||
|
|
||||||
export async function createAvatar (): Promise<string | undefined> {
|
export async function createAvatar (): Promise<string | undefined> {
|
||||||
if (selectedAvatarType === AvatarType.IMAGE && direct !== undefined) {
|
if (selectedAvatarType === AvatarType.IMAGE && direct !== undefined) {
|
||||||
@ -58,13 +67,26 @@
|
|||||||
selectedAvatarType = submittedAvatarType
|
selectedAvatarType = submittedAvatarType
|
||||||
selectedAvatar = submittedAvatar
|
selectedAvatar = submittedAvatar
|
||||||
direct = submittedDirect
|
direct = submittedDirect
|
||||||
|
avatar = selectedAvatarType === AvatarType.IMAGE ? selectedAvatar : `${selectedAvatarType}://${selectedAvatar}`
|
||||||
dispatch('done')
|
dispatch('done')
|
||||||
}
|
}
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
async function showSelectionPopup (e: MouseEvent) {
|
async function showSelectionPopup (e: MouseEvent) {
|
||||||
if (!disabled) {
|
if (!disabled) {
|
||||||
showPopup(SelectAvatarPopup, { avatar, email, id, file: direct, icon, onSubmit: handlePopupSubmit })
|
showPopup(SelectAvatarPopup, {
|
||||||
|
avatar:
|
||||||
|
selectedAvatarType === AvatarType.IMAGE
|
||||||
|
? selectedAvatar
|
||||||
|
: selectedAvatarType === AvatarType.COLOR && avatar == null
|
||||||
|
? undefined
|
||||||
|
: `${selectedAvatarType}://${selectedAvatar}`,
|
||||||
|
email,
|
||||||
|
name,
|
||||||
|
file: direct,
|
||||||
|
icon,
|
||||||
|
onSubmit: handlePopupSubmit
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -72,9 +94,14 @@
|
|||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div class="cursor-pointer" on:click|self={showSelectionPopup}>
|
<div class="cursor-pointer" on:click|self={showSelectionPopup}>
|
||||||
<AvatarComponent
|
<AvatarComponent
|
||||||
avatar={selectedAvatarType === AvatarType.IMAGE ? selectedAvatar : `${selectedAvatarType}://${selectedAvatar}`}
|
|
||||||
{direct}
|
{direct}
|
||||||
{size}
|
{size}
|
||||||
{icon}
|
{icon}
|
||||||
|
avatar={selectedAvatarType === AvatarType.IMAGE
|
||||||
|
? selectedAvatar
|
||||||
|
: selectedAvatarType === AvatarType.COLOR && avatar == null
|
||||||
|
? undefined
|
||||||
|
: `${selectedAvatarType}://${selectedAvatar}`}
|
||||||
|
{name}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -58,7 +58,7 @@
|
|||||||
>
|
>
|
||||||
{#if employee}
|
{#if employee}
|
||||||
<div class="flex-col-center pb-2">
|
<div class="flex-col-center pb-2">
|
||||||
<Avatar size="x-large" avatar={employee.avatar} />
|
<Avatar size={'x-large'} avatar={employee.avatar} name={employee.name} />
|
||||||
</div>
|
</div>
|
||||||
<div class="pb-2">{getName(client.getHierarchy(), employee)}</div>
|
<div class="pb-2">{getName(client.getHierarchy(), employee)}</div>
|
||||||
<DocNavLink object={employee}>
|
<DocNavLink object={employee}>
|
||||||
@ -79,6 +79,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{:else if editable}
|
{:else if editable}
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div class="flex-row-stretch over-underline pb-2" on:click={onEdit}>
|
<div class="flex-row-stretch over-underline pb-2" on:click={onEdit}>
|
||||||
<Label label={contact.string.SetStatus} />
|
<Label label={contact.string.SetStatus} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -359,7 +359,7 @@
|
|||||||
selected={update.avatar !== undefined}
|
selected={update.avatar !== undefined}
|
||||||
>
|
>
|
||||||
<svelte:fragment slot="item" let:item>
|
<svelte:fragment slot="item" let:item>
|
||||||
<Avatar avatar={item.avatar} size={'x-large'} icon={contact.icon.Person} />
|
<Avatar avatar={item.avatar} size={'x-large'} icon={contact.icon.Person} name={item.name} />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</MergeComparer>
|
</MergeComparer>
|
||||||
<MergeComparer
|
<MergeComparer
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
<div class="antiContactCard">
|
<div class="antiContactCard">
|
||||||
<div class="label uppercase"><Label label={contact.string.Person} /></div>
|
<div class="label uppercase"><Label label={contact.string.Person} /></div>
|
||||||
<div class="flex-center logo">
|
<div class="flex-center logo">
|
||||||
<Avatar avatar={object.avatar} size={'large'} icon={contact.icon.Company} />
|
<Avatar avatar={object.avatar} size={'large'} icon={contact.icon.Company} name={object.name} />
|
||||||
</div>
|
</div>
|
||||||
{#if object}
|
{#if object}
|
||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
@ -87,7 +87,7 @@
|
|||||||
class:mr-2={shouldShowName && !enlargedText}
|
class:mr-2={shouldShowName && !enlargedText}
|
||||||
class:mr-3={shouldShowName && enlargedText}
|
class:mr-3={shouldShowName && enlargedText}
|
||||||
>
|
>
|
||||||
<Avatar size={avatarSize} avatar={value.avatar} />
|
<Avatar size={avatarSize} avatar={value.avatar} name={value.name} />
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
{#if shouldShowName}
|
{#if shouldShowName}
|
||||||
|
@ -15,24 +15,33 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
|
|
||||||
import { AvatarType, buildGravatarId, checkHasGravatar, getAvatarColorForId } from '@hcengineering/contact'
|
import {
|
||||||
|
AvatarType,
|
||||||
|
buildGravatarId,
|
||||||
|
checkHasGravatar,
|
||||||
|
getAvatarColorForId,
|
||||||
|
getAvatarColors,
|
||||||
|
getAvatarColorName
|
||||||
|
} from '@hcengineering/contact'
|
||||||
import { Asset } from '@hcengineering/platform'
|
import { Asset } from '@hcengineering/platform'
|
||||||
import { AnySvelteComponent, Label, showPopup, TabList } from '@hcengineering/ui'
|
import { AnySvelteComponent, Label, showPopup, TabList, eventToHTMLElement } from '@hcengineering/ui'
|
||||||
|
import { ColorsPopup } from '@hcengineering/view-resources'
|
||||||
import presentation, { Card, getFileUrl } from '@hcengineering/presentation'
|
import presentation, { Card, getFileUrl } from '@hcengineering/presentation'
|
||||||
import contact from '../plugin'
|
import contact from '../plugin'
|
||||||
import { getAvatarTypeDropdownItems } from '../utils'
|
import { getAvatarTypeDropdownItems } from '../utils'
|
||||||
import AvatarComponent from './Avatar.svelte'
|
import AvatarComponent from './Avatar.svelte'
|
||||||
import EditAvatarPopup from './EditAvatarPopup.svelte'
|
import EditAvatarPopup from './EditAvatarPopup.svelte'
|
||||||
|
|
||||||
export let avatar: string | undefined
|
export let avatar: string | null | undefined = undefined
|
||||||
|
export let name: string | null | undefined = undefined
|
||||||
export let email: string | undefined
|
export let email: string | undefined
|
||||||
export let id: string
|
|
||||||
export let file: Blob | undefined
|
export let file: Blob | undefined
|
||||||
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
||||||
export let onSubmit: (avatarType?: AvatarType, avatar?: string, file?: Blob) => void
|
export let onSubmit: (avatarType?: AvatarType, avatar?: string, file?: Blob) => void
|
||||||
|
|
||||||
const [schema, uri] = avatar?.split('://') || []
|
const [schema, uri] = avatar?.split('://') || []
|
||||||
|
const colors = getAvatarColors()
|
||||||
|
let color: string | undefined = (schema as AvatarType) === AvatarType.COLOR ? uri : undefined
|
||||||
|
|
||||||
const initialSelectedType = (() => {
|
const initialSelectedType = (() => {
|
||||||
if (file) {
|
if (file) {
|
||||||
@ -47,7 +56,7 @@
|
|||||||
|
|
||||||
const initialSelectedAvatar = (() => {
|
const initialSelectedAvatar = (() => {
|
||||||
if (!avatar) {
|
if (!avatar) {
|
||||||
return getAvatarColorForId(id)
|
return getAvatarColorForId(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
return avatar.includes('://') ? uri : avatar
|
return avatar.includes('://') ? uri : avatar
|
||||||
@ -87,7 +96,7 @@
|
|||||||
inputRef.click()
|
inputRef.click()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
selectedAvatar = getAvatarColorForId(id)
|
selectedAvatar = color ?? getAvatarColorForId(name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,13 +119,13 @@
|
|||||||
if (blob === undefined) {
|
if (blob === undefined) {
|
||||||
if (!selectedFile && (!avatar || avatar.includes('://'))) {
|
if (!selectedFile && (!avatar || avatar.includes('://'))) {
|
||||||
selectedAvatarType = AvatarType.COLOR
|
selectedAvatarType = AvatarType.COLOR
|
||||||
selectedAvatar = getAvatarColorForId(id)
|
selectedAvatar = getAvatarColorForId(name)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (blob === null) {
|
if (blob === null) {
|
||||||
selectedAvatarType = AvatarType.COLOR
|
selectedAvatarType = AvatarType.COLOR
|
||||||
selectedAvatar = getAvatarColorForId(id)
|
selectedAvatar = getAvatarColorForId(name)
|
||||||
selectedFile = undefined
|
selectedFile = undefined
|
||||||
} else {
|
} else {
|
||||||
selectedFile = blob
|
selectedFile = blob
|
||||||
@ -142,10 +151,23 @@
|
|||||||
if (!inputRef.value.length) {
|
if (!inputRef.value.length) {
|
||||||
if (!selectedFile) {
|
if (!selectedFile) {
|
||||||
selectedAvatarType = AvatarType.COLOR
|
selectedAvatarType = AvatarType.COLOR
|
||||||
selectedAvatar = getAvatarColorForId(id)
|
selectedAvatar = getAvatarColorForId(name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const showColorPopup = (event: MouseEvent) => {
|
||||||
|
showPopup(
|
||||||
|
ColorsPopup,
|
||||||
|
{ colors, columns: 6, selected: getAvatarColorName(selectedAvatar) },
|
||||||
|
eventToHTMLElement(event),
|
||||||
|
(col) => {
|
||||||
|
if (col != null) {
|
||||||
|
color = selectedAvatar = colors[col].color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Card
|
<Card
|
||||||
@ -164,14 +186,26 @@
|
|||||||
on:changeContent
|
on:changeContent
|
||||||
>
|
>
|
||||||
<div class="flex-col-center gapV-4 mx-6">
|
<div class="flex-col-center gapV-4 mx-6">
|
||||||
{#if selectedAvatarType === AvatarType.IMAGE}
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
<div
|
||||||
<div class="cursor-pointer" on:click|self={handleImageAvatarClick}>
|
class="cursor-pointer"
|
||||||
<AvatarComponent avatar={selectedAvatar} direct={selectedFile} size={'2x-large'} {icon} />
|
on:click|self={(e) => {
|
||||||
</div>
|
if (selectedAvatarType === AvatarType.IMAGE) handleImageAvatarClick()
|
||||||
{:else}
|
else if (selectedAvatarType === AvatarType.COLOR) showColorPopup(e)
|
||||||
<AvatarComponent avatar={`${selectedAvatarType}://${selectedAvatar}`} size={'2x-large'} {icon} />
|
}}
|
||||||
{/if}
|
>
|
||||||
|
<AvatarComponent
|
||||||
|
avatar={selectedAvatarType === AvatarType.IMAGE
|
||||||
|
? selectedAvatar === ''
|
||||||
|
? `${AvatarType.COLOR}://${color}`
|
||||||
|
: selectedAvatar
|
||||||
|
: `${selectedAvatarType}://${selectedAvatar}`}
|
||||||
|
direct={selectedAvatarType === AvatarType.IMAGE ? selectedFile : undefined}
|
||||||
|
size={'2x-large'}
|
||||||
|
{icon}
|
||||||
|
{name}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<TabList
|
<TabList
|
||||||
items={getAvatarTypeDropdownItems(hasGravatar)}
|
items={getAvatarTypeDropdownItems(hasGravatar)}
|
||||||
kind={'separated-free'}
|
kind={'separated-free'}
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div class="flex-row-center" on:click>
|
<div class="flex-row-center" on:click>
|
||||||
<Avatar avatar={value.avatar} {size} {icon} on:accent-color />
|
<Avatar avatar={value.avatar} {size} {icon} name={value.name} on:accent-color />
|
||||||
<div class="flex-col min-w-0 {size === 'tiny' || size === 'inline' ? 'ml-1' : 'ml-2'}" class:max-w-20={short}>
|
<div class="flex-col min-w-0 {size === 'tiny' || size === 'inline' ? 'ml-1' : 'ml-2'}" class:max-w-20={short}>
|
||||||
{#if subtitle}<div class="content-dark-color text-sm">{subtitle}</div>{/if}
|
{#if subtitle}<div class="content-dark-color text-sm">{subtitle}</div>{/if}
|
||||||
<div class="label overflow-label text-left">{getName(client.getHierarchy(), value)}</div>
|
<div class="label overflow-label text-left">{getName(client.getHierarchy(), value)}</div>
|
||||||
|
@ -18,33 +18,29 @@
|
|||||||
|
|
||||||
export let size: IconSize
|
export let size: IconSize
|
||||||
|
|
||||||
export let fill: string = 'var(--caption-color)'
|
export let fill: string = 'var(--theme-caption-color)'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svg class="svg-avatar avaicon-{size}" {fill} viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg">
|
<svg class="svg-avatar avaicon-{size}" {fill} viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||||
<circle class="op" cx="20" cy="13.6" r="6.4" />
|
|
||||||
<path
|
<path
|
||||||
d="M33.1,33.3c-0.8-2.2-2.5-4.2-4.9-5.5c-2.3-1.3-5.2-2.1-8.2-2.1s-5.8,0.7-8.2,2.1c-2.4,1.4-4.1,3.3-4.9,5.5 c-0.1,0.4,0.1,0.8,0.5,1c0.4,0.1,0.8-0.1,1-0.5c0.7-1.9,2.2-3.5,4.2-4.7c2.1-1.2,4.7-1.9,7.4-1.9c2.7,0,5.3,0.7,7.4,1.9 c2.1,1.2,3.6,2.9,4.2,4.7c0.1,0.3,0.4,0.5,0.7,0.5c0.1,0,0.2,0,0.3,0C33,34.1,33.2,33.7,33.1,33.3z"
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M10 9.99988C12.0711 9.99988 13.75 8.32095 13.75 6.24988C13.75 4.17881 12.0711 2.49988 10 2.49988C7.92893 2.49988 6.25 4.17881 6.25 6.24988C6.25 8.32095 7.92893 9.99988 10 9.99988ZM10 11.2499C12.7614 11.2499 15 9.0113 15 6.24988C15 3.48845 12.7614 1.24988 10 1.24988C7.23858 1.24988 5 3.48845 5 6.24988C5 9.0113 7.23858 11.2499 10 11.2499Z"
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
d="M20,20.8c3.9,0,7.1-3.2,7.1-7.1S23.9,6.5,20,6.5c-3.9,0-7.1,3.2-7.1,7.1S16.1,20.8,20,20.8z M20,8 c3.1,0,5.6,2.5,5.6,5.6s-2.5,5.6-5.6,5.6c-3.1,0-5.6-2.5-5.6-5.6S16.9,8,20,8z"
|
d="M8.125 12.4999C5.70875 12.4999 3.75 14.4586 3.75 16.8749V18.1249C3.75 18.4701 4.02982 18.7499 4.375 18.7499C4.72018 18.7499 5 18.4701 5 18.1249V16.8749C5 15.149 6.39911 13.7499 8.125 13.7499H11.875C13.6009 13.7499 15 15.149 15 16.8749V18.1249C15 18.4701 15.2798 18.7499 15.625 18.7499C15.9702 18.7499 16.25 18.4701 16.25 18.1249V16.8749C16.25 14.4586 14.2912 12.4999 11.875 12.4999H8.125Z"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.svg-avatar {
|
|
||||||
.op {
|
|
||||||
opacity: 0.05;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.avaicon-inline {
|
.avaicon-inline {
|
||||||
width: 0.75rem;
|
width: 0.6125rem;
|
||||||
height: 0.75rem;
|
height: 0.6125rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avaicon-tiny {
|
.avaicon-tiny {
|
||||||
width: 0.875rem;
|
width: 0.8125rem;
|
||||||
height: 0.875rem;
|
height: 0.8125rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avaicon-x-small {
|
.avaicon-x-small {
|
||||||
@ -64,15 +60,15 @@
|
|||||||
height: 1.5rem;
|
height: 1.5rem;
|
||||||
}
|
}
|
||||||
.avaicon-large {
|
.avaicon-large {
|
||||||
width: 1.75rem;
|
width: 2.5rem;
|
||||||
height: 1.75rem;
|
height: 2.5rem;
|
||||||
}
|
}
|
||||||
.avaicon-x-large {
|
.avaicon-x-large {
|
||||||
width: 3rem;
|
|
||||||
height: 3rem;
|
|
||||||
}
|
|
||||||
.avaicon-2x-large {
|
|
||||||
width: 4rem;
|
width: 4rem;
|
||||||
height: 4rem;
|
height: 4rem;
|
||||||
}
|
}
|
||||||
|
.avaicon-2x-large {
|
||||||
|
width: 6rem;
|
||||||
|
height: 6rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import { ColorDefinition } from '@hcengineering/ui'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
@ -29,17 +31,17 @@ export type GravatarPlaceholderType =
|
|||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export const AVATAR_COLORS = [
|
export const AVATAR_COLORS: ColorDefinition[] = [
|
||||||
'#4674ca', // blue
|
{ name: 'blue', color: '#4674ca' }, // blue
|
||||||
'#315cac', // blue_dark
|
{ name: 'blue_dark', color: '#315cac' }, // blue_dark
|
||||||
'#57be8c', // green
|
{ name: 'green', color: '#57be8c' }, // green
|
||||||
'#3fa372', // green_dark
|
{ name: 'green_dark', color: '#3fa372' }, // green_dark
|
||||||
'#f9a66d', // yellow_orange
|
{ name: 'yellow_orange', color: '#f9a66d' }, // yellow_orange
|
||||||
'#ec5e44', // red
|
{ name: 'red', color: '#ec5e44' }, // red
|
||||||
'#e63717', // red_dark
|
{ name: 'red_dark', color: '#e63717' }, // red_dark
|
||||||
'#f868bc', // pink
|
{ name: 'pink', color: '#f868bc' }, // pink
|
||||||
'#6c5fc7', // purple
|
{ name: 'purple', color: '#6c5fc7' }, // purple
|
||||||
'#4e3fb4', // purple_dark
|
{ name: 'purple_dark', color: '#4e3fb4' }, // purple_dark
|
||||||
'#57b1be', // teal
|
{ name: 'teal', color: '#57b1be' }, // teal
|
||||||
'#847a8c' // gray
|
{ name: 'gray', color: '#847a8c' } // gray
|
||||||
]
|
]
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import { AttachedData, Class, Client, Doc, FindResult, Ref, Hierarchy } from '@hcengineering/core'
|
import { AttachedData, Class, Client, Doc, FindResult, Ref, Hierarchy } from '@hcengineering/core'
|
||||||
import { IconSize } from '@hcengineering/ui'
|
import { IconSize, ColorDefinition } from '@hcengineering/ui'
|
||||||
import { MD5 } from 'crypto-js'
|
import { MD5 } from 'crypto-js'
|
||||||
import { Channel, Contact, contactPlugin, Person } from '.'
|
import { Channel, Contact, contactPlugin, Person } from '.'
|
||||||
import { AVATAR_COLORS, GravatarPlaceholderType } from './types'
|
import { AVATAR_COLORS, GravatarPlaceholderType } from './types'
|
||||||
@ -22,14 +22,29 @@ import { AVATAR_COLORS, GravatarPlaceholderType } from './types'
|
|||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export function getAvatarColorForId (id: string): string {
|
export function getAvatarColorForId (id: string | null | undefined): string {
|
||||||
|
if (id == null) return AVATAR_COLORS[0].color
|
||||||
let hash = 0
|
let hash = 0
|
||||||
|
|
||||||
for (let i = 0; i < id.length; i++) {
|
for (let i = 0; i < id.length; i++) {
|
||||||
hash += id.charCodeAt(i)
|
hash += id.charCodeAt(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
return AVATAR_COLORS[hash % AVATAR_COLORS.length]
|
return AVATAR_COLORS[hash % AVATAR_COLORS.length].color
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function getAvatarColors (): readonly ColorDefinition[] {
|
||||||
|
return AVATAR_COLORS
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function getAvatarColorName (color: string): string {
|
||||||
|
return AVATAR_COLORS.find((col) => col.color === color)?.name ?? AVATAR_COLORS[0].name
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -126,7 +126,7 @@
|
|||||||
<div class="mr-2">
|
<div class="mr-2">
|
||||||
<Button icon={IconAdd} kind={'list'} on:click={createChild} />
|
<Button icon={IconAdd} kind={'list'} on:click={createChild} />
|
||||||
</div>
|
</div>
|
||||||
<Avatar size={'medium'} avatar={value.avatar} icon={hr.icon.Department} />
|
<Avatar size={'medium'} avatar={value.avatar} icon={hr.icon.Department} name={value.name} />
|
||||||
<div class="flex-row ml-2 mr-4">
|
<div class="flex-row ml-2 mr-4">
|
||||||
<div class="fs-title">
|
<div class="fs-title">
|
||||||
{value.name}
|
{value.name}
|
||||||
|
@ -74,7 +74,6 @@
|
|||||||
{#key object}
|
{#key object}
|
||||||
<EditableAvatar
|
<EditableAvatar
|
||||||
avatar={object.avatar}
|
avatar={object.avatar}
|
||||||
id={object._id}
|
|
||||||
size={'x-large'}
|
size={'x-large'}
|
||||||
icon={hr.icon.Department}
|
icon={hr.icon.Department}
|
||||||
bind:this={avatarEditor}
|
bind:this={avatarEditor}
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
<DocNavLink object={value}>
|
<DocNavLink object={value}>
|
||||||
<div class="flex-row-center">
|
<div class="flex-row-center">
|
||||||
<div class="member-icon mr-2">
|
<div class="member-icon mr-2">
|
||||||
<Avatar size={'medium'} avatar={value.avatar} />
|
<Avatar size={'medium'} avatar={value.avatar} name={value.name} />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-col">
|
<div class="flex-col">
|
||||||
<div class="member-title fs-title">
|
<div class="member-title fs-title">
|
||||||
|
@ -189,8 +189,8 @@
|
|||||||
<div class="ml-4 flex">
|
<div class="ml-4 flex">
|
||||||
<EditableAvatar
|
<EditableAvatar
|
||||||
avatar={object.avatar}
|
avatar={object.avatar}
|
||||||
id={customerId}
|
|
||||||
size={'large'}
|
size={'large'}
|
||||||
|
name={object.name}
|
||||||
bind:this={avatarEditor}
|
bind:this={avatarEditor}
|
||||||
bind:direct={avatar}
|
bind:direct={avatar}
|
||||||
/>
|
/>
|
||||||
|
@ -184,7 +184,7 @@
|
|||||||
<div class="flex-between header bottom-divider">
|
<div class="flex-between header bottom-divider">
|
||||||
<div class="flex-row-center">
|
<div class="flex-row-center">
|
||||||
{#if employee}
|
{#if employee}
|
||||||
<Avatar size="smaller" avatar={employee.avatar} />
|
<Avatar size={'smaller'} avatar={employee.avatar} name={employee.name} />
|
||||||
<span class="font-medium mx-2">{getName(client.getHierarchy(), employee)}</span>
|
<span class="font-medium mx-2">{getName(client.getHierarchy(), employee)}</span>
|
||||||
{/if}
|
{/if}
|
||||||
{#if newTxes > 0}
|
{#if newTxes > 0}
|
||||||
|
@ -88,7 +88,7 @@
|
|||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div class="inbox-activity__content shrink flex-grow clear-mins" class:read={newTxes === 0}>
|
<div class="inbox-activity__content shrink flex-grow clear-mins" class:read={newTxes === 0}>
|
||||||
<div class="flex-row-center gap-2">
|
<div class="flex-row-center gap-2">
|
||||||
<Avatar avatar={employee?.avatar} size="small" />
|
<Avatar avatar={employee?.avatar} size={'small'} name={employee?.name} />
|
||||||
{#if employee}
|
{#if employee}
|
||||||
<span class="font-medium">{getName(client.getHierarchy(), employee)}</span>
|
<span class="font-medium">{getName(client.getHierarchy(), employee)}</span>
|
||||||
{:else}
|
{:else}
|
||||||
|
@ -118,7 +118,7 @@
|
|||||||
<div class="msgactivity-container">
|
<div class="msgactivity-container">
|
||||||
{#if withAvatar}
|
{#if withAvatar}
|
||||||
<div class="msgactivity-avatar">
|
<div class="msgactivity-avatar">
|
||||||
<Avatar avatar={employee?.avatar} size="x-small" />
|
<Avatar avatar={employee?.avatar} size={'x-small'} name={employee?.name} />
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="msgactivity-icon">
|
<div class="msgactivity-icon">
|
||||||
|
@ -46,7 +46,7 @@
|
|||||||
|
|
||||||
<div class="antiContactCard">
|
<div class="antiContactCard">
|
||||||
<div class="label uppercase"><Label label={recruit.string.Talent} /></div>
|
<div class="label uppercase"><Label label={recruit.string.Talent} /></div>
|
||||||
<Avatar avatar={candidate?.avatar} size={'large'} />
|
<Avatar avatar={candidate?.avatar} size={'large'} name={candidate?.name} />
|
||||||
{#if candidate}
|
{#if candidate}
|
||||||
<DocNavLink object={candidate} {disabled}>
|
<DocNavLink object={candidate} {disabled}>
|
||||||
<div class="name lines-limit-2">
|
<div class="name lines-limit-2">
|
||||||
|
@ -562,8 +562,8 @@
|
|||||||
bind:this={avatarEditor}
|
bind:this={avatarEditor}
|
||||||
bind:direct={object.avatar}
|
bind:direct={object.avatar}
|
||||||
avatar={undefined}
|
avatar={undefined}
|
||||||
id={object._id}
|
|
||||||
size={'large'}
|
size={'large'}
|
||||||
|
name={combineName(object?.firstName?.trim() ?? '', object?.lastName?.trim() ?? '')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -61,7 +61,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
<div class="flex-between mb-1">
|
<div class="flex-between mb-1">
|
||||||
<div class="flex-row-center">
|
<div class="flex-row-center">
|
||||||
<Avatar avatar={object.$lookup?.attachedTo?.avatar} size={'medium'} />
|
<Avatar avatar={object.$lookup?.attachedTo?.avatar} size={'medium'} name={object.$lookup?.attachedTo?.name} />
|
||||||
<div class="flex-grow flex-col min-w-0 ml-2">
|
<div class="flex-grow flex-col min-w-0 ml-2">
|
||||||
<div class="fs-title over-underline lines-limit-2">
|
<div class="fs-title over-underline lines-limit-2">
|
||||||
{object.$lookup?.attachedTo ? getName(client.getHierarchy(), object.$lookup.attachedTo) : ''}
|
{object.$lookup?.attachedTo ? getName(client.getHierarchy(), object.$lookup.attachedTo) : ''}
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
{#if value}
|
{#if value}
|
||||||
<div class="flex persons">
|
<div class="flex persons">
|
||||||
{#each persons as p}
|
{#each persons as p}
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div
|
<div
|
||||||
class="flex-presenter"
|
class="flex-presenter"
|
||||||
class:inline-presenter={inline}
|
class:inline-presenter={inline}
|
||||||
@ -44,7 +45,7 @@
|
|||||||
on:click={() => onClick(p)}
|
on:click={() => onClick(p)}
|
||||||
>
|
>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<Avatar size={'x-small'} avatar={p.avatar} />
|
<Avatar size={'x-small'} avatar={p.avatar} name={p.name} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -95,8 +95,8 @@
|
|||||||
<EditableAvatar
|
<EditableAvatar
|
||||||
avatar={employee.avatar}
|
avatar={employee.avatar}
|
||||||
email={account.email}
|
email={account.email}
|
||||||
id={employee._id}
|
|
||||||
size={'x-large'}
|
size={'x-large'}
|
||||||
|
name={employee.name}
|
||||||
bind:this={avatarEditor}
|
bind:this={avatarEditor}
|
||||||
on:done={onAvatarDone}
|
on:done={onAvatarDone}
|
||||||
/>
|
/>
|
||||||
|
@ -233,7 +233,7 @@
|
|||||||
class="ml-2"
|
class="ml-2"
|
||||||
use:tooltip={{ label: getEmbeddedLabel(getContactName(client.getHierarchy(), participant)) }}
|
use:tooltip={{ label: getEmbeddedLabel(getContactName(client.getHierarchy(), participant)) }}
|
||||||
>
|
>
|
||||||
<Avatar size="small" avatar={participant.avatar} />
|
<Avatar size={'small'} avatar={participant.avatar} name={participant.name} />
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
<div class="root">
|
<div class="root">
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<Avatar avatar={lead.avatar} size="medium" />
|
<Avatar avatar={lead.avatar} size={'medium'} name={lead.name} />
|
||||||
</div>
|
</div>
|
||||||
<div class="textContainer">
|
<div class="textContainer">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
|
@ -38,7 +38,6 @@
|
|||||||
>
|
>
|
||||||
<div class="flex-center ml-2">
|
<div class="flex-center ml-2">
|
||||||
<div class="flex-no-shrink circles-mark" class:isDraggable><IconCircles size={'small'} /></div>
|
<div class="flex-no-shrink circles-mark" class:isDraggable><IconCircles size={'small'} /></div>
|
||||||
!!!
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="root flex flex-between items-center w-full p-2">
|
<div class="root flex flex-between items-center w-full p-2">
|
||||||
|
@ -163,7 +163,10 @@
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{#if employee}
|
{#if employee}
|
||||||
<Component is={contact.component.Avatar} props={{ avatar: employee.avatar, size: 'medium' }} />
|
<Component
|
||||||
|
is={contact.component.Avatar}
|
||||||
|
props={{ avatar: employee.avatar, size: 'medium', name: employee.name }}
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="ml-2 flex-col">
|
<div class="ml-2 flex-col">
|
||||||
{#if account}
|
{#if account}
|
||||||
|
@ -707,7 +707,10 @@
|
|||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
on:click|stopPropagation={() => showPopup(AccountPopup, {}, popupPosition)}
|
on:click|stopPropagation={() => showPopup(AccountPopup, {}, popupPosition)}
|
||||||
>
|
>
|
||||||
<Component is={contact.component.Avatar} props={{ avatar: employee?.avatar, size: 'small' }} />
|
<Component
|
||||||
|
is={contact.component.Avatar}
|
||||||
|
props={{ avatar: employee?.avatar, size: 'small', name: employee?.name }}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user