diff --git a/packages/presentation/src/components/DocPopup.svelte b/packages/presentation/src/components/DocPopup.svelte index 5e0275fbdc..e67e558e84 100644 --- a/packages/presentation/src/components/DocPopup.svelte +++ b/packages/presentation/src/components/DocPopup.svelte @@ -58,7 +58,8 @@ export let disallowDeselect: Ref[] | undefined = undefined export let created: Doc[] = [] export let embedded: boolean = false - export let loading = false + export let loading: boolean = false + export let type: 'text' | 'object' = 'text' let search: string = '' @@ -220,9 +221,13 @@ void handleSelection(undefined, objects, item) }} > - + {#if type === 'text'} + + + + {:else} - + {/if} {#if (allowDeselect && selected) || multiSelect || selected}
{#if obj._id === selected || selectedElements.has(obj._id)} diff --git a/packages/presentation/src/components/ObjectPopup.svelte b/packages/presentation/src/components/ObjectPopup.svelte index f1654f6900..7adf75858c 100644 --- a/packages/presentation/src/components/ObjectPopup.svelte +++ b/packages/presentation/src/components/ObjectPopup.svelte @@ -54,7 +54,8 @@ export let readonly = false export let disallowDeselect: Ref[] | undefined = undefined export let embedded: boolean = false - export let loading = false + export let loading: boolean = false + export let type: 'text' | 'object' = 'text' export let filter: (it: Doc) => boolean = () => { return true @@ -119,6 +120,7 @@ {disallowDeselect} {embedded} {loading} + {type} on:update on:close on:changeContent diff --git a/packages/theme/styles/components.scss b/packages/theme/styles/components.scss index ce873baedc..80e87f2f5c 100644 --- a/packages/theme/styles/components.scss +++ b/packages/theme/styles/components.scss @@ -107,6 +107,208 @@ } } +/* Avatar */ +.hulyAvatar-container { + position: relative; + display: flex; + justify-content: center; + align-items: center; + min-width: 0; + min-height: 0; + flex-shrink: 0; + aspect-ratio: 1; + background-color: var(--theme-button-default); + pointer-events: none; + + &.withStatus { + mask-repeat: no-repeat; + mask-size: cover; + mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M10,13.5c0-1.9,1.6-3.5,3.5-3.5c1,0,1.9,0.4,2.5,1.1V0H0v16h11.1C10.4,15.4,10,14.5,10,13.5z' /%3E%3C/svg%3E"); + } + + &.circle, + &.circle img.ava-image { border-radius: 50%; } + &.roundedRect, + &.roundedRect img.ava-image { border-radius: 20%; } + &.standby { + opacity: .5; + transition: opacity .5s ease-in-out; + pointer-events: all; + + &:hover, + &.standbyOn { opacity: 1; } + &:hover { transition-duration: .1s; } + } + + &.no-img { + color: var(--primary-button-color); + border-color: transparent; + } + &.bordered { + color: var(--theme-dark-color); + border: 1px solid var(--theme-button-border); + } + &.border { + border: 1px solid var(--theme-bg-color); + outline: 2px solid var(--border-color); + + &.hulyAvatarSize-xx-small, + &.hulyAvatarSize-inline, + &.hulyAvatarSize-tiny, + &.hulyAvatarSize-card, + &.hulyAvatarSize-x-small { outline-width: 1px; } + &.hulyAvatarSize-large, + &.hulyAvatarSize-x-large, + &.hulyAvatarSize-2x-large { border-width: 2px; } + } + img { object-fit: cover; } + .icon, + .ava-text::after { + position: absolute; + top: 50%; + left: 50%; + } + .icon { + width: 100%; + height: 100%; + color: inherit; + transform-origin: center; + transform: translate(-50%, -50%) scale(.6); + } + .ava-text { + font-weight: 500; + letter-spacing: -.05em; + + &::after { + content: attr(data-name); + transform: translate(-50%, -50%); + } + } +} + +/* Avatar sizes */ +.hulyAvatarSize-xx-small { + width: .75rem; // 12 - 10 + .small-font & { width: 10px; } + + .ava-text { font-size: .375rem; } +} +.hulyAvatarSize-inline { + width: .875rem; // 14 - 12 + .small-font & { width: 12px; } + + .ava-text { font-size: .525rem; } +} +.hulyAvatarSize-tiny { + width: 1.125rem; // 18 - 16 + .small-font & { width: 16px; } + + .ava-text { font-size: .625rem; } +} +.hulyAvatarSize-card { + width: 1.25rem; // 20 - 18 + .small-font & { width: 18px; } + + .ava-text { font-size: .75rem; } +} +.hulyAvatarSize-x-small { + width: 1.5rem; // 24 - 22 + .small-font & { width: 22px; } + + .ava-text { font-size: .875rem; } +} +.hulyAvatarSize-smaller { + width: 1.75rem; // 28 - 25 + .small-font & { width: 25px; } + + .ava-text { font-size: 1rem; } +} +.hulyAvatarSize-small { + width: 2rem; // 32 - 28 + + .ava-text { font-size: 1.125rem; } +} +.hulyAvatarSize-medium { + width: 2.5rem; // 40 - 35 + + .ava-text { font-size: 1.375rem; } +} +.hulyAvatarSize-large { + width: 4.5rem; // 72 - 63 + + .ava-text { font-size: 2.75rem; } +} +.hulyAvatarSize-x-large { + width: 7.5rem; // 120 - 105 + + .ava-text { font-size: 4.5rem; } +} +.hulyAvatarSize-2x-large { + width: 10rem; // 160 - 140 + + .ava-text { font-size: 6rem; } +} +.hulyAvatarSize-full { + width: 100%; + + .ava-text { font-size: inherit; } +} + +/* Avatar status marker */ +.hulyAvatar-statusMarker { + position: absolute; + right: -4%; + bottom: -4%; + width: 39%; + aspect-ratio: 1; + border-radius: 50%; + + &.xx-small, + &.inline, + &.tiny, + &.card, + &.x-small, + &.smaller, + &.small, + &.medium { + right: 0; + bottom: 0; + } + .small-font &.xx-small { width: 3px; } + &.xx-small, + &.inline, + .small-font &.inline { width: 4px; } + .small-font &.tiny { width: 5px; } + &.tiny, + &.card, + .small-font &.card { width: 6px; } + .small-font &.x-small { width: 7px; } + &.x-small, + .small-font &.smaller { width: 8px; } + &.smaller, + .small-font &.small { width: 9px; } + &.small { width: 10px; } + .small-font &.medium { width: 11px; } + &.medium { width: 13px; } + &.large { + right: -.125rem; + bottom: -.125rem; + width: 36.5%; + + .small-font & { + right: -2px; + bottom: -2px; + width: 37%; + } + } + &.online { background-color: var(--global-online-color); } + &.offline { + border: 1px solid var(--global-offline-color); + + &:not(.xx-small, .inline, .tiny, .card, .x-small, .smaller, .small, .medium) { border-width: 2px; } + } +} + /* Header */ .hulyHeader-container { display: flex; diff --git a/plugins/activity-resources/src/components/BasePreview.svelte b/plugins/activity-resources/src/components/BasePreview.svelte index 9f949569a1..259dffe337 100644 --- a/plugins/activity-resources/src/components/BasePreview.svelte +++ b/plugins/activity-resources/src/components/BasePreview.svelte @@ -15,7 +15,7 @@ @@ -115,7 +118,7 @@ {#if headerObject} {:else if person} - + {:else} {/if} diff --git a/plugins/chunter-resources/src/components/ChannelHeader.svelte b/plugins/chunter-resources/src/components/ChannelHeader.svelte index 07626d6dc2..f2edfffd7c 100644 --- a/plugins/chunter-resources/src/components/ChannelHeader.svelte +++ b/plugins/chunter-resources/src/components/ChannelHeader.svelte @@ -65,7 +65,7 @@ bind:filters {object} icon={getObjectIcon(_class)} - iconProps={{ value: object, showStatus: true, background: 'var(--theme-comp-header-color)' }} + iconProps={{ value: object, showStatus: true }} label={title} intlLabel={chunter.string.Channel} {description} diff --git a/plugins/chunter-resources/src/components/ChannelMembers.svelte b/plugins/chunter-resources/src/components/ChannelMembers.svelte index 5796ec86ed..c8205427aa 100644 --- a/plugins/chunter-resources/src/components/ChannelMembers.svelte +++ b/plugins/chunter-resources/src/components/ChannelMembers.svelte @@ -50,7 +50,7 @@ {#each persons as person, index}
- + {#if !disableRemoveFor.includes(person._id)}
{/if} diff --git a/plugins/chunter-resources/src/components/chat/navigator/ChatNavSection.svelte b/plugins/chunter-resources/src/components/chat/navigator/ChatNavSection.svelte index 6542b69a12..a656412b81 100644 --- a/plugins/chunter-resources/src/components/chat/navigator/ChatNavSection.svelte +++ b/plugins/chunter-resources/src/components/chat/navigator/ChatNavSection.svelte @@ -83,7 +83,7 @@ title: (await getChannelName(object._id, object._class, object)) ?? (await translate(titleIntl, {})), description: isDocChat && !isPerson ? await getDocTitle(client, object._id, object._class, object) : undefined, icon: icon ?? getObjectIcon(_class), - iconProps: { showStatus: true, background: 'var(--global-surface-01-BackgroundColor)' }, + iconProps: { showStatus: true }, iconSize, withIconBackground: !isDirect && !isPerson, isSecondary: isDocChat && !isPerson diff --git a/plugins/contact-resources/src/components/AssigneeBox.svelte b/plugins/contact-resources/src/components/AssigneeBox.svelte index ab6e24a46d..a292f38472 100644 --- a/plugins/contact-resources/src/components/AssigneeBox.svelte +++ b/plugins/contact-resources/src/components/AssigneeBox.svelte @@ -161,7 +161,7 @@ > diff --git a/plugins/contact-resources/src/components/Avatar.svelte b/plugins/contact-resources/src/components/Avatar.svelte index d561452494..efe2f04b32 100644 --- a/plugins/contact-resources/src/components/Avatar.svelte +++ b/plugins/contact-resources/src/components/Avatar.svelte @@ -37,19 +37,16 @@ import { AnySvelteComponent, ColorDefinition, - Icon, IconSize, getPlatformAvatarColorByName, getPlatformAvatarColorForTextDef, getPlatformColor, - resizeObserver, themeStore } from '@hcengineering/ui' import { onMount } from 'svelte' import { Account } from '@hcengineering/core' - - import AvatarIcon from './icons/Avatar.svelte' - import UserStatus from './UserStatus.svelte' + import AvatarInstance from './AvatarInstance.svelte' + import { loadUsersStatus, statusByUserStore } from '../utils' export let avatar: string | null | undefined = undefined export let name: string | null | undefined = undefined @@ -59,32 +56,18 @@ export let variant: 'circle' | 'roundedRect' | 'none' = 'roundedRect' export let borderColor: number | undefined = undefined export let standby: boolean = false - export let showStatus = false + export let showStatus: boolean = true export let account: Ref | undefined = undefined - export let background: string | undefined = undefined export function pulse (): void { - if (element) element.animate(pulsating, { duration: 150, easing: 'ease-out' }) - if (standby) { - standbyMode = false - if (timer) clearTimeout(timer) - timer = setTimeout(() => { - standbyMode = true - }, 2000) - } + avatarInst.pulse() } let url: string[] | undefined let avatarProvider: AvatarProvider | undefined let color: ColorDefinition | undefined = undefined - let standbyMode: boolean = standby - let timer: any | undefined = undefined - let fontSize: number = 16 let element: HTMLElement - const pulsating: Keyframe[] = [ - { boxShadow: '0 0 .125rem 0 var(--theme-bg-color), 0 0 0 .125rem var(--border-color)' }, - { boxShadow: '0 0 .375rem .375rem var(--theme-bg-color), 0 0 0 .25rem var(--border-color)' } - ] + let avatarInst: AvatarInstance $: displayName = getDisplayName(name) $: bColor = borderColor !== undefined ? getPlatformColor(borderColor, $themeStore.dark) : undefined @@ -138,264 +121,48 @@ $: srcset = url?.slice(1)?.join(', ') onMount(() => { - if (size === 'full' && !url && name && displayName && displayName !== '' && element) { - fontSize = element.clientWidth * 0.6 - } + loadUsersStatus() }) - function getStatusSize (avaSize: IconSize): 'small' | 'medium' { - switch (avaSize) { - case 'inline': - case 'tiny': - case 'card': - case 'x-small': - return 'small' - case 'small': - case 'medium': - case 'large': - case 'x-large': - case '2x-large': - return 'medium' - default: - return 'small' - } - } + $: userStatus = account ? $statusByUserStore.get(account) : undefined -{#if size === 'full' && !url && name && displayName && displayName !== ''} -
{ - fontSize = element.clientWidth * 0.6 - }} - > -
+ + {#if showStatus && account} +
+ {/if}
{:else} -
- {#if url} - {''} - {:else if name && displayName && displayName !== ''} -
- {:else} -
- -
- {/if} - {#if showStatus && account} - - - - {/if} -
+ {/if} - - diff --git a/plugins/contact-resources/src/components/AvatarInstance.svelte b/plugins/contact-resources/src/components/AvatarInstance.svelte new file mode 100644 index 0000000000..1f64e13248 --- /dev/null +++ b/plugins/contact-resources/src/components/AvatarInstance.svelte @@ -0,0 +1,102 @@ + + + +{#if size === 'full' && !url && displayName && displayName !== ''} +
{ + fontSize = element.clientWidth * 0.6 + }} + > +
+
+{:else} +
+ {#if url} + {''} + {:else if displayName && displayName !== ''} +
+ {:else} +
+ +
+ {/if} +
+{/if} diff --git a/plugins/contact-resources/src/components/CombineAvatars.svelte b/plugins/contact-resources/src/components/CombineAvatars.svelte index 84de32f572..2c1769ea2d 100644 --- a/plugins/contact-resources/src/components/CombineAvatars.svelte +++ b/plugins/contact-resources/src/components/CombineAvatars.svelte @@ -57,7 +57,7 @@ {/if} {#each persons as person, i}
- +
{/each}
diff --git a/plugins/contact-resources/src/components/EmployeePresenter.svelte b/plugins/contact-resources/src/components/EmployeePresenter.svelte index 1f11842117..e339bdcb39 100644 --- a/plugins/contact-resources/src/components/EmployeePresenter.svelte +++ b/plugins/contact-resources/src/components/EmployeePresenter.svelte @@ -21,8 +21,8 @@ export let defaultName: IntlString | undefined = ui.string.NotSelected // export let element: HTMLElement | undefined = undefined export let noUnderline: boolean = false - export let compact = false - export let showStatus = true + export let compact: boolean = false + export let showStatus: boolean = true $: employeeValue = typeof value === 'string' ? $personByIdStore.get(value) : value @@ -45,6 +45,7 @@ {defaultName} {noUnderline} {compact} + {showStatus} statusLabel={!active && shouldShowName && showStatus ? contact.string.Inactive : undefined} on:accent-color /> diff --git a/plugins/contact-resources/src/components/PersonContent.svelte b/plugins/contact-resources/src/components/PersonContent.svelte index 05e1e9694b..55e4fed58a 100644 --- a/plugins/contact-resources/src/components/PersonContent.svelte +++ b/plugins/contact-resources/src/components/PersonContent.svelte @@ -47,7 +47,8 @@ export let colorInherit: boolean = false export let accent: boolean = false export let maxWidth = '' - export let compact = false + export let compact: boolean = false + export let showStatus: boolean = false export let type: ObjectPresenterType = 'link' const client = getClient() @@ -91,6 +92,7 @@ {accent} {type} {maxWidth} + {showStatus} /> {/if} {#if shouldShowName} diff --git a/plugins/contact-resources/src/components/PersonPresenter.svelte b/plugins/contact-resources/src/components/PersonPresenter.svelte index 7fe56f859b..4133f85d1c 100644 --- a/plugins/contact-resources/src/components/PersonPresenter.svelte +++ b/plugins/contact-resources/src/components/PersonPresenter.svelte @@ -42,6 +42,7 @@ export let maxWidth = '' export let compact = false export let type: ObjectPresenterType = 'link' + export let showStatus: boolean = true const client = getClient() $: personValue = typeof value === 'string' ? $personByIdStore.get(value) : value @@ -97,6 +98,7 @@ {maxWidth} {compact} {type} + {showStatus} on:accent-color /> {/if} diff --git a/plugins/contact-resources/src/components/SelectUsersPopup.svelte b/plugins/contact-resources/src/components/SelectUsersPopup.svelte index ff1f3fe5b8..ac88587611 100644 --- a/plugins/contact-resources/src/components/SelectUsersPopup.svelte +++ b/plugins/contact-resources/src/components/SelectUsersPopup.svelte @@ -32,7 +32,7 @@ export let selected: Ref[] = [] export let skipCurrentAccount = false export let disableDeselectFor: Ref[] = [] - export let showStatus = false + export let showStatus = true const dispatch = createEventDispatcher() @@ -90,7 +90,6 @@ {showStatus} {disableDeselectFor} {skipCurrentAccount} - background="var(--theme-popup-color)" on:select={handleSelectionChanged} /> diff --git a/plugins/contact-resources/src/components/UserBox.svelte b/plugins/contact-resources/src/components/UserBox.svelte index 6a5f2b7e33..6874531b61 100644 --- a/plugins/contact-resources/src/components/UserBox.svelte +++ b/plugins/contact-resources/src/components/UserBox.svelte @@ -145,7 +145,7 @@ >
{getName(hierarchy, person)}
diff --git a/plugins/contact-resources/src/components/UserInfo.svelte b/plugins/contact-resources/src/components/UserInfo.svelte index 690dd95f4e..e9821037b4 100644 --- a/plugins/contact-resources/src/components/UserInfo.svelte +++ b/plugins/contact-resources/src/components/UserInfo.svelte @@ -15,26 +15,32 @@
- +
{#if subtitle}
{subtitle}
{/if} -
{getName(client.getHierarchy(), value)}
+
{getName(client.getHierarchy(), value)}
diff --git a/plugins/contact-resources/src/components/UserStatus.svelte b/plugins/contact-resources/src/components/UserStatus.svelte deleted file mode 100644 index fc423c1158..0000000000 --- a/plugins/contact-resources/src/components/UserStatus.svelte +++ /dev/null @@ -1,79 +0,0 @@ - - - -
-
-
- - diff --git a/plugins/contact-resources/src/components/UsersList.svelte b/plugins/contact-resources/src/components/UsersList.svelte index dbbaca8854..ea446af8f4 100644 --- a/plugins/contact-resources/src/components/UsersList.svelte +++ b/plugins/contact-resources/src/components/UsersList.svelte @@ -30,8 +30,7 @@ export let selected: Ref[] = [] export let skipCurrentAccount = false export let disableDeselectFor: Ref[] = [] - export let showStatus = false - export let background: string | undefined = undefined + export let showStatus = true const dispatch = createEventDispatcher() const query = createQuery() @@ -116,7 +115,7 @@ handleSelection(persons, index) }} > - + -
+
diff --git a/plugins/workbench-resources/src/components/Workbench.svelte b/plugins/workbench-resources/src/components/Workbench.svelte index 3925a049e7..554a520dc8 100644 --- a/plugins/workbench-resources/src/components/Workbench.svelte +++ b/plugins/workbench-resources/src/components/Workbench.svelte @@ -14,7 +14,7 @@ -->