@xen-orchestra/web-core 0.3.0 → 0.5.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 (124) hide show
  1. package/lib/assets/css/_colors.pcss +56 -24
  2. package/lib/assets/css/_context.pcss +9 -9
  3. package/lib/assets/css/base.pcss +6 -6
  4. package/lib/components/backup-item/VtsBackupItem.vue +47 -0
  5. package/lib/components/backup-state/{BackupState.vue → VtsBackupState.vue} +4 -4
  6. package/lib/components/{button/ButtonGroup.vue → button-group/VtsButtonGroup.vue} +7 -6
  7. package/lib/components/cell-object/{CellObject.vue → VtsCellObject.vue} +14 -14
  8. package/lib/components/cell-text/{CellText.vue → VtsCellText.vue} +11 -11
  9. package/lib/components/console/{RemoteConsole.vue → VtsRemoteConsole.vue} +12 -18
  10. package/lib/components/donut-chart-with-legend/{DonutChartWithLegend.vue → VtsDonutChartWithLegend.vue} +6 -6
  11. package/lib/components/dropdown/DropdownItem.vue +8 -14
  12. package/lib/components/dropdown/DropdownTitle.vue +3 -3
  13. package/lib/components/icon/VtsIcon.vue +38 -42
  14. package/lib/components/layout/{LayoutSidebar.vue → VtsLayoutSidebar.vue} +31 -30
  15. package/lib/components/legend-group/VtsLegendGroup.vue +44 -0
  16. package/lib/components/{legend/LegendList.vue → legend-list/VtsLegendList.vue} +2 -2
  17. package/lib/components/menu/MenuTrigger.vue +2 -2
  18. package/lib/components/stacked-bar-with-legend/{StackedBarWithLegend.vue → VtsStackedBarWithLegend.vue} +14 -14
  19. package/lib/components/state-hero/VtsComingSoonHero.vue +13 -0
  20. package/lib/components/state-hero/VtsLoadingHero.vue +15 -0
  21. package/lib/components/state-hero/VtsNoDataHero.vue +11 -0
  22. package/lib/components/state-hero/VtsObjectNotFoundHero.vue +13 -0
  23. package/lib/components/state-hero/{StateHero.vue → VtsStateHero.vue} +42 -35
  24. package/lib/components/tab/TabItem.vue +6 -6
  25. package/lib/components/table/ColumnTitle.vue +7 -7
  26. package/lib/components/table/{UiTable.vue → VtsTable.vue} +7 -3
  27. package/lib/components/task/{QuickTaskButton.vue → VtsQuickTaskButton.vue} +8 -7
  28. package/lib/components/task/{QuickTaskList.vue → VtsQuickTaskList.vue} +10 -11
  29. package/lib/components/task/{QuickTaskTabBar.vue → VtsQuickTaskTabBar.vue} +9 -13
  30. package/lib/components/{tooltip/TooltipList.vue → tooltip-list/VtsTooltipList.vue} +2 -3
  31. package/lib/components/tree/{TreeItem.vue → VtsTreeItem.vue} +3 -4
  32. package/lib/components/tree/VtsTreeItemError.vue +18 -0
  33. package/lib/components/tree/{TreeLine.vue → VtsTreeLine.vue} +9 -11
  34. package/lib/components/tree/{TreeList.vue → VtsTreeList.vue} +5 -2
  35. package/lib/components/tree/VtsTreeLoadingItem.vue +61 -0
  36. package/lib/components/ui/account-menu-button/UiAccountMenuButton.vue +64 -0
  37. package/lib/components/ui/actions-title/UiActionsTitle.vue +18 -0
  38. package/lib/components/ui/button/UiButton.vue +532 -0
  39. package/lib/components/ui/button-icon/UiButtonIcon.vue +248 -0
  40. package/lib/components/{UiCard.vue → ui/card/UiCard.vue} +8 -6
  41. package/lib/components/ui/card-numbers/UiCardNumbers.vue +103 -0
  42. package/lib/components/ui/card-subtitle/UiCardSubtitle.vue +24 -0
  43. package/lib/components/ui/card-title/UiCardTitle.vue +56 -0
  44. package/lib/components/ui/checkbox/UiCheckbox.vue +410 -0
  45. package/lib/components/ui/checkbox-group/UiCheckboxGroup.vue +57 -0
  46. package/lib/components/ui/chip/ChipIcon.vue +21 -0
  47. package/lib/components/ui/chip/ChipRemoveIcon.vue +13 -0
  48. package/lib/components/ui/chip/UiChip.vue +135 -0
  49. package/lib/components/{icon/ComplexIcon.vue → ui/complex-icon/UiComplexIcon.vue} +21 -27
  50. package/lib/components/ui/counter/UiCounter.vue +134 -0
  51. package/lib/components/{donut-chart/DonutChart.vue → ui/donut-chart/UiDonutChart.vue} +28 -30
  52. package/lib/components/{head-bar/HeadBar.vue → ui/head-bar/UiHeadBar.vue} +31 -31
  53. package/lib/components/{info/VtsInfo.vue → ui/info/UiInfo.vue} +13 -11
  54. package/lib/components/{input → ui/input}/UiInput.vue +9 -7
  55. package/lib/components/ui/input/UiTextarea.vue +120 -0
  56. package/lib/components/ui/label/UiLabel.vue +76 -0
  57. package/lib/components/ui/legend/UiLegend.vue +98 -0
  58. package/lib/components/{LegendTitle.vue → ui/legend-title/UiLegendTitle.vue} +4 -4
  59. package/lib/components/{UiSpinner.vue → ui/loader/UiLoader.vue} +3 -3
  60. package/lib/components/{icon/ObjectIcon.vue → ui/object-icon/UiObjectIcon.vue} +44 -36
  61. package/lib/components/ui/object-link/UiObjectLink.vue +83 -0
  62. package/lib/components/ui/panel/UiPanel.vue +57 -0
  63. package/lib/components/{query-search-bar/QuerySearchBar.vue → ui/query-search-bar/UiQuerySearchBar.vue} +16 -16
  64. package/lib/components/{task/QuickTaskItem.vue → ui/quick-task-item/UiQuickTaskItem.vue} +46 -34
  65. package/lib/components/{task/QuickTaskPanel.vue → ui/quick-task-panel/UiQuickTaskPanel.vue} +8 -7
  66. package/lib/components/ui/radio-button/UiRadioButton.vue +196 -0
  67. package/lib/components/ui/radio-button-group/UiRadioButtonGroup.vue +56 -0
  68. package/lib/components/{stacked-bar → ui/stacked-bar}/StackedBarSegment.vue +23 -26
  69. package/lib/components/{stacked-bar/StackedBar.vue → ui/stacked-bar/UiStackedBar.vue} +6 -6
  70. package/lib/components/ui/table-actions/UiTableActions.vue +46 -0
  71. package/lib/components/ui/tag/UiTag.vue +118 -0
  72. package/lib/components/ui/title/UiTitle.vue +39 -0
  73. package/lib/components/ui/toaster/UiToaster.vue +100 -0
  74. package/lib/components/ui/toggle/UiToggle.vue +117 -0
  75. package/lib/components/{tooltip/TooltipItem.vue → ui/tooltip/UiTooltip.vue} +15 -15
  76. package/lib/components/ui/top-bottom-table/UiTopBottomTable.vue +64 -0
  77. package/lib/components/{tree/TreeItemLabel.vue → ui/tree-item-label/UiTreeItemLabel.vue} +60 -59
  78. package/lib/components/ui/user-link/UiUserLink.vue +76 -0
  79. package/lib/components/ui/user-logo/UiUserLogo.vue +50 -0
  80. package/lib/composables/route-query.composable.md +136 -0
  81. package/lib/composables/table.composable.md +159 -0
  82. package/lib/composables/tree/define-tree.ts +1 -1
  83. package/lib/composables/tree/tree-node-base.ts +6 -6
  84. package/lib/composables/tree.composable.md +536 -0
  85. package/lib/layouts/CoreLayout.vue +8 -6
  86. package/lib/locales/de.json +3 -2
  87. package/lib/locales/en.json +7 -3
  88. package/lib/locales/fa.json +3 -2
  89. package/lib/locales/fr.json +7 -3
  90. package/lib/types/color.type.ts +0 -2
  91. package/lib/types/subscribable-store.type.ts +2 -2
  92. package/lib/utils/create-subscribable-store-context.util.ts +1 -1
  93. package/lib/utils/if-else.utils.md +23 -0
  94. package/lib/utils/if-else.utils.ts +2 -2
  95. package/lib/utils/to-variants.util.md +62 -0
  96. package/package.json +7 -7
  97. package/lib/components/CardNumbers.vue +0 -101
  98. package/lib/components/PowerStateIcon.vue +0 -46
  99. package/lib/components/UiTag.vue +0 -101
  100. package/lib/components/backup-item/BackupItem.vue +0 -40
  101. package/lib/components/button/ButtonIcon.vue +0 -220
  102. package/lib/components/button/UiButton.vue +0 -470
  103. package/lib/components/card/CardSubtitle.vue +0 -24
  104. package/lib/components/card/CardTitle.vue +0 -57
  105. package/lib/components/chip/ChipIcon.vue +0 -30
  106. package/lib/components/chip/ChipRemoveIcon.vue +0 -13
  107. package/lib/components/chip/UiChip.vue +0 -137
  108. package/lib/components/counter/VtsCounter.vue +0 -147
  109. package/lib/components/legend/LegendGroup.vue +0 -44
  110. package/lib/components/legend/LegendItem.vue +0 -86
  111. package/lib/components/object-link/ObjectLink.vue +0 -87
  112. package/lib/components/state-hero/ComingSoonHero.vue +0 -13
  113. package/lib/components/state-hero/LoadingHero.vue +0 -15
  114. package/lib/components/state-hero/NoDataHero.vue +0 -11
  115. package/lib/components/state-hero/ObjectNotFoundHero.vue +0 -13
  116. package/lib/components/tree/TreeItemError.vue +0 -13
  117. package/lib/components/tree/TreeLoadingItem.vue +0 -60
  118. package/lib/components/user/UserLink.vue +0 -72
  119. package/lib/components/user/UserLogo.vue +0 -57
  120. package/lib/types/backup.type.ts +0 -11
  121. package/lib/types/size.type.ts +0 -1
  122. package/lib/types/task.type.ts +0 -13
  123. /package/lib/components/backdrop/{Backdrop.vue → VtsBackdrop.vue} +0 -0
  124. /package/lib/components/divider/{Divider.vue → VtsDivider.vue} +0 -0
