@xen-orchestra/web-core 0.4.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.
- package/lib/assets/css/_colors.pcss +56 -24
- package/lib/assets/css/_context.pcss +9 -9
- package/lib/assets/css/base.pcss +6 -6
- package/lib/components/backup-item/VtsBackupItem.vue +47 -0
- package/lib/components/backup-state/{BackupState.vue → VtsBackupState.vue} +4 -4
- package/lib/components/{button/ButtonGroup.vue → button-group/VtsButtonGroup.vue} +7 -6
- package/lib/components/cell-object/{CellObject.vue → VtsCellObject.vue} +14 -14
- package/lib/components/cell-text/{CellText.vue → VtsCellText.vue} +11 -11
- package/lib/components/console/{RemoteConsole.vue → VtsRemoteConsole.vue} +12 -18
- package/lib/components/donut-chart-with-legend/{DonutChartWithLegend.vue → VtsDonutChartWithLegend.vue} +6 -6
- package/lib/components/dropdown/DropdownItem.vue +8 -14
- package/lib/components/dropdown/DropdownTitle.vue +3 -3
- package/lib/components/icon/VtsIcon.vue +38 -42
- package/lib/components/layout/{LayoutSidebar.vue → VtsLayoutSidebar.vue} +31 -30
- package/lib/components/legend-group/VtsLegendGroup.vue +44 -0
- package/lib/components/{legend/LegendList.vue → legend-list/VtsLegendList.vue} +2 -2
- package/lib/components/menu/MenuTrigger.vue +2 -2
- package/lib/components/stacked-bar-with-legend/{StackedBarWithLegend.vue → VtsStackedBarWithLegend.vue} +14 -14
- package/lib/components/state-hero/VtsComingSoonHero.vue +13 -0
- package/lib/components/state-hero/VtsLoadingHero.vue +15 -0
- package/lib/components/state-hero/VtsNoDataHero.vue +11 -0
- package/lib/components/state-hero/VtsObjectNotFoundHero.vue +13 -0
- package/lib/components/state-hero/{StateHero.vue → VtsStateHero.vue} +42 -35
- package/lib/components/tab/TabItem.vue +6 -6
- package/lib/components/table/ColumnTitle.vue +7 -7
- package/lib/components/table/{UiTable.vue → VtsTable.vue} +7 -3
- package/lib/components/task/{QuickTaskButton.vue → VtsQuickTaskButton.vue} +8 -7
- package/lib/components/task/{QuickTaskList.vue → VtsQuickTaskList.vue} +10 -11
- package/lib/components/task/{QuickTaskTabBar.vue → VtsQuickTaskTabBar.vue} +9 -13
- package/lib/components/{tooltip/TooltipList.vue → tooltip-list/VtsTooltipList.vue} +2 -3
- package/lib/components/tree/{TreeItem.vue → VtsTreeItem.vue} +3 -4
- package/lib/components/tree/VtsTreeItemError.vue +18 -0
- package/lib/components/tree/{TreeLine.vue → VtsTreeLine.vue} +9 -11
- package/lib/components/tree/{TreeList.vue → VtsTreeList.vue} +5 -2
- package/lib/components/tree/VtsTreeLoadingItem.vue +61 -0
- package/lib/components/ui/account-menu-button/UiAccountMenuButton.vue +64 -0
- package/lib/components/ui/actions-title/UiActionsTitle.vue +2 -2
- package/lib/components/ui/button/UiButton.vue +532 -0
- package/lib/components/ui/button-icon/UiButtonIcon.vue +248 -0
- package/lib/components/{UiCard.vue → ui/card/UiCard.vue} +8 -6
- package/lib/components/ui/card-numbers/UiCardNumbers.vue +103 -0
- package/lib/components/ui/card-subtitle/UiCardSubtitle.vue +24 -0
- package/lib/components/ui/card-title/UiCardTitle.vue +56 -0
- package/lib/components/ui/checkbox/UiCheckbox.vue +410 -0
- package/lib/components/ui/checkbox-group/UiCheckboxGroup.vue +57 -0
- package/lib/components/ui/chip/ChipIcon.vue +21 -0
- package/lib/components/ui/chip/ChipRemoveIcon.vue +13 -0
- package/lib/components/ui/chip/UiChip.vue +135 -0
- package/lib/components/{icon/ComplexIcon.vue → ui/complex-icon/UiComplexIcon.vue} +21 -27
- package/lib/components/ui/counter/UiCounter.vue +134 -0
- package/lib/components/{donut-chart/DonutChart.vue → ui/donut-chart/UiDonutChart.vue} +28 -30
- package/lib/components/{head-bar/HeadBar.vue → ui/head-bar/UiHeadBar.vue} +31 -31
- package/lib/components/{info/VtsInfo.vue → ui/info/UiInfo.vue} +13 -11
- package/lib/components/{input → ui/input}/UiInput.vue +9 -7
- package/lib/components/ui/input/UiTextarea.vue +120 -0
- package/lib/components/{input/VtsLabel.vue → ui/label/UiLabel.vue} +25 -25
- package/lib/components/ui/legend/UiLegend.vue +98 -0
- package/lib/components/{LegendTitle.vue → ui/legend-title/UiLegendTitle.vue} +4 -4
- package/lib/components/{UiSpinner.vue → ui/loader/UiLoader.vue} +3 -3
- package/lib/components/{icon/ObjectIcon.vue → ui/object-icon/UiObjectIcon.vue} +44 -36
- package/lib/components/ui/object-link/UiObjectLink.vue +83 -0
- package/lib/components/ui/panel/UiPanel.vue +57 -0
- package/lib/components/{query-search-bar/QuerySearchBar.vue → ui/query-search-bar/UiQuerySearchBar.vue} +16 -16
- package/lib/components/{task/QuickTaskItem.vue → ui/quick-task-item/UiQuickTaskItem.vue} +46 -34
- package/lib/components/{task/QuickTaskPanel.vue → ui/quick-task-panel/UiQuickTaskPanel.vue} +8 -7
- package/lib/components/ui/radio-button/UiRadioButton.vue +196 -0
- package/lib/components/ui/radio-button-group/UiRadioButtonGroup.vue +56 -0
- package/lib/components/{stacked-bar → ui/stacked-bar}/StackedBarSegment.vue +23 -26
- package/lib/components/{stacked-bar/StackedBar.vue → ui/stacked-bar/UiStackedBar.vue} +6 -6
- package/lib/components/ui/table-actions/UiTableActions.vue +46 -0
- package/lib/components/ui/tag/UiTag.vue +118 -0
- package/lib/components/ui/title/UiTitle.vue +2 -2
- package/lib/components/ui/toaster/UiToaster.vue +100 -0
- package/lib/components/ui/toggle/UiToggle.vue +117 -0
- package/lib/components/{tooltip/TooltipItem.vue → ui/tooltip/UiTooltip.vue} +15 -15
- package/lib/components/ui/top-bottom-table/UiTopBottomTable.vue +64 -0
- package/lib/components/{tree/TreeItemLabel.vue → ui/tree-item-label/UiTreeItemLabel.vue} +60 -59
- package/lib/components/ui/user-link/UiUserLink.vue +76 -0
- package/lib/components/ui/user-logo/UiUserLogo.vue +50 -0
- package/lib/composables/route-query.composable.md +136 -0
- package/lib/composables/table.composable.md +159 -0
- package/lib/composables/tree/define-tree.ts +1 -1
- package/lib/composables/tree/tree-node-base.ts +6 -6
- package/lib/composables/tree.composable.md +536 -0
- package/lib/layouts/CoreLayout.vue +8 -6
- package/lib/locales/de.json +2 -2
- package/lib/locales/en.json +6 -3
- package/lib/locales/fa.json +2 -2
- package/lib/locales/fr.json +6 -3
- package/lib/types/color.type.ts +0 -2
- package/lib/types/subscribable-store.type.ts +2 -2
- package/lib/utils/create-subscribable-store-context.util.ts +1 -1
- package/lib/utils/if-else.utils.md +23 -0
- package/lib/utils/if-else.utils.ts +2 -2
- package/lib/utils/to-variants.util.md +62 -0
- package/package.json +7 -7
- package/lib/components/CardNumbers.vue +0 -101
- package/lib/components/PowerStateIcon.vue +0 -46
- package/lib/components/UiTag.vue +0 -101
- package/lib/components/backup-item/BackupItem.vue +0 -40
- package/lib/components/button/ButtonIcon.vue +0 -220
- package/lib/components/button/UiButton.vue +0 -470
- package/lib/components/card/CardSubtitle.vue +0 -24
- package/lib/components/card/CardTitle.vue +0 -57
- package/lib/components/chip/ChipIcon.vue +0 -30
- package/lib/components/chip/ChipRemoveIcon.vue +0 -13
- package/lib/components/chip/UiChip.vue +0 -137
- package/lib/components/counter/VtsCounter.vue +0 -147
- package/lib/components/legend/LegendGroup.vue +0 -44
- package/lib/components/legend/LegendItem.vue +0 -86
- package/lib/components/object-link/ObjectLink.vue +0 -87
- package/lib/components/state-hero/ComingSoonHero.vue +0 -13
- package/lib/components/state-hero/LoadingHero.vue +0 -15
- package/lib/components/state-hero/NoDataHero.vue +0 -11
- package/lib/components/state-hero/ObjectNotFoundHero.vue +0 -13
- package/lib/components/tree/TreeItemError.vue +0 -13
- package/lib/components/tree/TreeLoadingItem.vue +0 -60
- package/lib/components/user/UserLink.vue +0 -72
- package/lib/components/user/UserLogo.vue +0 -57
- package/lib/types/backup.type.ts +0 -11
- package/lib/types/size.type.ts +0 -1
- package/lib/types/task.type.ts +0 -13
- /package/lib/components/backdrop/{Backdrop.vue → VtsBackdrop.vue} +0 -0
- /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
|
-
<
|
|
18
|
-
<
|
|
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
|
-
</
|
|
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
|
|
38
|
-
import
|
|
39
|
-
import
|
|
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'
|
package/lib/locales/de.json
CHANGED
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
"network": "Netzwerk",
|
|
29
29
|
"object-not-found": "Objekt {id} wurde nicht gefunden…",
|
|
30
30
|
"patches": "Patches",
|
|
31
|
-
"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",
|
|
32
33
|
"running-vm": "VM eingeschalten | VMs eingeschalten",
|
|
33
34
|
"stats": "Statistiken",
|
|
34
35
|
"storage": "Speicher",
|
|
35
|
-
"support-name": "{name} pro support",
|
|
36
36
|
"system": "System",
|
|
37
37
|
|
|
38
38
|
"tasks": "Aufgaben",
|
package/lib/locales/en.json
CHANGED
|
@@ -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,6 +40,7 @@
|
|
|
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",
|
|
@@ -52,12 +54,13 @@
|
|
|
52
54
|
"object-not-found": "Object {id} can't be found…",
|
|
53
55
|
"other": "Other",
|
|
54
56
|
"patches": "Patches",
|
|
55
|
-
"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",
|
|
56
60
|
"running-vm": "Running VM | Running VMs",
|
|
57
61
|
"see-all": "See all",
|
|
58
62
|
"stats": "Stats",
|
|
59
63
|
"storage": "Storage",
|
|
60
|
-
"support-name": "{name} pro support",
|
|
61
64
|
"system": "System",
|
|
62
65
|
|
|
63
66
|
"tasks": "Tasks",
|
package/lib/locales/fa.json
CHANGED
|
@@ -39,11 +39,11 @@
|
|
|
39
39
|
"master": "میزبان اصلی",
|
|
40
40
|
"network": "شبکه",
|
|
41
41
|
"patches": "وصله ها",
|
|
42
|
-
"power-on-for-console": "ماشین مجازی خود را روشن کنید تا به کنسول آن دسترسی داشته باشید",
|
|
42
|
+
"power-on-vm-for-console": "ماشین مجازی خود را روشن کنید تا به کنسول آن دسترسی داشته باشید",
|
|
43
|
+
"power-on-host-for-console": "هاست خود را برای دسترسی به کنسول آن روشن کنید",
|
|
43
44
|
"running-vm": "ماشین های مجازی در حال اجرا | ماشین مجازی در حال اجرا",
|
|
44
45
|
"stats": "آمار",
|
|
45
46
|
"storage": "ذخیره سازی",
|
|
46
|
-
"support-name": "پشتیبانی حرفه ای {name}",
|
|
47
47
|
"system": "سیستم",
|
|
48
48
|
"tasks": "کارها"
|
|
49
49
|
}
|
package/lib/locales/fr.json
CHANGED
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
|
|
27
27
|
"core.select.all": "Tout sélectionner",
|
|
28
28
|
"core.select.none": "Tout désélectionner",
|
|
29
|
-
|
|
29
|
+
"core.select.unselect": "Tout désélectionner",
|
|
30
|
+
"core.select.n-selected-of": "{count} objet sélectionné sur {total} | {count} objet sélectionné sur {total} | {count} objets sélectionnés sur {total}",
|
|
30
31
|
"core.sidebar.close": "Fermer la barre latérale",
|
|
31
32
|
"core.sidebar.lock": "Verrouiller la barre latérale",
|
|
32
33
|
"core.sidebar.open": "Ouvrir la barre latérale",
|
|
@@ -39,6 +40,7 @@
|
|
|
39
40
|
"dark-mode.disable": "Désactiver le mode sombre",
|
|
40
41
|
"dark-mode.auto": "Mode sombre automatique",
|
|
41
42
|
|
|
43
|
+
"access-forum": "Accès au forum",
|
|
42
44
|
"dashboard": "Tableau de bord",
|
|
43
45
|
"documentation-name": "Documentation {name}",
|
|
44
46
|
"hosts": "Hôtes",
|
|
@@ -52,12 +54,13 @@
|
|
|
52
54
|
"object-not-found": "L'objet {id} est introuvable…",
|
|
53
55
|
"other": "Autre",
|
|
54
56
|
"patches": "Patches",
|
|
55
|
-
"power-on-for-console": "Allumez votre VM pour accéder à sa console",
|
|
57
|
+
"power-on-vm-for-console": "Allumez votre VM pour accéder à sa console",
|
|
58
|
+
"power-on-host-for-console": "Allumez votre hôte pour accéder à sa console",
|
|
59
|
+
"professional-support": "Support professionel",
|
|
56
60
|
"running-vm": "VM en cours d'exécution | VMs en cours d'exécution",
|
|
57
61
|
"see-all": "Voir tout",
|
|
58
62
|
"stats": "Stats",
|
|
59
63
|
"storage": "Stockage",
|
|
60
|
-
"support-name": "Support pro {name}",
|
|
61
64
|
"system": "Système",
|
|
62
65
|
|
|
63
66
|
"tasks": "Tâches",
|
package/lib/types/color.type.ts
CHANGED