81 lines
2.9 KiB
Vue
81 lines
2.9 KiB
Vue
<script setup lang="ts">
|
|
import { Check, Monitor, Moon, Sun } from 'lucide-vue-next';
|
|
import { computed } from 'vue';
|
|
import { useAppearance } from '@/composables/useAppearance';
|
|
import { useLocalization } from '@/composables/useLocalization';
|
|
|
|
const { appearance, updateAppearance } = useAppearance();
|
|
const { t } = useLocalization();
|
|
|
|
const tabs = computed(() => [
|
|
{
|
|
value: 'light' as const,
|
|
Icon: Sun,
|
|
label: t('appearance.light'),
|
|
description: t('appearance.lightDescription'),
|
|
},
|
|
{
|
|
value: 'dark' as const,
|
|
Icon: Moon,
|
|
label: t('appearance.dark'),
|
|
description: t('appearance.darkDescription'),
|
|
},
|
|
{
|
|
value: 'system' as const,
|
|
Icon: Monitor,
|
|
label: t('appearance.system'),
|
|
description: t('appearance.systemDescription'),
|
|
},
|
|
]);
|
|
</script>
|
|
|
|
<template>
|
|
<fieldset class="min-w-0">
|
|
<legend class="sr-only">{{ t('appearance.label') }}</legend>
|
|
<div class="grid min-w-0 gap-3 sm:grid-cols-3">
|
|
<label
|
|
v-for="{ value, Icon, label, description } in tabs"
|
|
:key="value"
|
|
class="relative min-w-0"
|
|
>
|
|
<input
|
|
type="radio"
|
|
name="appearance"
|
|
:value="value"
|
|
:checked="appearance === value"
|
|
:aria-describedby="`appearance-${value}-description`"
|
|
class="peer sr-only"
|
|
@change="updateAppearance(value)"
|
|
/>
|
|
<span
|
|
class="peer-checked:border-primary peer-checked:bg-accent/50 peer-focus-visible:ring-ring/40 gap-stack bg-background hover:border-primary/50 flex h-full min-h-32 cursor-pointer flex-col rounded-lg border p-4 transition-colors peer-focus-visible:ring-2"
|
|
>
|
|
<span class="flex items-center justify-between gap-3">
|
|
<span
|
|
class="bg-accent text-primary flex size-10 items-center justify-center rounded-lg"
|
|
>
|
|
<component
|
|
:is="Icon"
|
|
class="size-5"
|
|
aria-hidden="true"
|
|
/>
|
|
</span>
|
|
<Check
|
|
v-if="appearance === value"
|
|
class="text-primary size-5"
|
|
aria-hidden="true"
|
|
/>
|
|
</span>
|
|
<span class="font-semibold">{{ label }}</span>
|
|
<span
|
|
:id="`appearance-${value}-description`"
|
|
class="text-caption text-muted-foreground"
|
|
>
|
|
{{ description }}
|
|
</span>
|
|
</span>
|
|
</label>
|
|
</div>
|
|
</fieldset>
|
|
</template>
|