@vcmap/ui 6.0.14 → 6.1.0-rc.2

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 (99) hide show
  1. package/config/base.config.json +25 -3
  2. package/config/dev.config.json +17 -3
  3. package/config/splashscreen.config.json +13 -0
  4. package/dist/assets/cesium.js +1 -1
  5. package/dist/assets/{core-882e211a.js → core-fd079400.js} +6861 -5788
  6. package/dist/assets/core.js +1 -1
  7. package/dist/assets/ol.js +1 -1
  8. package/dist/assets/ui-5135917c.css +1 -0
  9. package/dist/assets/{ui-b6bff1d9.js → ui-5135917c.js} +16764 -18447
  10. package/dist/assets/ui.js +1 -1
  11. package/dist/assets/vue.js +1 -1
  12. package/dist/assets/{vuetify-2d64c180.js → vuetify-f02b7bb9.js} +1 -1
  13. package/dist/assets/vuetify.js +1 -1
  14. package/index.d.ts +13 -1
  15. package/index.js +7 -0
  16. package/package.json +2 -2
  17. package/plugins/@vcmap-show-case/dev-tools/package.json +5 -0
  18. package/plugins/@vcmap-show-case/dev-tools/src/eventLogger.js +35 -0
  19. package/plugins/@vcmap-show-case/dev-tools/src/index.js +59 -0
  20. package/plugins/@vcmap-show-case/search-example/src/searchImpl.js +10 -0
  21. package/src/application/VcsApp.vue.d.ts +26 -0
  22. package/src/application/VcsContainer.vue +5 -3
  23. package/src/application/VcsContainer.vue.d.ts +21 -0
  24. package/src/application/VcsNavbar.vue +10 -6
  25. package/src/application/VcsNavbar.vue.d.ts +2 -0
  26. package/src/application/VcsSplashScreen.vue +35 -28
  27. package/src/application/VcsSplashScreen.vue.d.ts +1 -0
  28. package/src/callback/addModuleCallback.d.ts +29 -0
  29. package/src/callback/addModuleCallback.js +61 -0
  30. package/src/callback/removeModuleCallback.d.ts +29 -0
  31. package/src/callback/removeModuleCallback.js +53 -0
  32. package/src/callback/startRotationCallback.d.ts +37 -0
  33. package/src/callback/startRotationCallback.js +67 -0
  34. package/src/callback/stopRotationCallback.d.ts +8 -0
  35. package/src/callback/stopRotationCallback.js +37 -0
  36. package/src/components/buttons/VcsActionButtonList.vue +6 -4
  37. package/src/components/buttons/VcsToolButton.vue +0 -1
  38. package/src/components/form-inputs-controls/VcsDatePicker.vue +7 -1
  39. package/src/components/form-inputs-controls/VcsDatePicker.vue.d.ts +9 -0
  40. package/src/components/form-inputs-controls/VcsTextArea.vue +1 -1
  41. package/src/components/icons/+all.js +4 -0
  42. package/src/components/icons/View360Icon.vue +55 -0
  43. package/src/components/icons/View360Icon.vue.d.ts +2 -0
  44. package/src/components/import/VcsImportComponent.vue +2 -0
  45. package/src/components/lists/VcsList.vue +15 -11
  46. package/src/components/lists/VcsList.vue.d.ts +9 -0
  47. package/src/components/lists/VcsTreeNode.vue +244 -0
  48. package/src/components/lists/VcsTreeNode.vue.d.ts +31 -0
  49. package/src/components/lists/VcsTreeview.vue +111 -173
  50. package/src/components/lists/VcsTreeview.vue.d.ts +58 -4
  51. package/src/components/lists/VcsTreeviewTitle.vue +10 -3
  52. package/src/components/lists/VcsTreeviewTitle.vue.d.ts +2 -0
  53. package/src/components/tables/VcsDataTable.vue +14 -3
  54. package/src/components/tables/VcsDataTable.vue.d.ts +9 -0
  55. package/src/featureInfo/BalloonComponent.vue +18 -47
  56. package/src/featureInfo/BalloonComponent.vue.d.ts +0 -1
  57. package/src/featureInfo/IframeComponent.vue +1 -32
  58. package/src/featureInfo/IframeComponent.vue.d.ts +1 -4
  59. package/src/i18n/de.d.ts +1 -0
  60. package/src/i18n/de.js +1 -0
  61. package/src/i18n/en.d.ts +1 -0
  62. package/src/i18n/en.js +1 -0
  63. package/src/init.d.ts +6 -0
  64. package/src/init.js +26 -14
  65. package/src/legend/VcsLegend.vue +1 -1
  66. package/src/manager/toolbox/ToolboxManagerComponent.vue +4 -4
  67. package/src/manager/toolbox/ToolboxManagerComponent.vue.d.ts +2 -2
  68. package/src/manager/toolbox/toolboxManager.d.ts +5 -0
  69. package/src/manager/toolbox/toolboxManager.js +7 -1
  70. package/src/manager/window/WindowComponent.vue +11 -1
  71. package/src/manager/window/WindowComponent.vue.d.ts +1 -0
  72. package/src/manager/window/WindowManager.vue +14 -4
  73. package/src/manager/window/WindowManager.vue.d.ts +1 -0
  74. package/src/navigation/MapNavigation.vue +87 -5
  75. package/src/navigation/MapNavigation.vue.d.ts +3 -1
  76. package/src/navigation/overviewMap.d.ts +6 -0
  77. package/src/navigation/overviewMap.js +14 -1
  78. package/src/pluginHelper.d.ts +0 -7
  79. package/src/pluginHelper.js +4 -18
  80. package/src/search/ResultItem.vue +1 -10
  81. package/src/search/ResultsComponent.vue +11 -1
  82. package/src/search/ResultsComponent.vue.d.ts +9 -0
  83. package/src/search/SearchComponent.vue +88 -11
  84. package/src/search/SearchComponent.vue.d.ts +7 -0
  85. package/src/search/markText.d.ts +1 -1
  86. package/src/search/markText.js +4 -4
  87. package/src/search/search.d.ts +3 -0
  88. package/src/search/search.js +3 -2
  89. package/src/state.d.ts +2 -4
  90. package/src/state.js +31 -54
  91. package/src/uiConfig.d.ts +40 -0
  92. package/src/uiConfig.js +6 -0
  93. package/src/vcsUiApp.js +11 -7
  94. package/src/vuePlugins/vuetify.js +2 -0
  95. package/dist/assets/ui-b6bff1d9.css +0 -1
  96. /package/dist/assets/{cesium-615823f2.js → cesium-57fbd309.js} +0 -0
  97. /package/dist/assets/{ol-7fc05707.js → ol-50dfef96.js} +0 -0
  98. /package/dist/assets/{vue-74e8343e.js → vue-c3c55d88.js} +0 -0
  99. /package/dist/assets/{vuetify-2d64c180.css → vuetify-f02b7bb9.css} +0 -0
