@hina-ui/vue 1.6.0 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/dist/index.js +3170 -461
  2. package/dist/index.js.map +1 -1
  3. package/dist/types/components/anchor/Anchor.vue.d.ts +26 -10
  4. package/dist/types/components/anchor/Anchor.vue.d.ts.map +1 -1
  5. package/dist/types/components/anchor/composables/useAnchor.d.ts +15 -0
  6. package/dist/types/components/anchor/composables/useAnchor.d.ts.map +1 -0
  7. package/dist/types/components/anchor/composables/useScrollSpy.d.ts.map +1 -1
  8. package/dist/types/components/anchor/types.d.ts +9 -0
  9. package/dist/types/components/anchor/types.d.ts.map +1 -0
  10. package/dist/types/components/app-shell/AppShell.vue.d.ts.map +1 -1
  11. package/dist/types/components/app-shell/composables/useSidebarScrollUpdates.d.ts +4 -0
  12. package/dist/types/components/app-shell/composables/useSidebarScrollUpdates.d.ts.map +1 -0
  13. package/dist/types/components/combobox/Combobox.vue.d.ts +1 -0
  14. package/dist/types/components/combobox/Combobox.vue.d.ts.map +1 -1
  15. package/dist/types/components/data-table/DataTable.vue.d.ts +58 -0
  16. package/dist/types/components/data-table/DataTable.vue.d.ts.map +1 -0
  17. package/dist/types/components/data-table/DataTableAction.vue.d.ts +18 -0
  18. package/dist/types/components/data-table/DataTableAction.vue.d.ts.map +1 -0
  19. package/dist/types/components/data-table/DataTableDragPreview.vue.d.ts +24 -0
  20. package/dist/types/components/data-table/DataTableDragPreview.vue.d.ts.map +1 -0
  21. package/dist/types/components/data-table/DataTableEditor.vue.d.ts +24 -0
  22. package/dist/types/components/data-table/DataTableEditor.vue.d.ts.map +1 -0
  23. package/dist/types/components/data-table/DataTableHead.vue.d.ts +32 -0
  24. package/dist/types/components/data-table/DataTableHead.vue.d.ts.map +1 -0
  25. package/dist/types/components/data-table/DataTableRow.vue.d.ts +39 -0
  26. package/dist/types/components/data-table/DataTableRow.vue.d.ts.map +1 -0
  27. package/dist/types/components/data-table/DataTableSelection.vue.d.ts +15 -0
  28. package/dist/types/components/data-table/DataTableSelection.vue.d.ts.map +1 -0
  29. package/dist/types/components/data-table/DataTableText.vue.d.ts +8 -0
  30. package/dist/types/components/data-table/DataTableText.vue.d.ts.map +1 -0
  31. package/dist/types/components/data-table/Slot.d.ts +8 -0
  32. package/dist/types/components/data-table/Slot.d.ts.map +1 -0
  33. package/dist/types/components/data-table/column-sizing.d.ts +22 -0
  34. package/dist/types/components/data-table/column-sizing.d.ts.map +1 -0
  35. package/dist/types/components/data-table/composables/useDataTable.d.ts +80 -0
  36. package/dist/types/components/data-table/composables/useDataTable.d.ts.map +1 -0
  37. package/dist/types/components/data-table/composables/useTableColumns.d.ts +51 -0
  38. package/dist/types/components/data-table/composables/useTableColumns.d.ts.map +1 -0
  39. package/dist/types/components/data-table/composables/useTableDrag.d.ts +53 -0
  40. package/dist/types/components/data-table/composables/useTableDrag.d.ts.map +1 -0
  41. package/dist/types/components/data-table/composables/useTableEditing.d.ts +27 -0
  42. package/dist/types/components/data-table/composables/useTableEditing.d.ts.map +1 -0
  43. package/dist/types/components/data-table/composables/useTableExport.d.ts +3 -0
  44. package/dist/types/components/data-table/composables/useTableExport.d.ts.map +1 -0
  45. package/dist/types/components/data-table/composables/useTableResize.d.ts +42 -0
  46. package/dist/types/components/data-table/composables/useTableResize.d.ts.map +1 -0
  47. package/dist/types/components/data-table/composables/useTableResizeHandles.d.ts +8 -0
  48. package/dist/types/components/data-table/composables/useTableResizeHandles.d.ts.map +1 -0
  49. package/dist/types/components/data-table/composables/useTableText.d.ts +6 -0
  50. package/dist/types/components/data-table/composables/useTableText.d.ts.map +1 -0
  51. package/dist/types/components/data-table/composables/useTableVirtual.d.ts +55 -0
  52. package/dist/types/components/data-table/composables/useTableVirtual.d.ts.map +1 -0
  53. package/dist/types/components/data-table/features.d.ts +16 -0
  54. package/dist/types/components/data-table/features.d.ts.map +1 -0
  55. package/dist/types/components/data-table/types.d.ts +225 -0
  56. package/dist/types/components/data-table/types.d.ts.map +1 -0
  57. package/dist/types/components/data-table/utils.d.ts +10 -0
  58. package/dist/types/components/data-table/utils.d.ts.map +1 -0
  59. package/dist/types/components/form/composables/useForm.d.ts.map +1 -1
  60. package/dist/types/components/sidebar/DrawerScope.d.ts +13 -2
  61. package/dist/types/components/sidebar/DrawerScope.d.ts.map +1 -1
  62. package/dist/types/components/sidebar/Sidebar.vue.d.ts +4 -1
  63. package/dist/types/components/sidebar/Sidebar.vue.d.ts.map +1 -1
  64. package/dist/types/components/sidebar/context.d.ts +1 -0
  65. package/dist/types/components/sidebar/context.d.ts.map +1 -1
  66. package/dist/types/components/sidebar/sidebar.variants.d.ts +3 -3
  67. package/dist/types/components/table/TableHead.vue.d.ts +2 -2
  68. package/dist/types/components/table/TableHead.vue.d.ts.map +1 -1
  69. package/dist/types/index.d.ts +3 -1
  70. package/dist/types/index.d.ts.map +1 -1
  71. package/dist/types/locale/en-US.d.ts.map +1 -1
  72. package/dist/types/locale/types.d.ts +12 -0
  73. package/dist/types/locale/types.d.ts.map +1 -1
  74. package/dist/types/locale/zh-CN.d.ts.map +1 -1
  75. package/package.json +14 -12
  76. package/src/components/anchor/Anchor.vue +15 -19
  77. package/src/components/anchor/composables/useAnchor.ts +19 -0
  78. package/src/components/anchor/composables/useScrollSpy.ts +6 -1
  79. package/src/components/anchor/types.ts +8 -0
  80. package/src/components/app-shell/AppShell.vue +5 -2
  81. package/src/components/app-shell/composables/useSidebarScrollUpdates.ts +52 -0
  82. package/src/components/combobox/Combobox.vue +11 -1
  83. package/src/components/data-table/DataTable.vue +340 -0
  84. package/src/components/data-table/DataTableAction.vue +15 -0
  85. package/src/components/data-table/DataTableDragPreview.vue +72 -0
  86. package/src/components/data-table/DataTableEditor.vue +59 -0
  87. package/src/components/data-table/DataTableHead.vue +181 -0
  88. package/src/components/data-table/DataTableRow.vue +255 -0
  89. package/src/components/data-table/DataTableSelection.vue +51 -0
  90. package/src/components/data-table/DataTableText.vue +20 -0
  91. package/src/components/data-table/Slot.ts +12 -0
  92. package/src/components/data-table/column-sizing.ts +101 -0
  93. package/src/components/data-table/composables/useDataTable.ts +425 -0
  94. package/src/components/data-table/composables/useTableColumns.ts +253 -0
  95. package/src/components/data-table/composables/useTableDrag.ts +345 -0
  96. package/src/components/data-table/composables/useTableEditing.ts +184 -0
  97. package/src/components/data-table/composables/useTableExport.ts +44 -0
  98. package/src/components/data-table/composables/useTableResize.ts +292 -0
  99. package/src/components/data-table/composables/useTableResizeHandles.ts +72 -0
  100. package/src/components/data-table/composables/useTableText.ts +13 -0
  101. package/src/components/data-table/composables/useTableVirtual.ts +109 -0
  102. package/src/components/data-table/features.ts +32 -0
  103. package/src/components/data-table/types.ts +218 -0
  104. package/src/components/data-table/utils.ts +75 -0
  105. package/src/components/form/composables/useForm.ts +44 -8
  106. package/src/components/sidebar/DrawerScope.ts +12 -4
  107. package/src/components/sidebar/Sidebar.vue +27 -7
  108. package/src/components/sidebar/context.ts +1 -0
  109. package/src/components/sidebar/sidebar.variants.ts +1 -1
  110. package/src/components/table/TableHead.vue +1 -1
  111. package/src/index.ts +26 -1
  112. package/src/locale/en-US.ts +13 -0
  113. package/src/locale/types.ts +13 -0
  114. package/src/locale/zh-CN.ts +13 -0
  115. package/src/styles/data-table.css +331 -0
  116. package/src/styles/tokens.css +1 -0
  117. package/src/styles/typography.css +3 -0
