@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.
Files changed (120) hide show
  1. package/lib/components/console/VtsRemoteConsole.vue +1 -1
  2. package/lib/components/copy-button/VtsCopyButton.vue +1 -1
  3. package/lib/components/layout/VtsLayoutSidebar.vue +1 -1
  4. package/lib/components/quick-info-card/VtsQuickInfoCard.vue +1 -1
  5. package/lib/components/relative-time/VtsRelativeTime.vue +2 -3
  6. package/lib/components/select/VtsSelect.vue +1 -1
  7. package/lib/components/state-hero/VtsStateHero.vue +20 -10
  8. package/lib/components/table/VtsRow.vue +26 -0
  9. package/lib/components/table/VtsTable.vue +99 -42
  10. package/lib/components/table/cells/VtsCollapsedListCell.vue +59 -0
  11. package/lib/components/table/cells/VtsHeaderCell.vue +31 -0
  12. package/lib/components/table/cells/VtsLinkCell.vue +33 -0
  13. package/lib/components/table/cells/VtsNumberCell.vue +21 -0
  14. package/lib/components/{size-progress-cell/VtsSizeProgressCell.vue → table/cells/VtsProgressBarCell.vue} +8 -5
  15. package/lib/components/table/cells/VtsStatusCell.vue +69 -0
  16. package/lib/components/table/cells/VtsTagCell.vue +24 -0
  17. package/lib/components/table/cells/VtsTextCell.vue +32 -0
  18. package/lib/components/table/cells/VtsTruncatedTextCell.vue +64 -0
  19. package/lib/components/task/VtsQuickTaskButton.vue +1 -1
  20. package/lib/components/tree/VtsTreeLine.vue +1 -1
  21. package/lib/components/ui/alert/UiAlert.vue +1 -1
  22. package/lib/components/ui/button/UiButton.vue +4 -2
  23. package/lib/components/ui/button-icon/UiButtonIcon.vue +12 -11
  24. package/lib/components/ui/column-header/UiColumnHeader.vue +22 -0
  25. package/lib/components/ui/info/UiInfo.vue +5 -1
  26. package/lib/components/ui/input/UiInput.vue +3 -2
  27. package/lib/components/ui/link/UiLink.vue +5 -0
  28. package/lib/components/ui/log-entry-viewer/UiLogEntryViewer.vue +1 -1
  29. package/lib/components/ui/query-search-bar/UiQuerySearchBar.vue +2 -2
  30. package/lib/components/ui/table-cell/UiTableCell.vue +41 -0
  31. package/lib/components/ui/table-pagination/PaginationButton.vue +1 -1
  32. package/lib/components/ui/task-item/UiTaskItem.vue +229 -0
  33. package/lib/components/ui/task-list/UiTaskList.vue +31 -0
  34. package/lib/components/ui/toaster/UiToaster.vue +1 -1
  35. package/lib/components/ui/top-bottom-table/UiTopBottomTable.vue +6 -2
  36. package/lib/components/ui/tree-item-label/UiTreeItemLabel.vue +1 -7
  37. package/lib/composables/pagination.composable.ts +16 -1
  38. package/lib/composables/relative-time.composable.ts +8 -61
  39. package/lib/composables/table-state.composable.ts +56 -0
  40. package/lib/composables/tree-filter.composable.ts +2 -2
  41. package/lib/icons/fa-icons.ts +2 -0
  42. package/lib/icons/object-icons.ts +1 -1
  43. package/lib/locales/en.json +26 -1
  44. package/lib/locales/fr.json +26 -1
  45. package/lib/packages/form-select/use-form-select-controller.ts +1 -0
  46. package/lib/packages/table/README.md +53 -308
  47. package/lib/packages/table/define-column.ts +7 -0
  48. package/lib/packages/table/define-columns.ts +104 -50
  49. package/lib/packages/table/index.ts +3 -11
  50. package/lib/packages/table/types.ts +10 -0
  51. package/lib/{composables/tree.composable.md → packages/tree/README.md} +28 -23
  52. package/lib/{composables → packages}/tree/branch-definition.ts +9 -4
  53. package/lib/{composables → packages}/tree/branch.ts +15 -20
  54. package/lib/{composables → packages}/tree/build-nodes.ts +5 -5
  55. package/lib/{composables → packages}/tree/define-branch.ts +8 -4
  56. package/lib/{composables → packages}/tree/define-leaf.ts +8 -3
  57. package/lib/{composables → packages}/tree/define-tree.ts +10 -5
  58. package/lib/{composables → packages}/tree/leaf-definition.ts +1 -1
  59. package/lib/{composables → packages}/tree/leaf.ts +3 -3
  60. package/lib/{composables → packages}/tree/tree-node-base.ts +18 -3
  61. package/lib/{composables → packages}/tree/tree-node-definition-base.ts +4 -2
  62. package/lib/{composables → packages}/tree/types.ts +11 -9
  63. package/lib/{composables/tree.composable.ts → packages/tree/use-tree.ts} +24 -11
  64. package/lib/tables/column-definitions/address-column.ts +4 -0
  65. package/lib/tables/column-definitions/button-column.ts +35 -0
  66. package/lib/tables/column-definitions/button-icon-column.ts +30 -0
  67. package/lib/tables/column-definitions/collapsed-list-column.ts +12 -0
  68. package/lib/tables/column-definitions/date-column.ts +34 -0
  69. package/lib/tables/column-definitions/info-column.ts +12 -0
  70. package/lib/tables/column-definitions/input-column.ts +32 -0
  71. package/lib/tables/column-definitions/link-column.ts +14 -0
  72. package/lib/tables/column-definitions/literal-column.ts +9 -0
  73. package/lib/tables/column-definitions/number-column.ts +10 -0
  74. package/lib/tables/column-definitions/percent-column.ts +15 -0
  75. package/lib/tables/column-definitions/progress-bar-column.ts +10 -0
  76. package/lib/tables/column-definitions/select-column.ts +12 -0
  77. package/lib/tables/column-definitions/select-item-column.ts +8 -0
  78. package/lib/tables/column-definitions/status-column.ts +16 -0
  79. package/lib/tables/column-definitions/tag-column.ts +11 -0
  80. package/lib/tables/column-definitions/text-column.ts +11 -0
  81. package/lib/tables/column-definitions/truncated-text-column.ts +10 -0
  82. package/lib/tables/column-sets/backup-issue-columns.ts +15 -0
  83. package/lib/tables/column-sets/backup-job-columns.ts +23 -0
  84. package/lib/tables/column-sets/backup-job-schedule-columns.ts +21 -0
  85. package/lib/tables/column-sets/backup-log-columns.ts +19 -0
  86. package/lib/tables/column-sets/host-columns.ts +19 -0
  87. package/lib/tables/column-sets/network-columns.ts +22 -0
  88. package/lib/tables/column-sets/new-vm-network-columns.ts +24 -0
  89. package/lib/tables/column-sets/new-vm-sr-columns.ts +33 -0
  90. package/lib/tables/column-sets/patch-columns.ts +13 -0
  91. package/lib/tables/column-sets/pif-columns.ts +23 -0
  92. package/lib/tables/column-sets/server-columns.ts +18 -0
  93. package/lib/tables/column-sets/sr-columns.ts +20 -0
  94. package/lib/tables/column-sets/vdi-columns.ts +21 -0
  95. package/lib/tables/column-sets/vif-columns.ts +23 -0
  96. package/lib/tables/column-sets/vm-columns.ts +21 -0
  97. package/lib/tables/helpers/render-body-cell.ts +4 -0
  98. package/lib/tables/helpers/render-head-cell.ts +6 -0
  99. package/lib/tables/helpers/render-loading-cell.ts +5 -0
  100. package/lib/tables/types.ts +7 -0
  101. package/lib/utils/size.util.ts +5 -9
  102. package/package.json +1 -1
  103. package/lib/components/data-table/VtsDataTable.vue +0 -70
  104. package/lib/components/table/ColumnTitle.vue +0 -152
  105. package/lib/packages/table/apply-extensions.ts +0 -26
  106. package/lib/packages/table/define-renderer/define-table-cell-renderer.ts +0 -27
  107. package/lib/packages/table/define-renderer/define-table-renderer.ts +0 -47
  108. package/lib/packages/table/define-renderer/define-table-row-renderer.ts +0 -29
  109. package/lib/packages/table/define-renderer/define-table-section-renderer.ts +0 -29
  110. package/lib/packages/table/define-table/define-multi-source-table.ts +0 -39
  111. package/lib/packages/table/define-table/define-table.ts +0 -13
  112. package/lib/packages/table/define-table/define-typed-table.ts +0 -18
  113. package/lib/packages/table/transform-sources.ts +0 -13
  114. package/lib/packages/table/types/extensions.ts +0 -16
  115. package/lib/packages/table/types/index.ts +0 -47
  116. package/lib/packages/table/types/table-cell.ts +0 -18
  117. package/lib/packages/table/types/table-row.ts +0 -20
  118. package/lib/packages/table/types/table-section.ts +0 -19
  119. package/lib/packages/table/types/table.ts +0 -28
  120. package/lib/types/button.type.ts +0 -3