@@ -0,0 +1,536 @@
1
+ # `useTree` composable
2
+
3
+ The `useTree` composable handles a collection of nodes (called `Leaf` and `Branch`) in a tree structure.
4
+
5
+ `Leaf` and `Branch` can be _selected_, _activated_, and/or _filtered_.
6
+
7
+ Additionally, `Branch` can be _expanded_ and contains `Leaf` and/or `Branch` children.
8
+
9
+ Multiple nodes can be selected at the same time (if `allowMultiSelect` is `true`). But only one node can be activated at
10
+ a time.
11
+
12
+ ## Usage
13
+
14
+ The `useTree` composable takes an array of definitions (called `LeafDefinition` and `BranchDefinition`) as first
15
+ argument, and an optional object of options as second argument.
16
+
17
+ ```ts
18
+ useTree(definitions)
19
+ useTree(definitions, options)
20
+ ```
21
+
22
+ | | Required | Type | Default | |
23
+ | -------------------------- | :------: | ---------------------------------------- | ----------- | ----------------------------------------------------- |
24
+ | `definitions` | ✓ | `(LeafDefinition \| BranchDefinition)[]` | | The definitions of the nodes in the collection |
25
+ | `options.allowMultiSelect` | | `boolean` | `false` | Whether more than one node can be selected at a time. |
26
+ | `options.expand` | | `boolean` | `true` | Whether all branches are initially expanded. |
27
+ | `options.selectedLabel` | | `function \| object` | `undefined` | See below |
28
+
29
+ ### `options.selectedLabel`
30
+
31
+ This option allows you to customize the label of the selected nodes.
32
+
33
+ | Type of `options.selectedLabel` | Description |
34
+ | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------- |
35
+ | `undefined` | The generated label will be a join of the selected nodes' labels. |
36
+ | `(nodes: TreeNode[]) => string` | The selected nodes will be passed as first argument. |
37
+ | `{ max: number; fn: (count: number) => string }` | If more than `max` nodes are selected, the label will be the result of `fn(numberOfSelectedTreeNodes)`. |
38
+
39
+ ## `useTree` return values
40
+
41
+ | | Type | |
42
+ | --------------- | ------------------------------------------ | ---------------------------------------------------------------------------------- |
43
+ | `nodes` | `(Leaf \| Branch)[]` | Array of **visible** `Leaf` and `Branch` instances (See TreeNode Visibility below) |
44
+ | `activeId` | `Ref<string \| number \| undefined>` | The active node id |
45
+ | `activeNode` | `ComputedRef<Leaf \| Branch \| undefined>` | The active node instance |
46
+ | `selectedIds` | `Ref<(string \| number)[]>` | Array of selected nodes id |
47
+ | `selectedNodes` | `ComputedRef<(Leaf \| Branch)[]>` | Array of selected nodes instance |
48
+ | `selectedLabel` | `ComputedRef<string>` | The generator label for the selected nodes |
49
+
50
+ ## `LeafDefinition`
51
+
52
+ ```ts
53
+ new LeafDefinition(data)
54
+ new LeafDefinition(data, options)
55
+ ```
56
+
57
+ | | Required | Type | Default | |
58
+ | ----------------------- | :------------------: | ---------------------------------------------------- | --------------------------------------- | -------------------------------------------------------------------------------------- |
59
+ | `data` | ✓ | `TData` | | data to be stored in the node |
60
+ | `options.discriminator` | | `string` | `undefined` | discriminator for the node when you mix different data types (see Discriminator below) |
61
+ | `options.predicate` | | `(node: TreeNode) => boolean \| undefined` | `undefined` | filter function (see Filtering below) |
62
+ | `options.selectable` | | `boolean` | `true` for `Leaf`, `false` for `Branch` | whether the node can be selected |
63
+ | `options.getId` | if no `TData[id]` | `keyof TData` \| `(data: TData) => string \| number` | `id` | field or function to get a unique identifier for the node |
64
+ | `options.getLabel` | if no `TData[label]` | `keyof TData` \| `(data: TData) => string` | `label` | field or function to get a label for the node |
65
+
66
+ ### Example
67
+
68
+ ```ts
69
+ const definition = new LeafDefinition({ id: 1, label: 'John Doe', age: 30 })
70
+ ```
71
+
72
+ ## `BranchDefinition`
73
+
74
+ A `BranchDefinition` is very similar to a `LeafDefinition`, but it contains a collection of children definitions.
75
+
76
+ ```ts
77
+ new BranchDefinition(data, children)
78
+ new BranchDefinition(data, options, children)
79
+ ```
80
+
81
+ | | | Type | Default | |
82
+ | -------------------------- | --- | ---------------------- | ------- | ---------------------------------------------------------------- |
83
+ | (same as `LeafDefinition`) | | | | |
84
+ | `children` | ✓ | `TreeNodeDefinition[]` | | array of definitions for nodes that are contained in this branch |
85
+
86
+ ### Example
87
+
88
+ ```ts
89
+ const definition = new BranchDefinition({ id: 'smithes', name: 'The Smithes' }, [
90
+ new LeafDefinition({ id: 'jd-1', name: 'John Smith', age: 30 }),
91
+ new LeafDefinition({ id: 'jd-2', name: 'Jane Smith', age: 28 }),
92
+ ])
93
+ ```
94
+
95
+ ## Discriminator
96
+
97
+ The `discriminator` is a string used to differentiate between different types of nodes. This is useful when you want to
98
+ mix different types of nodes at the same collection depth.
99
+
100
+ ### Mixed data without discriminator
101
+
102
+ ```ts
103
+ const definitions = [
104
+ new LeafDefinition({ id: 'jd-1', label: 'John Doe', age: 30 }),
105
+ new LeafDefinition({ id: 'rx-1', label: 'Rex', breed: 'Golden Retriever' }),
106
+ ]
107
+
108
+ const { nodes } = useTree(definitions)
109
+
110
+ nodes.value.forEach(node => {
111
+ // node.data.<cursor> neither 'age' nor 'breed' are available here because we can't know the type of the node
112
+ })
113
+ ```
114
+
115
+ ### Using the discriminator
116
+
117
+ ```ts
118
+ const definitions = [
119
+ new LeafDefinition({ id: 'jd-1', label: 'John Doe', age: 30 }, { discriminator: 'person' }),
120
+ new LeafDefinition({ id: 'rx-1', label: 'Rex', breed: 'Golden Retriever' }, { discriminator: 'animal' }),
121
+ ]
122
+
123
+ const { nodes } = useTree(definitions)
124
+
125
+ nodes.value.forEach(node => {
126
+ if (node.discriminator === 'person') {
127
+ // node.data.<cursor> `label` and `age` are available here
128
+ } else {
129
+ // node.data.<cursor> `label` and `breed` are available here
130
+ }
131
+ })
132
+ ```
133
+
134
+ ### Mixing `BranchDefinition` and `LeafDefinition` (of same types each)
135
+
136
+ If you mix `BranchDefinition` and `LeafDefinition` of same types each, you don't need to use the discriminator because
137
+ the `isBranch` property will serve the same purpose.
138
+
139
+ ```ts
140
+ const definitions = [
141
+ new LeafDefinition({ id: 'jd-1', label: 'John Doe', age: 30 }),
142
+ new BranchDefinition({ id: 'dogs', label: 'Dogs', legs: 4 }, [
143
+ /* ... */
144
+ ]),
145
+ ]
146
+
147
+ const { nodes } = useTree(definitions)
148
+
149
+ nodes.value.forEach(node => {
150
+ if (node.isBranch) {
151
+ // node.data.<cursor> `label` and `legs` are available here
152
+ } else {
153
+ // node.data.<cursor> `label` and `age` are available here
154
+ }
155
+ })
156
+ ```
157
+
158
+ ## Filtering
159
+
160
+ The optional `predicate` function is used to filter the node across the tree and can affect its visibility (see
161
+ TreeNode Visibility below).
162
+
163
+ It takes the `data` as first argument and will return:
164
+
165
+ - `true` if the node explicitly passes the filter
166
+ - `false` if the node explicitly doesn't pass the filter
167
+ - `undefined` if the filter doesn't apply to the node
168
+
169
+ For basic filtering on label you can use the `useTreeFilter` composable which returns `filter` (`Ref<string>`) and a
170
+ predefined `predicate` function (`(node: TreeNodeBase) => boolean | undefined`) which can be passed as an option.
171
+
172
+ ## `defineTree` helper
173
+
174
+ The `defineTree` helper creates a collection of definitions in a more convenient way.
175
+
176
+ ```ts
177
+ defineTree(entries)
178
+ defineTree(entries, options)
179
+ defineTree(entries, defineChildTree)
180
+ defineTree(entries, options, defineChildTree)
181
+ ```
182
+
183
+ | | Required | Type | Default | |
184
+ | ----------------------- | :------------------: | ----------------------------------------------------- | ----------- | ------------------------------------------------------------------------------- |
185
+ | `entries` | ✓ | `(TData extends object)[]` | | array of entries for which to create a definition |
186
+ | `options.getId` | If no `TData[id]` | `keyof TData` \| (data: TData) => `string`\| `number` | `id` | field or function to get a unique identifier for the node |
187
+ | `options.getLabel` | If no `TData[label]` | `keyof TData` \| (data: TData) => `string` | `label` | field or function to get a label for the node |
188
+ | `options.discriminator` | | `string` | `undefined` | discriminator for the node when you mix different data types |
189
+ | `options.predicate` | | `(node: TreeNode) => boolean \| undefined` | `undefined` | filter function that takes the data as first argument |
190
+ | `defineChildTree` | | `(data: TData) => TreeNodeDefinition[]` | | function that returns an array of definitions that are contained in this branch |
191
+
192
+ Let's take this `families` example:
193
+
194
+ ```ts
195
+ const families = [
196
+ {
197
+ id: 'does',
198
+ label: 'The Does',
199
+ members: [
200
+ {
201
+ id: 'jd-1',
202
+ label: 'John Doe',
203
+ age: 30,
204
+ animals: [
205
+ {
206
+ id: 'jd-1-dog',
207
+ label: 'Rex',
208
+ },
209
+ ],
210
+ },
211
+ {
212
+ id: 'jd-2',
213
+ label: 'Jane Doe',
214
+ age: 28,
215
+ animals: [],
216
+ },
217
+ ],
218
+ },
219
+ {
220
+ id: 'smiths',
221
+ label: 'The Smiths',
222
+ members: [
223
+ {
224
+ id: 'js-1',
225
+ label: 'John Smith',
226
+ age: 35,
227
+ animals: [
228
+ {
229
+ id: 'js-1-cat',
230
+ label: 'Whiskers',
231
+ },
232
+ {
233
+ id: 'js-1-dog',
234
+ label: 'Fido',
235
+ },
236
+ ],
237
+ },
238
+ {
239
+ id: 'js-2',
240
+ label: 'Jane Smith',
241
+ age: 33,
242
+ animals: [
243
+ {
244
+ id: 'js-2-cat',
245
+ label: 'Mittens',
246
+ },
247
+ ],
248
+ },
249
+ ],
250
+ },
251
+ ]
252
+ ```
253
+
254
+ You can use the `defineTree` helper this way:
255
+
256
+ ```ts
257
+ const definitions = defineTree(families, family => defineTree(family.members, member => defineTree(member.animals)))
258
+ ```
259
+
260
+ This is the equivalent of the following code:
261
+
262
+ ```ts
263
+ const definitions = families.map(
264
+ family =>
265
+ new BranchDefinition(
266
+ family,
267
+ family.members.map(
268
+ member =>
269
+ new BranchDefinition(
270
+ member,
271
+ member.animals.map(animal => new LeafDefinition(animal))
272
+ )
273
+ )
274
+ )
275
+ )
276
+ ```
277
+
278
+ ### `getId` and `getLabel`
279
+
280
+ By default, ID will be retrieved from the `id` field and label from the `label` field.
281
+
282
+ You can override this behavior by passing a function or a field name.
283
+
284
+ ```ts
285
+ const entries = [
286
+ { uuid: 'jd-1', name: 'John Doe' },
287
+ { uuid: 'jd-2', name: 'Jane Doe' },
288
+ ]
289
+
290
+ const definitionsA = defineTree(entries, {
291
+ getId: 'uuid',
292
+ getLabel: 'name',
293
+ })
294
+
295
+ const definitionsB = defineTree(entries, {
296
+ getId: entry => entry.uuid,
297
+ getLabel: entry => entry.name,
298
+ })
299
+ ```
300
+
301
+ ## `Leaf` and `Branch` instances
302
+
303
+ `Leaf` and `Branch` instances have the following properties:
304
+
305
+ | | | |
306
+ | --------------- | --------------------------- | ------------------------------------------------------------------- |
307
+ | `id` | `string` \| `number` | unique identifier across the whole collection of leafs and branches |
308
+ | `label` | `string` | the label of the node |
309
+ | `isBranch` | `boolean` | `true`for `Branch` instances, `false` for `Leaf` instances |
310
+ | `discriminator` | `string` \| `undefined` | discriminator for the node when you mix different data types |
311
+ | `data` | `TData` | data stored in the node |
312
+ | `depth` | `number` | depth of the node in the collection |
313
+ | `isSelected` | `boolean` | whether the node is selected |
314
+ | `isActive` | `boolean` | whether the node is active |
315
+ | `isVisible` | `boolean` | whether the node is visible (see TreeNode Visibility below) |
316
+ | `activate` | `() => void` | function to activate the node |
317
+ | `toggleSelect` | `(force?: boolean) => void` | function to toggle the selection of the node |
318
+ | `statuses` | `{ [name]: boolean }` | object of Node statuses (see below) |
319
+
320
+ ### `statuses`
321
+
322
+ The `statuses` properties is an object for Node statuses.
323
+
324
+ It can, for example, be used in the template `:class`.
325
+
326
+ _This object is just a helper. It doesn't come with any default style._
327
+
328
+ For a `Leaf` instance, it contains the following properties:
329
+
330
+ - `selected`: whether the leaf is selected
331
+ - `active`: whether the leaf is active
332
+ - `matches`: whether the leaf matches the filter
333
+
334
+ ## `Branch` instances
335
+
336
+ Additionally, `Branch` instances have the following properties:
337
+
338
+ | | | |
339
+ | ------------------------------ | ------------ | ----------------------------------------------- |
340
+ | `isExpanded` | `boolean` | whether the branch is expanded |
341
+ | `areChildrenFullySelected` | `boolean` | whether all children are selected |
342
+ | `areChildrenPartiallySelected` | `boolean` | whether some children are selected |
343
+ | `rawChildren` | `TreeNode[]` | array of all children instances |
344
+ | `children` | `TreeNode[]` | array of visible children instances (see below) |
345
+
346
+ ### `statuses`
347
+
348
+ _This object is just a helper. It doesn't come with any default style._
349
+
350
+ For a `Branch` instance, it contains the following properties:
351
+
352
+ - `selected`: whether the branch is selected
353
+ - `selected-partial`: whether the branch is partially selected (i.e., some children are selected)
354
+ - `selected-full`: whether the branch is fully selected (i.e., all children are selected)
355
+ - `expanded`: whether the branch is expanded
356
+ - `active`: whether the branch is active
357
+ - `matches`: whether the branch matches the filter
358
+
359
+ ## TreeNode Visibility
360
+
361
+ Here are the rules to determine whether a node is visible or not.
362
+
363
+ **Note**: Only the first matching rule determines a node's visibility. Subsequent rules are not evaluated.
364
+
365
+ 1. If `predicate` returns `true` => _visible_
366
+ 2. If any of its ancestors `predicate` returns `true` => _visible_
367
+ 3. _(`Branch` only)_ If any of its descendants `predicate` returns `true` => _visible_
368
+ 4. If `predicate` returns `false` => _**not** visible_
369
+ 5. If it doesn't have a parent => _visible_
370
+ 6. If the parent's `isExpanded` is `true` => _visible_
371
+ 7. If the parent's `isExpanded` is `false` => _**not** visible_
372
+
373
+ ## Example 1: Tree View
374
+
375
+ ```html
376
+ <template>
377
+ <ul>
378
+ <li v-for="familyNode in nodes" :key="familyNode.id">
379
+ <div class="label" @click="familyNode.toggleExpand()">
380
+ {{ familyNode.isExpanded ? '↓' : '→' }} {{ familyNode.label }}
381
+ </div>
382
+ <ul v-if="familyNode.isExpanded" class="members">
383
+ <li v-for="memberNode in familyNode.children" :key="memberNode.id">
384
+ <div class="label" @click="memberNode.toggleExpand()">
385
+ {{ memberNode.isExpanded ? '↓' : '→' }} {{ memberNode.label }} ({{ memberNode.data.age }})
386
+ </div>
387
+ <ul v-if="memberNode.isExpanded" class="animals">
388
+ <li v-for="animalNode in memberNode.children" :key="animalNode.id">{{ animalNode.label }}</li>
389
+ </ul>
390
+ </li>
391
+ </ul>
392
+ </li>
393
+ </ul>
394
+ </template>
395
+
396
+ <script lang="ts" setup>
397
+ const familyDefinitions = defineTree(families, ({ members }) =>
398
+ defineTree(members, ({ animals }) => defineTree(animals))
399
+ )
400
+
401
+ const { nodes } = useTree(familyDefinitions)
402
+ </script>
403
+
404
+ <style lang="postcss" scoped>
405
+ .persons,
406
+ .animals {
407
+ padding-left: 20px;
408
+ }
409
+
410
+ .animals li {
411
+ padding-left: 10px;
412
+ }
413
+
414
+ .label {
415
+ cursor: pointer;
416
+ }
417
+ </style>
418
+ ```
419
+
420
+ ## Example 2: Multi-select
421
+
422
+ ```html
423
+ <template>
424
+ <ul>
425
+ <li v-for="family in nodes" :key="family.id">
426
+ <div
427
+ class="label family"
428
+ :class="family.statuses"
429
+ @mouseenter="family.activate()"
430
+ @click="family.toggleChildrenSelect()"
431
+ >
432
+ {{ family.label }}
433
+ </div>
434
+ <ul class="persons">
435
+ <li v-for="person in family.children" :key="person.id">
436
+ <div
437
+ class="label person"
438
+ :class="person.statuses"
439
+ @mouseenter="person.activate()"
440
+ @click="person.toggleSelect()"
441
+ >
442
+ {{ person.label }} ({{ person.data.age }})
443
+ </div>
444
+ </li>
445
+ </ul>
446
+ </li>
447
+ </ul>
448
+ </template>
449
+
450
+ <script lang="ts" setup>
451
+ const definitions = defineTree(families, ({ members }) => defineTree(members))
452
+
453
+ const { nodes } = useTree(definitions, { allowMultiSelect: true })
454
+ </script>
455
+
456
+ <style lang="postcss" scoped>
457
+ .persons {
458
+ padding-left: 20px;
459
+ }
460
+
461
+ .family {
462
+ background-color: #eaeaea;
463
+
464
+ &.selected-full {
465
+ background-color: #add8e6;
466
+ }
467
+
468
+ &.active {
469
+ filter: brightness(1.1);
470
+ }
471
+ }
472
+
473
+ .person {
474
+ background-color: #f5f5f5;
475
+
476
+ &.selected {
477
+ background-color: #b5e2f1;
478
+ }
479
+
480
+ &.active {
481
+ filter: brightness(1.07);
482
+ }
483
+ }
484
+ </style>
485
+ ```
486
+
487
+ ### Example 3: Filtering
488
+
489
+ ```html
490
+ <template>
491
+ <div>
492
+ <input v-model="filter" placeholder="Filter" />
493
+ </div>
494
+ <ul>
495
+ <li v-for="family in nodes" :key="family.id">
496
+ <div :class="family.statuses">{{ family.label }}</div>
497
+ <ul class="sub">
498
+ <li v-for="person in family.children" :key="person.id">
499
+ <div :class="person.statuses">{{ person.label }} ({{ person.data.age }})</div>
500
+ <ul class="sub">
501
+ <li v-for="animal in person.children" :key="animal.id">
502
+ <div :class="animal.statuses">{{ animal.label }}</div>
503
+ </li>
504
+ </ul>
505
+ </li>
506
+ </ul>
507
+ </li>
508
+ </ul>
509
+ </template>
510
+
511
+ <script lang="ts" setup>
512
+ const filter = ref<string>()
513
+
514
+ const predicate = ({ label }: { label: string }) => {
515
+ const filterValue = filter.value?.trim().toLocaleLowerCase() ?? false
516
+
517
+ return !filterValue ? undefined : label.toLocaleLowerCase().includes(filterValue)
518
+ }
519
+
520
+ const definitions = defineTree(families, { predicate }, ({ members }) =>
521
+ defineTree(members, { predicate }, ({ animals }) => defineTree(animals, { predicate }))
522
+ )
523
+
524
+ const { nodes } = useTree(definitions, { expand: false })
525
+ </script>
526
+
527
+ <style lang="postcss" scoped>
528
+ .sub {
529
+ padding-left: 20px;
530
+ }
531
+
532
+ .matches {
533
+ font-weight: bold;
534
+ }
535
+ </style>
536
+ ```
@@ -7,6 +7,8 @@
7
7
  content: sidebarStore.isExpanded ? $t('core.sidebar.close') : $t('core.sidebar.open'),
