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:
2026-08-16 21:50:50 +09:00
parent 5213ea142b
commit b12fc7411d
25 changed files with 1105 additions and 85 deletions
@@ -0,0 +1,91 @@
import { ref, onMounted } from 'vue'
export function useFormFieldNavigation() {
const inputRef = ref<HTMLElement | null>(null)
function findFormElement(): HTMLFormElement | null {
if (!inputRef.value) return null
return inputRef.value.closest('form')
}
function getFormInputElements(): HTMLElement[] {
const form = findFormElement()
if (!form) return []
const selectors = [
'input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"])',
'textarea',
'[role="button"][tabindex="0"]',
'select'
]
return Array.from(form.querySelectorAll(selectors.join(',')))
.filter((el: Element): el is HTMLElement => {
if (el instanceof HTMLInputElement) {
return !el.disabled && !el.readonly && el.type !== 'hidden'
}
if (el instanceof HTMLTextAreaElement) {
return !el.disabled && !el.readonly
}
if (el instanceof HTMLSelectElement) {
return !el.disabled && !el.readonly
}
return !el.hasAttribute('disabled')
})
}
function findNextField(): HTMLElement | null {
const elements = getFormInputElements()
if (!inputRef.value || elements.length === 0) return null
const currentIndex = elements.indexOf(inputRef.value)
if (currentIndex === -1) return null
const nextIndex = currentIndex + 1
return nextIndex < elements.length ? elements[nextIndex] : elements[0]
}
function moveToNextField(): void {
const nextField = findNextField()
if (nextField) {
nextField.focus()
// 如果是 input/textareaselect all
if (nextField instanceof HTMLInputElement || nextField instanceof HTMLTextAreaElement) {
nextField.select?.()
}
}
}
function handleKeyDown(event: KeyboardEvent, isTextArea: boolean = false): boolean {
if (!isTextArea) {
// Input/Select: Enter → 다음 필드
if (event.key === 'Enter') {
event.preventDefault()
moveToNextField()
return true
}
} else {
// TextArea: Ctrl+Enter → 줄바꿈, Enter → 다음 필드
if (event.key === 'Enter') {
if (event.ctrlKey || event.metaKey) {
// Ctrl+Enter: 줄바꿈 (기본 동작)
return false
} else {
// Enter: 다음 필드
event.preventDefault()
moveToNextField()
return true
}
}
}
return false
}
return {
inputRef,
handleKeyDown,
moveToNextField,
findNextField,
getFormInputElements
}
}