@@ -1,336 +1,81 @@
1
- # Table System
1
+ # `defineColumn`
2
2
 
3
- Table system that separates data logic from presentation through reusable renderers.
3
+ Define the head and body renderers for a column.
4
4
 
5
- ## Understanding Renderers
5
+ The render functions can receive arguments and must return a VNode.
6
6
 
7
- A **renderer** is a function that creates a VNode for a specific part of the table (cell, row, or table). When you define a renderer, you specify:
7
+ ## Example 1 - A simple text column
8
8
 
9
- 1. **Component**: The Vue component to render (loaded asynchronously)
10
- 2. **Props function** (optional): Default props based on configuration
11
- 3. **Extensions** (optional): Named categories of additional functionality
12
-
13
- ### Props System
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
- ### Extensions System
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
- ```typescript
56
- const MyRow = defineTableRowRenderer({
57
- component: () => import('./Row.vue'),
58
- extensions: {
59
- selectable: (config: { id: string; selectedId: Ref<string | null> }) => ({
60
- props: {
61
- selected: config.id === config.selectedId.value,
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
- highlightable: (config: { isHighlighted: boolean }) => ({
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
- Extensions are optional when using the renderer - you only provide the ones you need.
83
-
84
- ## Defining Renderers
85
-
86
- ### Cell Renderers
29
+ # `defineColumns`
87
30
 
88
- Create header and body cell renderers:
31
+ Define a set of columns.
89
32
 
90
- ```typescript
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
- // Usage
107
- TextHeader({ label: 'Name' })
108
- TextBody({ text: user.name })
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
- ### Row Renderers
43
+ # Using the columns set
112
44
 
