@xen-orchestra/web-core 0.31.1 → 0.32.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 (64) hide show
  1. package/lib/assets/css/_colors.pcss +8 -0
  2. package/lib/components/button-group/VtsButtonGroup.vue +5 -1
  3. package/lib/components/menu/MenuList.vue +1 -0
  4. package/lib/components/modal/VtsModal.vue +82 -0
  5. package/lib/components/modal/VtsModalButton.vue +36 -0
  6. package/lib/components/modal/VtsModalCancelButton.vue +37 -0
  7. package/lib/components/modal/VtsModalConfirmButton.vue +21 -0
  8. package/lib/components/modal/VtsModalList.vue +34 -0
  9. package/lib/components/object-icon/VtsObjectIcon.vue +3 -8
  10. package/lib/components/status/VtsStatus.vue +66 -0
  11. package/lib/components/task/VtsQuickTaskList.vue +17 -5
  12. package/lib/components/tree/VtsTreeItem.vue +2 -2
  13. package/lib/components/ui/button/UiButton.vue +13 -67
  14. package/lib/components/ui/input/UiInput.vue +4 -1
  15. package/lib/components/ui/modal/UiModal.vue +164 -0
  16. package/lib/components/ui/quick-task-item/UiQuickTaskItem.vue +2 -2
  17. package/lib/composables/context.composable.ts +3 -5
  18. package/lib/composables/link-component.composable.ts +3 -2
  19. package/lib/composables/pagination.composable.ts +3 -2
  20. package/lib/composables/tree-filter.composable.ts +5 -3
  21. package/lib/icons/fa-icons.ts +4 -0
  22. package/lib/icons/index.ts +17 -0
  23. package/lib/locales/en.json +14 -1
  24. package/lib/locales/fr.json +14 -1
  25. package/lib/packages/collection/use-collection.ts +3 -2
  26. package/lib/packages/form-select/use-form-option-controller.ts +3 -2
  27. package/lib/packages/form-select/use-form-select.ts +8 -7
  28. package/lib/packages/menu/action.ts +4 -3
  29. package/lib/packages/menu/link.ts +5 -4
  30. package/lib/packages/menu/router-link.ts +3 -2
  31. package/lib/packages/menu/toggle-target.ts +3 -2
  32. package/lib/packages/modal/ModalProvider.vue +17 -0
  33. package/lib/packages/modal/README.md +253 -0
  34. package/lib/packages/modal/create-modal-opener.ts +103 -0
  35. package/lib/packages/modal/modal.store.ts +22 -0
  36. package/lib/packages/modal/types.ts +92 -0
  37. package/lib/packages/modal/use-modal.ts +53 -0
  38. package/lib/packages/progress/use-progress.ts +4 -3
  39. package/lib/packages/table/README.md +336 -0
  40. package/lib/packages/table/apply-extensions.ts +26 -0
  41. package/lib/packages/table/define-columns.ts +62 -0
  42. package/lib/packages/table/define-renderer/define-table-cell-renderer.ts +27 -0
  43. package/lib/packages/table/define-renderer/define-table-renderer.ts +47 -0
  44. package/lib/packages/table/define-renderer/define-table-row-renderer.ts +29 -0
  45. package/lib/packages/table/define-renderer/define-table-section-renderer.ts +29 -0
  46. package/lib/packages/table/define-table/define-multi-source-table.ts +39 -0
  47. package/lib/packages/table/define-table/define-table.ts +13 -0
  48. package/lib/packages/table/define-table/define-typed-table.ts +18 -0
  49. package/lib/packages/table/index.ts +11 -0
  50. package/lib/packages/table/transform-sources.ts +13 -0
  51. package/lib/packages/table/types/extensions.ts +16 -0
  52. package/lib/packages/table/types/index.ts +47 -0
  53. package/lib/packages/table/types/table-cell.ts +18 -0
  54. package/lib/packages/table/types/table-row.ts +20 -0
  55. package/lib/packages/table/types/table-section.ts +19 -0
  56. package/lib/packages/table/types/table.ts +28 -0
  57. package/lib/packages/threshold/use-threshold.ts +4 -3
  58. package/lib/types/vue-virtual-scroller.d.ts +101 -0
  59. package/lib/utils/injection-keys.util.ts +3 -0
  60. package/lib/utils/progress.util.ts +2 -1
  61. package/lib/utils/to-computed.util.ts +15 -0
  62. package/package.json +3 -2
  63. package/lib/components/backup-state/VtsBackupState.vue +0 -37
  64. package/lib/components/connection-status/VtsConnectionStatus.vue +0 -36
