@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.
- package/dist/index.js +3170 -461
- package/dist/index.js.map +1 -1
- package/dist/types/components/anchor/Anchor.vue.d.ts +26 -10
- package/dist/types/components/anchor/Anchor.vue.d.ts.map +1 -1
- package/dist/types/components/anchor/composables/useAnchor.d.ts +15 -0
- package/dist/types/components/anchor/composables/useAnchor.d.ts.map +1 -0
- package/dist/types/components/anchor/composables/useScrollSpy.d.ts.map +1 -1
- package/dist/types/components/anchor/types.d.ts +9 -0
- package/dist/types/components/anchor/types.d.ts.map +1 -0
- package/dist/types/components/app-shell/AppShell.vue.d.ts.map +1 -1
- package/dist/types/components/app-shell/composables/useSidebarScrollUpdates.d.ts +4 -0
- package/dist/types/components/app-shell/composables/useSidebarScrollUpdates.d.ts.map +1 -0
- package/dist/types/components/combobox/Combobox.vue.d.ts +1 -0
- package/dist/types/components/combobox/Combobox.vue.d.ts.map +1 -1
- package/dist/types/components/data-table/DataTable.vue.d.ts +58 -0
- package/dist/types/components/data-table/DataTable.vue.d.ts.map +1 -0
- package/dist/types/components/data-table/DataTableAction.vue.d.ts +18 -0
- package/dist/types/components/data-table/DataTableAction.vue.d.ts.map +1 -0
- package/dist/types/components/data-table/DataTableDragPreview.vue.d.ts +24 -0
- package/dist/types/components/data-table/DataTableDragPreview.vue.d.ts.map +1 -0
- package/dist/types/components/data-table/DataTableEditor.vue.d.ts +24 -0
- package/dist/types/components/data-table/DataTableEditor.vue.d.ts.map +1 -0
- package/dist/types/components/data-table/DataTableHead.vue.d.ts +32 -0
- package/dist/types/components/data-table/DataTableHead.vue.d.ts.map +1 -0
- package/dist/types/components/data-table/DataTableRow.vue.d.ts +39 -0
- package/dist/types/components/data-table/DataTableRow.vue.d.ts.map +1 -0
- package/dist/types/components/data-table/DataTableSelection.vue.d.ts +15 -0
- package/dist/types/components/data-table/DataTableSelection.vue.d.ts.map +1 -0
- package/dist/types/components/data-table/DataTableText.vue.d.ts +8 -0
- package/dist/types/components/data-table/DataTableText.vue.d.ts.map +1 -0
- package/dist/types/components/data-table/Slot.d.ts +8 -0
- package/dist/types/components/data-table/Slot.d.ts.map +1 -0
- package/dist/types/components/data-table/column-sizing.d.ts +22 -0
- package/dist/types/components/data-table/column-sizing.d.ts.map +1 -0
- package/dist/types/components/data-table/composables/useDataTable.d.ts +80 -0
- package/dist/types/components/data-table/composables/useDataTable.d.ts.map +1 -0
- package/dist/types/components/data-table/composables/useTableColumns.d.ts +51 -0
- package/dist/types/components/data-table/composables/useTableColumns.d.ts.map +1 -0
- package/dist/types/components/data-table/composables/useTableDrag.d.ts +53 -0
- package/dist/types/components/data-table/composables/useTableDrag.d.ts.map +1 -0
- package/dist/types/components/data-table/composables/useTableEditing.d.ts +27 -0
- package/dist/types/components/data-table/composables/useTableEditing.d.ts.map +1 -0
- package/dist/types/components/data-table/composables/useTableExport.d.ts +3 -0
- package/dist/types/components/data-table/composables/useTableExport.d.ts.map +1 -0
- package/dist/types/components/data-table/composables/useTableResize.d.ts +42 -0
- package/dist/types/components/data-table/composables/useTableResize.d.ts.map +1 -0
- package/dist/types/components/data-table/composables/useTableResizeHandles.d.ts +8 -0
- package/dist/types/components/data-table/composables/useTableResizeHandles.d.ts.map +1 -0
- package/dist/types/components/data-table/composables/useTableText.d.ts +6 -0
- package/dist/types/components/data-table/composables/useTableText.d.ts.map +1 -0
- package/dist/types/components/data-table/composables/useTableVirtual.d.ts +55 -0
- package/dist/types/components/data-table/composables/useTableVirtual.d.ts.map +1 -0
- package/dist/types/components/data-table/features.d.ts +16 -0
- package/dist/types/components/data-table/features.d.ts.map +1 -0
- package/dist/types/components/data-table/types.d.ts +225 -0
- package/dist/types/components/data-table/types.d.ts.map +1 -0
- package/dist/types/components/data-table/utils.d.ts +10 -0
- package/dist/types/components/data-table/utils.d.ts.map +1 -0
- package/dist/types/components/form/composables/useForm.d.ts.map +1 -1
- package/dist/types/components/sidebar/DrawerScope.d.ts +13 -2
- package/dist/types/components/sidebar/DrawerScope.d.ts.map +1 -1
- package/dist/types/components/sidebar/Sidebar.vue.d.ts +4 -1
- package/dist/types/components/sidebar/Sidebar.vue.d.ts.map +1 -1
- package/dist/types/components/sidebar/context.d.ts +1 -0
- package/dist/types/components/sidebar/context.d.ts.map +1 -1
- package/dist/types/components/sidebar/sidebar.variants.d.ts +3 -3
- package/dist/types/components/table/TableHead.vue.d.ts +2 -2
- package/dist/types/components/table/TableHead.vue.d.ts.map +1 -1
- package/dist/types/index.d.ts +3 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/locale/en-US.d.ts.map +1 -1
- package/dist/types/locale/types.d.ts +12 -0
- package/dist/types/locale/types.d.ts.map +1 -1
- package/dist/types/locale/zh-CN.d.ts.map +1 -1
- package/package.json +14 -12
- package/src/components/anchor/Anchor.vue +15 -19
- package/src/components/anchor/composables/useAnchor.ts +19 -0
- package/src/components/anchor/composables/useScrollSpy.ts +6 -1
- package/src/components/anchor/types.ts +8 -0
- package/src/components/app-shell/AppShell.vue +5 -2
- package/src/components/app-shell/composables/useSidebarScrollUpdates.ts +52 -0
- package/src/components/combobox/Combobox.vue +11 -1
- package/src/components/data-table/DataTable.vue +340 -0
- package/src/components/data-table/DataTableAction.vue +15 -0
- package/src/components/data-table/DataTableDragPreview.vue +72 -0
- package/src/components/data-table/DataTableEditor.vue +59 -0
- package/src/components/data-table/DataTableHead.vue +181 -0
- package/src/components/data-table/DataTableRow.vue +255 -0
- package/src/components/data-table/DataTableSelection.vue +51 -0
- package/src/components/data-table/DataTableText.vue +20 -0
- package/src/components/data-table/Slot.ts +12 -0
- package/src/components/data-table/column-sizing.ts +101 -0
- package/src/components/data-table/composables/useDataTable.ts +425 -0
- package/src/components/data-table/composables/useTableColumns.ts +253 -0
- package/src/components/data-table/composables/useTableDrag.ts +345 -0
- package/src/components/data-table/composables/useTableEditing.ts +184 -0
- package/src/components/data-table/composables/useTableExport.ts +44 -0
- package/src/components/data-table/composables/useTableResize.ts +292 -0
- package/src/components/data-table/composables/useTableResizeHandles.ts +72 -0
- package/src/components/data-table/composables/useTableText.ts +13 -0
- package/src/components/data-table/composables/useTableVirtual.ts +109 -0
- package/src/components/data-table/features.ts +32 -0
- package/src/components/data-table/types.ts +218 -0
- package/src/components/data-table/utils.ts +75 -0
- package/src/components/form/composables/useForm.ts +44 -8
- package/src/components/sidebar/DrawerScope.ts +12 -4
- package/src/components/sidebar/Sidebar.vue +27 -7
- package/src/components/sidebar/context.ts +1 -0
- package/src/components/sidebar/sidebar.variants.ts +1 -1
- package/src/components/table/TableHead.vue +1 -1
- package/src/index.ts +26 -1
- package/src/locale/en-US.ts +13 -0
- package/src/locale/types.ts +13 -0
- package/src/locale/zh-CN.ts +13 -0
- package/src/styles/data-table.css +331 -0
- package/src/styles/tokens.css +1 -0
- 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
|
-
|
|
46
|
-
|
|
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')
|
|
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')
|
|
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
|
-
|
|
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 () =>
|
|
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 =
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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')"
|
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/
|
|
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'
|
package/src/locale/en-US.ts
CHANGED
|
@@ -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',
|
package/src/locale/types.ts
CHANGED
|
@@ -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
|
package/src/locale/zh-CN.ts
CHANGED
|
@@ -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: '升序排列',
|