mirror of
https://github.com/versia-pub/frontend.git
synced 2025-12-06 08:28:20 +01:00
feat: ✨ Add preferences page
This commit is contained in:
parent
a6635bc888
commit
5203f47409
|
|
@ -1,6 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div ref="container" :class="['overflow-y-hidden relative duration-200']" :style="{
|
<div ref="container" :class="['overflow-y-hidden relative duration-200']" :style="{
|
||||||
maxHeight: collapsed ? '18rem' : `${container?.scrollHeight}px` }">
|
maxHeight: collapsed ? '18rem' : `${container?.scrollHeight}px`
|
||||||
|
}">
|
||||||
<div :class="['prose prose-sm block relative dark:prose-invert duration-200 !max-w-full break-words prose-a:no-underline prose-a:hover:underline', $style.content]"
|
<div :class="['prose prose-sm block relative dark:prose-invert duration-200 !max-w-full break-words prose-a:no-underline prose-a:hover:underline', $style.content]"
|
||||||
v-html="content">
|
v-html="content">
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
31
components/preferences/switch.vue
Normal file
31
components/preferences/switch.vue
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
<template>
|
||||||
|
<Card class="grid grid-cols-[1fr,auto] items-center p-6">
|
||||||
|
<CardHeader class="space-y-0.5 p-0">
|
||||||
|
<CardTitle class="text-base">
|
||||||
|
{{ setting.title }}
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
{{ setting.description }}
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardFooter class="p-0">
|
||||||
|
<Switch :disabled="setting.notImplemented" :checked="setting.value" @update:checked="v => { setting.value = v }" />
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardDescription,
|
||||||
|
CardFooter,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "~/components/ui/card";
|
||||||
|
import { Switch } from "~/components/ui/switch";
|
||||||
|
import type { BooleanSetting } from "~/settings.ts";
|
||||||
|
|
||||||
|
defineModel<BooleanSetting>("setting", {
|
||||||
|
required: true,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
@ -61,25 +61,25 @@ import ThemeSwitcher from "./theme-switcher.vue";
|
||||||
const data = {
|
const data = {
|
||||||
navMain: [
|
navMain: [
|
||||||
{
|
{
|
||||||
title: "Settings",
|
title: "Preferences",
|
||||||
url: "#",
|
url: "/preferences",
|
||||||
icon: Settings2,
|
icon: Settings2,
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
title: "Appearance",
|
title: "Appearance",
|
||||||
url: "#",
|
url: "/preferences/appearance",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Behaviour",
|
title: "Behaviour",
|
||||||
url: "#",
|
url: "/preferences/behaviour",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Emojis",
|
title: "Emojis",
|
||||||
url: "#",
|
url: "/preferences/emojis",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Roles",
|
title: "Roles",
|
||||||
url: "#",
|
url: "/preferences/roles",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
||||||
41
components/ui/switch/Switch.vue
Normal file
41
components/ui/switch/Switch.vue
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
SwitchRoot,
|
||||||
|
type SwitchRootEmits,
|
||||||
|
type SwitchRootProps,
|
||||||
|
SwitchThumb,
|
||||||
|
useForwardPropsEmits,
|
||||||
|
} from "radix-vue";
|
||||||
|
import { type HTMLAttributes, computed } from "vue";
|
||||||
|
|
||||||
|
const props = defineProps<
|
||||||
|
SwitchRootProps & { class?: HTMLAttributes["class"] }
|
||||||
|
>();
|
||||||
|
|
||||||
|
const emits = defineEmits<SwitchRootEmits>();
|
||||||
|
|
||||||
|
const delegatedProps = computed(() => {
|
||||||
|
const { class: _, ...delegated } = props;
|
||||||
|
|
||||||
|
return delegated;
|
||||||
|
});
|
||||||
|
|
||||||
|
const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<SwitchRoot
|
||||||
|
v-bind="forwarded"
|
||||||
|
:class="cn(
|
||||||
|
'peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input',
|
||||||
|
props.class,
|
||||||
|
)"
|
||||||
|
>
|
||||||
|
<SwitchThumb
|
||||||
|
:class="cn('pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5')"
|
||||||
|
>
|
||||||
|
<slot name="thumb" />
|
||||||
|
</SwitchThumb>
|
||||||
|
</SwitchRoot>
|
||||||
|
</template>
|
||||||
1
components/ui/switch/index.ts
Normal file
1
components/ui/switch/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export { default as Switch } from "./Switch.vue";
|
||||||
43
pages/preferences/[page].vue
Normal file
43
pages/preferences/[page].vue
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
<template>
|
||||||
|
<div class="md:px-8 px-4 py-2 max-w-7xl mx-auto w-full">
|
||||||
|
<h1 class="scroll-m-20 text-3xl font-extrabold tracking-tight lg:text-4xl capitalize">
|
||||||
|
{{ page }}
|
||||||
|
</h1>
|
||||||
|
<div class="grid grid-cols-1 2xl:grid-cols-2 gap-4 mt-6">
|
||||||
|
<template v-for="[id, setting] of settingEntries">
|
||||||
|
<SwitchPreference v-if="setting.type === SettingType.Boolean" :setting="(setting as BooleanSetting)" @update:setting="updateSetting(id, setting)" />
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import SwitchPreference from "~/components/preferences/switch.vue";
|
||||||
|
import {
|
||||||
|
type BooleanSetting,
|
||||||
|
type Setting,
|
||||||
|
type SettingIds,
|
||||||
|
type SettingPages,
|
||||||
|
SettingType,
|
||||||
|
} from "~/settings.ts";
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
layout: "app",
|
||||||
|
});
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const page = route.params.page as SettingPages;
|
||||||
|
const settingEntries = computed(() =>
|
||||||
|
(Object.entries(settings.value) as [SettingIds, Setting][]).filter(
|
||||||
|
([, s]) => s.page === page && !s.notImplemented,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateSetting = (id: SettingIds, setting: Setting) => {
|
||||||
|
settings.value = {
|
||||||
|
...settings.value,
|
||||||
|
[id]: setting,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
</script>
|
||||||
11
pages/preferences/index.vue
Normal file
11
pages/preferences/index.vue
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
definePageMeta({
|
||||||
|
layout: "app",
|
||||||
|
});
|
||||||
|
</script>
|
||||||
Loading…
Reference in a new issue