feat(fe): Add form field navigation - Enter key moves to next field
- Create useFormFieldNavigation composable for Tab-like Enter behavior - KsTextField: Enter -> next field - KsTextArea: Ctrl+Enter for newline, Enter -> next field - KsSelect: Enter -> next field after selection - Implements standard form navigation pattern across input components
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { useFieldLayout } from '../contracts/layoutContext'
|
||||
import { useFormFieldNavigation } from '../composables/useFormFieldNavigation'
|
||||
|
||||
export interface KsTextFieldProps {
|
||||
modelValue: string
|
||||
@@ -17,6 +19,7 @@ export interface KsTextFieldProps {
|
||||
autocomplete?: string
|
||||
inputId?: string
|
||||
size?: 'sm' | 'md' | 'lg'
|
||||
inline?: boolean
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<KsTextFieldProps>(), {
|
||||
@@ -36,11 +39,14 @@ const emit = defineEmits<{
|
||||
|
||||
const isFocused = ref(false)
|
||||
const id = computed(() => props.inputId || `input-${Math.random().toString(36).substr(2, 9)}`)
|
||||
const { isInline } = useFieldLayout(props.inline)
|
||||
const { inputRef, handleKeyDown } = useFormFieldNavigation()
|
||||
|
||||
const containerClass = computed(() => [
|
||||
'ks-text-field',
|
||||
`ks-text-field--${props.size}`,
|
||||
{
|
||||
'ks-text-field--inline': isInline.value,
|
||||
'ks-text-field--focused': isFocused.value,
|
||||
'ks-text-field--filled': props.modelValue,
|
||||
'ks-text-field--disabled': props.disabled,
|
||||
@@ -49,6 +55,8 @@ const containerClass = computed(() => [
|
||||
},
|
||||
])
|
||||
|
||||
|
||||
|
||||
const handleFocus = (event: FocusEvent) => {
|
||||
isFocused.value = true
|
||||
emit('focus', event)
|
||||
@@ -64,6 +72,10 @@ const handleInput = (event: Event) => {
|
||||
emit('update:modelValue', target.value)
|
||||
emit('input', event)
|
||||
}
|
||||
|
||||
const handleKeyPress = (event: KeyboardEvent) => {
|
||||
handleKeyDown(event, false)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -77,6 +89,7 @@ const handleInput = (event: Event) => {
|
||||
<!-- Input container with focus ring -->
|
||||
<div class="ks-text-field__container">
|
||||
<input
|
||||
ref="inputRef"
|
||||
:id="id"
|
||||
:type="type"
|
||||
:value="modelValue"
|
||||
@@ -94,6 +107,7 @@ const handleInput = (event: Event) => {
|
||||
@input="handleInput"
|
||||
@focus="handleFocus"
|
||||
@blur="handleBlur"
|
||||
@keydown="handleKeyPress"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -119,6 +133,32 @@ const handleInput = (event: Event) => {
|
||||
gap: var(--spacing-2);
|
||||
}
|
||||
|
||||
.ks-text-field--inline {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: var(--ks-space-2, 6px);
|
||||
}
|
||||
|
||||
.ks-text-field--inline .ks-text-field__label {
|
||||
width: var(--ks-field-label-width, 72px);
|
||||
min-width: var(--ks-field-label-width, 72px);
|
||||
max-width: var(--ks-field-label-width, 72px);
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.ks-text-field--inline .ks-text-field__container {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.ks-text-field--sm {
|
||||
--input-height: 32px;
|
||||
--font-size: var(--font-size-sm);
|
||||
|
||||
Reference in New Issue
Block a user