added settings page, cleanup

This commit is contained in:
Martin Drechsel
2025-05-19 12:29:37 +02:00
parent 6a1351752b
commit 0699dbef31
35 changed files with 889 additions and 653 deletions

View File

@ -0,0 +1,54 @@
<template>
<div class="dropdown relative inline-flex">
<button
:id
class="dropdown-toggle"
:class="activatorClass"
aria-haspopup="menu"
aria-expanded="false"
:aria-label="label"
>
<slot name="activator">
<slot name="label">
{{ label }}
</slot>
<span
class="icon-[tabler--chevron-down] dropdown-open:rotate-180 size-4"
>
</span>
</slot>
</button>
<slot name="items" :items>
<ul
class="dropdown-menu dropdown-open:opacity-100 hidden min-w-28"
role="menu"
aria-orientation="vertical"
:aria-labelledby="id"
>
<component
:is="itemIs"
class="dropdown-item"
v-for="item in items"
@click="$emit('select', item)"
>
<slot name="item" :item>
{{ item }}
</slot>
</component>
</ul>
</slot>
</div>
</template>
<script setup lang="ts" generic="T">
const { itemIs = 'li' } = defineProps<{
label?: string
items?: T[]
itemIs?: string
activatorClass?: string
}>()
defineEmits<{ select: [T] }>()
const id = useId()
</script>

View File

@ -0,0 +1,33 @@
<template>
<UiDropdown
:items="availableLocales"
@select="(locale) => $emit('select', locale)"
activator-class="btn btn-primary"
>
<template #label>
<Icon :name="flags[locale]" />
</template>
<template #item="{ item }">
<div class="flex gap-2 justify-center">
<Icon :name="flags[item]" class="my-auto" />
<p>
{{ item }}
</p>
</div>
</template>
</UiDropdown>
</template>
<script setup lang="ts">
import { type Locale } from 'vue-i18n'
const flags = {
de: 'emojione:flag-for-germany',
en: 'emojione:flag-for-united-kingdom',
}
const { availableLocales, locale } = useI18n()
defineEmits<{ select: [Locale] }>()
</script>

View File

@ -0,0 +1,26 @@
<template>
<UiDropdown
:items="availableThemes"
@select="(theme) => $emit('select', theme)"
activator-class="btn btn-primary"
>
<template #label>
<Icon :name="currentTheme.icon" />
</template>
<template #item="{ item }">
<div class="flex gap-2 justify-center">
<Icon :name="item.icon" class="my-auto" />
<p>
{{ item.name }}
</p>
</div>
</template>
</UiDropdown>
</template>
<script setup lang="ts">
const { availableThemes, currentTheme } = storeToRefs(useUiStore())
defineEmits<{ select: [ITheme] }>()
</script>

View File