8
8
  placement: 'right',
9
9
  }"
10
+ accent="info"
11
+ size="medium"
10
12
  :icon="sidebarStore.isExpanded ? faAngleDoubleLeft : faBars"
11
13
  class="sidebar-toggle"
12
14
  @click="sidebarStore.toggleExpand()"
@@ -14,8 +16,8 @@
14
16
  <slot name="app-header" />
15
17
  </header>
16
18
  <div class="container">
17
- <Backdrop v-if="sidebarStore.isExpanded && !sidebarStore.isLocked" @click="sidebarStore.toggleExpand(false)" />
18
- <LayoutSidebar class="sidebar">
19
+ <VtsBackdrop v-if="sidebarStore.isExpanded && !sidebarStore.isLocked" @click="sidebarStore.toggleExpand(false)" />
20
+ <VtsLayoutSidebar class="sidebar">
19
21
  <template #header>
20
22
  <slot name="sidebar-header" />
21
23
  </template>
@@ -25,7 +27,7 @@
25
27
  <template #footer>
26
28
  <slot name="sidebar-footer" />
27
29
  </template>
28
- </LayoutSidebar>
30
+ </VtsLayoutSidebar>
29
31
  <main class="main-container">
30
32
  <slot name="content" />
31
33
  </main>
@@ -34,9 +36,9 @@
34
36
  </template>
