item handling

This commit is contained in:
2025-06-16 22:06:15 +02:00
parent 0b8f2c5532
commit 2972bb9e91
63 changed files with 3975 additions and 979 deletions

View File

@ -1,18 +1,18 @@
<template>
<div class="z-10">
<div class="z-10 pointer-events-auto">
<div
class="dropdown relative inline-flex [--placement:top] [--strategy:absolute]"
>
<button
:id
class="dropdown-toggle btn btn-primary btn-lg btn-square dropdown-open:rotate-45 transition-transform"
class="dropdown-toggle btn btn-primary btn-xl btn-square dropdown-open:rotate-45 transition-transform"
aria-haspopup="menu"
aria-expanded="false"
aria-label="Menu"
>
<Icon
:name="icon"
size="46"
class="size-11 shrink-0"
/>
</button>
@ -25,7 +25,7 @@
>
<li
v-for="link in menu"
class="dropdown-item hover:bg-transparent px-0"
class="dropdown-item hover:bg-transparent px-0 py-1"
>
<NuxtLinkLocale
v-if="link.to"

View File

@ -1,14 +1,22 @@
<template>
<button class="btn join-item" :type>
<slot />
<button
class="btn join-item pointer-events-auto"
:type
>
<UiTooltip
:tooltip
v-if="tooltip"
>
<slot />
</UiTooltip>
<slot v-else />
</button>
</template>
<script setup lang="ts">
defineProps({
type: {
type: String as PropType<"reset" | "submit" | "button">,
default: "button",
},
});
</script>
const { type = 'button' } = defineProps<{
type?: 'reset' | 'submit' | 'button'
tooltip?: string
}>()
</script>

View File

@ -2,7 +2,10 @@
<div class="card">
<slot name="image" />
<div class="card-header">
<div
class="card-header"
v-if="$slots.title || title"
>
<slot name="header">
<div
v-if="$slots.title || title"
@ -28,7 +31,10 @@
</slot>
</div>
<div class="card-body px-2 sm:px-6">
<div
class="card-body"
:class="bodyClass"
>
<slot />
<div
v-if="$slots.action"
@ -37,13 +43,25 @@
<slot name="action" />
</div>
</div>
<div
v-if="$slots.footer"
class="card-footer"
>
<slot name="footer" />
</div>
</div>
</template>
<script setup lang="ts">
const emit = defineEmits(['close', 'submit'])
defineProps<{ title?: string; subtitle?: string; icon?: string }>()
defineProps<{
title?: string
subtitle?: string
icon?: string
bodyClass?: string
}>()
const { escape, enter } = useMagicKeys()

View File

@ -1,7 +1,8 @@
<template>
<UiDialog
v-model:open="open"
:title
@close="onAbort"
v-model:open="open"
>
<template #trigger>
<slot name="trigger" />
@ -33,19 +34,19 @@
</template>
<script setup lang="ts">
defineProps<{ confirmLabel?: string; abortLabel?: string }>()
defineProps<{ confirmLabel?: string; abortLabel?: string; title?: string }>()
const open = defineModel<boolean>('open', { default: false })
const { t } = useI18n()
const emit = defineEmits(['confirm', 'abort'])
const onAbort = () => {
emit('abort')
open.value = false
}
const onConfirm = () => {
emit('confirm')
open.value = false
}
</script>

View File