@@ -0,0 +1,244 @@
1
+ <template>
2
+ <div class="vcs-tree-node" v-if="matchFilter">
3
+ <v-row
4
+ no-gutters
5
+ class="treenode flex-nowrap text-truncate"
6
+ :class="`level-${level} ${children.length ? 'group' : 'item'}`"
7
+ >
8
+ <VBtn
9
+ v-if="children.length"
10
+ class="chevron-btn"
11
+ variant="text"
12
+ :icon="isOpen ? 'mdi-chevron-down' : 'mdi-chevron-right'"
13
+ @click="bubbleItemToggled(item.name)"
14
+ />
15
+ <slot name="prepend" v-bind="{ item }">
16
+ <span class="prepend">
17
+ <div v-if="item.icon">
18
+ <v-icon v-if="typeof item?.icon === 'string'" :size="iconSize">
19
+ {{ item.icon }}
20
+ </v-icon>
21
+ <ImageElementInjector :element="item.icon" v-else />
22
+ </div>
23
+ </span>
24
+ </slot>
25
+ <slot name="title" v-bind="{ item }">
26
+ <VcsTreeviewTitle
27
+ :item="item"
28
+ :cursor-pointer="item.clickable || (openOnClick && !!children.length)"
29
+ @click="(event) => $emit('click', item, event)"
30
+ />
31
+ </slot>
32
+ <slot name="append" v-bind="{ item }">
33
+ <VcsActionButtonList
34
+ v-if="item.actions?.length > 0"
35
+ :actions="item.actions"
36
+ :overflow-count="3"
37
+ :disabled="item.disabled"
38
+ right
39
+ tooltip-position="right"
40
+ block-overflow
41
+ class="col-4 pa-0 ml-auto pr-4"
42
+ />
43
+ </slot>
44
+ </v-row>
45
+ <v-expand-transition>
46
+ <div v-if="isOpen && children.length" class="children">
47
+ <VcsTreeNode
48
+ v-for="child in children"
49
+ :key="child.name"
50
+ :item="child"
51
+ :search="search"
52
+ :custom-filter="customFilter"
53
+ :level="level + 1"
54
+ :open-on-click="openOnClick"
55
+ :item-children="itemChildren"
56
+ :opened="opened"
57
+ @item-toggled="bubbleItemToggled"
58
+ @click="bubbleItemClicked"
59
+ >
60
+ <template v-for="slot of forwardSlots" #[slot]="scope">
61
+ <slot :name="slot" v-bind="scope ?? {}" />
62
+ </template>
63
+ </VcsTreeNode>
64
+ </div>
65
+ </v-expand-transition>
66
+ </div>
67
+ </template>
68
+
69
+ <script>
70
+ import { computed, getCurrentInstance } from 'vue';
71
+ import { VBtn, VExpandTransition, VIcon, VRow } from 'vuetify/components';
72
+ import { useIconSize } from '../../vuePlugins/vuetify.js';
73
+ import { getForwardSlots } from '../composables.js';
74
+ import ImageElementInjector from '../ImageElementInjector.vue';
75
+ import VcsActionButtonList from '../buttons/VcsActionButtonList.vue';
76
+ import VcsTreeviewTitle from './VcsTreeviewTitle.vue';
77
+
78
+ /**
79
+ * @typedef {Object} VcsTreeNodeItem
80
+ * @property {string} name
81
+ * @property {string} [title] - An optional translatable title.
82
+ * @property {string} [tooltip]
83
+ * @property {boolean} [clickable] - Whether this item reacts to click events.
84
+ * @property {boolean} [disabled] - Whether this item should be displayed as disabled.
85
+ * @property {Array<import("../../actions/actionHelper.js").VcsAction>} [actions]
86
+ * @property {Array<VcsTreeNodeItem>} [children] - An optional array of children. Can be binded to another key, using the `item-children` attributes of the VcsTreeview component.
87
+ * @property {string|HTMLCanvasElement|HTMLImageElement|undefined} [icon] - An optional icon to display with this item. Can be a URL or HTMLElement.
88
+ * @property {function(string):void} [clicked] - A callback called when the item is clicked.
89
+ */
90
+
91
+ /**
92
+ * @description
93
+ * A recursive component to render a treenode and its children.
94
+ * Exposes the `prepend`, `title` and `append` slots for customization.
95
+ * Emits `itemToggled` and `click` events.
96
+ * @vue-prop {VcsTreeNodeItem} item - The item to render.
97
+ * @vue-prop {Array<string>} opened - Names of the opened items.
98
+ * @vue-prop {number} [level=0] - The level of the item
99
+ * @vue-prop {string} [itemChildren='children'] - The property key of the children.
100
+ * @vue-prop {boolean} [openOnClick=false] - Whether to open items on title click..
101
+ * @vue-prop {string} [search] - The search string to filter the tree.
102
+ * @vue-prop {function(VcsTreeNodeItem, string|undefined):boolean}} [customFilter] - a function to customize filtering when searching.
103
+ * @vue-data {slot} [#prepend] - A slot prepended to the item, binding the item. Default fallback renders an image.
104
+ * @vue-data {slot} [#title] - A slot to render the item title, binding the item. Default fallback renders a translatable title using the VcsTreeviewTitle component.
105
+ * @vue-data {slot} [#append] - A slot appended to the item, binding the item. Default fallback renders the VcsActionButtonList if the item has an array of Actions.
106
+ */
107
+ export default {
108
+ name: 'VcsTreeNode',
109
+ components: {
110
+ ImageElementInjector,
111
+ VBtn,
112
+ VExpandTransition,
113
+ VIcon,
114
+ VRow,
115
+ VcsActionButtonList,
116
+ VcsTreeviewTitle,
117
+ },
118
+ props: {
119
+ /** @type {VcsTreeNodeItem} */
120
+ item: {
121
+ type: Object,
122
+ required: true,
123
+ },
124
+ opened: {
125
+ type: Array,
126
+ required: true,
127
+ },
128
+ level: {
129
+ type: Number,
130
+ default: 0,
131
+ },
132
+ openOnClick: {
133
+ type: Boolean,
134
+ default: false,
135
+ },
136
+ itemChildren: {
137
+ type: String,
138
+ default: 'children',
139
+ },
140
+ search: {
141
+ type: String,
142
+ default: undefined,
143
+ },
144
+ /**
145
+ * @param {VcsTreeNodeItem} item The item to check the match for.
146
+ * @param {string} search The search value.
147
+ */
148
+ customFilter: {
149
+ type: Function,
150
+ default: undefined,
151
+ },
152
+ },
153
+ emits: ['itemToggled', 'click'],
154
+ setup(props, { emit, slots }) {
155
+ const vm = getCurrentInstance().proxy;
156
+ const iconSize = useIconSize();
157
+ const forwardSlots = getForwardSlots(slots);
158
+
159
+ const isOpen = computed(() => props.opened.includes(props.item.name));
160
+ const children = computed(() => props.item[props.itemChildren] ?? []);
161
+
162
+ const matchFilter = computed(() => {
163
+ if (!props.search) {
164
+ return true;
165
+ }
166
+ if (props.customFilter) {
167
+ return props.customFilter(props.item, props.search);
168
+ }
169
+ const translatedTitle = (item) =>
170
+ item.title ? vm.$t(item.title) : item.name;
171
+
172
+ const hasText = (item) =>
173
+ translatedTitle(item)
174
+ .toLocaleLowerCase()
175
+ .indexOf(props.search.toLocaleLowerCase()) > -1 ||
176
+ item[props.itemChildren]?.some(hasText);
177
+
178
+ return hasText(props.item);
179
+ });
180
+
181
+ return {
182
+ forwardSlots,
183
+ isOpen,
184
+ matchFilter,
185
+ iconSize,
186
+ children,
187
+
188
+ // Bubble up events for the nested tree-items
189
+ bubbleItemToggled: (itemName) => {
190
+ emit('itemToggled', itemName);
191
+ },
192
+ bubbleItemClicked: (item, event) => {
193
+ emit('click', item, event);
194
+ },
195
+ };
196
+ },
197
+ };
198
+ </script>
199
+
200
+ <style lang="scss" scoped>
201
+ .treenode {
202
+ align-items: center !important;
203
+ min-height: calc(var(--v-vcs-font-size) * 2 + 6px);
204
+ }
205
+ @for $i from 0 through 6 {
206
+ .level-#{$i} {
207
+ padding-left: calc(var(--v-vcs-font-size) * $i + 2px);
208
+ &.item {
209
+ & > .prepend:not(:has(.v-icon)) {
210
+ margin-left: calc(var(--v-vcs-font-size) * 2);
211
+ }
212
+ & > .prepend:has(.v-icon) {
213
+ margin-left: 4px;
214
+ }
215
+ }
216
+ }
217
+ }
218
+ .prepend {
219
+ :deep(.v-icon) {
220
+ margin-right: 6px;
221
+ display: flex;
222
+ align-items: start;
223
+ }
224
+ }
225
+ // Indent children padding
226
+ .children {
227
+ padding-left: var(--v-vcs-font-size);
228
+ }
229
+ // set the size of the buttons, except for the ActionButtonList
230
+ :deep(.v-btn):not(.vcs-button) {
231
+ width: var(--v-vcs-font-size);
232
+ height: var(--v-vcs-font-size);
233
+ // for alignment of chevron
234
+ display: flex;
235
+ &.chevron-btn {
236
+ margin-left: 4px;
237
+ margin-right: 8px;
238
+ }
239
+ }
240
+ // remove hover shadow over button
241
+ :deep(.v-btn__overlay) {
242
+ --v-hover-opacity: 0;
243
+ }
244
+ </style>
@@ -0,0 +1,31 @@
1
+ declare const _default: import("vue").DefineSetupFnComponent<Record<string, any>, {}, {}, Record<string, any> & {}, import("vue").PublicProps>;
2
+ export default _default;
3
+ export type VcsTreeNodeItem = {
4
+ name: string;
5
+ /**
6
+ * - An optional translatable title.
7
+ */
8
+ title?: string | undefined;
9
+ tooltip?: string | undefined;
10
+ /**
11
+ * - Whether this item reacts to click events.
12
+ */
13
+ clickable?: boolean | undefined;
14
+ /**
15
+ * - Whether this item should be displayed as disabled.
16
+ */
17
+ disabled?: boolean | undefined;
18
+ actions?: import("../../actions/actionHelper.js", { with: { "resolution-mode": "import" } }).VcsAction[] | undefined;
19
+ /**
20
+ * - An optional array of children. Can be binded to another key, using the `item-children` attributes of the VcsTreeview component.
21
+ */
22
+ children?: VcsTreeNodeItem[] | undefined;
23
+ /**
24
+ * - An optional icon to display with this item. Can be a URL or HTMLElement.
25
+ */
26
+ icon?: string | HTMLCanvasElement | HTMLImageElement | undefined;
27
+ /**
28
+ * - A callback called when the item is clicked.
29
+ */
30
+ clicked?: ((arg0: string) => void) | undefined;
31
+ };
@@ -1,163 +1,82 @@
1
1
  <template>