@@ -0,0 +1,336 @@
1
+ # Table System
2
+
3
+ Table system that separates data logic from presentation through reusable renderers.
4
+
5
+ ## Understanding Renderers
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:
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
+ })
45
+ ```
46
+
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
54
+
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
+ },
63
+ }),
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
+ })
80
+ ```
81
+
82
+ Extensions are optional when using the renderer - you only provide the ones you need.
83
+
84
+ ## Defining Renderers
85
+
86
+ ### Cell Renderers
87
+
88
+ Create header and body cell renderers:
89
+
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
+ })
105
+
106
+ // Usage
107
+ TextHeader({ label: 'Name' })
108
+ TextBody({ text: user.name })
109
+ ```
110
+
111
+ ### Row Renderers
112
+
113
+ Create row renderer:
114
+
115
+ ```typescript
116
+ import { defineTableRowRenderer } from '@core/packages/table'
117
+
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
151
+
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
+ ```
173
+
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
+ ```
193
+
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}`,
293
+ }),
294
+ })
295
+
296
+ // Transform is optional when types already match
297
+ useUserTable(users, {})
298
+ ```
299
+
300
+ ## Rendering the table
301
+
302
+ ```vue
303
+ <template>
304
+ <MyUsersTable />
305
+ </template>
306
+
307
+ <script setup lang="ts">
308
+ const MyUsersTable = useUsersTable(users, {})
309
+ </script>
310
+ ```
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,26 @@
1
+ import type { Extensions } from '.'
2
+ import { mergeProps } from 'vue'
3
+
4
+ export function applyExtensions(
5
+ config: {
6
+ props?: (config: any) => Record<string, any>
7
+ extensions?: Extensions<any>
8
+ },
9
+ renderConfig: {
10
+ props?: Record<string, any>
11
+ extensions?: Record<string, any>
12
+ }
13
+ ): { props: Record<string, any> } {
14
+ const baseProps = mergeProps(config.props?.(renderConfig) ?? {}, renderConfig.props ?? {})
15
+
16
+ if (!renderConfig.extensions) {
17
+ return { props: baseProps }
18
+ }
19
+
20
+ const props = Object.entries(renderConfig.extensions).reduce(
21
+ (props, [extName, extData]) => mergeProps(props, config.extensions?.[extName!](extData).props ?? {}),
22
+ baseProps
23
+ )
24
+
25
+ return { props }
26
+ }
@@ -0,0 +1,62 @@
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
+ >
27
+ ) {
28
+ const columnNames = Object.keys(config).filter(key => config[key] !== undefined) as TColumnName[]
29
+
30
+ const hiddenColumnNames = reactive(new Set()) as Set<TColumnName>
31
+
32
+ const visibleColumnNames = computed(() => columnNames.filter(name => !hiddenColumnNames.has(name)))
33
+
34
+ const visibleColumns = computed(() => visibleColumnNames.value.map(name => config[name]!))
35
+
36
+ const visibleColumnsCount = computed(() => visibleColumnNames.value.length)
37
+
38
+ type GetHeaderCells = THeaderArg extends undefined
39
+ ? (arg?: THeaderArg) => TableCellVNode[]
40
+ : (arg: THeaderArg) => TableCellVNode[]
41
+
42
+ type GetBodyCells = TBodyArg extends undefined
43
+ ? (source: TSource, arg?: TBodyArg) => TableCellVNode[]
44
+ : (source: TSource, arg: TBodyArg) => TableCellVNode[]
45
+
46
+ return {
47
+ getHeaderCells: ((arg?: THeaderArg) =>
48
+ visibleColumns.value.map(column => column.header(arg as THeaderArg))) as GetHeaderCells,
49
+
50
+ getBodyCells: ((source: TSource, arg?: TBodyArg) =>
51
+ visibleColumns.value.map(column => column.body(source, arg as TBodyArg))) as GetBodyCells,
52
+
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,
61
+ }
62
+ }
@@ -0,0 +1,27 @@
1
+ import { type VNode, defineAsyncComponent, h } from 'vue'
2
+ import {
3
+ type TableCellRenderer,
4
+ type TableCellVNode,
5
+ type Extensions,
6
+ applyExtensions,
7
+ type ComponentLoader,
8
+ type PropsOverride,
9
+ } from '..'
10
+
11
+ export function defineTableCellRenderer<
12
+ TComponentProps extends Record<string, any>,
13
+ TExtensions extends Extensions<TComponentProps>,
14
+ TPropsConfig extends Record<string, any>,
15
+ >(config: {
16
+ component: ComponentLoader<TComponentProps>
17
+ props?: (config: TPropsConfig) => PropsOverride<TComponentProps>
18
+ extensions?: TExtensions
19
+ }): TableCellRenderer<TComponentProps, TExtensions, TPropsConfig> {
20
+ const component = defineAsyncComponent(config.component)
21
+
22
+ return function RenderCell(renderConfig) {
23
+ const extension = applyExtensions(config, renderConfig)
24
+
25
+ return h(component, extension.props) satisfies VNode as TableCellVNode
26
+ }
27
+ }
@@ -0,0 +1,47 @@
1
+ import { type VNode, defineAsyncComponent, h } from 'vue'
2
+ import {
3
+ applyExtensions,
4
+ type ComponentLoader,
5
+ type PropsOverride,
6
+ type Extensions,
7
+ type TableRenderer,
8
+ type TableVNode,
9
+ } from '..'
10
+
11
+ export function defineTableRenderer<
12
+ TComponentProps extends Record<string, any>,
13
+ TExtensions extends Extensions<TComponentProps>,
14
+ TPropsConfig extends Record<string, any>,
15
+ >(config: {
16
+ component: ComponentLoader<TComponentProps>
17
+ props?: (config: TPropsConfig) => PropsOverride<TComponentProps>
18
+ extensions?: TExtensions
19
+ }): TableRenderer<TComponentProps, TExtensions, TPropsConfig> {
20
+ const component = defineAsyncComponent(config.component)
21
+
22
+ return function RenderTable(renderConfig) {
23
+ const { thead, tbody } = renderConfig
24
+
25
+ const renderThead =
26
+ typeof thead === 'function'
27
+ ? thead
28
+ : 'cells' in thead && thead.cells
29
+ ? () => h('thead', {}, h('tr', {}, { default: () => thead.cells() }))
30
+ : 'rows' in thead && thead.rows
31
+ ? () => h('thead', {}, { default: () => thead.rows() })
32
+ : () => undefined
33
+
34
+ const renderTbody =
35
+ typeof tbody === 'function'
36
+ ? tbody
37
+ : 'rows' in tbody && tbody.rows
38
+ ? () => h('tbody', {}, { default: () => tbody.rows() })
39
+ : () => undefined
40
+
41
+ const extension = applyExtensions(config, renderConfig)
42
+
43
+ return h(component, extension.props, () => {
44
+ return [renderThead(), renderTbody()]
45
+ }) satisfies VNode as TableVNode
46
+ }
47
+ }
@@ -0,0 +1,29 @@
1
+ import { type VNode, defineAsyncComponent, h } from 'vue'
2
+ import {
3
+ applyExtensions,
4
+ type TableRowRenderer,
5
+ type TableRowVNode,
6
+ type Extensions,
7
+ type ComponentLoader,
8
+ type PropsOverride,
9
+ } from '..'
10
+
11
+ export function defineRowRenderer<
12
+ TComponentProps extends Record<string, any>,
13
+ TExtensions extends Extensions<TComponentProps>,
14
+ TPropsConfig extends Record<string, any>,
15
+ >(config: {
16
+ component: ComponentLoader<TComponentProps>
17
+ props?: (config: TPropsConfig) => PropsOverride<TComponentProps>
18
+ extensions?: TExtensions
19
+ }): TableRowRenderer<TComponentProps, TExtensions, TPropsConfig> {
20
+ const component = defineAsyncComponent(config.component)
21
+
22
+ return function RenderRow(renderConfig): TableRowVNode {
23
+ const extension = applyExtensions(config, renderConfig)
24
+
25
+ return h(component, extension.props, {
26
+ default: () => renderConfig.cells(),
27
+ }) satisfies VNode as TableRowVNode
28
+ }
29
+ }
@@ -0,0 +1,29 @@
1
+ import { type VNode, defineAsyncComponent, h } from 'vue'
2
+ import {
3
+ applyExtensions,
4
+ type TableSectionRenderer,
5
+ type TableSectionVNode,
6
+ type Extensions,
7
+ type ComponentLoader,
8
+ type PropsOverride,
9
+ } from '..'
10
+
11
+ export function defineSectionRenderer<
12
+ TComponentProps extends Record<string, any>,
13
+ TExtensions extends Extensions<TComponentProps>,
14
+ TPropsConfig extends Record<string, any>,
15
+ >(config: {
16
+ component: ComponentLoader<TComponentProps>
17
+ props?: (config: TPropsConfig) => PropsOverride<TComponentProps>
18
+ extensions?: TExtensions
19
+ }): TableSectionRenderer<TComponentProps, TExtensions, TPropsConfig> {
20
+ const component = defineAsyncComponent(config.component)
21
+
22
+ return function RenderSection(renderConfig): TableSectionVNode {
23
+ const extension = applyExtensions(config, renderConfig)
24
+
25
+ return h(component, extension.props, {
26
+ default: () => renderConfig.rows(),
27
+ }) satisfies VNode as TableSectionVNode
28
+ }
29
+ }
@@ -0,0 +1,39 @@
1
+ import { type MaybeRefOrGetter } from 'vue'
2
+ import { type Sources, type TransformProperty, transformSources } from '..'
3
+
4
+ export function defineMultiSourceTable<
5
+ TSources extends Record<string, Sources>,
6
+ TConfig extends Record<string, unknown> = Record<string, unknown>,
7
+ >(setup: (sources: TSources, config: TConfig) => any) {
8
+ return function useTable<
9
+ TUseSources extends {
10
+ [K in keyof TSources]: any
11
+ },
12
+ TTransforms extends {
13
+ [K in keyof TSources]: TSources[K] extends Sources<infer T>
14
+ ? TransformProperty<T, TUseSources[K], K & string>[K & string]
15
+ : never
16
+ },
17
+ >(
18
+ sources: {
19
+ [K in keyof TUseSources]: MaybeRefOrGetter<TUseSources[K][]>
20
+ },
21
+ config: TConfig &
22
+ (Record<keyof TTransforms, undefined> extends TTransforms
23
+ ? { transform?: Partial<TTransforms> }
24
+ : {
25
+ transform: { [K in keyof TTransforms as undefined extends TTransforms[K] ? K : never]?: TTransforms[K] } & {
26
+ [K in keyof TTransforms as undefined extends TTransforms[K] ? never : K]: TTransforms[K]
27
+ }
28
+ })
29
+ ) {
30
+ const transformedSources = Object.fromEntries(
31
+ Object.entries(sources).map(([key, value]) => [
32
+ key,
33
+ transformSources(value, (config.transform as any)?.[key as any]),
34
+ ])
35
+ )
36
+
37
+ return setup(transformedSources as TSources, config)
38
+ }
39
+ }
@@ -0,0 +1,13 @@
1
+ import { type MaybeRefOrGetter } from 'vue'
2
+ import { type Sources, type TableVNode, type TransformProperty, transformSources } from '..'
3
+
4
+ export function defineTable<TSource, TConfig extends Record<string, unknown> = Record<string, unknown>>(
5
+ setup: (sources: Sources<TSource>, config: TConfig) => () => TableVNode
6
+ ) {
7
+ return function useTable<TUseSource>(
8
+ sources: MaybeRefOrGetter<TUseSource[]>,
9
+ config: TConfig & TransformProperty<TSource, TUseSource>
10
+ ) {
11
+ return setup(transformSources<TSource>(sources, config?.transform), config)
12
+ }
13
+ }
@@ -0,0 +1,18 @@
1
+ import { type MaybeRefOrGetter } from 'vue'
2
+ import { type Sources, type TransformProperty, transformSources } from '..'
3
+
4
+ export function defineTypedTable<
5
+ TTypedSource extends { type: string; data: Sources },
6
+ TConfig extends Record<string, unknown> = Record<string, unknown>,
7
+ >(setup: (typedSource: TTypedSource, config: TConfig) => any) {
8
+ return function useTable<
9
+ TUseSource,
10
+ const TType extends TTypedSource['type'],
11
+ TSource extends TTypedSource extends { type: TType; data: Sources<infer TSource> } ? TSource : never,
12
+ >(type: TType, sources: MaybeRefOrGetter<TUseSource[]>, config: TConfig & TransformProperty<TSource, TUseSource>) {
13
+ return setup(
14
+ { type, data: transformSources<TSource>(sources, config.transform) } as unknown as TTypedSource,
15
+ config
16
+ )
17
+ }
18
+ }
@@ -0,0 +1,11 @@
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'
@@ -0,0 +1,13 @@
1
+ import { useArrayMap } from '@vueuse/shared'
2
+ import { type MaybeRefOrGetter, type ComputedRef, computed, toValue } from 'vue'
3
+
4
+ export function transformSources<TSource>(
5
+ sources: MaybeRefOrGetter<any[]>,
6
+ transformer?: (source: any, index: number) => any
7
+ ) {
8
+ return transformer
9
+ ? (useArrayMap(sources, (source, index) => ({ ...source, ...transformer!(source, index) })) as ComputedRef<
10
+ TSource[]
11
+ >)
12
+ : (computed(() => toValue(sources)) as unknown as ComputedRef<TSource[]>)
13
+ }
@@ -0,0 +1,16 @@
1
+ import type { PropsOverride, Renderer } from '.'
2
+
3
+ export type Extension<
4
+ TConfig extends Record<string, any> = Record<string, any>,
5
+ TComponentProps extends Record<string, any> = Record<string, any>,
6
+ > = (config: TConfig) => { props: PropsOverride<TComponentProps> }
7
+
8
+ export type Extensions<TComponentProps extends Record<string, any>> = Record<string, Extension<any, TComponentProps>>
9
+
10
+ export type ExtensionConfig<
11
+ TRenderer extends Renderer<any, any, any>,
12
+ TExtensionName extends keyof $Extensions,
13
+ $Extensions extends Record<string, any> = TRenderer extends Renderer<any, infer TExtensions, any>
14
+ ? { [K in keyof TExtensions]: Parameters<TExtensions[K]>[0] }
15
+ : never,
16
+ > = $Extensions[TExtensionName]