fix: language buttons real design / conformance

This commit is contained in:
fsylvain committed 2026-10-04 17:08:43 +02:00
1 parent 2c8f39273c
commit 0490a4c494
23 files changed
+1408 -412

No files matched your search

+53 -45
View File
@@ -1,13 +1,17 @@
<script setup lang="ts">
import { t } from '@/i18n'
import { locale, t } from '@/i18n'
import { computed, ref } from 'vue'
import { useEventListener, useMediaQuery } from '@vueuse/core'
import { useMediaQuery } from '@vueuse/core'
import { colorModeOptions, useAccountMenu } from '@/composables/useAccountMenu'
import LanguagePicker from '@/components/LanguagePicker.vue'
import { chooseLanguage, languageOptions } from '@/composables/useLanguage'
import { useAccountMenuRadios } from '@/composables/useRadioMenuRoles'
const DESKTOP_QUERY = '(min-width: 64rem)'
const ITEM_CLASS = 'h-8 items-center py-0'
const ITEM_CLASS = 'h-8 items-center gap-2.5 px-2 py-0 text-sm font-normal text-highlighted'
const CHOICE_CLASS = 'h-8 items-center gap-2.5 px-2 py-0 text-base font-normal text-highlighted'
const SECTION_LABEL_CLASS =
'px-2 pt-1.5 pb-0.5 text-[0.6875rem] font-medium uppercase tracking-[0.04em] text-muted'
const props = defineProps<{
username: string
@@ -25,31 +29,8 @@ const emit = defineEmits<{
const { colorMode, name, initials, onProfile, goToProfile } = useAccountMenu(() => props)
const isDesktop = useMediaQuery(DESKTOP_QUERY)
const open = ref(false)
useAccountMenuRadios(open)
const chip = ref<{ $el: HTMLElement } | null>(null)
const languagePicker = ref<{ $el: HTMLElement } | null>(null)
function focusLanguage(event: KeyboardEvent) {
const target = event.target
if (!(target instanceof HTMLElement)) return
if (target.closest('[role="menu"]') !== languagePicker.value?.$el.closest('[role="menu"]'))
return
if (target.closest('[role="combobox"]')) return
const entries = Array.from(
target
.closest('[role="menu"]')
?.querySelectorAll<HTMLElement>('[role="menuitem"], [role="menuitemcheckbox"]') ?? [],
)
const movesToLanguage =
event.key === 'End' ||
(event.key === 'ArrowDown' && target === entries[entries.length - 1]) ||
(event.key === 'ArrowUp' && target === entries[0])
if (movesToLanguage) {
event.preventDefault()
languagePicker.value?.$el.querySelector<HTMLElement>('[role="combobox"]')?.focus()
}
}
useEventListener(document, 'keydown', focusLanguage, { capture: true })
const chipActive = computed(() => open.value || onProfile.value)
@@ -94,18 +75,44 @@ const items = computed(() => [
onSelect: goToProfile,
},
],
colorModeOptions.map((option) => ({
type: 'checkbox' as const,
label: option.label,
checked: colorMode.preference === option.value,
class: ITEM_CLASS,
onSelect: (event: Event) => event.preventDefault(),
onUpdateChecked: (checked: boolean) => {
if (checked) {
colorMode.preference = option.value
}
[
{
type: 'label' as const,
get label() {
return t('Theme')
},
class: SECTION_LABEL_CLASS,
},
})),
...colorModeOptions.map((option) => ({
type: 'checkbox' as const,
label: option.label,
checked: colorMode.preference === option.value,
class: CHOICE_CLASS,
onSelect: (event: Event) => event.preventDefault(),
onUpdateChecked: (checked: boolean) => {
if (checked) {
colorMode.preference = option.value
}
},
})),
],
[
{
type: 'label' as const,
get label() {
return t('Language')
},
class: SECTION_LABEL_CLASS,
},
...languageOptions.map((option) => ({
type: 'checkbox' as const,
label: option.label,
checked: locale.value === option.value,
class: CHOICE_CLASS,
onSelect: (event: Event) => event.preventDefault(),
onUpdateChecked: () => chooseLanguage(option.value),
})),
],
[
{
get label() {
@@ -123,8 +130,12 @@ const items = computed(() => [
<UDropdownMenu
:open="open"
:items="items"
:content="{ side: 'top', align: 'start' }"
:ui="{ content: 'w-56 p-1' }"
:content="{ side: 'top', align: 'start', sideOffset: 4, loop: true }"
:ui="{
content: 'w-56 p-0 ring-0 border border-default',
itemLeadingIcon: 'size-4 text-highlighted',
itemTrailingIcon: 'size-4',
}"
@update:open="onUpdateOpen"
>
<UButton
@@ -156,11 +167,8 @@ const items = computed(() => [
<template #account-header>
<span class="flex min-w-0 flex-col gap-0.5">
<span class="truncate text-sm font-semibold text-highlighted">{{ name }}</span>
<span class="truncate text-xs font-normal text-muted">{{ email }}</span>
<span class="truncate text-xs/5 font-normal text-muted">{{ email }}</span>
</span>
</template>
<template #content-bottom>
<LanguagePicker ref="languagePicker" />
</template>
</UDropdownMenu>
</template>