feat(ui): implement autofocus prop on input and link

This commit is contained in:
upsiflu 2025-01-02 11:27:49 +01:00
parent dcddaaa561
commit e9a79dfaf8
6 changed files with 26 additions and 18 deletions

View File

@ -48,12 +48,6 @@ const labels = computed(() => ({
usernamePlaceholder: t('components.auth.LoginForm.placeholder.username') usernamePlaceholder: t('components.auth.LoginForm.placeholder.username')
})) }))
const username = ref()
onMounted(async () => {
await nextTick()
username.value?.focus()
})
const isLoading = ref(false) const isLoading = ref(false)
const errors = ref([] as string[]) const errors = ref([] as string[])
const submit = async () => { const submit = async () => {

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue' import { computed, onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import onKeyboardShortcut from '~/composables/onKeyboardShortcut'; import onKeyboardShortcut from '~/composables/onKeyboardShortcut';
@ -13,6 +13,8 @@ const { icon, placeholder, ...restProps } = defineProps<{
search?: true, search?: true,
numeric?: true, numeric?: true,
label?: string, label?: string,
autofocus? : boolean
}>() }>()
// TODO(A11y): Add `inputmode="numeric" pattern="[0-9]*"` to input if model type is number: // TODO(A11y): Add `inputmode="numeric" pattern="[0-9]*"` to input if model type is number:
@ -34,6 +36,12 @@ const attributes = computed(() => ({
const { t } = useI18n() const { t } = useI18n()
const input = ref()
onMounted(() => {
if (restProps.autofocus) input.value.focus();
})
const model = defineModel<string|number>() const model = defineModel<string|number>()
</script> </script>

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { computed, onMounted, ref } from 'vue'
import { type RouterLinkProps } from 'vue-router' import { type RouterLinkProps } from 'vue-router'
import { type ColorProps, type DefaultProps, type VariantProps, color, isNoColors } from '~/composables/color'; import { type ColorProps, type DefaultProps, type VariantProps, color, isNoColors } from '~/composables/color';
@ -13,6 +13,8 @@ const props = defineProps<{
icon?: string; icon?: string;
round?: true; round?: true;
autofocus? : boolean
} & RouterLinkProps } & RouterLinkProps
& (ColorProps | DefaultProps) & (ColorProps | DefaultProps)
& VariantProps & VariantProps
@ -26,6 +28,12 @@ const isExternalLink = computed(() =>
const [fontWeight, activeFontWeight] = props.thickWhenActive ? [600, 900] : [400, 400] const [fontWeight, activeFontWeight] = props.thickWhenActive ? [600, 900] : [400, 400]
const isIconOnly = computed(() => !!props.icon) const isIconOnly = computed(() => !!props.icon)
const button = ref()
onMounted(() => {
if (props.autofocus) button.value.focus();
})
</script> </script>
<template> <template>
@ -34,6 +42,7 @@ const isIconOnly = computed(() => !!props.icon)
width(props)( width(props)(
align(props)( align(props)(
)))" )))"
ref="button"
:class="[ :class="[
$style.link, $style.link,
round && $style['is-round'], round && $style['is-round'],

View File

@ -31,13 +31,13 @@ const styles = {
} as const; } as const;
const getStyle = (props : Partial<AlignmentProps>) => ([key, value]: Entry<AlignmentProps>) => const getStyle = (props : Partial<AlignmentProps>) => ([key, value]: Entry<AlignmentProps>) =>
(trace(`getStyle: key=${key}, value=${value}`), (
typeof styles[key] === 'function' ? typeof styles[key] === 'function' ?
(trace(`getStyle: key=${key}, value=${value}`), styles[key]( (styles[key](
// We know that props[key] is a value accepted by styles[key]. The ts compiler is not so smart. // We know that props[key] is a value accepted by styles[key]. The ts compiler is not so smart.
// @ts-ignore // @ts-ignore
(key in props && props[key]) ? (key in props && props[key]) ?
props[(trace(props[key]), trace(key))] props[((props[key]), (key))]
: value : value
)) ))
: styles[key] : styles[key]
@ -73,14 +73,14 @@ export const align = <TProps extends Partial<AlignmentProps>>(
props: TProps, props: TProps,
defaults: Partial<AlignmentProps> = {} defaults: Partial<AlignmentProps> = {}
) => merge ( ) => merge (
trace((Object.entries(props) as Entries<TProps>).reduce( ((Object.entries(props) as Entries<TProps>).reduce(
((acc, [key, value]) => ((acc, [key, value]) =>
value && key in styles ? value && key in styles ?
acc.filter(([accKey, _]) => !conflicts.find(set => set.has(accKey) && set.has(key))) acc.filter(([accKey, _]) => !conflicts.find(set => set.has(accKey) && set.has(key)))
.concat([[key, value]]) .concat([[key, value]])
: acc : acc
), ),
trace(Object.entries(defaults)) as Entries<Partial<AlignmentProps>> (Object.entries(defaults)) as Entries<Partial<AlignmentProps>>
)).map(getStyle(props)) )).map(getStyle(props))
) )

View File

@ -91,7 +91,7 @@ const logoUrl = computed(() => store.state.auth.authenticated ? 'library.index'
<!-- Sign up, Log in --> <!-- Sign up, Log in -->
<div style="display:contents;" v-if="!store.state.auth.authenticated"> <div style="display:contents;" v-if="!store.state.auth.authenticated">
<Layout flex grow no-gap> <Layout flex grow style="--gap:16px;">
<Link :to="{ name: 'login' }" <Link :to="{ name: 'login' }"
solid solid
icon="bi-box-arrow-in-right" icon="bi-box-arrow-in-right"

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { BackendError } from '~/types' import type { BackendError } from '~/types'
import { computed, ref, onMounted } from 'vue' import { computed, ref, onMounted, nextTick } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
@ -44,9 +44,6 @@ const submit = async () => {
isLoading.value = false isLoading.value = false
} }
const emailInput = ref()
onMounted(() => emailInput.value.focus())
</script> </script>
<template> <template>