2
- <div class="d-contents">
2
+ <div class="vcs-treeview">
3
3
  <VcsTreeviewSearchbar
4
4
  v-if="showSearchbar"
5
5
  :placeholder="searchbarPlaceholder"
6
6
  v-model="localSearchValue"
7
7
  />
8
- <v-treeview
9
- class="vcs-treeview"
10
- density="compact"
11
- item-value="name"
12
- :item-props="true"
13
- :custom-filter="handleFilter"
14
- :selectable="false"
15
- :activatable="false"
16
- expand-icon="mdi-chevron-right"
17
- collapse-icon="mdi-chevron-down"
18
- v-bind="{ ...$props, ...$attrs }"
19
- :search="localSearchValue"
20
- @click:select="itemClicked($event.id, $event.event)"
21
- >
22
- <template #title="scope">
23
- <slot name="title" v-bind="scope ?? {}">
24
- <VcsTreeviewTitle :item="scope.item"></VcsTreeviewTitle>
25
- </slot>
26
- </template>
27
- <template v-for="slot of forwardSlots" #[slot]="scope">
28
- <slot :name="slot" v-bind="scope ?? {}" />
29
- </template>
30
- <template #prepend="scope">
31
- <slot name="prepend" v-bind="scope ?? {}">
32
- <template v-if="scope.item?.icon">
33
- <v-icon
34
- v-if="typeof scope.item?.icon === 'string'"
35
- :size="iconSize"
36
- >
37
- {{ scope.item.icon }}
38
- </v-icon>
39
- <ImageElementInjector :element="scope.item.icon" v-else />
40
- </template>
41
- </slot>
42
- </template>
43
- <template #append="scope">
44
- <slot name="append" v-bind="scope ?? {}">
45
- <VcsActionButtonList
46
- v-if="scope.item.actions?.length > 0"
47
- :actions="scope.item.actions"
48
- :overflow-count="3"
49
- :disabled="scope.item.disabled"
50
- right
51
- tooltip-position="right"
52
- block-overflow
53
- class="col-4 pa-0 d-flex align-center"
54
- />
55
- </slot>
56
- </template>
57
- </v-treeview>
8
+ <div v-for="item in items" :key="item.name" class="vcs-treeitem">
9
+ <VcsTreeNode
10
+ class="root-node"
11
+ :item="item"
12
+ :search="localSearchValue"
13
+ v-model:opened="localOpenedItems"
14
+ :custom-filter="customFilter"
15
+ :open-on-click="openOnClick"
16
+ :item-children="itemChildren"
17
+ @item-toggled="itemToggled"
18
+ @click="itemClicked"
19
+ >
20
+ <template v-for="slot of forwardSlots" #[slot]="scope">
21
+ <slot :name="slot" v-bind="scope ?? {}" />
22
+ </template>
23
+ </VcsTreeNode>
24
+ </div>
58
25
  </div>