35
37
 
36
38
  <script lang="ts" setup>
37
- import Backdrop from '@core/components/backdrop/Backdrop.vue'
38
- import UiButtonIcon from '@core/components/button/ButtonIcon.vue'
39
- import LayoutSidebar from '@core/components/layout/LayoutSidebar.vue'
39
+ import VtsBackdrop from '@core/components/backdrop/VtsBackdrop.vue'
40
+ import VtsLayoutSidebar from '@core/components/layout/VtsLayoutSidebar.vue'
41
+ import UiButtonIcon from '@core/components/ui/button-icon/UiButtonIcon.vue'
40
42
  import { vTooltip } from '@core/directives/tooltip.directive'
41
43
  import { useSidebarStore } from '@core/stores/sidebar.store'
42
44
  import { faAngleDoubleLeft, faBars } from '@fortawesome/free-solid-svg-icons'
@@ -20,6 +20,7 @@
20
20
  "dashboard": "Dashboard",
21
21
  "documentation-name": "{name} Dokumentation",
22
22
  "hosts": "Hosts",
23
+ "learn-more": "Mehr erfahren",
23
24
  "loading-in-progress": "Ladevorgang läuft…",
24
25
  "log-out": "Abmelden",
25
26
  "master": "Primärer Host",
@@ -27,11 +28,11 @@
27
28
  "network": "Netzwerk",