@ -6,7 +6,6 @@
aria-haspopup="dialog"
aria-expanded="false"
:aria-label="label"
class="--prevent-on-load-init"
@click="$emit('open')"
>
<slot name="trigger">
@ -14,55 +13,58 @@
</slot>
</button>
<div
:id
ref="modalRef"
class="overlay modal overlay-open:opacity-100 hidden overlay-open:duration-300 sm:modal-middle p-0 xs:p-2"
role="dialog"
tabindex="-1"
>
<Teleport to="body">
<div
class="overlay-animation-target overlay-open:duration-300 transition-all ease-out modal-dialog overlay-open:opacity-100 pointer-events-auto overflow-y-auto"
:id
ref="modalRef"
class="overlay modal overlay-open:opacity-100 overlay-open:duration-300 hidden modal-middle p-0 xs:p-2 --prevent-on-load-init pointer-events-auto max-w-none"
role="dialog"
tabindex="-1"
>
<div class="modal-content justify-between h-full max-h-none">
<div class="modal-header">
<div
v-if="title || $slots.title"
class="modal-title py-4 break-all"
>
<slot name="title">
{{ title }}
</slot>
<div
class="overlay-animation-target overlay-open:duration-300 overlay-open:opacity-100 transition-all ease-out modal-dialog"
>
<div class="modal-content justify-between">
<div class="modal-header">
<div
v-if="title || $slots.title"
class="modal-title py-4 break-all"
>
<slot name="title">
{{ title }}
</slot>
</div>
<button
type="button"
class="btn btn-text btn-circle btn-sm absolute end-3 top-3"
:aria-label="t('close')"
tabindex="1"
@click="open = false"
>
<Icon
name="mdi:close"
size="18"
/>
</button>
</div>
<button
type="button"
class="btn btn-text btn-circle btn-sm absolute end-3 top-3"
:aria-label="t('close')"
tabindex="1"
@click="open = false"
>
<Icon
name="mdi:close"
size="18"
/>
</button>
</div>
<div class="modal-body text-sm sm:text-base grow">
<slot />
</div>
<div class="modal-body text-sm sm:text-base">
<slot />
</div>
<div class="modal-footer flex-col sm:flex-row">
<slot name="buttons" />
<div class="modal-footer flex-col sm:flex-row">
<slot name="buttons" />
</div>
</div>
</div>
</div>
</div>
</Teleport>
</template>
<script setup lang="ts">
import type { HSOverlay } from 'flyonui/flyonui'
const { currentTheme } = storeToRefs(useUiStore())
defineProps<{ title?: string; label?: string }>()
@ -81,10 +83,12 @@ defineExpose({ modalRef })
const modal = ref<HSOverlay>()
watch(open, async () => {
if (!modal.value) return
if (open.value) {
await modal.value?.open()
await modal.value.open()
} else {
await modal.value?.close(true)
await modal.value.close(true)
emit('close')
}
})
@ -92,10 +96,9 @@ watch(open, async () => {
onMounted(async () => {
if (!modalRef.value) return
modal.value = new window.HSOverlay(modalRef.value, {
isClosePrev: true,
})
modal.value = new window.HSOverlay(modalRef.value)
modal.value.isLayoutAffect = true
modal.value.on('close', () => {
open.value = false
})

View File

@ -0,0 +1,55 @@
<template>
<UiDialogConfirm
:confirm-label="t('apply')"
:title="t('title')"
@abort="open = false"
@click="open = true"
class="btn btn-square btn-accent btn-outline"
v-model:open="open"
>
<template #trigger>
<Icon name="mdi:dice" />
</template>
<form class="flex flex-col gap-4">
<UiInputPassword
v-model="newPassword"
prepend-icon="mdi:key-outline"
with-copy-button
>
<template #append>
<UiButton class="btn-square btn-accent btn-outline">
<Icon name="mdi:refresh" />
</UiButton>
</template>
</UiInputPassword>
</form>
</UiDialogConfirm>
</template>
<script setup lang="ts">
const open = defineModel<boolean>()
const { t } = useI18n()
const { password } = defineProps<{
autofocus?: boolean
checkInput?: boolean
label?: string
placeholder?: string
withCopyButton?: boolean
password: string | null
}>()
const newPassword = computed(() => password)
</script>
<i18n lang="yaml">
de:
title: Passwortgenerator
apply: Übernehmen
en:
title: Passwordgenerator
apply: Apply
</i18n>

View File

@ -1,15 +1,16 @@
<template>
<div
class="dropdown relative inline-flex"
:class="offset"
class="dropdown relative inline-flex"
>
<button
:id
class="dropdown-toggle"
v-bind="$attrs"
aria-haspopup="menu"
aria-expanded="false"
:aria-label="label"
:id
aria-expanded="false"
aria-haspopup="menu"
class="dropdown-toggle"
type="button"
v-bind="$attrs"
>
<slot name="activator">
{{ label }}
@ -20,21 +21,24 @@
</button>
<ul
class="dropdown-menu dropdown-open:opacity-100 hidden min-w-28"
role="menu"
aria-orientation="vertical"
:aria-labelledby="id"
aria-orientation="vertical"
class="dropdown-menu dropdown-open:opacity-100 hidden min-w-28 z-20 shadow shadow-primary"
role="menu"
>
<slot name="items">
<slot
name="items"
:items
>
<li
:is="itemIs"
v-for="item in items"
class="dropdown-item"
@click="$emit('select', item)"
class="dropdown-item"
v-for="item in items"
>
<slot
name="item"
:item
name="item"
>
{{ item }}
</slot>
@ -45,6 +49,10 @@
</template>
<script setup lang="ts" generic="T">
defineOptions({
inheritAttrs: false,
})
const { itemIs = 'li', offset = '[--offset:0]' } = defineProps<{
label?: string
items?: T[]
@ -53,10 +61,6 @@ const { itemIs = 'li', offset = '[--offset:0]' } = defineProps<{
offset?: string
}>()
defineOptions({
inheritAttrs: false,
})
defineEmits<{ select: [T] }>()
const id = useId()

View File

@ -0,0 +1,132 @@
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 782.04441 701.88002"
xmlns:xlink="http://www.w3.org/1999/xlink"
role="img"
artist="Katerina Limpitsouni"
source="https://undraw.co/"
>
<path
d="M609.48783,100.59015l-25.44631,6.56209L270.53735,187.9987,245.091,194.56079A48.17927,48.17927,0,0,0,210.508,253.17865L320.849,681.05606a48.17924,48.17924,0,0,0,58.61776,34.58317l.06572-.01695,364.26536-93.93675.06572-.01695a48.17923,48.17923,0,0,0,34.58309-58.6178l-110.341-427.87741A48.17928,48.17928,0,0,0,609.48783,100.59015Z"
transform="translate(-208.9778 -99.05999)"
fill="#f2f2f2"
/>
<path
d="M612.94784,114.00532l-30.13945,7.77236L278.68955,200.20385l-30.139,7.77223a34.30949,34.30949,0,0,0-24.6275,41.74308l110.341,427.87741a34.30946,34.30946,0,0,0,41.7431,24.62736l.06572-.01695,364.26536-93.93674.06619-.01707a34.30935,34.30935,0,0,0,24.627-41.7429l-110.341-427.87741A34.30938,34.30938,0,0,0,612.94784,114.00532Z"
transform="translate(-208.9778 -99.05999)"
fill="#fff"
/>
<path
d="M590.19,252.56327,405.917,300.08359a8.01411,8.01411,0,0,1-4.00241-15.52046l184.273-47.52033A8.01412,8.01412,0,0,1,590.19,252.56327Z"
transform="translate(-208.9778 -99.05999)"
fill="#f2f2f2"
/>
<path
d="M628.955,270.49906,412.671,326.27437a8.01411,8.01411,0,1,1-4.00241-15.52046l216.284-55.77531a8.01411,8.01411,0,0,1,4.00242,15.52046Z"
transform="translate(-208.9778 -99.05999)"
fill="#f2f2f2"
/>
<path
d="M620.45825,369.93676l-184.273,47.52032a8.01411,8.01411,0,1,1-4.00242-15.52046l184.273-47.52032a8.01411,8.01411,0,1,1,4.00241,15.52046Z"
transform="translate(-208.9778 -99.05999)"
fill="#f2f2f2"
/>
<path
d="M659.22329,387.87255l-216.284,55.77531a8.01411,8.01411,0,1,1-4.00242-15.52046l216.284-55.77531a8.01411,8.01411,0,0,1,4.00242,15.52046Z"
transform="translate(-208.9778 -99.05999)"
fill="#f2f2f2"
/>
<path
d="M650.72653,487.31025l-184.273,47.52033a8.01412,8.01412,0,0,1-4.00242-15.52047l184.273-47.52032a8.01411,8.01411,0,0,1,4.00242,15.52046Z"
transform="translate(-208.9778 -99.05999)"
fill="#f2f2f2"
/>
<path
d="M689.49156,505.246l-216.284,55.77532a8.01412,8.01412,0,1,1-4.00241-15.52047l216.284-55.77531a8.01411,8.01411,0,0,1,4.00242,15.52046Z"
transform="translate(-208.9778 -99.05999)"
fill="#f2f2f2"
/>
<path
d="M374.45884,348.80871l-65.21246,16.817a3.847,3.847,0,0,1-4.68062-2.76146L289.5963,304.81607a3.847,3.847,0,0,1,2.76145-4.68061l65.21247-16.817a3.847,3.847,0,0,1,4.68061,2.76145l14.96947,58.04817A3.847,3.847,0,0,1,374.45884,348.80871Z"
transform="translate(-208.9778 -99.05999)"
fill="#e6e6e6"
/>
<path
d="M404.72712,466.1822l-65.21247,16.817a3.847,3.847,0,0,1-4.68062-2.76146l-14.96946-58.04816A3.847,3.847,0,0,1,322.626,417.509l65.21246-16.817a3.847,3.847,0,0,1,4.68062,2.76145l14.96946,58.04817A3.847,3.847,0,0,1,404.72712,466.1822Z"
transform="translate(-208.9778 -99.05999)"
fill="#e6e6e6"
/>
<path
d="M434.99539,583.55569l-65.21246,16.817a3.847,3.847,0,0,1-4.68062-2.76145l-14.96946-58.04817a3.847,3.847,0,0,1,2.76145-4.68062l65.21247-16.817a3.847,3.847,0,0,1,4.68061,2.76146l14.96947,58.04816A3.847,3.847,0,0,1,434.99539,583.55569Z"
transform="translate(-208.9778 -99.05999)"
fill="#e6e6e6"
/>
<path
d="M863.63647,209.0517H487.31811a48.17928,48.17928,0,0,0-48.125,48.12512V699.05261a48.17924,48.17924,0,0,0,48.125,48.12507H863.63647a48.17924,48.17924,0,0,0,48.125-48.12507V257.17682A48.17928,48.17928,0,0,0,863.63647,209.0517Z"
transform="translate(-208.9778 -99.05999)"
fill="#e6e6e6"
/>
<path
d="M863.637,222.90589H487.31811a34.30948,34.30948,0,0,0-34.271,34.27093V699.05261a34.30947,34.30947,0,0,0,34.271,34.27088H863.637a34.30936,34.30936,0,0,0,34.27051-34.27088V257.17682A34.30937,34.30937,0,0,0,863.637,222.90589Z"
transform="translate(-208.9778 -99.05999)"
fill="#fff"
/>
<circle
cx="694.19401"
cy="614.02963"
r="87.85039"
fill="currentColor"
/>
<path
d="M945.18722,701.63087H914.63056V671.07421a11.45875,11.45875,0,0,0-22.9175,0v30.55666H861.1564a11.45875,11.45875,0,0,0,0,22.9175h30.55666V755.105a11.45875,11.45875,0,1,0,22.9175,0V724.54837h30.55666a11.45875,11.45875,0,0,0,0-22.9175Z"
transform="translate(-208.9778 -99.05999)"
fill="#fff"
/>
<path
d="M807.00068,465.71551H616.699a8.01412,8.01412,0,1,1,0-16.02823H807.00068a8.01412,8.01412,0,0,1,0,16.02823Z"
transform="translate(-208.9778 -99.05999)"
fill="#e6e6e6"
/>
<path
d="M840.05889,492.76314H616.699a8.01412,8.01412,0,1,1,0-16.02823H840.05889a8.01411,8.01411,0,1,1,0,16.02823Z"
transform="translate(-208.9778 -99.05999)"
fill="#e6e6e6"
/>
<path
d="M807.00068,586.929H616.699a8.01412,8.01412,0,1,1,0-16.02823H807.00068a8.01411,8.01411,0,0,1,0,16.02823Z"
transform="translate(-208.9778 -99.05999)"
fill="#e6e6e6"
/>
<path
d="M840.05889,613.97661H616.699a8.01412,8.01412,0,1,1,0-16.02823H840.05889a8.01412,8.01412,0,1,1,0,16.02823Z"
transform="translate(-208.9778 -99.05999)"
fill="#e6e6e6"
/>
<path
d="M574.07028,505.04162H506.72434a3.847,3.847,0,0,1-3.84278-3.84278V441.25158a3.847,3.847,0,0,1,3.84278-3.84278h67.34594a3.847,3.847,0,0,1,3.84278,3.84278v59.94726A3.847,3.847,0,0,1,574.07028,505.04162Z"
transform="translate(-208.9778 -99.05999)"
fill="#e6e6e6"
/>
<path
d="M574.07028,626.25509H506.72434a3.847,3.847,0,0,1-3.84278-3.84278V562.46505a3.847,3.847,0,0,1,3.84278-3.84278h67.34594a3.847,3.847,0,0,1,3.84278,3.84278v59.94726A3.847,3.847,0,0,1,574.07028,626.25509Z"
transform="translate(-208.9778 -99.05999)"
fill="#e6e6e6"
/>
<path
d="M807.21185,330.781H666.91017a8.01411,8.01411,0,0,1,0-16.02823H807.21185a8.01411,8.01411,0,0,1,0,16.02823Z"
transform="translate(-208.9778 -99.05999)"
fill="#ccc"
/>
<path
d="M840.27007,357.82862H666.91017a8.01411,8.01411,0,1,1,0-16.02822h173.3599a8.01411,8.01411,0,0,1,0,16.02822Z"
transform="translate(-208.9778 -99.05999)"
fill="#ccc"
/>
<path
d="M635.85911,390.6071H506.51316a3.847,3.847,0,0,1-3.84277-3.84277V285.81706a3.847,3.847,0,0,1,3.84277-3.84277H635.85911a3.847,3.847,0,0,1,3.84277,3.84277V386.76433A3.847,3.847,0,0,1,635.85911,390.6071Z"
transform="translate(-208.9778 -99.05999)"
fill="currentColor"
/>
</svg>
</template>

View File

@ -0,0 +1,65 @@
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 647.63626 632.17383"
xmlns:xlink="http://www.w3.org/1999/xlink"
role="img"
artist="Katerina Limpitsouni"
source="https://undraw.co/"
>
<path
d="M687.3279,276.08691H512.81813a15.01828,15.01828,0,0,0-15,15v387.85l-2,.61005-42.81006,13.11a8.00676,8.00676,0,0,1-9.98974-5.31L315.678,271.39691a8.00313,8.00313,0,0,1,5.31006-9.99l65.97022-20.2,191.25-58.54,65.96972-20.2a7.98927,7.98927,0,0,1,9.99024,5.3l32.5498,106.32Z"
transform="translate(-276.18187 -133.91309)"
fill="#f2f2f2"
/>
<path
d="M725.408,274.08691l-39.23-128.14a16.99368,16.99368,0,0,0-21.23-11.28l-92.75,28.39L380.95827,221.60693l-92.75,28.4a17.0152,17.0152,0,0,0-11.28028,21.23l134.08008,437.93a17.02661,17.02661,0,0,0,16.26026,12.03,16.78926,16.78926,0,0,0,4.96972-.75l63.58008-19.46,2-.62v-2.09l-2,.61-64.16992,19.65a15.01489,15.01489,0,0,1-18.73-9.95l-134.06983-437.94a14.97935,14.97935,0,0,1,9.94971-18.73l92.75-28.4,191.24024-58.54,92.75-28.4a15.15551,15.15551,0,0,1,4.40966-.66,15.01461,15.01461,0,0,1,14.32032,10.61l39.0498,127.56.62012,2h2.08008Z"
transform="translate(-276.18187 -133.91309)"
fill="#3f3d56"
/>
<path
d="M398.86279,261.73389a9.0157,9.0157,0,0,1-8.61133-6.3667l-12.88037-42.07178a8.99884,8.99884,0,0,1,5.9712-11.24023l175.939-53.86377a9.00867,9.00867,0,0,1,11.24072,5.9707l12.88037,42.07227a9.01029,9.01029,0,0,1-5.9707,11.24072L401.49219,261.33887A8.976,8.976,0,0,1,398.86279,261.73389Z"
transform="translate(-276.18187 -133.91309)"
fill="currentColor"
/>
<circle
cx="190.15351"
cy="24.95465"
r="20"
fill="currentColor"
/>
<circle
cx="190.15351"
cy="24.95465"
r="12.66462"
fill="#fff"
/>
<path
d="M878.81836,716.08691h-338a8.50981,8.50981,0,0,1-8.5-8.5v-405a8.50951,8.50951,0,0,1,8.5-8.5h338a8.50982,8.50982,0,0,1,8.5,8.5v405A8.51013,8.51013,0,0,1,878.81836,716.08691Z"
transform="translate(-276.18187 -133.91309)"
fill="#e6e6e6"
/>
<path
d="M723.31813,274.08691h-210.5a17.02411,17.02411,0,0,0-17,17v407.8l2-.61v-407.19a15.01828,15.01828,0,0,1,15-15H723.93825Zm183.5,0h-394a17.02411,17.02411,0,0,0-17,17v458a17.0241,17.0241,0,0,0,17,17h394a17.0241,17.0241,0,0,0,17-17v-458A17.02411,17.02411,0,0,0,906.81813,274.08691Zm15,475a15.01828,15.01828,0,0,1-15,15h-394a15.01828,15.01828,0,0,1-15-15v-458a15.01828,15.01828,0,0,1,15-15h394a15.01828,15.01828,0,0,1,15,15Z"
transform="translate(-276.18187 -133.91309)"
fill="#3f3d56"
/>
<path
d="M801.81836,318.08691h-184a9.01015,9.01015,0,0,1-9-9v-44a9.01016,9.01016,0,0,1,9-9h184a9.01016,9.01016,0,0,1,9,9v44A9.01015,9.01015,0,0,1,801.81836,318.08691Z"
transform="translate(-276.18187 -133.91309)"
fill="currentColor"
/>
<circle
cx="433.63626"
cy="105.17383"
r="20"
fill="currentColor"
/>
<circle
cx="433.63626"
cy="105.17383"
r="12.18187"
fill="#fff"
/>
</svg>
</template>

View File

@ -29,8 +29,9 @@
<label
class="input-floating-label"
:for="id"
>{{ label }}</label
>
{{ label }}
</label>
</div>
<Icon
@ -47,7 +48,8 @@
<UiButton
v-if="withCopyButton"
class="btn-outline btn-accent btn-square join-item h-auto"
:tooltip="t('copy')"
class="btn-outline btn-accent btn-square"
@click="copy(`${input}`)"
>
<Icon :name="copied ? 'mdi:check' : 'mdi:content-copy'" />
@ -167,4 +169,14 @@ const checkInput = () => {
}
const { copy, copied } = useClipboard()
const { t } = useI18n()
</script>
<i18n lang="yaml">
de:
copy: Kopieren
en:
copy: Copy
</i18n>

View File

@ -1,9 +1,21 @@
<template>
<UiInput
v-model="value" :check-input :label="label || t('password')" :placeholder="placeholder || t('password')" :rules
:type="type" :autofocus>
v-model="value"
:autofocus
:check-input
:label="label || t('password')"
:placeholder="placeholder || t('password')"
:rules
:type="type"
:with-copy-button
>
<template #append>
<UiButton class="btn-outline btn-accent btn-square h-auto" @click="tooglePasswordType">
<slot name="append" />
<UiButton
class="btn-outline btn-accent btn-square join-item"
@click="tooglePasswordType"
>
<Icon :name="type === 'password' ? 'mdi:eye-off' : 'mdi:eye'" />
</UiButton>
</template>
@ -11,33 +23,35 @@ v-model="value" :check-input :label="label || t('password')" :placeholder="place
</template>
<script setup lang="ts">
import type { ZodSchema } from "zod";
import type { ZodSchema } from 'zod'
const { t } = useI18n();
const { t } = useI18n()
const value = defineModel<string | number | null | undefined>();
const value = defineModel<string | number | null | undefined>()
defineProps({
label: String,
placeholder: String,
checkInput: Boolean,
rules: Object as PropType<ZodSchema>,
autofocus: Boolean,
});
defineProps<{
autofocus?: boolean
checkInput?: boolean
label?: string
placeholder?: string
rules?: ZodSchema
withCopyButton?: 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">{
<i18n lang="json">
{
"de": {
"password": "Passwort"
},
"en": {
"password": "Password"
}
}</i18n>
}
</i18n>

View File

@ -0,0 +1,52 @@
<template>
<UiInput
:autofocus
:check-input="checkInput"
:label="label || t('url')"
:placeholder="placeholder || t('url')"
:read_only
:rules
:with-copy-button
v-model.trim="value"
>
<template #append>
<UiButton
:disabled="!value?.length"
@click="openUrl(`${value}`)"
class="btn-outline btn-accent btn-square"
>
<Icon name="streamline:web" />
</UiButton>
</template>
</UiInput>
</template>
<script setup lang="ts">
import type { ZodSchema } from 'zod'
import { openUrl } from '@tauri-apps/plugin-opener'
const { t } = useI18n()
const value = defineModel<string | null | undefined>()
defineProps({
label: String,
placeholder: String,
checkInput: Boolean,
rules: Object as PropType<ZodSchema>,
autofocus: Boolean,
withCopyButton: Boolean,
read_only: Boolean,
})
</script>
<i18n lang="json">
{
"de": {
"url": "Url"
},
"en": {
"url": "Url"
}
}
</i18n>

View File

@ -0,0 +1,7 @@
<template>
<div
class="flex flex-col h-fit border border-base-content/25 divide-base-content/25 divide-y rounded-md first:rounded-t-md last:rounded-b-md"
>
<slot />
</div>
</template>

View File

@ -1,29 +1,34 @@
<template>
<div class="flex items-center gap-4">
<label
:for="id"
class="font-medium"
<div class="flex items-center gap-4 relative">
<UiButton
:style="{ 'background-color': model }"
:class="[textColorClass]"
@click="colorRef?.click()"
>
{{ t('label') }}
</label>
</UiButton>
<input
:id
:readonly="read_only"
:disabled="read_only"
:title="t('title')"
class="p-0 cursor-pointer disabled:opacity-50 disabled:pointer-events-none w-14 h-10"
:title="t('pick')"
class="top-0 left-0 absolute size-0"
type="color"
v-model="model"
ref="colorRef"
/>
<button
@click="model = null"
class="btn btn-sm text-sm"
:class="{ 'btn-disabled': read_only }"
>
{{ t('reset') }}
</button>
<UiTooltip :tooltip="t('reset')">
<button
@click="model = ''"
class="btn btn-sm text-sm btn-outline btn-error"
:class="{ 'btn-disabled': read_only }"
type="button"
>
<Icon name="mdi:refresh" />
</button>
</UiTooltip>
</div>
</template>
@ -31,11 +36,20 @@
const id = useId()
const { t } = useI18n()
const model = defineModel()
const model = defineModel<string | null>()
const colorRef = useTemplateRef('colorRef')
defineProps({
read_only: Boolean,
})
const { currentTheme } = storeToRefs(useUiStore())
const textColorClass = computed(() => {
if (!model.value)
return currentTheme.value.value === 'dark' ? 'text-black' : 'text-white'
const color = getContrastingTextColor(model.value)
return color === 'white' ? 'text-white' : 'text-black'
})
</script>
<i18n lang="json">
@ -43,12 +57,14 @@ defineProps({
"de": {
"label": "Farbauswahl",
"title": "Wähle eine Farbe aus",
"reset": "zurücksetzen"
"reset": "zurücksetzen",
"pick": "Auswahl"
},
"en": {
"label": "Color Picker",
"title": "Choose a color",
"reset": "Reset"
"reset": "Reset",
"pick": "Pick"
}
}
</i18n>

View File

@ -1,37 +1,77 @@
<template>
<UiSelect
v-model="icon"
:options="icons"
label="Icon Picker"
<UiDropdown
:items="icons"
class="btn"
@select="(newIcon) => (iconName = newIcon)"
>
<template #value="{ value }">
<Icon
:name="value"
v-if="value"
/>
<template #activator>
<Icon :name="iconName ? iconName : defaultIcon || icons.at(0)" />
</template>
<template #option="{ option }">
<Icon :name="option ?? ''" />
</template>
</UiSelect>
<UiDropdown :items="icons">
<template #activator> {{ icons.find((_icon) => _icon === icon) }}</template>
<template #item="{ item }">
<Icon :name="`mdi:${item}`" />
{{ item }}
<template #items="{ items }">
<div class="grid grid-cols-6 -ml-2">
<li
class="dropdown-item"
v-for="item in items"
@click="iconName = item"
>
<Icon
:name="item"
size="36"
/>
</li>
</div>
</template>
</UiDropdown>
</template>
<script setup lang="ts">
const icons = [
'streamline:money-bank-institution-money-saving-bank-payment-finance',
'material-symbols:star-outline-rounded',
'mdi:folder-outline',
'mdi:key-outline',
'pepicons-pop:smartphone-home-button',
'majesticons:desktop-computer-line',
'mdi:folder',
'mdi:amazon',
'proicons:bank',
'mdi:bitcoin',
'mdi:piggy-bank-outline',
'mdi:account-outline',
'proicons:computer',
'proicons:cloud',
'proicons:game',
'proicons:github',
'proicons:wrench',
'proicons:vehicle-car',
'proicons:wi-fi',
'meteor-icons:microchip',
'meteor-icons:headphones',
'meteor-icons:star',
'fe:mail',
'fe:rocket',
'fxemoji:trolleybus',
'mdi:folder-outline',
'mdi:key-outline',
'pepicons-pop:smartphone-home-button',
'mdi:amazon',
'proicons:bank',
'mdi:bitcoin',
'mdi:piggy-bank-outline',
'mdi:account-outline',
'proicons:computer',
'proicons:cloud',
'proicons:game',
'proicons:github',
'proicons:wrench',
'proicons:vehicle-car',
'proicons:wi-fi',
'meteor-icons:microchip',
'meteor-icons:headphones',
'meteor-icons:star',
'fe:mail',
'fe:rocket',
'fxemoji:trolleybus',
]
const icon = defineModel<string | undefined | null>({ default: '' })
const iconName = defineModel<string | undefined | null>()
defineProps<{ defaultIcon?: string }>()
</script>

View File

@ -14,7 +14,7 @@
:value
class=""
>
<span>{{ value }}</span>
<span>{{ value ?? label }}</span>
</slot>
</button>
<button

View File

@ -0,0 +1,54 @@
<template>
<div class="relative">
<UiButton
v-if="withCopyButton"
:tooltip="t('copy')"
class="btn-square btn-outline btn-accent absolute z-10 top-2 right-2"
@click="copy(`${value}`)"
>
<Icon :name="copied ? 'mdi:check' : 'mdi:content-copy'" />
</UiButton>
<div class="textarea-floating">
<textarea
:class="{ 'pr-10': withCopyButton }"
:id
:placeholder
:readonly="read_only"
class="textarea"
v-bind="$attrs"
v-model="value"
></textarea>
<label
class="textarea-floating-label"
:for="id"
>
{{ label }}
</label>
</div>
</div>
</template>
<script setup lang="ts">
defineProps<{
placeholder?: string
label?: string
read_only?: boolean
withCopyButton?: boolean
}>()
const id = useId()
const value = defineModel<string | null | undefined>()
const { copy, copied } = useClipboard()
const { t } = useI18n()
</script>
<i18n lang="yaml">
de:
copy: Kopieren
en:
copy: Copy
</i18n>

View File

@ -2,7 +2,7 @@
<div class="tooltip [--prevent-popper:false]">
<div
class="tooltip-toggle"
aria-label="Tooltip"
:aria-label="tooltip"
>
<slot>
<button class="btn btn-square">
@ -11,7 +11,7 @@
</slot>
<span
class="tooltip-content tooltip-shown:opacity-100 tooltip-shown:visible z-40"
class="tooltip-content tooltip-shown:opacity-100 tooltip-shown:visible z-40 pointer-events-none"
role="tooltip"
>
<span