59
26
  </template>
60
- <style lang="scss" scoped>
61
- :deep(.vcs-treeview) {
62
- // Root Level Entries should be 40px high
63
- > .v-list-item,
64
- > .v-list-group > .v-list-item {
65
- min-height: calc(var(--v-vcs-font-size) * 2 + 14px) !important;
66
- padding-left: 6px;
67
- }
68
- // Border around root nodes with children included
69
- > .v-list-item:not(:last-child),
70
- > .v-list-group:not(:last-child) {
71
- border-bottom: 1px solid rgb(var(--v-theme-base-lighten-2));
72
- }
73
- // Only Group Entries have a bold font
74
- > .v-list-group
75
- > .v-list-item
76
- > .v-list-item__content
77
- > .v-list-item-title {
78
- font-weight: 700 !important;
79
- }
80
- }
81
-
82
- // leaf indent
83
- :deep(.v-list--slim .v-treeview-group.v-list-group) {
84
- --prepend-width: 0px;
85
- }
86
-
87
- // Padding left of root nodes
88
- :deep(.v-list-item__prepend) {
89
- width: var(--v-vcs-font-size);
90
- margin-right: 8px;
91
- > .v-list-item-action > .v-btn {
92
- width: var(--v-vcs-font-size);
93
- height: var(--v-vcs-font-size);
94
- margin: auto;
95
- // for alignment of chevron
96
- display: flex;
97
- }
98
- }
99
-
100
- // Width of prepend for group nodes having two icons (chevron and custom icon)
101
- :deep(.v-list-item__prepend:has(> .v-list-item-action + .v-icon)) {
102
- width: calc(var(--v-vcs-font-size) + 23px);
103
- .v-list-item-action {
104
- margin-right: calc(22px - var(--v-vcs-font-size));
105
- }
106
- }
107
-
108
- // remove hover shadow over button
109
- :deep(.v-btn__overlay) {
110
- --v-hover-opacity: 0;
111
- }
112
- // remove ripple effect
113
- :deep(.v-ripple__container) {
114
- display: none;
115
- }
116
- // hide active class
117
- :deep(.v-list-item__overlay) {
118
- display: none;
119
- }
120
-
121
- .d-contents {
122
- display: contents;
123
- }
124
- </style>
125
27
 