28
29
  "object-not-found": "Objekt {id} wurde nicht gefunden…",
29
30
  "patches": "Patches",
30
- "power-on-for-console": "Konsole ist nach Start der VM verfügbar",
31
+ "power-on-vm-for-console": "Konsole ist nach Start der VM verfügbar",
32
+ "power-on-host-for-console": "Konsole ist nach Start des Hosts verfügbar",
31
33
  "running-vm": "VM eingeschalten | VMs eingeschalten",
32
34
  "stats": "Statistiken",
33
35
  "storage": "Speicher",
34
- "support-name": "{name} pro support",
35
36
  "system": "System",
36
37
 
37
38
  "tasks": "Aufgaben",
@@ -26,7 +26,8 @@
26
26
 
27
27
  "core.select.all": "Select all",
28
28
  "core.select.none": "Select none",
29
-
29
+ "core.select.unselect": "Unselect all",
30
+ "core.select.n-selected-of": "{count} selected of {total} objects",
30
31
  "core.sidebar.close": "Close sidebar",
31
32
  "core.sidebar.lock": "Lock sidebar open",
32
33
  "core.sidebar.open": "Open sidebar",
@@ -39,9 +40,11 @@
39
40
  "dark-mode.disable": "Disable dark mode",
40
41
  "dark-mode.auto": "Auto dark mode",
