diff --git a/packages/core/src/status.ts b/packages/core/src/status.ts index 2cb49793bf..1bd08a1e62 100644 --- a/packages/core/src/status.ts +++ b/packages/core/src/status.ts @@ -23,7 +23,7 @@ export interface StatusCategory extends Doc { ofAttribute: Ref> icon: Asset label: IntlString - color: number + color: number | number[] defaultStatusName: string order: number // category order } @@ -47,7 +47,7 @@ export interface Status extends Doc { name: string // Optional color - color?: number + color?: number | number[] // Optional description description?: string } diff --git a/packages/presentation/src/components/SpaceSelect.svelte b/packages/presentation/src/components/SpaceSelect.svelte index c76b9b5640..670fb3d442 100644 --- a/packages/presentation/src/components/SpaceSelect.svelte +++ b/packages/presentation/src/components/SpaceSelect.svelte @@ -144,7 +144,7 @@ : { fill: selected?.color !== undefined - ? getPlatformColorDef(selected?.color, $themeStore.dark).icon + ? getPlatformColorDef(selected?.color ?? 0, $themeStore.dark).icon : getPlatformColorForTextDef(selected?.name ?? '', $themeStore.dark).icon }} {size} diff --git a/packages/theme/styles/_layouts.scss b/packages/theme/styles/_layouts.scss index fb6e9bbec2..39f34d0b75 100644 --- a/packages/theme/styles/_layouts.scss +++ b/packages/theme/styles/_layouts.scss @@ -753,6 +753,7 @@ input.search { .min-h-16 { min-height: 4rem; } .min-h-30 { min-height: 7.5rem; } .min-h-60 { min-height: 15rem; } +.min-h-full { min-height: 100%; } .max-w-0 { max-width: 0; } .max-w-2 { max-width: .5rem; } .max-w-4 { max-width: 1rem; } diff --git a/packages/theme/styles/button.scss b/packages/theme/styles/button.scss index 5c5b8c2734..ea63ab5052 100644 --- a/packages/theme/styles/button.scss +++ b/packages/theme/styles/button.scss @@ -13,8 +13,8 @@ display: flex; align-items: center; justify-content: center; - width: var(--spacing-2_5); - height: var(--spacing-2_5); + min-width: var(--spacing-2_5); + min-height: var(--spacing-2_5); } span { white-space: nowrap; } diff --git a/packages/theme/styles/popups.scss b/packages/theme/styles/popups.scss index c26ce2fdb4..9527798730 100644 --- a/packages/theme/styles/popups.scss +++ b/packages/theme/styles/popups.scss @@ -101,7 +101,7 @@ } } - &:not(.disabled, :disabled):hover { + &:not(.disabled, :disabled, .noHover):hover { background-color: var(--theme-popup-hover); // var(--global-popover-hover-BackgroundColor); cursor: pointer; } @@ -110,6 +110,9 @@ cursor: default; .hulyPopup-row__label { opacity: .5; } } + &.noHover { + cursor: default; + } } .hulyPopup-group { display: flex; diff --git a/packages/ui/src/colors.ts b/packages/ui/src/colors.ts index 2fe1c6984f..e8fe303a4f 100644 --- a/packages/ui/src/colors.ts +++ b/packages/ui/src/colors.ts @@ -240,17 +240,17 @@ export const CadetGreyColor = '#95A2B3' /** * @public */ -export function getPlatformColor (hash: number, darkTheme: boolean): string { +export function getPlatformColor (hash: number | number[], darkTheme: boolean): string { const palette = darkTheme ? darkPalette : whitePalette - return (palette[Math.abs(hash) % palette.length] ?? palette[0]).color + return (palette[Math.abs(Array.isArray(hash) ? hash[0] : hash) % palette.length] ?? palette[0]).color } /** * @public */ -export function getPlatformColorDef (hash: number, darkTheme: boolean): ColorDefinition { +export function getPlatformColorDef (hash: number | number[], darkTheme: boolean): ColorDefinition { const palette = darkTheme ? darkPalette : whitePalette - return palette[Math.abs(hash) % palette.length] ?? palette[0] + return palette[Math.abs(Array.isArray(hash) ? hash[0] : hash) % palette.length] ?? palette[0] } /** diff --git a/packages/ui/src/components/ButtonBase.svelte b/packages/ui/src/components/ButtonBase.svelte index 16ea9743d7..a7e7527765 100644 --- a/packages/ui/src/components/ButtonBase.svelte +++ b/packages/ui/src/components/ButtonBase.svelte @@ -49,7 +49,7 @@ $: if (iconSize) { actualIconSize = iconSize - } else if (type === 'type-button' && !hasMenu) { + } else if (type === 'type-button' && !hasMenu && iconProps?.size === undefined) { actualIconSize = 'medium' } $: iconOnly = title === undefined && label === undefined && $$slots.default === undefined && icon !== undefined diff --git a/packages/ui/src/components/IconWithEmoji.svelte b/packages/ui/src/components/IconWithEmoji.svelte index a1db3e9436..5043388a4b 100644 --- a/packages/ui/src/components/IconWithEmoji.svelte +++ b/packages/ui/src/components/IconWithEmoji.svelte @@ -16,12 +16,12 @@ import { IconSize } from '../types' import { fromCodePoint } from '../utils' - export let icon: number + export let icon: number | number[] export let size: IconSize let value: string = '' $: try { - value = fromCodePoint(icon) + value = Array.isArray(icon) ? fromCodePoint(...icon) : fromCodePoint(icon) } catch (err) {} @@ -49,7 +49,7 @@ .emoji-small { width: 1rem; height: 1rem; - font-size: 0.8125rem; + font-size: 1rem; } .emoji-medium { width: 1.25rem; diff --git a/packages/ui/src/components/TabsControl.svelte b/packages/ui/src/components/TabsControl.svelte index e87ff36ddf..04a5285ed1 100644 --- a/packages/ui/src/components/TabsControl.svelte +++ b/packages/ui/src/components/TabsControl.svelte @@ -21,9 +21,10 @@ export let padding: string | undefined = undefined export let noMargin: boolean = false export let size: 'small' | 'medium' | 'large' = 'medium' + export let gap: 'small' | 'medium' | 'large' = 'large' -
+
{#each model as tab, i} @@ -61,6 +62,15 @@ min-height: 0; border-bottom: 1px solid var(--theme-divider-color); + &.gap-small { + gap: 0.5rem; + } + &.gap-medium { + gap: 1.5rem; + } + &.gap-large { + gap: 2.5rem; + } &:not(.noMargin) { margin-bottom: 0.5rem; } @@ -95,9 +105,6 @@ color: var(--theme-content-color); } } - .tab + .tab { - margin-left: 2.5rem; - } .grow { min-width: 2.5rem; flex-grow: 1; diff --git a/packages/ui/src/components/emoji/EmojiButton.svelte b/packages/ui/src/components/emoji/EmojiButton.svelte index b12725facb..8d4e82e546 100644 --- a/packages/ui/src/components/emoji/EmojiButton.svelte +++ b/packages/ui/src/components/emoji/EmojiButton.svelte @@ -55,7 +55,7 @@ align-items: center; flex-shrink: 0; line-height: 150%; - color: var(--theme-content-color); + border: 1px solid transparent; &:not(.preview) { margin: 0.125rem; @@ -78,13 +78,16 @@ pointer-events: none; } &:enabled:hover { - color: var(--theme-caption-color); background-color: var(--theme-popup-hover); } &.selected { - background-color: var(--theme-popup-header); - border: 1px solid var(--theme-popup-divider); + border-color: var(--button-primary-BorderColor); + background-color: var(--button-primary-BackgroundColor); + + &:not(.disabled, :disabled):hover { + background-color: var(--button-primary-hover-BackgroundColor); + } } :global(.mobile-theme) & { diff --git a/packages/ui/src/components/emoji/EmojiPopup.svelte b/packages/ui/src/components/emoji/EmojiPopup.svelte index 89a2de9e7f..0232e4d7fe 100644 --- a/packages/ui/src/components/emoji/EmojiPopup.svelte +++ b/packages/ui/src/components/emoji/EmojiPopup.svelte @@ -4,6 +4,7 @@ // Licensed under the Eclipse Public License v2.0 (SPDX: EPL-2.0). // import { createEventDispatcher, onMount, onDestroy } from 'svelte' + import type { Emoji } from 'emojibase' import { Label, Scroller, @@ -13,7 +14,8 @@ showPopup, eventToHTMLElement, ButtonBase, - closeTooltip + closeTooltip, + Spinner } from '../../' import plugin from '../../plugin' import { @@ -38,6 +40,7 @@ export let embedded = false export let selected: string | undefined export let disabled: boolean = false + export let kind: 'default' | 'fade' = 'fade' const dispatch = createEventDispatcher() closeTooltip() @@ -58,62 +61,63 @@ } ] let timer: any = null + const isMobile = $deviceInfo.isMobile - emojiCategories.forEach((em, index) => { - if (em.id === 'frequently-used') { - emojiCategories[index].emojis = getFrequentlyEmojis() - } - if (em.emojisString !== undefined && Array.isArray(em.emojisString) && em.emojis === undefined) { - const tempEmojis: string[] = em.emojisString - const emojis: EmojiWithGroup[] = [] - tempEmojis.forEach((te) => { - const e = $emojiStore.find((es) => es.hexcode === te) - if (e !== undefined) emojis.push(e) - }) - emojiCategories[index].emojis = emojis - } - }) - let emojisCat = emojiCategories.filter( - (em) => em.categories !== undefined || (Array.isArray(em.emojis) && em.emojis.length > 0) - ) - + let loading: boolean = true + let emojisCat = emojiCategories let currentCategory = emojisCat[0] + $: emojiTabs = emojisCat + .map((ec) => { + if (ec.categories !== undefined || ec.emojisString !== undefined || ec.emojis !== undefined) { + return { id: ec.id, label: ec.label, icon: ec.icon } + } else return undefined + }) + .filter((f) => f !== undefined) as EmojiCategory[] + $: categoryTabs = searching ? ([...searchCategory, ...emojiTabs] as EmojiCategory[]) : emojiTabs - function handleScrollToCategory (categoryId: string) { + function handleScrollToCategory (categoryId: string): void { if (searching && categoryId !== searchCategory[0].id) $searchEmoji = '' - - setTimeout(() => { - const labelElement = document.getElementById(categoryId) - if (labelElement) { - const emojisElement = labelElement.nextElementSibling as HTMLElement - scrollElement.scroll(0, emojisElement.offsetTop - $deviceInfo.fontSize * 1.75) - } - }, 0) + if (isMobile) { + const tempCat = emojiTabs.find((ct) => ct?.id === categoryId) + if (tempCat === undefined) return + currentCategory = tempCat + outputGroups = updateGroups(searching, emojisCat) + } else { + setTimeout(() => { + const labelElement = document.getElementById(categoryId) + if (labelElement) { + const emojisElement = labelElement.nextElementSibling as HTMLElement + scrollElement.scroll(0, emojisElement.offsetTop - $deviceInfo.fontSize * 1.75) + } + }) + } } - function handleCategoryScrolled () { + function handleCategoryScrolled (): void { + if (isMobile) return const selectedCategory = emojisCat.find((category) => { const labelElement = document.getElementById(category.id) - - if (!labelElement) { - return false - } - + if (labelElement == null) return false const emojisElement = labelElement.nextElementSibling as HTMLElement return emojisElement.offsetTop + emojisElement.offsetHeight - emojiRowHeightPx > scrollElement.scrollTop }) + if (selectedCategory !== undefined) currentCategory = selectedCategory + } - if (selectedCategory) { - currentCategory = selectedCategory - } + const sendEmoji = (emoji: Emoji | EmojiWithGroup): void => { + selected = emoji.emoji + addFrequentlyEmojis(emoji.hexcode) + dispatch('close', { + emoji: emoji.emoji, + codes: emoji.hexcode.split('-').map((hc) => parseInt(hc, 16)) + }) } const selectedEmoji = (event: CustomEvent<{ detail: EmojiWithGroup }>): void => { if (event.detail === undefined || typeof event.detail !== 'object') return const detail = event.detail as unknown as EmojiWithGroup - addFrequentlyEmojis(detail.hexcode) - dispatch('close', detail.emoji) + sendEmoji(detail) } function openContextMenu (event: TouchEvent | MouseEvent, _emoji: EmojiWithGroup, remove: boolean): void { @@ -124,20 +128,27 @@ clearTimer() shownContext = true - showPopup(ActionsPopup, { emoji, remove }, eventToHTMLElement(event), (result) => { - if (result === 'remove') { - removeFrequentlyEmojis(emoji.hexcode) - const index = emojisCat.findIndex((ec) => ec.id === 'frequently-used') - if (index > -1) emojisCat[index].emojis = getFrequentlyEmojis() - emojisCat = emojisCat.filter( - (em) => em.categories !== undefined || (Array.isArray(em.emojis) && em.emojis.length > 0) - ) - } else if (result !== undefined) { - addFrequentlyEmojis(result.hexcode) - dispatch('close', result.emoji) + showPopup( + ActionsPopup, + { emoji, remove }, + eventToHTMLElement(event), + (result: 'remove' | Emoji | EmojiWithGroup) => { + if (result === 'remove') { + removeFrequentlyEmojis(emoji.hexcode) + const index = emojisCat.findIndex((ec) => ec.id === 'frequently-used') + if (index > -1) emojisCat[index].emojis = getFrequentlyEmojis() + emojisCat = emojisCat.filter( + (em) => em.categories !== undefined || (Array.isArray(em.emojis) && em.emojis.length > 0) + ) + if (currentCategory.emojis?.length === 0) { + currentCategory = emojisCat.find((ec) => ec.emojis !== undefined && ec.emojis.length > 0) ?? emojisCat[0] + } + } else if (result !== undefined) { + sendEmoji(result) + } + shownContext = false } - shownContext = false - }) + ) } function handleContextMenu (event: MouseEvent, emoji: EmojiWithGroup, remove: boolean): void { event.preventDefault() @@ -174,41 +185,71 @@ }) } - let hidden: boolean = false + let hidden: boolean = true const checkScroll = (event: Event): void => { if (timer != null) clearTimer() const target = event.target as HTMLElement if (target == null) return hidden = target.scrollHeight - target.scrollTop - target.clientHeight < 5 } + + const initEmoji = (): void => { + emojiCategories.forEach((em, index) => { + if (em.id === 'frequently-used') { + emojiCategories[index].emojis = getFrequentlyEmojis() + } + if (em.emojisString !== undefined && Array.isArray(em.emojisString) && em.emojis === undefined) { + const tempEmojis: string[] = em.emojisString + const emojis: EmojiWithGroup[] = [] + tempEmojis.forEach((te) => { + const e = $emojiStore.find((es) => es.hexcode === te) + if (e !== undefined) emojis.push(e) + }) + emojiCategories[index].emojis = emojis + } + }) + emojisCat = emojiCategories.filter( + (em) => em.categories !== undefined || (Array.isArray(em.emojis) && em.emojis.length > 0) + ) + currentCategory = emojisCat.find((ec) => ec.emojis !== undefined) ?? emojisCat[0] + loading = false + } + onMount(() => { if (scrollElement !== undefined) scrollElement.addEventListener('scroll', checkScroll) + setTimeout(initEmoji) }) onDestroy(() => { if (scrollElement !== undefined) scrollElement.removeEventListener('scroll', checkScroll) }) + + const updateGroups = (s: boolean, ec: EmojiCategory[]): EmojiCategory[] => { + return s ? searchCategory : isMobile ? ec.filter((e) => e.id === currentCategory.id) : ec + } + $: outputGroups = updateGroups(searching, emojisCat) -
+
- {#each searching ? searchCategory.concat(emojisCat) : emojisCat as category} + {#each categoryTabs as category (category.id)} {/each}
ec.id === currentCategory.id)) * - ($deviceInfo.isMobile ? 2 : 2.25) + (searching ? 0 : emojisCat.findIndex((ec) => ec.id === currentCategory.id)) * (isMobile ? 1.875 : 2.125) }rem`} class="hulyPopupEmoji-header__tab-cursor" /> @@ -239,38 +280,43 @@
- {#each searching ? searchCategory : emojisCat as group} - {@const canRemove = group.id === 'frequently-used'} - {@const emojisGroup = searching - ? $resultEmojis - : Array.isArray(group.emojis) - ? group.emojis - : $resultEmojis.filter((re) => re.key === group.id)} -
-
-