@ -1,78 +1,10 @@
<template>
<span>
<!-- <fieldset class="join w-full">
<slot name="prepend" />
<span class="input-group join-item">
<span
v-if="prependIcon || prependLabel"
class="input-group-text"
>
<label v-if="prependLabel">
{{ prependLabel }}
</label>
<Icon :name="prependIcon" />
</span>
<div class="relative w-full">
<input
:id
:name="name ?? id"
:placeholder="placeholder || label"
:type
:autofocus
class="input input-floating peer join-item"
:class="{
'input-sm':
currentScreenSize === 'sm' ||
currentScreenSize === '' ||
currentScreenSize === 'xs',
}"
v-bind="$attrs"
v-model="input"
ref="inputRef"
:readonly="read_only"
/>
<label
v-if="label"
:for="id"
class="input-floating-label"
>
{{ label }}
</label>
</div>
<span
v-if="appendIcon || appendLabel"
class="input-group-text"
>
<label
v-if="appendLabel"
class=""
>
{{ appendLabel }}
</label>
<Icon :name="appendIcon" />
</span>
</span>
<slot name="append" />
<UiButton
v-if="withCopyButton"
class="btn-outline btn-accent h-auto"
@click="copy(`${input}`)"
>
<Icon :name="copied ? 'mdi:check' : 'mdi:content-copy'" />
</UiButton>
</fieldset> -->
<fieldset class="join w-full p-1">
<div>
<fieldset class="join w-full">
<slot name="prepend" />
<div class="input join-item">
<Icon :name="prependIcon" class="my-auto shrink-0" />
<Icon v-if="prependIcon" :name="prependIcon" class="my-auto shrink-0" />
<div class="input-floating grow">
<input
@ -90,12 +22,13 @@
<label class="input-floating-label" :for="id">{{ label }}</label>
</div>
<Icon :name="appendIcon" class="my-auto shrink-0" />
<Icon v-if="appendIcon" :name="appendIcon" class="my-auto shrink-0" />
</div>
<slot name="append" class="h-auto" />
<UiButton
v-if="withCopyButton"
class="btn-outline btn-accent btn-square join-item h-auto"
@click="copy(`${input}`)"
>
@ -108,61 +41,61 @@
{{ error }}
</span>
</span>
</span>
</div>
</template>
<script setup lang="ts">
import { type ZodSchema } from "zod";
import { type ZodSchema } from 'zod'
const inputRef = useTemplateRef("inputRef");
defineExpose({ inputRef });
const inputRef = useTemplateRef('inputRef')
defineExpose({ inputRef })
defineOptions({
inheritAttrs: false,
});
})
const props = defineProps({
placeholder: {
type: String,
default: "",
default: '',
},
type: {
type: String as PropType<
| "button"
| "checkbox"
| "color"
| "date"
| "datetime-local"
| "email"
| "file"
| "hidden"
| "image"
| "month"
| "number"
| "password"
| "radio"
| "range"
| "reset"
| "search"
| "submit"
| "tel"
| "text"
| "time"
| "url"
| "week"
| 'button'
| 'checkbox'
| 'color'
| 'date'
| 'datetime-local'
| 'email'
| 'file'
| 'hidden'
| 'image'
| 'month'
| 'number'
| 'password'
| 'radio'
| 'range'
| 'reset'
| 'search'
| 'submit'
| 'tel'
| 'text'
| 'time'
| 'url'
| 'week'
>,
default: "text",
default: 'text',
},
label: String,
name: String,
prependIcon: {
type: String,
default: "",
default: '',
},
prependLabel: String,
appendIcon: {
type: String,
default: "",
default: '',
},
appendLabel: String,
rules: Object as PropType<ZodSchema>,
@ -170,45 +103,45 @@ const props = defineProps({
withCopyButton: Boolean,
autofocus: Boolean,
read_only: Boolean,
});
})
const input = defineModel<string | number | undefined | null>({
default: "",
default: '',
required: true,
});
})
const { currentScreenSize } = storeToRefs(useUiStore());
const { currentScreenSize } = storeToRefs(useUiStore())
onMounted(() => {
if (props.autofocus && inputRef.value) inputRef.value.focus();
});
if (props.autofocus && inputRef.value) inputRef.value.focus()
})
const errors = defineModel<string[] | undefined>("errors");
const errors = defineModel<string[] | undefined>('errors')
const id = useId();
const id = useId()
watch(input, () => checkInput());
watch(input, () => checkInput())
watch(
() => props.checkInput,
() => {
checkInput();
checkInput()
}
);
)
const emit = defineEmits(["error"]);
const emit = defineEmits(['error'])
const checkInput = () => {
if (props.rules) {
const result = props.rules.safeParse(input.value);
const result = props.rules.safeParse(input.value)
//console.log('check result', result.error, props.rules);
if (!result.success) {
errors.value = result.error.errors.map((error) => error.message);
emit("error", errors.value);
errors.value = result.error.errors.map((error) => error.message)
emit('error', errors.value)
} else {
errors.value = [];
errors.value = []
}
}
};
}
const { copy, copied } = useClipboard();
const { copy, copied } = useClipboard()
</script>

View File

@ -9,20 +9,22 @@
v-model="value"
>
<template #append>
<UiButton class="btn-outline btn-accent btn-square h-auto" @click="tooglePasswordType">
<Icon :name="type === 'password' ? 'mdi:eye' : 'mdi:eye-off'" />
<UiButton
class="btn-outline btn-accent btn-square h-auto"
@click="tooglePasswordType"
>
<Icon :name="type === 'password' ? 'mdi:eye-off' : 'mdi:eye'" />
</UiButton>
</template>
</UiInput>
</template>
<script setup lang="ts">
import type { ZodSchema } from "zod";
import type { ZodSchema } from 'zod'
const { t } = useI18n();
const { currentScreenSize } = storeToRefs(useUiStore());
const { t } = useI18n()
const value = defineModel<string | number | null | undefined>();
const value = defineModel<string | number | null | undefined>()
defineProps({
label: String,
@ -30,13 +32,13 @@ defineProps({
checkInput: Boolean,
rules: Object as PropType<ZodSchema>,
autofocus: Boolean,
});
})
const type = ref<"password" | "text">("password");
const type = ref<'password' | 'text'>('password')
const tooglePasswordType = () => {
type.value = type.value === "password" ? "text" : "password";
};
type.value = type.value === 'password' ? 'text' : 'password'
}
</script>
<i18n lang="json">