126
28
  <script>
127
- import { getCurrentInstance } from 'vue';
128
- import { VIcon } from 'vuetify/components';
129
- import { VTreeview } from 'vuetify/labs/VTreeview';
130
- import { useProxiedAtomicModel } from '../modelHelper.js';
29
+ import { watch } from 'vue';
30
+ import {
31
+ useProxiedAtomicModel,
32
+ useProxiedComplexModel,
33
+ } from '../modelHelper.js';
131
34
  import { getForwardSlots } from '../composables.js';
132
35
  import VcsTreeviewSearchbar from './VcsTreeviewSearchbar.vue';
133
- import VcsActionButtonList from '../buttons/VcsActionButtonList.vue';
134
- import ImageElementInjector from '../ImageElementInjector.vue';
135
- import VcsTreeviewTitle from './VcsTreeviewTitle.vue';
136
- import { useIconSize } from '../../vuePlugins/vuetify.js';
36
+ import VcsTreeNode from './VcsTreeNode.vue';
137
37
 
138
38
  /**
139
- * @description extends API of https://vuetifyjs.com/en/api/v-treeview/
140
- * Can render dynamic components as leaf items.
141
- * In order to display an item needs to be registered and added to `availableComponents`.
39
+ * @description The VcsTreeview is heavily inspired by the Vuetify VTreeview component. Can render dynamic components as leaf items.
142
40
  * Exposes the `search` value for filtering the treeview.
143
- * @vue-prop {boolean} [showSearchbar=false] - Whether there is a searchbar for this treeview
144
- * @vue-prop {string} [searchbarPlaceholder] - Placeholder text for the searchbar, will be translated
41
+ * Exposes the `opened` model-value for controlling the opened state of the treeview.
42
+ * Forwards the `prepend`, `title` and `append` slots to the VcsTreeNode component.
43
+ * @vue-prop {Array<import("./VcsTreeNode.vue").VcsTreeNodeItem>} items.
44
+ * @vue-prop {Array<import("./VcsTreeNode.vue").VcsTreeNodeItem>} opened - Array of name of opened nodes.
45
+ * @vue-prop {string} [itemChildren='children'] - The property key of the children.
46
+ * @vue-prop {boolean} [openAll=false] - Whether to open all root items on startup.
47
+ * @vue-prop {boolean} [openOnClick=false] - Whether to open items on title click.
48
+ * @vue-prop {string} [search=''] - The value used to filter the items.
49
+ * @vue-prop {boolean} [showSearchbar=false] - Whether there is a searchbar for this treeview.
50
+ * @vue-prop {function(import("./VcsTreeNode.vue").VcsTreeNodeItem, string|undefined):boolean}} [customFilter] - a function to customize filtering when searching.
51
+ * @vue-prop {string} [searchbarPlaceholder] - Placeholder text for the searchbar, will be translated.
145
52
  */
