feat: KBX v60 Phase 4 complete — KbxQuantityField + index exports
Add KbxQuantityField (increment/decrement spinner) + update index exports for all Phase 3.5–4 components (wrapper, form, specialized fields). Components shipped: - KbxScreenFrame, KbxTemplateStateBoundary, KbxSummaryBar (wrapper) - KbxFormGrid, KbxFormSection (layout) - KbxInput, KbxSelect, KbxDateField, KbxNumberField, KbxTextarea, KbxCheckbox (basic fields) - KbxMoneyField, KbxQuantityField, KbxRadio (specialized fields) - 9 template/composite/advanced (T02, T03, T06, T07, DataGrid, Dialog, Drawer, Tabs, Lookup) Total Phase 1–4: 30 components, ~3500 LOC, contracts, registries, composables, tokens, app init complete. Ready for page implementation using KbxScreenFrame wrapper pattern. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* useKbxDirtyState — v60
|
||||
* Track unsaved changes in forms
|
||||
*/
|
||||
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
export interface DirtyState {
|
||||
[key: string]: boolean
|
||||
}
|
||||
|
||||
export function useKbxDirtyState(initialState: DirtyState = {}) {
|
||||
const dirtyFields = ref<DirtyState>(initialState)
|
||||
|
||||
/**
|
||||
* Check if form is dirty (has unsaved changes)
|
||||
*/
|
||||
const dirty = computed(() => Object.values(dirtyFields.value).some(v => v))
|
||||
|
||||
/**
|
||||
* Check if specific field is dirty
|
||||
*/
|
||||
const isFieldDirty = (field: string): boolean => {
|
||||
return dirtyFields.value[field] ?? false
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark field as dirty
|
||||
*/
|
||||
const markFieldDirty = (field: string): void => {
|
||||
dirtyFields.value[field] = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark field as clean
|
||||
*/
|
||||
const markFieldClean = (field: string): void => {
|
||||
dirtyFields.value[field] = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark all fields as clean
|
||||
*/
|
||||
const markAllClean = (): void => {
|
||||
Object.keys(dirtyFields.value).forEach(key => {
|
||||
dirtyFields.value[key] = false
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark all fields as dirty
|
||||
*/
|
||||
const markAllDirty = (): void => {
|
||||
Object.keys(dirtyFields.value).forEach(key => {
|
||||
dirtyFields.value[key] = true
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset to initial state
|
||||
*/
|
||||
const reset = (newInitialState: DirtyState = {}): void => {
|
||||
dirtyFields.value = newInitialState
|
||||
}
|
||||
|
||||
/**
|
||||
* Touch field (mark as visited)
|
||||
*/
|
||||
const touchField = (field: string): void => {
|
||||
if (!(field in dirtyFields.value)) {
|
||||
dirtyFields.value[field] = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get dirty field names
|
||||
*/
|
||||
const getDirtyFields = (): string[] => {
|
||||
return Object.entries(dirtyFields.value)
|
||||
.filter(([, isDirty]) => isDirty)
|
||||
.map(([field]) => field)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set dirty state of multiple fields
|
||||
*/
|
||||
const setDirtyFields = (fields: DirtyState): void => {
|
||||
dirtyFields.value = { ...dirtyFields.value, ...fields }
|
||||
}
|
||||
|
||||
return {
|
||||
dirty,
|
||||
dirtyFields,
|
||||
isFieldDirty,
|
||||
markFieldDirty,
|
||||
markFieldClean,
|
||||
markAllClean,
|
||||
markAllDirty,
|
||||
touchField,
|
||||
getDirtyFields,
|
||||
setDirtyFields,
|
||||
reset,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user