mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-17 14:30:52 +01:00
switch to nuxt ui
This commit is contained in:
@ -1,181 +1,88 @@
|
||||
<template>
|
||||
<div>
|
||||
<fieldset
|
||||
class="join w-full"
|
||||
:class="{ 'pt-1.5': label }"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<slot name="prepend" />
|
||||
|
||||
<div class="input join-item">
|
||||
<Icon
|
||||
v-if="prependIcon"
|
||||
:name="prependIcon"
|
||||
class="my-auto shrink-0"
|
||||
/>
|
||||
|
||||
<div class="input-floating grow">
|
||||
<input
|
||||
:autofocus
|
||||
:id
|
||||
:name="name ?? id"
|
||||
:placeholder="placeholder || label"
|
||||
:readonly="read_only"
|
||||
:type
|
||||
class="ps-2"
|
||||
ref="inputRef"
|
||||
v-model="input"
|
||||
@keyup="(e:KeyboardEvent) => $emit('keyup', e)"
|
||||
/>
|
||||
<label
|
||||
:for="id"
|
||||
class="input-floating-label"
|
||||
>
|
||||
{{ label }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<Icon
|
||||
v-if="appendIcon"
|
||||
:name="appendIcon"
|
||||
class="my-auto shrink-0"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<UiButton
|
||||
v-if="withClearButton"
|
||||
class="btn-outline btn-square"
|
||||
@click="input = ''"
|
||||
>
|
||||
<Icon name="mdi:close" />
|
||||
</UiButton>
|
||||
|
||||
<slot name="append" />
|
||||
|
||||
<UiButton
|
||||
v-if="withCopyButton"
|
||||
:tooltip="t('copy')"
|
||||
class="btn-outline btn-accent btn-square"
|
||||
@click="copy(`${input}`)"
|
||||
>
|
||||
<Icon :name="copied ? 'mdi:check' : 'mdi:content-copy'" />
|
||||
</UiButton>
|
||||
</fieldset>
|
||||
|
||||
<span
|
||||
v-show="errors"
|
||||
class="flex flex-col px-2 pt-0.5"
|
||||
<UInput
|
||||
v-model="value"
|
||||
:placeholder="props.placeholder || ' '"
|
||||
:readonly="props.readOnly"
|
||||
:leading-icon="props.leadingIcon"
|
||||
:ui="{ base: 'peer' }"
|
||||
@change="(e) => $emit('change', e)"
|
||||
@blur="(e) => $emit('blur', e)"
|
||||
@keyup="(e: KeyboardEvent) => $emit('keyup', e)"
|
||||
@keydown="(e: KeyboardEvent) => $emit('keydown', e)"
|
||||
>
|
||||
<label
|
||||
class="absolute pointer-events-none -top-2.5 left-0 text-highlighted text-xs font-medium px-1.5 transition-all peer-focus:-top-2.5 peer-focus:text-highlighted peer-focus:text-xs peer-focus:font-medium peer-placeholder-shown:text-sm peer-placeholder-shown:text-dimmed peer-placeholder-shown:top-1.5 peer-placeholder-shown:font-normal"
|
||||
>
|
||||
<span
|
||||
v-for="error in errors"
|
||||
class="label-text-alt text-error"
|
||||
class="inline-flex bg-default px-1"
|
||||
:class="props?.leadingIcon ? 'mx-6' : 'mx-0'"
|
||||
>
|
||||
{{ error }}
|
||||
{{ props?.label }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<template #trailing>
|
||||
<slot name="trailing" />
|
||||
|
||||
<UiButton
|
||||
v-show="props.withCopyButton"
|
||||
:color="copied ? 'success' : 'neutral'"
|
||||
:tooltip="t('copy')"
|
||||
:icon="copied ? 'mdi:check' : 'mdi:content-copy'"
|
||||
size="sm"
|
||||
variant="link"
|
||||
@click="copy(`${value}`)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template
|
||||
v-for="(_, slotName) in filteredSlots"
|
||||
#[slotName]="slotProps"
|
||||
>
|
||||
<slot
|
||||
:name="slotName"
|
||||
v-bind="slotProps"
|
||||
/>
|
||||
</template>
|
||||
</UInput>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { ZodSchema } from 'zod'
|
||||
import type { AcceptableValue, InputProps } from '@nuxt/ui'
|
||||
|
||||
const input = defineModel<string | number | undefined | null>({
|
||||
required: true,
|
||||
})
|
||||
const value = defineModel<AcceptableValue | undefined>()
|
||||
|
||||
const inputRef = useTemplateRef('inputRef')
|
||||
defineExpose({ inputRef })
|
||||
|
||||
const emit = defineEmits<{
|
||||
error: [string[]]
|
||||
keyup: [KeyboardEvent]
|
||||
}>()
|
||||
|
||||
const props = defineProps({
|
||||
placeholder: {
|
||||
type: String,
|
||||
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'
|
||||
>,
|
||||
default: 'text',
|
||||
},
|
||||
label: String,
|
||||
name: String,
|
||||
prependIcon: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
prependLabel: String,
|
||||
appendIcon: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
appendLabel: String,
|
||||
rules: Object as PropType<ZodSchema>,
|
||||
checkInput: Boolean,
|
||||
withCopyButton: Boolean,
|
||||
withClearButton: Boolean,
|
||||
autofocus: Boolean,
|
||||
read_only: Boolean,
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
if (props.autofocus && inputRef.value) inputRef.value.focus()
|
||||
})
|
||||
|
||||
const errors = defineModel<string[] | undefined>('errors')
|
||||
|
||||
const id = useId()
|
||||
|
||||
watch(input, () => checkInput())
|
||||
|
||||
watch(
|
||||
() => props.checkInput,
|
||||
() => {
|
||||
checkInput()
|
||||
},
|
||||
)
|
||||
|
||||
const checkInput = () => {
|
||||
if (props.rules) {
|
||||
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)
|
||||
} else {
|
||||
errors.value = []
|
||||
}
|
||||
}
|
||||
interface IInputProps extends /* @vue-ignore */ InputProps {
|
||||
tooltip?: string
|
||||
}
|
||||
|
||||
const props = defineProps<
|
||||
IInputProps & {
|
||||
withCopyButton?: boolean
|
||||
readOnly?: boolean
|
||||
label?: string
|
||||
leadingIcon?: string
|
||||
}
|
||||
>()
|
||||
|
||||
defineEmits<{
|
||||
change: [Event]
|
||||
blur: [Event]
|
||||
keyup: [KeyboardEvent]
|
||||
keydown: [KeyboardEvent]
|
||||
}>()
|
||||
|
||||
const { copy, copied } = useClipboard()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const filteredSlots = computed(() => {
|
||||
return Object.fromEntries(
|
||||
Object.entries(useSlots()).filter(([name]) => name !== 'trailing'),
|
||||
)
|
||||
})
|
||||
|
||||
watchImmediate(props, () => console.log('props', props))
|
||||
</script>
|
||||
|
||||
<i18n lang="yaml">
|
||||
|
||||
@ -1,62 +1,53 @@
|
||||
<template>
|
||||
<UiInput
|
||||
v-model="value"
|
||||
:autofocus
|
||||
:check-input
|
||||
:label="label || t('password')"
|
||||
:placeholder="placeholder || t('password')"
|
||||
:rules
|
||||
:type="type"
|
||||
:label="t('label')"
|
||||
:leading-icon
|
||||
:placeholder="placeholder || ' '"
|
||||
:read-only
|
||||
:type="show ? 'text' : 'password'"
|
||||
:with-copy-button
|
||||
@keyup="(e) => $emit('keyup', e)"
|
||||
>
|
||||
<template #append>
|
||||
<slot name="append" />
|
||||
|
||||
<template #trailing>
|
||||
<UiButton
|
||||
class="btn-outline btn-accent btn-square join-item"
|
||||
@click="tooglePasswordType"
|
||||
>
|
||||
<Icon :name="type === 'password' ? 'mdi:eye-off' : 'mdi:eye'" />
|
||||
</UiButton>
|
||||
aria-controls="password"
|
||||
color="neutral"
|
||||
variant="link"
|
||||
:aria-label="show ? t('hide') : t('show')"
|
||||
:aria-pressed="show"
|
||||
:icon="show ? 'i-lucide-eye-off' : 'i-lucide-eye'"
|
||||
:tooltip="show ? t('hide') : t('show')"
|
||||
size="sm"
|
||||
@click="show = !show"
|
||||
/>
|
||||
</template>
|
||||
</UiInput>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { ZodSchema } from 'zod'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const value = defineModel<string | number | null | undefined>()
|
||||
import type { AcceptableValue } from '@nuxt/ui'
|
||||
|
||||
defineProps<{
|
||||
autofocus?: boolean
|
||||
checkInput?: boolean
|
||||
label?: string
|
||||
placeholder?: string
|
||||
rules?: ZodSchema
|
||||
leadingIcon?: string
|
||||
withCopyButton?: boolean
|
||||
readOnly?: boolean
|
||||
}>()
|
||||
const value = defineModel<AcceptableValue | undefined>()
|
||||
|
||||
defineEmits<{
|
||||
keyup: [KeyboardEvent]
|
||||
}>()
|
||||
|
||||
const type = ref<'password' | 'text'>('password')
|
||||
|
||||
const tooglePasswordType = () => {
|
||||
type.value = type.value === 'password' ? 'text' : 'password'
|
||||
}
|
||||
const show = ref(false)
|
||||
const { t } = useI18n()
|
||||
</script>
|
||||
|
||||
<i18n lang="json">
|
||||
{
|
||||
"de": {
|
||||
"password": "Passwort"
|
||||
},
|
||||
"en": {
|
||||
"password": "Password"
|
||||
}
|
||||
}
|
||||
<i18n lang="yaml">
|
||||
de:
|
||||
show: Passwort ansehen
|
||||
hide: Passwort verstecken
|
||||
label: Passwort
|
||||
|
||||
en:
|
||||
show: Show password
|
||||
hide: Hide password
|
||||
label: Password
|
||||
</i18n>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<UiInput
|
||||
v-model.trim="value"
|
||||
:autofocus
|
||||
:check-input="checkInput"
|
||||
:label="label || t('url')"
|
||||
@ -7,17 +8,18 @@
|
||||
:read_only
|
||||
:rules
|
||||
:with-copy-button
|
||||
v-model.trim="value"
|
||||
@keyup="(e) => $emit('keyup', e)"
|
||||
>
|
||||
<template #append>
|
||||
<template #trailing>
|
||||
<UiButton
|
||||
color="neutral"
|
||||
variant="link"
|
||||
size="sm"
|
||||
icon="streamline:web"
|
||||
:disabled="!value?.length"
|
||||
:tooltip="t('browse')"
|
||||
@click="openUrl(`${value}`)"
|
||||
class="btn-outline btn-accent btn-square"
|
||||
>
|
||||
<Icon name="streamline:web" />
|
||||
</UiButton>
|
||||
/>
|
||||
</template>
|
||||
</UiInput>
|
||||
</template>
|
||||
@ -45,13 +47,12 @@ defineEmits<{
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<i18n lang="json">
|
||||
{
|
||||
"de": {
|
||||
"url": "Url"
|
||||
},
|
||||
"en": {
|
||||
"url": "Url"
|
||||
}
|
||||
}
|
||||
<i18n lang="yaml">
|
||||
de:
|
||||
url: Url
|
||||
browse: Url öffnen
|
||||
|
||||
en:
|
||||
url: Url
|
||||
browse: Open url
|
||||
</i18n>
|
||||
|
||||
Reference in New Issue
Block a user