UBERF-9699 Refactor settings UI (#8370)

Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
This commit is contained in:
Alexander Onnikov
2025-03-28 14:32:28 +07:00
committed by GitHub
parent 357d0dcfe9
commit 32d96e5dce
24 changed files with 297 additions and 425 deletions
+1
View File
@@ -110,6 +110,7 @@
"FullSize": "Plná velikost",
"UseMaxWidth": "Maximální šířka",
"Sidebar": "Postranní panel",
"FontSize": "Velikost písma",
"Language": "Jazyk"
}
}
+1
View File
@@ -110,6 +110,7 @@
"FullSize": "Volle Größe",
"UseMaxWidth": "Maximale Breite",
"Sidebar": "Seitenleiste",
"FontSize": "Schriftgröße",
"Language": "Sprache"
}
}
+1
View File
@@ -111,6 +111,7 @@
"FullSize": "Full size",
"UseMaxWidth": "Max width",
"Sidebar": "Sidebar",
"FontSize": "Font size",
"Language": "Language"
}
}
+1
View File
@@ -111,6 +111,7 @@
"FullSize": "Tamaño completo",
"UseMaxWidth": "Ancho máximo",
"Sidebar": "Barra lateral",
"FontSize": "Tamaño de fuente",
"Language": "Idioma"
}
}
+1
View File
@@ -111,6 +111,7 @@
"FullSize": "Taille réelle",
"UseMaxWidth": "Largeur maximale",
"Sidebar": "Barre latérale",
"FontSize": "Taille de police",
"Language": "Langue"
}
}
+1
View File
@@ -111,6 +111,7 @@
"FullSize": "Dimensione intera",
"UseMaxWidth": "Larghezza massima",
"Sidebar": "Barra laterale",
"FontSize ": "Dimensione del carattere",
"Language": "Lingua"
}
}
+1
View File
@@ -111,6 +111,7 @@
"FullSize": "Tamanho completo",
"UseMaxWidth": "Largura máxima",
"Sidebar": "Barra lateral",
"FontSize": "Tamanho da fonte",
"Language": "Idioma"
}
}
+1
View File
@@ -111,6 +111,7 @@
"FullSize": "Полный размер",
"UseMaxWidth": "Максимальная ширина",
"Sidebar": "Боковая панель",
"FontSize": "Размер шрифта",
"Language": "Язык"
}
}
+1
View File
@@ -111,6 +111,7 @@
"FullSize": "全尺寸",
"UseMaxWidth": "使用最大宽度",
"Sidebar": "侧边栏",
"FontSize": "字体大小",
"Language": "语言"
}
}
@@ -60,7 +60,7 @@
>
{#if withIcons}
<div class="hulyPopup-row__icon">
{#if item.icon}<Icon icon={item.icon} size={'small'} />{/if}
{#if item.icon}<Icon icon={item.icon} iconProps={item.iconProps} size={'small'} />{/if}
</div>
{/if}
{#if item.description}
@@ -1,55 +0,0 @@
<!--
// Copyright © 2023 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { onMount } from 'svelte'
import FontSize from './icons/FontSize.svelte'
export let size: string
export let focused: string
let btn: HTMLButtonElement
onMount(() => {
if (focused === size) btn.focus()
})
</script>
<button
bind:this={btn}
class="antiButton regular sh-no-shape jf-center bs-solid no-focus statusPopupButton"
class:focused={focused === size}
>
<FontSize size={size === 'small-font' ? '20px' : '28px'} />
</button>
<style lang="scss">
.statusPopupButton {
position: relative;
flex-shrink: 0;
width: 52px;
height: 52px;
border-radius: 6px;
&.focused::before,
&:focus::before {
position: absolute;
content: '';
inset: -3px;
border: 1px solid var(--primary-button-default);
border-radius: 8.5px;
cursor: default;
}
}
</style>
@@ -1,50 +0,0 @@
<!--
// Copyright © 2023 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { createEventDispatcher } from 'svelte'
import type { IntlString } from '@hcengineering/platform'
import FontSizeButton from './FontSizeButton.svelte'
import { Label, deviceOptionsStore as deviceInfo } from '../..'
export let fontsizes: Array<{ id: string, label: IntlString, size: number }>
export let selected: string = ''
const dispatch = createEventDispatcher()
const select = (size: string): void => {
if (selected === size) return
selected = size
dispatch('close', size)
}
</script>
<div class="antiPopup" style:flex-direction={'row'} style:padding={'12px'}>
{#each fontsizes as font}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
class="statusPopup-option"
class:selected={selected === font.id}
on:click={() => {
select(font.id)
}}
>
<FontSizeButton size={font.id} focused={selected} />
<span class="label overflow-label" class:tracking--05px={$deviceInfo.language === 'ru'}>
<Label label={font.label} />
</span>
</div>
{/each}
</div>
@@ -1,58 +0,0 @@
<!--
// Copyright © 2020 Anticrm Platform Contributors.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { IntlString } from '@hcengineering/platform'
import { getContext } from 'svelte'
import ui, { deviceOptionsStore as deviceInfo, modalStore, showPopup } from '../..'
import FontSizePopup from './FontSizePopup.svelte'
import FontSize from './icons/FontSize.svelte'
const { currentFontSize, setFontSize } = getContext<{
currentFontSize: string
setFontSize: (value: number) => void
}>('fontsize')
const fontsizes: Array<{ id: string, label: IntlString, size: number }> = [
{ id: 'normal-font', label: ui.string.Spacious, size: 16 },
{ id: 'small-font', label: ui.string.Compact, size: 14 }
]
let pressed: boolean = false
let btn: HTMLButtonElement
let current = fontsizes.findIndex((fs) => fs.id === currentFontSize)
function changeFontSize (ev: MouseEvent) {
pressed = true
showPopup(FontSizePopup, { fontsizes, selected: fontsizes[current].id }, btn, (result) => {
if (result) {
setFontSize(result)
current = fontsizes.findIndex((fs) => fs.id === result)
$modalStore = $modalStore
}
pressed = false
})
}
$: $deviceInfo.fontSize = fontsizes[current].size
</script>
<button
bind:this={btn}
class="antiButton ghost jf-center bs-none no-focus resetIconSize statusButton square"
class:pressed
style:color={'var(--theme-dark-color)'}
on:click={changeFontSize}
>
<FontSize />
</button>
@@ -1,48 +0,0 @@
<!--
// Copyright © 2020 Anticrm Platform Contributors.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { createEventDispatcher } from 'svelte'
import Label from '../Label.svelte'
import IconCheck from '../icons/Check.svelte'
import Html from '../Html.svelte'
export let langs: any
export let selected: string
const dispatch = createEventDispatcher()
</script>
<div class="antiPopup" style:min-width={'200px'}>
<div class="ap-space x2" />
{#each langs as lang}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
class="ap-menuItem hoverable flex-row-center leading-16px"
on:click={() => {
dispatch('close', lang.id)
}}
>
<div class="svg-16px flex-no-shrink text-16px mr-2"><Html value={lang.logo} /></div>
<span class="overflow-label flex-grow"><Label label={lang.label} /></span>
<div class="ap-check">
{#if lang.id === selected}
<IconCheck size={'small'} fill={'var(--primary-button-default)'} />
{/if}
</div>
</div>
{/each}
<div class="ap-space x2" />
</div>
@@ -1,85 +0,0 @@
<!--
// Copyright © 2020 Anticrm Platform Contributors.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { getContext, onMount } from 'svelte'
import { getMetadata } from '@hcengineering/platform'
import ui, { showPopup, deviceOptionsStore as deviceInfo } from '../..'
import LangPopup from './LangPopup.svelte'
import Html from '../Html.svelte'
import { updateEmojis } from '../emoji'
let pressed: boolean = false
const { currentLanguage, setLanguage } = getContext<{ currentLanguage: string, setLanguage: (lang: string) => void }>(
'lang'
)
const uiLangs = new Set(getMetadata(ui.metadata.Languages))
const langs = [
{ id: 'en', label: ui.string.English, logo: '&#x1F1FA;&#x1F1F8;' },
{ id: 'pt', label: ui.string.Portuguese, logo: '&#x1F1F5;&#x1F1F9;' },
{ id: 'es', label: ui.string.Spanish, logo: '&#x1F1EA;&#x1F1F8;' },
{ id: 'ru', label: ui.string.Russian, logo: '&#x1F1F7;&#x1F1FA;' },
{ id: 'zh', label: ui.string.Chinese, logo: '&#x1F1E8;&#x1F1F3;' },
{ id: 'fr', label: ui.string.French, logo: '&#x1F1EB;&#x1F1F7;' },
{ id: 'it', label: ui.string.Italian, logo: '&#x1F1EE;&#x1F1F9;' },
{ id: 'cs', label: ui.string.Czech, logo: '&#x1F1E8;&#x1F1FF;' },
{ id: 'de', label: ui.string.German, logo: '&#x1F1E9;&#x1F1EA;' }
].filter((lang) => uiLangs.has(lang.id))
if (langs.findIndex((l) => l.id === currentLanguage) < 0 && langs.length !== 0) {
setLanguage(langs[0].id)
}
if (langs.length === 0) {
console.error(
`List of configured UI languages: [${getMetadata(ui.metadata.Languages)?.join(
', '
)}] doesn't contain any languages available in the app. Please check you configuration.`
)
}
const isSelectable = langs.length > 1
$: selected = langs.find((item) => item.id === currentLanguage)
const selectLanguage = (): void => {
if (!isSelectable) {
return
}
pressed = true
showPopup(LangPopup, { langs, selected: selected?.id }, 'status', (result) => {
if (result) {
selected = langs.find((item) => item.id === result)
setLanguage(result)
void updateEmojis(result)
}
pressed = false
})
}
$: $deviceInfo.language = selected?.id
onMount(() => {
void updateEmojis()
})
</script>
<button
class="antiButton ghost jf-center bs-none no-focus resetIconSize statusButton square"
class:pressed
on:click={selectLanguage}
>
{#if selected}
<Html value={selected.logo} />
{/if}
</button>
+12 -16
View File
@@ -4,14 +4,15 @@
import type { AnyComponent, WidthType } from '../../types'
import { deviceSizes, deviceWidths } from '../../types'
// import { applicationShortcutKey } from '../../utils'
import { Theme } from '@hcengineering/theme'
import { Theme, themeStore } from '@hcengineering/theme'
import {
IconArrowLeft,
IconArrowRight,
checkMobile,
deviceOptionsStore as deviceInfo,
checkAdaptiveMatching,
getLocalWeekStart
getLocalWeekStart,
updateEmojis
} from '../../'
import { desktopPlatform, getCurrentLocation, location, locationStorageKeyId, navigate } from '../../location'
import uiPlugin from '../../plugin'
@@ -19,10 +20,8 @@
import Label from '../Label.svelte'
import StatusComponent from '../Status.svelte'
import Clock from './Clock.svelte'
import FontSizeSelector from './FontSizeSelector.svelte'
import LangSelector from './LangSelector.svelte'
import RootBarExtension from './RootBarExtension.svelte'
import ThemeSelector from './ThemeSelector.svelte'
import Settings from './Settings.svelte'
let application: AnyComponent | undefined
@@ -89,9 +88,13 @@
$: $deviceInfo.isMobile = isMobile
$: $deviceInfo.minWidth = docWidth <= 480
$: $deviceInfo.twoRows = docWidth <= 680
$: $deviceInfo.language = $themeStore.language
$: $deviceInfo.fontSize = $themeStore.fontSize
$: document.documentElement.style.setProperty('--app-height', `${docHeight}px`)
$: void updateEmojis($themeStore.language)
let doubleTouchStartTimestamp = 0
document.addEventListener('touchstart', (event) => {
const now = +new Date()
@@ -155,7 +158,7 @@
<Theme>
<div id="ui-root" class:mobile-theme={isMobile}>
<div class="antiStatusBar">
<div class="flex-row-center h-full content-color gap-3 pl-4">
<div class="flex-row-center h-full content-color gap-3 px-4">
{#if desktopPlatform}
<div class="history-box flex-row-center gap-3">
<button
@@ -201,17 +204,13 @@
<StatusComponent {status} />
{/if}
</div>
<div class="flex-row-reverse" style:-webkit-app-region={'no-drag'}>
<div class="clock">
<Clock />
</div>
<div class="flex-row-reverse flex-gap-2" style:-webkit-app-region={'no-drag'}>
<Settings />
<Clock />
<div class="flex-row-center gap-statusbar">
{#if !secondRow}
<RootBarExtension position="right" />
{/if}
<FontSizeSelector />
<ThemeSelector />
<LangSelector />
</div>
</div>
</div>
@@ -282,9 +281,6 @@
font-size: 14px;
color: var(--theme-content-color);
}
.clock {
margin: 0 12px 0 8px;
}
.second-row {
display: none;
@@ -0,0 +1,39 @@
<!--
// Copyright © 2025 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { showPopup } from '../..'
import SettingsPopup from './SettingsPopup.svelte'
import Settings from './icons/Settings.svelte'
let pressed: boolean = false
let btn: HTMLButtonElement
function showSettings (): void {
pressed = true
showPopup(SettingsPopup, {}, btn, () => {
pressed = false
})
}
</script>
<button
bind:this={btn}
class="antiButton ghost jf-center bs-none no-focus resetIconSize statusButton square"
class:pressed
style:color={'var(--theme-dark-color)'}
on:click={showSettings}
>
<Settings />
</button>
@@ -0,0 +1,196 @@
<!--
// Copyright © 2025 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { type IntlString, getMetadata } from '@hcengineering/platform'
import { getContext } from 'svelte'
import FontSize from './icons/FontSize.svelte'
import Language from './icons/Language.svelte'
import ThemeButton from './ThemeButton.svelte'
import ui, {
Html,
Icon,
Label,
ModernPopup,
showPopup,
deviceOptionsStore as deviceInfo,
modalStore,
eventToHTMLElement
} from '../..'
const fontSizeContext = getContext<{ currentFontSize: string, setFontSize: (value: string) => void }>('fontsize')
const setFontSize = fontSizeContext.setFontSize
let currentFontSize = fontSizeContext.currentFontSize
const themeContext = getContext<{ currentTheme: string, setTheme: (theme: string) => void }>('theme')
const setTheme = themeContext.setTheme
let currentTheme = themeContext.currentTheme
const languageContext = getContext<{ currentLanguage: string, setLanguage: (language: string) => void }>('lang')
const setLanguage = languageContext.setLanguage
let currentLanguage = languageContext.currentLanguage
const fontsizes: Array<{ id: string, label: IntlString, size: number }> = [
{ id: 'normal-font', label: ui.string.Spacious, size: 16 },
{ id: 'small-font', label: ui.string.Compact, size: 14 }
]
const themes: Array<{ id: string, label: IntlString }> = [
{ id: 'theme-light', label: ui.string.ThemeLight },
{ id: 'theme-dark', label: ui.string.ThemeDark },
{ id: 'theme-system', label: ui.string.ThemeSystem }
]
const uiLangs = new Set(getMetadata(ui.metadata.Languages))
const langs = [
{ id: 'en', label: ui.string.English, logo: '&#x1F1FA;&#x1F1F8;' },
{ id: 'pt', label: ui.string.Portuguese, logo: '&#x1F1F5;&#x1F1F9;' },
{ id: 'es', label: ui.string.Spanish, logo: '&#x1F1EA;&#x1F1F8;' },
{ id: 'ru', label: ui.string.Russian, logo: '&#x1F1F7;&#x1F1FA;' },
{ id: 'zh', label: ui.string.Chinese, logo: '&#x1F1E8;&#x1F1F3;' },
{ id: 'fr', label: ui.string.French, logo: '&#x1F1EB;&#x1F1F7;' },
{ id: 'it', label: ui.string.Italian, logo: '&#x1F1EE;&#x1F1F9;' },
{ id: 'cs', label: ui.string.Czech, logo: '&#x1F1E8;&#x1F1FF;' },
{ id: 'de', label: ui.string.German, logo: '&#x1F1E9;&#x1F1EA;' }
].filter((lang) => uiLangs.has(lang.id))
if (langs.findIndex((l) => l.id === currentLanguage) < 0 && langs.length !== 0) {
setLanguage(langs[0].id)
}
if (langs.length === 0) {
console.error(
`List of configured UI languages: [${getMetadata(ui.metadata.Languages)?.join(
', '
)}] doesn't contain any languages available in the app. Please check you configuration.`
)
}
function selectFontSize (size: string): void {
if (currentFontSize === size) return
currentFontSize = size
setFontSize(size)
$modalStore = $modalStore
}
function selectTheme (theme: string): void {
if (currentTheme === theme) return
currentTheme = theme
setTheme(theme)
}
function selectLanguage (language: string): void {
if (currentLanguage === language) return
currentLanguage = language
setLanguage(language)
}
$: $deviceInfo.theme = currentTheme
$: fontsize = fontsizes.find((fs) => fs.id === currentFontSize) ?? fontsizes[0]
$: language = langs.find((lang) => lang.id === currentLanguage) ?? langs[0]
</script>
<div class="antiPopup">
<div class="ap-scroll">
<div class="ap-box">
<div class="flex-row-center m-4">
{#each themes as theme}
{@const selected = currentTheme === theme.id}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
class="statusPopup-option"
class:selected
on:click={() => {
selectTheme(theme.id)
}}
>
<ThemeButton theme={theme.id} focused={currentTheme} />
<span class="label overflow-label">
<Label label={theme.label} />
</span>
</div>
{/each}
</div>
<div class="ap-menuItem separator halfMargin" />
<button
class="ap-menuItem antiPopup-submenu withIcon flex-row-center flex-grow"
on:click={(ev) => {
const items = fontsizes.map((p) => {
return {
...p,
icon: FontSize,
iconProps: {
size: p.size
}
}
})
showPopup(ModernPopup, { items, selected: fontsize.id }, eventToHTMLElement(ev), (id) => {
if (id !== undefined) {
selectFontSize(id)
}
})
}}
>
<div class="flex-between flex-grow">
<div class="flex-row-center">
<div class="icon mr-2"><FontSize size={'16px'} /></div>
<span class="label font-medium"><Label label={ui.string.FontSize} /></span>
</div>
<div class="flex-row-center pr-4">
<Label label={fontsize.label} />
</div>
</div>
</button>
<div class="ap-menuItem separator halfMargin" />
<button
class="ap-menuItem antiPopup-submenu withIcon flex-row-center flex-grow"
on:click={(ev) => {
const items = langs.map((p) => {
return {
...p,
icon: Html,
iconProps: {
value: p.logo
}
}
})
showPopup(ModernPopup, { items, selected: language.id }, eventToHTMLElement(ev), (id) => {
if (id !== undefined) {
selectLanguage(id)
}
})
}}
>
<div class="flex-between flex-grow">
<div class="flex-row-center">
<div class="icon mr-2"><Icon icon={Language} size={'small'} /></div>
<span class="label font-medium"><Label label={ui.string.Language} /></span>
</div>
<div class="flex-row-center pr-4">
<div class="label text-lgd">
<Html value={language.logo} />
</div>
</div>
</div>
</button>
</div>
</div>
</div>
@@ -17,33 +17,33 @@
import FontSize from './icons/FontSize.svelte'
import CheckCircled from './icons/CheckCircled.svelte'
export let size: string
export let theme: string
export let focused: string
let btn: HTMLButtonElement
onMount(() => {
if (focused === size) btn.focus()
if (focused === theme) btn.focus()
})
</script>
<button
bind:this={btn}
class="antiButton regular sh-no-shape jf-center bs-none no-focus statusPopupThemeButton"
class:focused={focused === size}
class:both={size === 'theme-system'}
class:focused={theme === focused}
class:both={theme === 'theme-system'}
>
{#if size === 'theme-light' || size === 'theme-system'}
{#if theme === 'theme-light' || theme === 'theme-system'}
<div class="light-container">
<div class="paper"><FontSize /></div>
<div class="paper"><FontSize size={'small'} /></div>
</div>
{/if}
{#if size === 'theme-dark' || size === 'theme-system'}
{#if theme === 'theme-dark' || theme === 'theme-system'}
<div class="dark-container">
<div class="paper"><FontSize /></div>
<div class="paper"><FontSize size={'small'} /></div>
</div>
{/if}
{#if focused === size}
{#if focused === theme}
<CheckCircled />
{/if}
</button>
@@ -1,50 +0,0 @@
<!--
// Copyright © 2023 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { createEventDispatcher } from 'svelte'
import type { IntlString } from '@hcengineering/platform'
import ThemeButton from './ThemeButton.svelte'
import { Label } from '../..'
export let themes: Array<{ id: string, label: IntlString, size: number }>
export let selected: string = ''
const dispatch = createEventDispatcher()
const select = (size: string): void => {
if (selected === size) return
selected = size
dispatch('close', size)
}
</script>
<div class="antiPopup" style:flex-direction={'row'} style:padding={'12px'}>
{#each themes as theme}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
class="statusPopup-option"
class:selected={selected === theme.id}
on:click={() => {
select(theme.id)
}}
>
<ThemeButton size={theme.id} focused={selected} />
<span class="label overflow-label">
<Label label={theme.label} />
</span>
</div>
{/each}
</div>
@@ -1,53 +0,0 @@
<script lang="ts">
import { getContext } from 'svelte'
import { IntlString } from '@hcengineering/platform'
import ui, { showPopup, deviceOptionsStore as deviceInfo } from '../..'
import Theme from './icons/Theme.svelte'
import ThemePopup from './ThemePopup.svelte'
import { isSystemThemeDark } from '@hcengineering/theme'
const { currentTheme, setTheme } = getContext<{ currentTheme: string, setTheme: (theme: string) => void }>('theme')
const themes: Array<{ id: string, label: IntlString }> = [
{ id: 'theme-light', label: ui.string.ThemeLight },
{ id: 'theme-dark', label: ui.string.ThemeDark },
{ id: 'theme-system', label: ui.string.ThemeSystem }
]
let pressed: boolean = false
let remove: any = null
let current = themes.findIndex((th) => th.id === currentTheme)
function changeTheme (ev: MouseEvent) {
pressed = true
showPopup(ThemePopup, { themes, selected: themes[current].id }, 'status', (result) => {
if (result) {
setTheme(result)
current = themes.findIndex((th) => th.id === result)
}
pressed = false
})
}
$: $deviceInfo.theme = themes[current].id
$: if (themes[current].id === 'theme-system') checkSystemTheme()
const checkSystemTheme = () => {
if (remove !== null || themes[current].id !== 'theme-system') remove()
const isDark = isSystemThemeDark()
const media = matchMedia(`(prefers-color-scheme: ${isDark ? 'light' : 'dark'})`)
setTheme(themes[current].id)
media.addEventListener('change', checkSystemTheme)
remove = () => {
media.removeEventListener('change', checkSystemTheme)
}
}
</script>
<button
class="antiButton ghost jf-center bs-none no-focus resetIconSize statusButton square"
class:pressed
style:color={'var(--theme-dark-color)'}
on:click={changeTheme}
>
<Theme />
</button>
@@ -0,0 +1,17 @@
<script lang="ts">
export let size: 'small' | 'medium' | 'large'
export let fill: string = 'currentColor'
</script>
<svg class="svg-{size}" {fill} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path
d="M4.68191 1.69187C4.87991 1.49938 4.88436 1.18283 4.69187 0.984833C4.49938 0.786842 4.18283 0.782385 3.98483 0.974878C2.65823 2.26464 1.83337 4.06435 1.83337 6.05505C1.83337 9.59167 4.43291 12.5202 7.83337 13.0729V14.1667H7.00004C6.7239 14.1667 6.50004 14.3906 6.50004 14.6667C6.50004 14.9429 6.7239 15.1667 7.00004 15.1667H9.66671C9.94285 15.1667 10.1667 14.9429 10.1667 14.6667C10.1667 14.3906 9.94285 14.1667 9.66671 14.1667H8.83337V13.1648C8.88878 13.1661 8.94434 13.1667 9.00004 13.1667C10.9518 13.1667 12.7227 12.3918 14.0152 11.1352C14.2132 10.9427 14.2177 10.6262 14.0252 10.4282C13.8327 10.2302 13.5162 10.2257 13.3182 10.4182C12.2056 11.4999 10.6823 12.1667 9.00004 12.1667C5.59037 12.1667 2.83337 9.42653 2.83337 6.05505C2.83337 4.34666 3.54004 2.80204 4.68191 1.69187Z"
fill="#84868B"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M14.1667 6.00004C14.1667 3.14657 11.8535 0.833374 9.00004 0.833374C6.14657 0.833374 3.83337 3.14657 3.83337 6.00004C3.83337 8.85351 6.14657 11.1667 9.00004 11.1667C11.8535 11.1667 14.1667 8.85351 14.1667 6.00004ZM10.4809 2.1042C12.051 2.70134 13.1667 4.2204 13.1667 6.00004C13.1667 6.05806 13.1655 6.11581 13.1632 6.17325C12.6127 6.21617 12.2572 6.46117 12.0059 6.6343L11.9834 6.6498C11.7159 6.83381 11.622 6.88121 11.4314 6.84309C10.588 6.67441 9.91228 6.65022 9.42916 6.87348C9.16608 6.99505 8.96919 7.18556 8.84651 7.43242C8.7297 7.66746 8.69443 7.92441 8.69443 8.16744C8.69443 8.59239 8.69082 8.71663 8.66595 8.81643C8.6436 8.90609 8.60163 8.98698 8.4156 9.3069C8.29623 9.51219 8.17257 9.78133 8.16691 10.0834C6.26494 9.69742 4.83337 8.01591 4.83337 6.00004C4.83337 4.93814 5.23061 3.96903 5.8846 3.23318C5.99594 3.29692 6.12615 3.41985 6.35256 3.65906C7.16302 4.51543 8.09936 4.69029 8.81521 4.31208C9.07412 4.17529 9.24817 3.99547 9.35513 3.77965C9.44011 3.60819 9.4714 3.42827 9.48901 3.32701L9.49446 3.29611C9.51553 3.18052 9.52807 3.15412 9.53787 3.13932C9.54107 3.13436 9.56782 3.09285 9.69883 3.04094C10.1188 2.87452 10.3883 2.54386 10.4722 2.14918C10.4754 2.13421 10.4783 2.11921 10.4809 2.1042ZM12.5502 7.47369C12.7083 7.36492 12.8367 7.27751 12.984 7.22419C12.4784 8.87157 10.9791 10.0842 9.18534 10.1627C9.18342 10.1598 9.18179 10.1571 9.1804 10.1548C9.17035 10.1376 9.16748 10.1253 9.16683 10.1108C9.16493 10.0679 9.18335 9.97592 9.28007 9.80958L9.30549 9.76591C9.45445 9.51018 9.57539 9.30254 9.63627 9.05825C9.68712 8.8542 9.69367 8.63306 9.69439 8.35499C9.6945 8.31317 9.69448 8.27006 9.69445 8.22552L9.69443 8.16744C9.69443 8.01307 9.7182 7.92539 9.74202 7.87746C9.75998 7.84133 9.78532 7.8105 9.84864 7.78124C9.9615 7.72909 10.1734 7.69224 10.5449 7.72185C10.732 7.73676 10.9596 7.76852 11.2353 7.82366C11.86 7.94863 12.264 7.67061 12.5407 7.48024L12.5502 7.47369ZM9.4995 1.863C9.33574 1.84344 9.16907 1.83337 9.00004 1.83337C8.12977 1.83337 7.32181 2.10018 6.6535 2.55646C6.80213 2.67993 6.93823 2.82311 7.07885 2.97168C7.66786 3.59405 8.13098 3.54259 8.34807 3.4279C8.43679 3.38102 8.45311 3.34785 8.45873 3.33641L8.45914 3.33558C8.47687 3.29981 8.48428 3.26156 8.51067 3.11678C8.53273 2.99578 8.5711 2.78807 8.7039 2.58751C8.84434 2.37543 9.05399 2.22081 9.33046 2.11126C9.44573 2.06559 9.4812 2.00164 9.49401 1.94135C9.49878 1.9189 9.50098 1.89249 9.4995 1.863Z"
fill="#84868B"
/>
</svg>
File diff suppressed because one or more lines are too long
+1
View File
@@ -138,6 +138,7 @@ export const uis = plugin(uiId, {
FullSize: '' as IntlString,
UseMaxWidth: '' as IntlString,
Sidebar: '' as IntlString,
FontSize: '' as IntlString,
Language: '' as IntlString
},
metadata: {