@@ -0,0 +1,218 @@
1
+ import type { CSSProperties } from 'vue'
2
+ import type { TableVariants } from '../table/table.variants'
3
+
4
+ export type DataTableKey = string | number
5
+ export type DataTableKeyField<T> = {
6
+ [K in keyof T]-?: T[K] extends DataTableKey ? K : never
7
+ }[keyof T] &
8
+ string
9
+ export type DataTableAggregate = 'sum' | 'min' | 'max' | 'mean' | 'count' | 'uniqueCount'
10
+ export type DataTableScope = 'page' | 'filtered' | 'selected' | 'all'
11
+ export type DataTableFilterMode = 'contains' | 'equals' | 'in' | 'range'
12
+
13
+ export interface DataTableColumn<T> {
14
+ key: string
15
+ label: string
16
+ children?: DataTableColumn<T>[]
17
+ field?: keyof T
18
+ accessor?: (row: T) => unknown
19
+ sortable?: boolean
20
+ sort?: (a: T, b: T) => number
21
+ filterable?: boolean
22
+ filter?: (row: T, query: string) => boolean
23
+ filterValue?: (row: T, value: unknown) => boolean
24
+ filterMode?: DataTableFilterMode
25
+ format?: (value: unknown, row: T) => string | number
26
+ align?: 'start' | 'center' | 'end'
27
+ width?: number | string
28
+ minWidth?: number | string
29
+ maxWidth?: number | string
30
+ pin?: 'start' | 'end'
31
+ truncate?: boolean
32
+ resizable?: boolean
33
+ reorderable?: boolean
34
+ aggregate?: DataTableAggregate | ((rows: T[]) => unknown)
35
+ footer?: boolean | string | ((rows: T[]) => string | number)
36
+ editable?: boolean | ((row: T) => boolean)
37
+ parse?: (value: unknown, row: T) => unknown
38
+ validate?: (value: unknown, row: T) => string | undefined | Promise<string | undefined>
39
+ exportable?: boolean
40
+ exportValue?: (row: T) => unknown
41
+ headerClass?: string
42
+ cellClass?: string | ((row: T) => string | undefined)
43
+ }
44
+ export interface DataTableSort {
45
+ key: string
46
+ desc: boolean
47
+ }
48
+ export interface DataTableFilter {
49
+ key: string
50
+ value: unknown
51
+ }
52
+ export interface DataTableQuery {
53
+ page: number
54
+ pageSize: number
55
+ sorting: DataTableSort[]
56
+ filter: string
57
+ columnFilters: DataTableFilter[]
58
+ grouping: string[]
59
+ }
60
+ export interface DataTableEdit<T> {
61
+ key: DataTableKey
62
+ row: T
63
+ values: Record<string, unknown>
64
+ column?: string
65
+ }
66
+ export interface DataTableReorder<T> {
67
+ row: T
68
+ target: T
69
+ parent?: T
70
+ from: number
71
+ to: number
72
+ rows: T[]
73
+ }
74
+ export interface DataTableExportOptions {
75
+ scope?: DataTableScope
76
+ columns?: string[]
77
+ formatted?: boolean
78
+ delimiter?: ',' | ';' | '\t'
79
+ bom?: boolean
80
+ filename?: string
81
+ }
82
+ export interface DataTableProps<T> {
83
+ rows: T[]
84
+ columns: DataTableColumn<T>[]
85
+ rowKey: DataTableKeyField<T> | ((row: T) => DataTableKey)
86
+ rowLabel?: keyof T | ((row: T) => string)
87
+ selectable?: boolean | ((row: T) => boolean)
88
+ selectionMode?: 'single' | 'multiple'
89
+ selectAll?: 'page' | 'filtered'
90
+ selectChildren?: boolean
91
+ expandable?: boolean | ((row: T) => boolean)
92
+ getChildren?: (row: T) => T[] | undefined
93
+ pagination?: boolean
94
+ manual?: boolean
95
+ total?: number
96
+ hasNextPage?: boolean
97
+ autoResetPage?: boolean
98
+ multiSort?: boolean
99
+ resizable?: boolean
100
+ resizeMode?: 'fit' | 'expand'
101
+ reorderColumns?: boolean
102
+ reorderable?: boolean | ((row: T) => boolean)
103
+ virtualize?: boolean | { estimateSize?: number; overscan?: number }
104
+ editMode?: 'cell' | 'row'
105
+ onSave?: (edit: DataTableEdit<T>) => void | Promise<void>
106
+ loading?: boolean
107
+ disabled?: boolean
108
+ rowClickable?: boolean
109
+ rowClass?: (row: T) => string | undefined
110
+ variant?: TableVariants['variant']
111
+ hover?: boolean
112
+ stickyHeader?: boolean
113
+ stickyFooter?: boolean
114
+ height?: number | string
115
+ maxHeight?: number | string
116
+ fill?: boolean
117
+ layout?: 'auto' | 'fixed'
118
+ caption?: string
119
+ label?: string
120
+ emptyText?: string
121
+ class?: string
122
+ tableClass?: string
123
+ }
124
+ export interface DataTableRowContext<T> {
125
+ row: T
126
+ key: DataTableKey
127
+ index: number
128
+ depth: number
129
+ selected: boolean
130
+ indeterminate: boolean
131
+ selectable: boolean
132
+ expanded: boolean
133
+ expandable: boolean
134
+ toggleSelected: (selected?: boolean) => void
135
+ toggleExpanded: (expanded?: boolean) => void
136
+ startEdit: (column?: string) => void
137
+ }
138
+ export interface DataTableCellContext<T> extends DataTableRowContext<T> {
139
+ column: DataTableColumn<T>
140
+ value: unknown
141
+ }
142
+ export interface DataTableHeaderContext<T> {
143
+ column: DataTableColumn<T>
144
+ sorting: 'asc' | 'desc' | false
145
+ sortIndex: number
146
+ filterValue: unknown
147
+ toggleSort: (multi?: boolean) => void
148
+ setFilter: (value: unknown) => void
149
+ }
150
+ export interface DataTableGroupContext<T> {
151
+ key: string
152
+ column: DataTableColumn<T>
153
+ value: unknown
154
+ rows: T[]
155
+ depth: number
156
+ expanded: boolean
157
+ toggleExpanded: (expanded?: boolean) => void
158
+ aggregate: (column: string) => unknown
159
+ }
160
+ export interface DataTableEditorContext<T> extends DataTableCellContext<T> {
161
+ value: unknown
162
+ pending: boolean
163
+ error?: string
164
+ updateValue: (value: unknown) => void
165
+ commit: () => Promise<void>
166
+ cancel: () => void
167
+ }
168
+ export interface DataTableApi<T> {
169
+ getRows: (scope?: DataTableScope) => T[]
170
+ toggleSelected: (key: DataTableKey, selected?: boolean) => void
171
+ toggleExpanded: (key: DataTableKey, expanded?: boolean) => void
172
+ setFilter: (column: string, value: unknown) => void
173
+ setColumnHidden: (column: string, hidden: boolean) => void
174
+ setColumnWidth: (column: string, width: number) => void
175
+ moveColumn: (column: string, target: string) => void
176
+ moveRow: (key: DataTableKey, target: DataTableKey) => void
177
+ startEdit: (key: DataTableKey, column?: string) => void
178
+ cancelEdit: () => void
179
+ commitEdit: () => Promise<void>
180
+ scrollToRow: (key: DataTableKey) => void
181
+ toCsv: (options?: DataTableExportOptions) => string
182
+ exportCsv: (options?: DataTableExportOptions) => void
183
+ }
184
+ export interface DataTableState<T> extends DataTableQuery {
185
+ total: number
186
+ rows: T[]
187
+ selected: DataTableKey[]
188
+ expanded: DataTableKey[]
189
+ visibleColumns: DataTableColumn<T>[]
190
+ api: DataTableApi<T>
191
+ }
192
+ export type DataTableSlots<T> = {
193
+ toolbar?(state: DataTableState<T>): unknown
194
+ footer?(state: DataTableState<T>): unknown
195
+ empty?(): unknown
196
+ loading?(): unknown
197
+ header?(context: DataTableHeaderContext<T>): unknown
198
+ cell?(context: DataTableCellContext<T>): unknown
199
+ editor?(context: DataTableEditorContext<T>): unknown
200
+ expansion?(context: DataTableRowContext<T>): unknown
201
+ group?(context: DataTableGroupContext<T>): unknown
202
+ summary?(context: DataTableState<T>): unknown
203
+ } & { [K in `cell-${string}`]?: (context: DataTableCellContext<T>) => unknown } & {
204
+ [K in `header-${string}`]?: (context: DataTableHeaderContext<T>) => unknown
205
+ } & { [K in `editor-${string}`]?: (context: DataTableEditorContext<T>) => unknown } & {
206
+ [K in `footer-${string}`]?: (context: { column: DataTableColumn<T>; rows: T[] }) => unknown
207
+ }
208
+
209
+ export interface DataTableHeader<T> extends DataTableHeaderContext<T> {
210
+ id: string
211
+ colspan: number
212
+ rowspan: number
213
+ leaf: boolean
214
+ top: number
215
+ style: CSSProperties
216
+ ariaSort?: 'ascending' | 'descending'
217
+ nextLabel: string
218
+ }
@@ -0,0 +1,75 @@
1
+ import type { DataTableColumn, DataTableFilterMode, DataTableKey } from './types'
2
+
3
+ export function rowId(key: DataTableKey) {
4
+ return `${typeof key}:${key}`
5
+ }
6
+
7
+ export function rowKey(id: string): DataTableKey {
8
+ return id.startsWith('number:') ? Number(id.slice(7)) : id.slice(7)
9
+ }
10
+
11
+ export function positiveInteger(value: number, fallback: number) {
12
+ return Number.isFinite(value) ? Math.max(1, Math.floor(value)) : fallback
13
+ }
14
+
15
+ export function cssSize(value: number | string | undefined) {
16
+ return typeof value === 'number' ? `${value}px` : value
17
+ }
18
+
19
+ export function isRowAction(event: MouseEvent | KeyboardEvent) {
20
+ const target = event.target as HTMLElement | null
21
+ if (!target || event.defaultPrevented) return false
22
+ if (event.type === 'keydown') return target === event.currentTarget
23
+ const control = target.closest(
24
+ 'a, button, input, select, textarea, label, [role="button"], [role="checkbox"], [role="switch"], [contenteditable]:not([contenteditable="false"]), [tabindex]',
25
+ )
26
+ return !control || control === event.currentTarget
27
+ }
28
+
29
+ export function flattenColumns<T>(columns: DataTableColumn<T>[]): DataTableColumn<T>[] {
30
+ return columns.flatMap(column =>
31
+ column.children?.length ? flattenColumns(column.children) : [column],
32
+ )
33
+ }
34
+ export function filterValue(
35
+ value: unknown,
36
+ filter: unknown,
37
+ mode: DataTableFilterMode = 'contains',
38
+ locale = 'en',
39
+ ) {
40
+ if (mode === 'equals') return Object.is(value, filter)
41
+ if (mode === 'in') return Array.isArray(filter) && filter.some(item => Object.is(item, value))
42
+ if (mode === 'range') {
43
+ if (!Array.isArray(filter)) return true
44
+ const numeric = value instanceof Date ? value.getTime() : Number(value)
45
+ const bound = (value: unknown) => (value instanceof Date ? value.getTime() : Number(value))
46
+ return (
47
+ value != null &&
48
+ Number.isFinite(numeric) &&
49
+ (filter[0] == null || numeric >= bound(filter[0])) &&
50
+ (filter[1] == null || numeric <= bound(filter[1]))
51
+ )
52
+ }
53
+ return String(value ?? '')
54
+ .toLocaleLowerCase(locale)
55
+ .includes(String(filter ?? '').toLocaleLowerCase(locale))
56
+ }
57
+ export function aggregate<T>(
58
+ rows: T[],
59
+ column: DataTableColumn<T>,
60
+ valueOf: (row: T, column: DataTableColumn<T>) => unknown,
61
+ ) {
62
+ if (typeof column.aggregate === 'function') return column.aggregate(rows)
63
+ if (!column.aggregate) return undefined
64
+ if (column.aggregate === 'count') return rows.length
65
+ const values = rows.map(row => valueOf(row, column))
66
+ if (column.aggregate === 'uniqueCount') return new Set(values).size
67
+ const numbers = values.filter(
68
+ (value): value is number => typeof value === 'number' && Number.isFinite(value),
69
+ )
70
+ if (!numbers.length) return column.aggregate === 'sum' ? 0 : undefined
71
+ if (column.aggregate === 'min') return numbers.reduce((a, b) => Math.min(a, b))
72
+ if (column.aggregate === 'max') return numbers.reduce((a, b) => Math.max(a, b))
73
+ const sum = numbers.reduce((a, b) => a + b, 0)
74
+ return column.aggregate === 'mean' ? sum / numbers.length : sum
75
+ }
@@ -22,6 +22,9 @@ export function useForm(options: Options) {
22
22
  const submitted = ref(false)
23
23
  const submitting = ref(false)
24
24
  const server = shallowRef(new Map<string, ServerError>())
25
+ let pending: { values: FormValues; rules: FormRules | undefined } | undefined
26
+ let validation = 0
27
+ let submission = 0
25
28
  let initial = snapshot(options.values()) as FormValues
26
29
 
27
30
  function shown(key: string) {
@@ -42,14 +45,33 @@ export function useForm(options: Options) {
42
45
  const invalid = computed(() => Object.keys(errors.value).length > 0 || !!formError.value)
43
46
 
44
47
  async function validate() {
45
- found.value = await runRules(options.rules(), options.values())
46
- return Object.keys(found.value).length === 0
48
+ const version = ++validation
49
+ const values = snapshot(options.values()) as FormValues
50
+ const rules = options.rules()
51
+ const request = { values, rules }
52
+ pending = request
53
+ try {
54
+ const result = await runRules(rules, values)
55
+ if (version !== validation || rules !== options.rules() || !same(values, options.values())) {
56
+ return false
57
+ }
58
+ found.value = result
59
+ return Object.keys(result).length === 0
60
+ } finally {
61
+ if (pending === request) pending = undefined
62
+ }
63
+ }
64
+
65
+ function revalidate() {
66
+ if (pending && pending.rules === options.rules() && same(pending.values, options.values()))
67
+ return
68
+ void validate()
47
69
  }
48
70
 
49
71
  function touch(name: string) {
50
72
  if (touched.value.has(name)) return
51
73
  touched.value = new Set(touched.value).add(name)
52
- if (options.validateOn() !== 'submit') void validate()
74
+ if (options.validateOn() !== 'submit') revalidate()
53
75
  }
54
76
 
55
77
  function onChange() {
@@ -59,20 +81,30 @@ export function useForm(options: Options) {
59
81
  for (const [key, entry] of next) if (readPath(values, key) !== entry.value) next.delete(key)
60
82
  if (next.size !== server.value.size) server.value = next
61
83
  }
62
- if (submitted.value || options.validateOn() !== 'submit') void validate()
84
+ if (submitted.value || options.validateOn() !== 'submit') revalidate()
63
85
  }
64
86
 
65
87
  async function submit(handler?: (values: FormValues) => unknown) {
88
+ if (submitting.value) return false
89
+ const version = ++submission
90
+ const values = snapshot(options.values()) as FormValues
66
91
  submitted.value = true
67
- const valid = await validate()
68
- if (!valid || server.value.size) return false
69
92
  submitting.value = true
70
93
  try {
94
+ const valid = await validate()
95
+ if (
96
+ !valid ||
97
+ version !== submission ||
98
+ server.value.size ||
99
+ !same(values, options.values())
100
+ ) {
101
+ return false
102
+ }
71
103
  await handler?.(options.values())
104
+ return true
72
105
  } finally {
73
- submitting.value = false
106
+ if (version === submission) submitting.value = false
74
107
  }
75
- return true
76
108
  }
77
109
 
78
110
  function setErrors(next: FormErrors) {
@@ -85,6 +117,10 @@ export function useForm(options: Options) {
85
117
  }
86
118
 
87
119
  function reset() {
120
+ validation++
121
+ submission++
122
+ pending = undefined
123
+ submitting.value = false
88
124
  found.value = {}
89
125
  touched.value = new Set()
90
126
  submitted.value = false
@@ -1,15 +1,23 @@
1
- import { defineComponent, h, ref } from 'vue'
1
+ import { defineComponent, h, ref, type PropType } from 'vue'
2
2
  import { provideSidebar, type SidebarState } from './context'
3
3
 
4
4
  export default defineComponent({
5
5
  name: 'HnSidebarDrawerScope',
6
- setup(_, { slots }) {
6
+ props: {
7
+ close: { type: Function as PropType<() => void>, required: true },
8
+ },
9
+ setup(props, { slots }) {
7
10
  provideSidebar({
8
11
  state: ref<SidebarState>('expanded'),
9
- toggle: () => {},
12
+ toggle: () => props.close(),
10
13
  openMobile: () => {},
11
14
  inDrawer: true,
12
15
  })
13
- return () => h('div', { class: 'contents' }, slots.default?.())
16
+ return () =>
17
+ h(
18
+ 'div',
19
+ { class: 'flex min-h-0 flex-1 flex-col overflow-hidden px-(--hn-panel-p)' },
20
+ slots.default?.(),
21
+ )
14
22
  },
15
23
  })
@@ -2,6 +2,7 @@
2
2
  import { computed } from 'vue'
3
3
  import { cn } from '../../lib/cn'
4
4
  import { useUiLocale } from '../../locale'
5
+ import CloseButton from '../close-button/CloseButton.vue'
5
6
  import ScrollArea from '../scroll-area/ScrollArea.vue'
6
7
  import { useSidebar, type SidebarState } from './context'
7
8
  import SidebarLabel from './SidebarLabel.vue'
@@ -15,10 +16,14 @@
15
16
 
16
17
  defineOptions({ name: 'HnSidebar' })
17
18
 
18
- const props = defineProps<{
19
- label?: string
20
- class?: string
21
- }>()
19
+ const props = withDefaults(
20
+ defineProps<{
21
+ label?: string
22
+ closable?: boolean
23
+ class?: string
24
+ }>(),
25
+ { closable: true },
26
+ )
22
27
 
23
28
  defineSlots<{
24
29
  default?(): unknown
@@ -39,9 +44,13 @@
39
44
  :data-state="state"
40
45
  :inert="state === 'hidden'"
41
46
  :class="cn(sidebarRoot({ inDrawer }), props.class)"
47
+ @transitionrun="sidebar?.onTransitionRun"
42
48
  >
43
- <div v-if="$slots.header" :class="sidebarRegion({ inDrawer })">
44
- <slot name="header" :state="state" />
49
+ <div v-if="$slots.header" :class="cn(sidebarRegion({ inDrawer }), 'flex items-center gap-2')">
50
+ <div class="min-w-0 flex-1">
51
+ <slot name="header" :state="state" />
52
+ </div>
53
+ <CloseButton v-if="inDrawer && props.closable" class="shrink-0" @click="sidebar?.toggle()" />
45
54
  </div>
46
55
  <div
47
56
  v-else-if="$slots.icon || $slots.wordmark"
@@ -58,11 +67,22 @@
58
67
  <SidebarLabel v-if="$slots.wordmark" as="div" :class="sidebarWordmark()">
59
68
  <slot name="wordmark" :state="state" />
60
69
  </SidebarLabel>
70
+ <CloseButton
71
+ v-if="inDrawer && props.closable"
72
+ class="ms-auto shrink-0"
73
+ @click="sidebar?.toggle()"
74
+ />
61
75
  </div>
62
76
  </div>
63
77
  </div>
64
78
  </div>
65
- <ScrollArea class="min-h-0 flex-1">
79
+ <div
80
+ v-else-if="inDrawer && props.closable"
81
+ :class="cn(sidebarRegion({ inDrawer }), 'flex justify-end')"
82
+ >
83
+ <CloseButton @click="sidebar?.toggle()" />
84
+ </div>
85
+ <ScrollArea :shadow="false" class="min-h-0 flex-1">
66
86
  <nav
67
87
  :aria-label="props.label ?? t.sidebar.navLabel"
68
88
  :class="cn('flex flex-col gap-1 py-2', !inDrawer && 'px-2.5')"
@@ -7,6 +7,7 @@ export interface SidebarContext {
7
7
  toggle: () => void
8
8
  openMobile: () => void
9
9
  inDrawer?: boolean
10
+ onTransitionRun?: (event: TransitionEvent) => void
10
11
  }
11
12
 
12
13
  const SIDEBAR_KEY = Symbol('hn-sidebar') as InjectionKey<SidebarContext>
@@ -15,7 +15,7 @@ export const sidebarRegion = tv({
15
15
  base: 'shrink-0 py-3',
16
16
  variants: {
17
17
  inDrawer: { true: 'w-full', false: 'w-[calc(16rem-1px)] px-3' },
18
- footer: { true: 'border-line border-t' },
18
+ footer: { true: 'border-line w-full border-t' },
19
19
  },
20
20
  })
21
21
 
@@ -6,7 +6,7 @@
6
6
 
7
7
  const props = withDefaults(
8
8
  defineProps<{
9
- scope?: 'col' | 'row'
9
+ scope?: 'col' | 'row' | 'colgroup' | 'rowgroup'
10
10
  align?: TableCellVariants['align']
11
11
  sticky?: boolean
12
12
  class?: string
package/src/index.ts CHANGED
@@ -209,7 +209,7 @@ export { default as PageAside } from './components/page/PageAside.vue'
209
209
  export { default as Section } from './components/section/Section.vue'
210
210
  export { default as Highlight } from './components/highlight/Highlight.vue'
211
211
  export { default as Anchor } from './components/anchor/Anchor.vue'
212
- export type { AnchorItem } from './components/anchor/Anchor.vue'
212
+ export type { AnchorItem } from './components/anchor/types'
213
213
  export { default as Breadcrumb } from './components/breadcrumb/Breadcrumb.vue'
214
214
  export { default as BreadcrumbItem } from './components/breadcrumb/BreadcrumbItem.vue'
215
215
  export { default as BreadcrumbSeparator } from './components/breadcrumb/BreadcrumbSeparator.vue'
@@ -265,3 +265,28 @@ export type {
265
265
  ScrollRestorer,
266
266
  ScrollRestorerPorts,
267
267
  } from './components/scroll-area/scroll-restore'
268
+
269
+ export { default as DataTable } from './components/data-table/DataTable.vue'
270
+ export type {
271
+ DataTableColumn,
272
+ DataTableKey,
273
+ DataTableAggregate,
274
+ DataTableApi,
275
+ DataTableEdit,
276
+ DataTableEditorContext,
277
+ DataTableExportOptions,
278
+ DataTableFilter,
279
+ DataTableFilterMode,
280
+ DataTableGroupContext,
281
+ DataTableReorder,
282
+ DataTableScope,
283
+ DataTableKeyField,
284
+ DataTableSort,
285
+ DataTableQuery,
286
+ DataTableState,
287
+ DataTableRowContext,
288
+ DataTableCellContext,
289
+ DataTableHeaderContext,
290
+ DataTableProps,
291
+ DataTableSlots,
292
+ } from './components/data-table/types'
@@ -35,6 +35,19 @@ export const enUS: UiMessages = {
35
35
  totalLabel: n => `${n} items`,
36
36
  },
37
37
  table: {
38
+ edit: 'Edit',
39
+ save: 'Save',
40
+ cancel: 'Cancel',
41
+ editFailed: 'Could not save. Try again.',
42
+ selectPage: 'Select current page',
43
+ selectFiltered: 'Select filtered rows',
44
+ expand: 'Expand',
45
+ collapse: 'Collapse',
46
+ moveRow: 'Move row',
47
+ moveColumn: 'Move column',
48
+ resizeColumn: 'Resize column',
49
+
50
+ noColumns: 'No visible columns',
38
51
  empty: 'No data',
39
52
  loading: 'Loading',
40
53
  sortAsc: 'Sort ascending',
@@ -32,6 +32,19 @@ export interface UiMessages {
32
32
  totalLabel: (n: number) => string
33
33
  }
34
34
  table: {
35
+ edit: string
36
+ save: string
37
+ cancel: string
38
+ editFailed: string
39
+ selectPage: string
40
+ selectFiltered: string
41
+ expand: string
42
+ collapse: string
43
+ moveRow: string
44
+ moveColumn: string
45
+ resizeColumn: string
46
+
47
+ noColumns: string
35
48
  empty: string
36
49
  loading: string
37
50
  sortAsc: string
@@ -35,6 +35,19 @@ export const zhCN: UiMessages = {
35
35
  totalLabel: n => `共 ${n} 条`,
36
36
  },
37
37
  table: {
38
+ edit: '编辑',
39
+ save: '保存',
40
+ cancel: '取消',
41
+ editFailed: '保存失败,请重试',
42
+ selectPage: '选择当前页',
43
+ selectFiltered: '选择筛选结果',
44
+ expand: '展开',
45
+ collapse: '收起',
46
+ moveRow: '移动行',
47
+ moveColumn: '移动列',
48
+ resizeColumn: '调整列宽',
49
+
50
+ noColumns: '没有可见列',
38
51
  empty: '暂无数据',
39
52
  loading: '加载中',
40
53
  sortAsc: '升序排列',