b12fc7411d
- 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
120 lines
3.5 KiB
TypeScript
120 lines
3.5 KiB
TypeScript
import type { Component, InjectionKey } from 'vue'
|
|
import type { UiGridStatusMap } from '../gridStatus'
|
|
|
|
export type UiSeverity = 'primary' | 'secondary' | 'success' | 'info' | 'warning' | 'danger'
|
|
export type UiButtonType = 'button' | 'submit' | 'reset'
|
|
export type UiAdapterId = 'primevue-aggrid' | 'native-accessible'
|
|
export type UiAdapterContractVersion = '4.0'
|
|
export type UiAdapterCapability =
|
|
| 'button'
|
|
| 'text-field'
|
|
| 'text-area'
|
|
| 'select'
|
|
| 'multi-select'
|
|
| 'checkbox'
|
|
| 'date-field'
|
|
| 'number-field'
|
|
| 'dialog'
|
|
| 'status-tag'
|
|
| 'inline-message'
|
|
| 'paginator'
|
|
| 'tabs'
|
|
| 'data-grid'
|
|
|
|
export interface UiSelectOption {
|
|
label: string
|
|
value: string | number | boolean | null
|
|
disabled?: boolean
|
|
}
|
|
|
|
export interface UiTabItem {
|
|
id: string
|
|
label: string
|
|
disabled?: boolean
|
|
badge?: string | number
|
|
}
|
|
|
|
export type UiGridSortDirection = 'asc' | 'desc'
|
|
export interface UiGridSort { field: string; direction: UiGridSortDirection }
|
|
export interface UiGridFilter { field: string; operator: string; value: unknown }
|
|
export interface UiGridQuery {
|
|
page: number
|
|
pageSize: number
|
|
sorts: UiGridSort[]
|
|
filters: UiGridFilter[]
|
|
search?: string
|
|
}
|
|
|
|
export interface UiGridColumn {
|
|
field: string
|
|
header: string
|
|
width?: number
|
|
minWidth?: number
|
|
maxWidth?: number
|
|
flex?: number
|
|
sortable?: boolean
|
|
filterable?: boolean
|
|
sensitive?: boolean
|
|
editable?: boolean
|
|
formatter?: (value: unknown, row: unknown) => string
|
|
statusMap?: UiGridStatusMap
|
|
}
|
|
|
|
|
|
|
|
export type UiTextFieldType = 'text' | 'email' | 'password' | 'number' | 'date'
|
|
|
|
export interface UiAdapterDescriptor {
|
|
readonly id: UiAdapterId
|
|
readonly version: string
|
|
readonly contractVersion: UiAdapterContractVersion
|
|
readonly vendor: string
|
|
readonly capabilities: ReadonlySet<UiAdapterCapability>
|
|
readonly productionEligible: boolean
|
|
readonly accessibilityBaseline: 'WCAG_2_2_AA_TARGET'
|
|
}
|
|
|
|
export interface UiAdapter {
|
|
readonly descriptor: UiAdapterDescriptor
|
|
readonly components: {
|
|
Button: Component
|
|
TextField: Component
|
|
TextArea: Component
|
|
Select: Component
|
|
MultiSelect: Component
|
|
Checkbox: Component
|
|
DateField: Component
|
|
NumberField: Component
|
|
Dialog: Component
|
|
StatusTag: Component
|
|
InlineMessage: Component
|
|
Paginator: Component
|
|
Tabs: Component
|
|
DataGrid: Component
|
|
}
|
|
}
|
|
|
|
export const requiredUiAdapterCapabilities: readonly UiAdapterCapability[] = Object.freeze([
|
|
'button', 'text-field', 'text-area', 'select', 'multi-select', 'checkbox', 'date-field',
|
|
'number-field', 'dialog', 'status-tag', 'inline-message', 'paginator', 'tabs', 'data-grid'
|
|
])
|
|
|
|
export function assertUiAdapterContract(adapter: UiAdapter): void {
|
|
if (adapter.descriptor.contractVersion !== '4.0') {
|
|
throw new Error(`Unsupported UI adapter contract: ${adapter.descriptor.contractVersion}`)
|
|
}
|
|
const missing = requiredUiAdapterCapabilities.filter(x => !adapter.descriptor.capabilities.has(x))
|
|
if (missing.length > 0) {
|
|
throw new Error(`UI adapter ${adapter.descriptor.id} is missing capabilities: ${missing.join(', ')}`)
|
|
}
|
|
const componentNames = [
|
|
'Button', 'TextField', 'TextArea', 'Select', 'MultiSelect', 'Checkbox', 'DateField',
|
|
'NumberField', 'Dialog', 'StatusTag', 'InlineMessage', 'Paginator', 'Tabs', 'DataGrid'
|
|
] as const
|
|
for (const name of componentNames) {
|
|
if (!adapter.components[name]) throw new Error(`UI adapter ${adapter.descriptor.id} has no component for ${name}`)
|
|
}
|
|
}
|
|
|
|
export const uiAdapterKey: InjectionKey<UiAdapter> = Symbol('KArtSellUiAdapterV4')
|