@xen-orchestra/web-core 0.35.1 → 0.37.0
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/lib/components/console/VtsRemoteConsole.vue +1 -1
- package/lib/components/copy-button/VtsCopyButton.vue +1 -1
- package/lib/components/layout/VtsLayoutSidebar.vue +1 -1
- package/lib/components/quick-info-card/VtsQuickInfoCard.vue +1 -1
- package/lib/components/relative-time/VtsRelativeTime.vue +2 -3
- package/lib/components/select/VtsSelect.vue +1 -1
- package/lib/components/state-hero/VtsStateHero.vue +20 -10
- package/lib/components/table/VtsRow.vue +26 -0
- package/lib/components/table/VtsTable.vue +99 -42
- package/lib/components/table/cells/VtsCollapsedListCell.vue +59 -0
- package/lib/components/table/cells/VtsHeaderCell.vue +31 -0
- package/lib/components/table/cells/VtsLinkCell.vue +33 -0
- package/lib/components/table/cells/VtsNumberCell.vue +21 -0
- package/lib/components/{size-progress-cell/VtsSizeProgressCell.vue → table/cells/VtsProgressBarCell.vue} +8 -5
- package/lib/components/table/cells/VtsStatusCell.vue +69 -0
- package/lib/components/table/cells/VtsTagCell.vue +24 -0
- package/lib/components/table/cells/VtsTextCell.vue +32 -0
- package/lib/components/table/cells/VtsTruncatedTextCell.vue +64 -0
- package/lib/components/task/VtsQuickTaskButton.vue +1 -1
- package/lib/components/tree/VtsTreeLine.vue +1 -1
- package/lib/components/ui/alert/UiAlert.vue +1 -1
- package/lib/components/ui/button/UiButton.vue +4 -2
- package/lib/components/ui/button-icon/UiButtonIcon.vue +12 -11
- package/lib/components/ui/column-header/UiColumnHeader.vue +22 -0
- package/lib/components/ui/info/UiInfo.vue +5 -1
- package/lib/components/ui/input/UiInput.vue +3 -2
- package/lib/components/ui/link/UiLink.vue +5 -0
- package/lib/components/ui/log-entry-viewer/UiLogEntryViewer.vue +1 -1
- package/lib/components/ui/query-search-bar/UiQuerySearchBar.vue +2 -2
- package/lib/components/ui/table-cell/UiTableCell.vue +41 -0
- package/lib/components/ui/table-pagination/PaginationButton.vue +1 -1
- package/lib/components/ui/task-item/UiTaskItem.vue +229 -0
- package/lib/components/ui/task-list/UiTaskList.vue +31 -0
- package/lib/components/ui/toaster/UiToaster.vue +1 -1
- package/lib/components/ui/top-bottom-table/UiTopBottomTable.vue +6 -2
- package/lib/components/ui/tree-item-label/UiTreeItemLabel.vue +1 -7
- package/lib/composables/pagination.composable.ts +16 -1
- package/lib/composables/relative-time.composable.ts +8 -61
- package/lib/composables/table-state.composable.ts +56 -0
- package/lib/composables/tree-filter.composable.ts +2 -2
- package/lib/icons/fa-icons.ts +2 -0
- package/lib/icons/object-icons.ts +1 -1
- package/lib/locales/en.json +26 -1
- package/lib/locales/fr.json +26 -1
- package/lib/packages/form-select/use-form-select-controller.ts +1 -0
- package/lib/packages/table/README.md +53 -308
- package/lib/packages/table/define-column.ts +7 -0
- package/lib/packages/table/define-columns.ts +104 -50
- package/lib/packages/table/index.ts +3 -11
- package/lib/packages/table/types.ts +10 -0
- package/lib/{composables/tree.composable.md → packages/tree/README.md} +28 -23
- package/lib/{composables → packages}/tree/branch-definition.ts +9 -4
- package/lib/{composables → packages}/tree/branch.ts +15 -20
- package/lib/{composables → packages}/tree/build-nodes.ts +5 -5
- package/lib/{composables → packages}/tree/define-branch.ts +8 -4
- package/lib/{composables → packages}/tree/define-leaf.ts +8 -3
- package/lib/{composables → packages}/tree/define-tree.ts +10 -5
- package/lib/{composables → packages}/tree/leaf-definition.ts +1 -1
- package/lib/{composables → packages}/tree/leaf.ts +3 -3
- package/lib/{composables → packages}/tree/tree-node-base.ts +18 -3
- package/lib/{composables → packages}/tree/tree-node-definition-base.ts +4 -2
- package/lib/{composables → packages}/tree/types.ts +11 -9
- package/lib/{composables/tree.composable.ts → packages/tree/use-tree.ts} +24 -11
- package/lib/tables/column-definitions/address-column.ts +4 -0
- package/lib/tables/column-definitions/button-column.ts +35 -0
- package/lib/tables/column-definitions/button-icon-column.ts +30 -0
- package/lib/tables/column-definitions/collapsed-list-column.ts +12 -0
- package/lib/tables/column-definitions/date-column.ts +34 -0
- package/lib/tables/column-definitions/info-column.ts +12 -0
- package/lib/tables/column-definitions/input-column.ts +32 -0
- package/lib/tables/column-definitions/link-column.ts +14 -0
- package/lib/tables/column-definitions/literal-column.ts +9 -0
- package/lib/tables/column-definitions/number-column.ts +10 -0
- package/lib/tables/column-definitions/percent-column.ts +15 -0
- package/lib/tables/column-definitions/progress-bar-column.ts +10 -0
- package/lib/tables/column-definitions/select-column.ts +12 -0
- package/lib/tables/column-definitions/select-item-column.ts +8 -0
- package/lib/tables/column-definitions/status-column.ts +16 -0
- package/lib/tables/column-definitions/tag-column.ts +11 -0
- package/lib/tables/column-definitions/text-column.ts +11 -0
- package/lib/tables/column-definitions/truncated-text-column.ts +10 -0
- package/lib/tables/column-sets/backup-issue-columns.ts +15 -0
- package/lib/tables/column-sets/backup-job-columns.ts +23 -0
- package/lib/tables/column-sets/backup-job-schedule-columns.ts +21 -0
- package/lib/tables/column-sets/backup-log-columns.ts +19 -0
- package/lib/tables/column-sets/host-columns.ts +19 -0
- package/lib/tables/column-sets/network-columns.ts +22 -0
- package/lib/tables/column-sets/new-vm-network-columns.ts +24 -0
- package/lib/tables/column-sets/new-vm-sr-columns.ts +33 -0
- package/lib/tables/column-sets/patch-columns.ts +13 -0
- package/lib/tables/column-sets/pif-columns.ts +23 -0
- package/lib/tables/column-sets/server-columns.ts +18 -0
- package/lib/tables/column-sets/sr-columns.ts +20 -0
- package/lib/tables/column-sets/vdi-columns.ts +21 -0
- package/lib/tables/column-sets/vif-columns.ts +23 -0
- package/lib/tables/column-sets/vm-columns.ts +21 -0
- package/lib/tables/helpers/render-body-cell.ts +4 -0
- package/lib/tables/helpers/render-head-cell.ts +6 -0
- package/lib/tables/helpers/render-loading-cell.ts +5 -0
- package/lib/tables/types.ts +7 -0
- package/lib/utils/size.util.ts +5 -9
- package/package.json +1 -1
- package/lib/components/data-table/VtsDataTable.vue +0 -70
- package/lib/components/table/ColumnTitle.vue +0 -152
- package/lib/packages/table/apply-extensions.ts +0 -26
- package/lib/packages/table/define-renderer/define-table-cell-renderer.ts +0 -27
- package/lib/packages/table/define-renderer/define-table-renderer.ts +0 -47
- package/lib/packages/table/define-renderer/define-table-row-renderer.ts +0 -29
- package/lib/packages/table/define-renderer/define-table-section-renderer.ts +0 -29
- package/lib/packages/table/define-table/define-multi-source-table.ts +0 -39
- package/lib/packages/table/define-table/define-table.ts +0 -13
- package/lib/packages/table/define-table/define-typed-table.ts +0 -18
- package/lib/packages/table/transform-sources.ts +0 -13
- package/lib/packages/table/types/extensions.ts +0 -16
- package/lib/packages/table/types/index.ts +0 -47
- package/lib/packages/table/types/table-cell.ts +0 -18
- package/lib/packages/table/types/table-row.ts +0 -20
- package/lib/packages/table/types/table-section.ts +0 -19
- package/lib/packages/table/types/table.ts +0 -28
- package/lib/types/button.type.ts +0 -3
|
@@ -1,336 +1,81 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `defineColumn`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Define the head and body renderers for a column.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The render functions can receive arguments and must return a VNode.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Example 1 - A simple text column
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
The `props` parameter is a function that receives an optional typed config and returns default props:
|
|
16
|
-
|
|
17
|
-
```typescript
|
|
18
|
-
const TextBody = defineTableCellRenderer({
|
|
19
|
-
component: () => import('./TextCell.vue'),
|
|
20
|
-
props: (config: { text: string }) => ({ data: config.text }),
|
|
21
|
-
// ^ This config type will be enforced when using the renderer
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
// Usage - TypeScript knows you need to provide `text`
|
|
25
|
-
TextBody({ text: 'Hello' })
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
When you use the renderer, you can:
|
|
29
|
-
|
|
30
|
-
- Provide the expected config to satisfy the `props` function
|
|
31
|
-
- Add additional props that will be merged
|
|
32
|
-
- Override default props
|
|
33
|
-
|
|
34
|
-
```typescript
|
|
35
|
-
// The renderer merges:
|
|
36
|
-
// 1. Props from the props function: { text: 'Hello' }
|
|
37
|
-
// 2. Additional/override props: { class: 'custom' }
|
|
38
|
-
TextBody({
|
|
39
|
-
text: 'Hello', // Used by props function
|
|
40
|
-
props: {
|
|
41
|
-
// Additional or override props
|
|
42
|
-
class: 'custom',
|
|
43
|
-
},
|
|
44
|
-
})
|
|
9
|
+
```ts
|
|
10
|
+
const useTextColumn = defineColumn((config?: { headLabel?: string }) => ({
|
|
11
|
+
renderHead: () => h('th', { class: 'table-head-cell' }, config?.headLabel),
|
|
12
|
+
renderBody: (text: string) => h('td', { class: 'table-text-cell' }, text),
|
|
13
|
+
}))
|
|
45
14
|
```
|
|
46
15
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
Extensions work like the `props` function but are optional and named. Each extension:
|
|
50
|
-
|
|
51
|
-
- Has a unique name (like `selectable`, `highlightable`)
|
|
52
|
-
- Receives typed configuration
|
|
53
|
-
- Returns the extension arguments (only `props` for now) to merge into the component
|
|
16
|
+
## Example 2 - A "number with unit" column
|
|
54
17
|
|
|
55
|
-
```
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
},
|
|
18
|
+
```ts
|
|
19
|
+
const useNumberWithUnitColumn = defineColumn((config?: { headLabel?: string; defaultUnit?: string }) => ({
|
|
20
|
+
renderHead: () => h('th', config?.headLabel ?? 'User'),
|
|
21
|
+
renderBody: (value: number, unit?: string) =>
|
|
22
|
+
h(NumberCell, {
|
|
23
|
+
value,
|
|
24
|
+
suffix: unit ?? config?.defaultUnit,
|
|
63
25
|
}),
|
|
64
|
-
|
|
65
|
-
props: {
|
|
66
|
-
highlighted: config.isHighlighted,
|
|
67
|
-
},
|
|
68
|
-
}),
|
|
69
|
-
},
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
// Usage - provide config for the extensions you want to use
|
|
73
|
-
MyRow({
|
|
74
|
-
cells: () => [...],
|
|
75
|
-
extensions: {
|
|
76
|
-
selectable: { id: user.id, selectedId },
|
|
77
|
-
highlightable: { isHighlighted: true },
|
|
78
|
-
}
|
|
79
|
-
})
|
|
26
|
+
}))
|
|
80
27
|
```
|
|
81
28
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
## Defining Renderers
|
|
85
|
-
|
|
86
|
-
### Cell Renderers
|
|
29
|
+
# `defineColumns`
|
|
87
30
|
|
|
88
|
-
|
|
31
|
+
Define a set of columns.
|
|
89
32
|
|
|
90
|
-
|
|
91
|
-
import { defineTableCellRenderer } from '@core/packages/table'
|
|
92
|
-
|
|
93
|
-
const TextHeader = defineTableCellRenderer({
|
|
94
|
-
component: () => import('./VtsHeaderCell.vue'),
|
|
95
|
-
props: (config: { label: string }) => ({
|
|
96
|
-
label: config.label,
|
|
97
|
-
icon: icon('fa:align-left'),
|
|
98
|
-
}),
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
const TextBody = defineTableCellRenderer({
|
|
102
|
-
component: () => import('./body-cells/VtsTextCell.vue'),
|
|
103
|
-
props: (config: { text: string | number }) => ({ text: config.text }),
|
|
104
|
-
})
|
|
33
|
+
## Example
|
|
105
34
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
35
|
+
```ts
|
|
36
|
+
const useUserTableColumns = defineColumns(() => ({
|
|
37
|
+
fullName: useTextColumn({ headLabel: 'Full name' }),
|
|
38
|
+
email: useTextColumn({ headLabel: 'Email' }),
|
|
39
|
+
age: useNumberWithUnitColumn({ headLabel: 'Age', defaultUnit: 'years' }),
|
|
40
|
+
}))
|
|
109
41
|
```
|
|
110
42
|
|
|
111
|
-
|
|
43
|
+
# Using the columns set
|
|
112
44
|
|
|
113
|
-
|
|
45
|
+
When using the columns set, you could have to provide `head` and/or `body` arguments based on whether any `renderHead` and/or `renderBody` used in the columns set expect arguments.
|
|
114
46
|
|
|
115
|
-
|
|
116
|
-
import { defineTableRowRenderer } from '@core/packages/table'
|
|
47
|
+
## Example
|
|
117
48
|
|
|
118
|
-
|
|
119
|
-
component: () => import('./VtsRow.vue'),
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
DefaultRow({
|
|
123
|
-
cells: () => [...]
|
|
124
|
-
})
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### Table Renderers
|
|
128
|
-
|
|
129
|
-
Create table renderer:
|
|
130
|
-
|
|
131
|
-
```typescript
|
|
132
|
-
import { defineTableRenderer } from '@core/packages/table'
|
|
133
|
-
|
|
134
|
-
const DefaultTable = defineTableRenderer({
|
|
135
|
-
component: () => import('./VtsTableNew.vue'),
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
// Usage
|
|
139
|
-
DefaultTable({
|
|
140
|
-
thead: MyThead(...),
|
|
141
|
-
// thead: { rows: () => [...] }, // to use native "thead"
|
|
142
|
-
// thead: { cells: () => [...] }, // to use native "thead" + "tr",
|
|
143
|
-
tbody: MyTBody(...),
|
|
144
|
-
// tbody: { rows: () => [...] }, // to use native "tbody"
|
|
145
|
-
})
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
## Building Tables
|
|
149
|
-
|
|
150
|
-
### Column Definition
|
|
49
|
+
The `body` function receives the row item (here, a `User`) and returns an object where:
|
|
151
50
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
```typescript
|
|
155
|
-
const columns = defineColumns({
|
|
156
|
-
name: {
|
|
157
|
-
header: () => TextHeader({ label: 'Name' }),
|
|
158
|
-
body: user => TextBody({ text: user.name }),
|
|
159
|
-
},
|
|
160
|
-
email: {
|
|
161
|
-
header: () => TextHeader({ label: 'Email' }),
|
|
162
|
-
body: user => TextBody({ text: user.email }),
|
|
163
|
-
},
|
|
164
|
-
// Conditional column
|
|
165
|
-
role: isAdmin
|
|
166
|
-
? {
|
|
167
|
-
header: () => TextHeader({ label: 'Role' }),
|
|
168
|
-
body: user => TextBody({ text: user.role }),
|
|
169
|
-
}
|
|
170
|
-
: undefined,
|
|
171
|
-
})
|
|
172
|
-
```
|
|
51
|
+
- **Keys** are column names (`fullName`, `email`, `age`)
|
|
52
|
+
- **Values** are functions that receive the column's original renderer (`r`) and must return a VNode
|
|
173
53
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
```typescript
|
|
177
|
-
const columns = defineColumns({
|
|
178
|
-
name: {
|
|
179
|
-
header: (config) => TextHeader(...),
|
|
180
|
-
body: (user, config) => TextBody(...),
|
|
181
|
-
},
|
|
182
|
-
})
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
```typescript
|
|
186
|
-
// API
|
|
187
|
-
columns.getHeaderCells(config?) // Array of header cell VNodes
|
|
188
|
-
columns.getBodyCells(user, config?) // Array of body cell VNodes for a row
|
|
189
|
-
columns.toggleColumn('name') // Toggle column visibility
|
|
190
|
-
columns.toggleColumn('name', true) // Force column visibility
|
|
191
|
-
columns.visibleColumnsCount // ComputedRef<number>, useful for colspan
|
|
192
|
-
```
|
|
54
|
+
You can either use the original renderer by calling `r(data)`, or ignore it and return your own VNode.
|
|
193
55
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
```typescript
|
|
201
|
-
const { getHeaderCells, getBodyCells } = defineColumns(...)
|
|
202
|
-
|
|
203
|
-
const useUserTable = defineTable((sources: ComputedRef<User[]>) =>
|
|
204
|
-
() => DefaultTable({
|
|
205
|
-
thead: {
|
|
206
|
-
cells: () => getHeaderCells()
|
|
207
|
-
},
|
|
208
|
-
tbody: {
|
|
209
|
-
rows: () => sources.value.map(user =>
|
|
210
|
-
DefaultRow({
|
|
211
|
-
cells: () => getBodyCells(user)
|
|
212
|
-
})
|
|
213
|
-
)
|
|
214
|
-
},
|
|
215
|
-
})
|
|
216
|
-
)
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
```typescript
|
|
220
|
-
// Usage
|
|
221
|
-
const users = ref<User[]>([...])
|
|
222
|
-
|
|
223
|
-
const table = useUserTable(users, {})
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
`defineTable` setup function can also define a config parameter as second argument:
|
|
227
|
-
|
|
228
|
-
```typescript
|
|
229
|
-
const useUserTable = defineTable((sources: ComputedRef<User[]>, config: { needThis: string }) => ...)
|
|
230
|
-
|
|
231
|
-
const table = useUserTable(users, { needThis: 'value' })
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
#### Define type-discriminated table: `defineTypedTable`
|
|
235
|
-
|
|
236
|
-
```typescript
|
|
237
|
-
type Source = { type: 'user'; sources: ComputedRef<User[]> } | { type: 'admin'; sources: ComputedRef<Admin[]> }
|
|
238
|
-
|
|
239
|
-
const useItemTable = defineTypedTable(({ type, sources }: Source) => {
|
|
240
|
-
// If type === 'user', sources is ComputedRef<User[]>
|
|
241
|
-
// If type === 'admin', sources is ComputedRef<Admin[]>
|
|
242
|
-
|
|
243
|
-
return () => DefaultTable({...})
|
|
244
|
-
})
|
|
245
|
-
|
|
246
|
-
// Usage
|
|
247
|
-
useItemTable('admin', admins, {})
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
#### Define multiple sources table: `defineMultiSourceTable`
|
|
251
|
-
|
|
252
|
-
```typescript
|
|
253
|
-
type Sources = {
|
|
254
|
-
users: ComputedRef<User[]>
|
|
255
|
-
admins: ComputedRef<Admin[]>
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
const useDashboard = defineMultiSourceTable((sources: Sources) => {
|
|
259
|
-
// sources.users: ComputedRef<User[]>
|
|
260
|
-
// sources.admins: ComputedRef<Admin[]>
|
|
261
|
-
|
|
262
|
-
return () => DefaultTable({...})
|
|
263
|
-
})
|
|
264
|
-
|
|
265
|
-
// Usage
|
|
266
|
-
useDashboard({ users, admins }, {})
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
### Source Transformation
|
|
270
|
-
|
|
271
|
-
When using a defined table, if passed sources doesn't match expected sources, then a `transform` config will be required to add missing or incorrectly typed properties:
|
|
272
|
-
|
|
273
|
-
```typescript
|
|
274
|
-
type User = {
|
|
275
|
-
id: string
|
|
276
|
-
fullName: string
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
const useUserTable = defineTable((sources: ComputedRef<User[]>) => {})
|
|
280
|
-
|
|
281
|
-
// Raw data has different shape
|
|
282
|
-
interface RawUser {
|
|
283
|
-
uuid: string
|
|
284
|
-
firstName: string
|
|
285
|
-
lastName: string
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// Transform is required when types don't match
|
|
289
|
-
useUserTable(rawUsers, {
|
|
290
|
-
transform: user => ({
|
|
291
|
-
id: user.uuid,
|
|
292
|
-
fullName: `${user.firstName} ${user.lastName}`,
|
|
56
|
+
```ts
|
|
57
|
+
const { HeadCells, BodyCells } = useUserTableColumns({
|
|
58
|
+
body: (user: User) => ({
|
|
59
|
+
fullName: r => r(`${user.firstName} ${user.lastName}`),
|
|
60
|
+
email: r => r(user.email),
|
|
61
|
+
age: r => r(user.age < 2 ? { value: toMonths(user.age), unit: 'months' } : { value: user.age }),
|
|
293
62
|
}),
|
|
294
63
|
})
|
|
295
|
-
|
|
296
|
-
// Transform is optional when types already match
|
|
297
|
-
useUserTable(users, {})
|
|
298
64
|
```
|
|
299
65
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
```vue
|
|
66
|
+
```html
|
|
303
67
|
<template>
|
|
304
|
-
<
|
|
68
|
+
<table>
|
|
69
|
+
<thead>
|
|
70
|
+
<tr>
|
|
71
|
+
<HeadCells />
|
|
72
|
+
</tr>
|
|
73
|
+
</thead>
|
|
74
|
+
<tbody>
|
|
75
|
+
<tr v-for="user in users" :key="user.id">
|
|
76
|
+
<BodyCells :item="user" />
|
|
77
|
+
</tr>
|
|
78
|
+
</tbody>
|
|
79
|
+
</table>
|
|
305
80
|
</template>
|
|
306
|
-
|
|
307
|
-
<script setup lang="ts">
|
|
308
|
-
const MyUsersTable = useUsersTable(users, {})
|
|
309
|
-
</script>
|
|
310
81
|
```
|
|
311
|
-
|
|
312
|
-
## Props
|
|
313
|
-
|
|
314
|
-
When a table is rendered, each element's props will be merged together in the following order:
|
|
315
|
-
|
|
316
|
-
1. Props from the renderer `props` function
|
|
317
|
-
2. Props from extensions `props` functions
|
|
318
|
-
3. Props provided when using the renderer
|
|
319
|
-
|
|
320
|
-
They will be merged with Vue's default merging strategy (for example, `class` and `style` will be concatenated).
|
|
321
|
-
|
|
322
|
-
## API Reference
|
|
323
|
-
|
|
324
|
-
### Renderer Functions
|
|
325
|
-
|
|
326
|
-
- `defineTableRenderer` - Define table wrapper (`table`)
|
|
327
|
-
- `defineTableSectionRenderer` - Define table sections (`thead` / `tbody`)
|
|
328
|
-
- `defineTableRowRenderer` - Define table rows (`tr`)
|
|
329
|
-
- `defineTableCellRenderer` - Define table cells (`th` / `td`)
|
|
330
|
-
|
|
331
|
-
### Table Functions
|
|
332
|
-
|
|
333
|
-
- `defineTable` - Single source table
|
|
334
|
-
- `defineTypedTable` - Type-discriminated table
|
|
335
|
-
- `defineMultiSourceTable` - Multiple sources table
|
|
336
|
-
- `defineColumns` - Column definitions
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ColumnRenderer } from '@core/packages/table/types.ts'
|
|
2
|
+
|
|
3
|
+
export function defineColumn<TSetupArgs extends any[], TRenderHeadArgs extends any[], TRenderBodyArgs extends any[]>(
|
|
4
|
+
setup: (...args: TSetupArgs) => ColumnRenderer<TRenderHeadArgs, TRenderBodyArgs>
|
|
5
|
+
) {
|
|
6
|
+
return setup
|
|
7
|
+
}
|
|
@@ -1,62 +1,116 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
string,
|
|
8
|
-
| {
|
|
9
|
-
header: (arg: THeaderArg) => TableCellVNode
|
|
10
|
-
body: (source: TSource, arg: TBodyArg) => TableCellVNode
|
|
11
|
-
}
|
|
12
|
-
| undefined
|
|
13
|
-
>,
|
|
14
|
-
TColumnName extends Extract<keyof TColumns, string>,
|
|
15
|
-
THeaderArg = undefined,
|
|
16
|
-
TBodyArg = undefined,
|
|
17
|
-
>(
|
|
18
|
-
config: TColumns &
|
|
19
|
-
Record<
|
|
20
|
-
string,
|
|
21
|
-
| {
|
|
22
|
-
header: (arg: THeaderArg) => TableCellVNode
|
|
23
|
-
body: (source: TSource, arg: TBodyArg) => TableCellVNode
|
|
24
|
-
}
|
|
25
|
-
| undefined
|
|
26
|
-
>
|
|
1
|
+
import type { AreAllPropertiesOptional, Columns } from '@core/packages/table/types.ts'
|
|
2
|
+
import { objectOmit } from '@vueuse/shared'
|
|
3
|
+
import { computed, defineComponent, Fragment, h, ref, type Component, type Ref, type VNode } from 'vue'
|
|
4
|
+
|
|
5
|
+
export function defineColumns<TSetupArgs extends any[], TColumns extends Columns>(
|
|
6
|
+
setup: (...args: TSetupArgs) => TColumns
|
|
27
7
|
) {
|
|
28
|
-
|
|
8
|
+
function useColumns<
|
|
9
|
+
THeadRenderers extends {
|
|
10
|
+
[K in keyof TColumns as [] extends Parameters<TColumns[K]['renderHead']> ? K : never]?: (
|
|
11
|
+
renderer: TColumns[K]['renderHead']
|
|
12
|
+
) => VNode
|
|
13
|
+
} & {
|
|
14
|
+
[K in keyof TColumns as [] extends Parameters<TColumns[K]['renderHead']> ? never : K]: (
|
|
15
|
+
renderer: TColumns[K]['renderHead']
|
|
16
|
+
) => VNode
|
|
17
|
+
},
|
|
18
|
+
TBodyRenderers extends {
|
|
19
|
+
[K in keyof TColumns]: (renderer: TColumns[K]['renderBody']) => VNode
|
|
20
|
+
},
|
|
21
|
+
THeadItem,
|
|
22
|
+
TBodyItem,
|
|
23
|
+
TExcludedId extends keyof TColumns = never,
|
|
24
|
+
>(
|
|
25
|
+
config: {
|
|
26
|
+
exclude?: TExcludedId[]
|
|
27
|
+
body: (item: TBodyItem) => Omit<TBodyRenderers, TExcludedId>
|
|
28
|
+
} & (AreAllPropertiesOptional<Omit<THeadRenderers, TExcludedId>> extends true
|
|
29
|
+
? { head?: (item: THeadItem) => Omit<THeadRenderers, TExcludedId> }
|
|
30
|
+
: { head: (item: THeadItem) => Omit<THeadRenderers, TExcludedId> }),
|
|
31
|
+
...args: TSetupArgs
|
|
32
|
+
) {
|
|
33
|
+
type $TAvailableColumns = Omit<TColumns, TExcludedId>
|
|
34
|
+
type $TAvailableColumnId = keyof $TAvailableColumns
|
|
29
35
|
|
|
30
|
-
|
|
36
|
+
const allColumns = setup(...args)
|
|
31
37
|
|
|
32
|
-
|
|
38
|
+
const availableColumns = objectOmit(allColumns, config.exclude ?? []) as $TAvailableColumns
|
|
33
39
|
|
|
34
|
-
|
|
40
|
+
const hiddenColumnIds = ref(new Set()) as Ref<Set<$TAvailableColumnId>>
|
|
35
41
|
|
|
36
|
-
|
|
42
|
+
const visibleColumnIds = computed(
|
|
43
|
+
() =>
|
|
44
|
+
Object.keys(availableColumns).filter(
|
|
45
|
+
id => !hiddenColumnIds.value.has(id as $TAvailableColumnId)
|
|
46
|
+
) as $TAvailableColumnId[]
|
|
47
|
+
)
|
|
37
48
|
|
|
38
|
-
|
|
39
|
-
? (arg?: THeaderArg) => TableCellVNode[]
|
|
40
|
-
: (arg: THeaderArg) => TableCellVNode[]
|
|
49
|
+
const colspan = computed(() => visibleColumnIds.value.length)
|
|
41
50
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
51
|
+
function toggle(columnId: $TAvailableColumnId, visible: boolean = hiddenColumnIds.value.has(columnId)) {
|
|
52
|
+
if (visible) {
|
|
53
|
+
hiddenColumnIds.value.delete(columnId)
|
|
54
|
+
} else {
|
|
55
|
+
hiddenColumnIds.value.add(columnId)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
45
58
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
59
|
+
const HeadCells = defineComponent({
|
|
60
|
+
props: {
|
|
61
|
+
item: {
|
|
62
|
+
type: Object as () => THeadItem,
|
|
63
|
+
required: false,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
setup(props) {
|
|
67
|
+
const headCellRenderers = config.head?.(props.item as THeadItem) ?? ({} as THeadRenderers)
|
|
49
68
|
|
|
50
|
-
|
|
51
|
-
|
|
69
|
+
return () =>
|
|
70
|
+
visibleColumnIds.value.map(columnId => {
|
|
71
|
+
const { renderHead } = availableColumns[columnId]
|
|
52
72
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
73
|
+
const headCellRenderer = headCellRenderers[columnId as keyof typeof headCellRenderers]
|
|
74
|
+
|
|
75
|
+
return h(Fragment, { key: columnId }, [headCellRenderer ? headCellRenderer(renderHead) : renderHead()])
|
|
76
|
+
})
|
|
77
|
+
},
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
const BodyCells = defineComponent({
|
|
81
|
+
props: {
|
|
82
|
+
item: {
|
|
83
|
+
type: Object as () => TBodyItem,
|
|
84
|
+
required: true,
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
setup(props) {
|
|
88
|
+
const bodyCellRenderers = config.body(props.item as TBodyItem) ?? ({} as TBodyRenderers)
|
|
89
|
+
|
|
90
|
+
return () =>
|
|
91
|
+
visibleColumnIds.value.map(columnId => {
|
|
92
|
+
const { renderBody } = availableColumns[columnId]
|
|
93
|
+
|
|
94
|
+
const bodyCellRenderer = bodyCellRenderers[columnId as keyof typeof bodyCellRenderers]
|
|
95
|
+
|
|
96
|
+
return h(Fragment, { key: columnId }, [bodyCellRenderer ? bodyCellRenderer(renderBody) : renderBody()])
|
|
97
|
+
})
|
|
98
|
+
},
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
HeadCells: HeadCells as Component<
|
|
103
|
+
[THeadItem] extends [undefined]
|
|
104
|
+
? Record<string, never>
|
|
105
|
+
: undefined extends THeadItem
|
|
106
|
+
? { item?: THeadItem }
|
|
107
|
+
: { item: THeadItem }
|
|
108
|
+
>,
|
|
109
|
+
BodyCells: BodyCells as Component<{ item: TBodyItem }>,
|
|
110
|
+
toggle,
|
|
111
|
+
colspan,
|
|
112
|
+
}
|
|
61
113
|
}
|
|
114
|
+
|
|
115
|
+
return useColumns
|
|
62
116
|
}
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export * from './define-columns'
|
|
5
|
-
export * from './define-table/define-multi-source-table'
|
|
6
|
-
export * from './define-table/define-table'
|
|
7
|
-
export * from './define-renderer/define-table-renderer'
|
|
8
|
-
export * from './define-renderer/define-table-section-renderer'
|
|
9
|
-
export * from './define-table/define-typed-table'
|
|
10
|
-
export * from './transform-sources'
|
|
11
|
-
export * from './types'
|
|
1
|
+
export { defineColumn } from './define-column.ts'
|
|
2
|
+
export { defineColumns } from './define-columns.ts'
|
|
3
|
+
export type { ColumnRenderer, Columns } from './types.ts'
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type VNode } from 'vue'
|
|
2
|
+
|
|
3
|
+
export type ColumnRenderer<THeadArgs extends any[], TBodyArgs extends any[]> = {
|
|
4
|
+
renderHead: (...args: THeadArgs) => VNode
|
|
5
|
+
renderBody: (...args: TBodyArgs) => VNode
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type Columns = Record<string, ColumnRenderer<any, any>>
|
|
9
|
+
|
|
10
|
+
export type AreAllPropertiesOptional<T> = Record<string, never> extends T ? true : false
|