41
42
 
43
+ "access-forum": "Access forum",
42
44
  "dashboard": "Dashboard",
43
45
  "documentation-name": "{name} documentation",
44
46
  "hosts": "Hosts",
47
+ "learn-more": "Learn more",
45
48
  "loading-in-progress": "Loading in progress…",
46
49
  "log-out": "Log out",
47
50
  "master": "Primary host",
@@ -51,12 +54,13 @@
51
54
  "object-not-found": "Object {id} can't be found…",
52
55
  "other": "Other",
53
56
  "patches": "Patches",
54
- "power-on-for-console": "Power on your VM to access its console",
57
+ "power-on-vm-for-console": "Power on your VM to access its console",
58
+ "power-on-host-for-console": "Power on your host to access its console",
59
+ "professional-support": "Professional support",
55
60
  "running-vm": "Running VM | Running VMs",
56
61
  "see-all": "See all",
57
62
  "stats": "Stats",
58
63
  "storage": "Storage",
59
- "support-name": "{name} pro support",
60
64
  "system": "System",
61
65
 
62
66
  "tasks": "Tasks",
@@ -33,16 +33,17 @@
33
33
 
34
34
  "dashboard": "داشبورد",
35
35
  "documentation-name": "اسناد {name}",
36
+ "learn-more": "بیشتر بدانید",
36
37
  "loading-in-progress": "بارگیری در حال انجام است…",
37
38
  "log-out": "خروج",
38
39
  "master": "میزبان اصلی",
39
40
  "network": "شبکه",
40
41
  "patches": "وصله ها",
41
- "power-on-for-console": "ماشین مجازی خود را روشن کنید تا به کنسول آن دسترسی داشته باشید",
42
+ "power-on-vm-for-console": "ماشین مجازی خود را روشن کنید تا به کنسول آن دسترسی داشته باشید",
43
+ "power-on-host-for-console": "هاست خود را برای دسترسی به کنسول آن روشن کنید",
42
44
  "running-vm": "ماشین های مجازی در حال اجرا | ماشین مجازی در حال اجرا",
43
45
  "stats": "آمار",
44
46
  "storage": "ذخیره سازی",
45
- "support-name": "پشتیبانی حرفه ای {name}",
46
47
  "system": "سیستم",
47
48
  "tasks": "کارها"
48
49
  }