113
- Create row renderer:
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
- ```typescript
116
- import { defineTableRowRenderer } from '@core/packages/table'
47
+ ## Example
117
48
 
118
- const DefaultRow = defineTableRowRenderer({
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
- Use `defineColumns` to create columns configuration.
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
- `header` and `body` can also take a config parameter if needed:
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
- ### Table Definition
195
-
196
- Use one of three table definition functions:
197
-
198
- #### Define basic single-source table: `defineTable`
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
- ## Rendering the table
301
-
302
- ```vue
66
+ ```html
303
67
  <template>
304
- <MyUsersTable />
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 { TableCellVNode } from '.'
2
- import { reactive, computed } from 'vue'
3
-
4
- export function defineColumns<
5
- TSource,
6
- TColumns extends Record<
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
- const columnNames = Object.keys(config).filter(key => config[key] !== undefined) as TColumnName[]
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
- const hiddenColumnNames = reactive(new Set()) as Set<TColumnName>
36
+ const allColumns = setup(...args)
31
37
 
32
- const visibleColumnNames = computed(() => columnNames.filter(name => !hiddenColumnNames.has(name)))
38
+ const availableColumns = objectOmit(allColumns, config.exclude ?? []) as $TAvailableColumns
33
39
 
34
- const visibleColumns = computed(() => visibleColumnNames.value.map(name => config[name]!))
40
+ const hiddenColumnIds = ref(new Set()) as Ref<Set<$TAvailableColumnId>>
35
41
 
36
- const visibleColumnsCount = computed(() => visibleColumnNames.value.length)
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
- type GetHeaderCells = THeaderArg extends undefined
39
- ? (arg?: THeaderArg) => TableCellVNode[]
40
- : (arg: THeaderArg) => TableCellVNode[]
49
+ const colspan = computed(() => visibleColumnIds.value.length)
41
50
 
42
- type GetBodyCells = TBodyArg extends undefined
43
- ? (source: TSource, arg?: TBodyArg) => TableCellVNode[]
44
- : (source: TSource, arg: TBodyArg) => TableCellVNode[]
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
- return {
47
- getHeaderCells: ((arg?: THeaderArg) =>
48
- visibleColumns.value.map(column => column.header(arg as THeaderArg))) as GetHeaderCells,
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
- getBodyCells: ((source: TSource, arg?: TBodyArg) =>
51
- visibleColumns.value.map(column => column.body(source, arg as TBodyArg))) as GetBodyCells,
69
+ return () =>
70
+ visibleColumnIds.value.map(columnId => {
71
+ const { renderHead } = availableColumns[columnId]
52
72
 
53
- toggleColumn: (name: TColumnName, forcedValue = !hiddenColumnNames.has(name)) => {
54
- if (forcedValue) {
55
- hiddenColumnNames.add(name)
56
- } else {
57
- hiddenColumnNames.delete(name)
58
- }
59
- },
60
- visibleColumnsCount,
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 * from './apply-extensions'
2
- export * from './define-renderer/define-table-cell-renderer'
3
- export * from './define-renderer/define-table-row-renderer'
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