146
53
  export default {
147
54
  name: 'VcsTreeview',
148
55
  components: {
149
- VcsTreeviewTitle,
150
- VIcon,
151
- ImageElementInjector,
152
- VcsActionButtonList,
153
56
  VcsTreeviewSearchbar,
154
- VTreeview,
57
+ VcsTreeNode,
155
58
  },
156
59
  props: {
157
60
  items: {
158
61
  type: Array,
159
62
  default: () => [],
160
63
  },
64
+ opened: {
65
+ type: Array,
66
+ default: () => [],
67
+ },
68
+ itemChildren: {
69
+ type: String,
70
+ default: 'children',
71
+ },
72
+ openAll: {
73
+ type: Boolean,
74
+ default: false,
75
+ },
76
+ openOnClick: {
77
+ type: Boolean,
78
+ default: false,
79
+ },
161
80
  search: {
162
81
  type: String,
163
82
  default: '',
@@ -170,59 +89,78 @@
170
89
  type: String,
171
90
  default: undefined,
172
91
  },
92
+ /**
93
+ * @param {import("./VcsTreeNode.vue").VcsTreeNodeItem} item The item to filter.
94
+ * @param {string} search The query value.
95
+ */
96
+ customFilter: {
97
+ type: Function,
98
+ default: undefined,
99
+ },
173
100
  },
174
- emits: ['update:search'],
101
+ emits: ['update:search', 'update:opened'],
175
102
  setup(props, { emit, slots }) {
103
+ const forwardSlots = getForwardSlots(slots);
176
104
  const localSearchValue = useProxiedAtomicModel(props, 'search', emit);
105
+ const localOpenedItems = useProxiedComplexModel(props, 'opened', emit);
177
106
 
178
- // TODO properly type the tree view item interface & export in index.d.ts
107
+ if ((props.openAll ?? false) !== false) {
108
+ localOpenedItems.value = props.items.map((item) => item.name);
109
+ watch(
110
+ () => props.items,
111
+ (items) => {
112
+ const newItems = items.filter(
113
+ (item) => !localOpenedItems.value.includes(item.name),
114
+ );
115
+ localOpenedItems.value.push(...newItems.map((item) => item.name));
116
+ },
117
+ );
118
+ }
179
119
 
180
- const vm = getCurrentInstance().proxy;
181
- /**
182
- * @param {string} value
183
- * @param {string} q
184
- * @param {Object} item
185
- * @returns {number}
186
- */
187
- const handleFilter = (value, q, item) => {
188
- if (value == null || q == null) {
189
- return -1;
120
+ function itemToggled(itemName) {
121
+ const idx = localOpenedItems.value.indexOf(itemName);
122
+ if (idx >= 0) {
123
+ localOpenedItems.value.splice(idx, 1);
124
+ } else {
125
+ localOpenedItems.value.push(itemName);
190
126
  }
191
- const translatedTitle = item.title ? vm.$st(item.title) : item.value;
192
- return translatedTitle
193
- .toLocaleLowerCase()
194
- .indexOf(q.toLocaleLowerCase());
195
- };
127
+ }
196
128
 
197
- const forwardSlots = getForwardSlots(slots, [
198
- 'append',
199
- 'title',
200
- 'prepend',
201
- ]);
202
- const iconSize = useIconSize();
203
129
  return {
204
- iconSize,
205
130
  localSearchValue,
206
- handleFilter,
131
+ localOpenedItems,
207
132
  forwardSlots,
208
- itemClicked(name, event) {
209
- const items = props.items.slice();
210
- let item;
211
- while (items.length > 0) {
212
- item = items.pop();
213
- if (item.name === name) {
214
- break;
215
- }
216
- if (item.children?.length > 0) {
217
- items.push(...item.children);
133
+ itemToggled,
134
+ itemClicked(item, event) {
135
+ if (item?.clickable) {
136
+ if (item?.clicked && !item?.disabled) {
137
+ item.clicked(event);
218
138
  }
219
- }
220
-
221
- if (item?.clicked && !item?.disabled) {
222
- item.clicked(event);
139
+ } else if ((props.openOnClick ?? false) !== false) {
140
+ itemToggled(item.name);
223
141
  }
224
142
  },
225
143
  };
226
144
  },
227
145
  };
228
146
  </script>
147
+
148
+ <style lang="scss" scoped>
149
+ // Hide node component when not rendered (e.g. filtered by search)
150
+ .vcs-treeitem:not(:has(.vcs-tree-node)) {
151
+ display: none;
152
+ }
153
+ .vcs-treeitem:not(:last-child) {
154
+ border-bottom: 1px solid rgb(var(--v-theme-base-lighten-2));
155
+ }
156
+ .root-node {
157
+ :deep(.level-0) {
158
+ // Root Level Entries should be 40px high
159
+ min-height: calc(var(--v-vcs-font-size) * 2 + 14px) !important;
160
+ }
161
+ // Only Group Entries have a bold font
162
+ > :deep(.group) {
163
+ font-weight: 700 !important;
164
+ }
165
+ }
166
+ </style>