@geode/opengeodeweb-front 10.34.0-rc.16 → 10.34.0-rc.18

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.
@@ -31,6 +31,7 @@ interface LabeledItem {
31
31
  id: string;
32
32
  title?: string;
33
33
  is_active?: boolean;
34
+ category?: string;
34
35
  children?: unknown[];
35
36
  }
36
37
 
@@ -52,6 +53,9 @@ const tooltipTitle = computed<string>(() =>
52
53
  );
53
54
 
54
55
  const tooltipDisabled = computed<boolean>(() => {
56
+ if (actualItem.value.category !== undefined) {
57
+ return false;
58
+ }
55
59
  if (isLeaf !== undefined) {
56
60
  return !isLeaf;
57
61
  }
@@ -1,7 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import GlobalObjects from "@ogw_front/components/Viewer/ObjectTree/Views/GlobalObjects.vue";
3
- import ModelCollections from "@ogw_front/components/Viewer/ObjectTree/Views/ModelCollections.vue";
4
- import ModelComponents from "@ogw_front/components/Viewer/ObjectTree/Views/ModelComponents.vue";
3
+ import ModelTree from "@ogw_front/components/Viewer/ObjectTree/Views/ModelTree.vue";
5
4
  import ViewerObjectTreeBox from "@ogw_front/components/Viewer/ObjectTree/Box.vue";
6
5
  import { geode_objects } from "@ogw_front/assets/geode_objects";
7
6
  import { useAdaptiveStyles } from "@ogw_front/composables/use_adaptive_styles";
@@ -35,10 +34,8 @@ const { adaptiveStyles: activityBarAdaptiveStyles } = useAdaptiveStyles(activity
35
34
 
36
35
  const maxWidth = computed<number>(() => containerWidth * MAX_PANEL_WIDTH_RATIO);
37
36
 
38
- const mainView = computed<TreeViewItem | undefined>(() =>
39
- treeviewStore.opened_views.find((view) => view.id === "main"),
40
- );
41
- const additionalViews = computed<TreeViewItem[]>(() =>
37
+ const mainView = computed(() => treeviewStore.opened_views.find((view) => view.id === "main"));
38
+ const additionalViews = computed(() =>
42
39
  treeviewStore.opened_views.filter((view) => view.id !== "main"),
43
40
  );
44
41
 
@@ -284,11 +281,11 @@ function onVerticalResizeStart(event: MouseEvent, index: number): void {
284
281
  @dragstart="onDragStart(index + 1)"
285
282
  @update:scroll-top="treeviewStore.setScrollTop(view.id, $event)"
286
283
  >
287
- <component
288
- :is="view.viewType === 'model_collections' ? ModelCollections : ModelComponents"
284
+ <ModelTree
289
285
  data-testid="modelComponentsObjectTree"
290
286
  :id="view.modelId || view.id"
291
287
  :view-id="view.id"
288
+ :view-type="view.viewType"
292
289
  @show-menu="emit('show-menu', $event)"
293
290
  />
294
291
  </ViewerObjectTreeBox>
@@ -8,14 +8,16 @@ import ObjectTreeItemLabel from "@ogw_front/components/Viewer/ObjectTree/Base/It
8
8
  import { useHoverhighlight } from "@ogw_front/composables/hover_highlight";
9
9
  import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer";
10
10
  import { useModelCollections } from "@ogw_front/composables/model_collections";
11
+ import { useModelComponents } from "@ogw_front/composables/model_components";
11
12
  import { useTreeviewStore } from "@ogw_front/stores/treeview";
12
13
 
13
14
  interface Props {
14
15
  id: string;
15
16
  viewId?: string;
17
+ viewType?: string;
16
18
  }
17
19
 
18
- const { id, viewId = undefined } = defineProps<Props>();
20
+ const { id, viewId = undefined, viewType = "model_components" } = defineProps<Props>();
19
21
  const actualViewId = viewId || id;
20
22
 
21
23
  interface CollectionTreeItem {
@@ -29,7 +31,6 @@ interface CollectionTreeItem {
29
31
 
30
32
  const { onHoverEnter, onHoverLeave } = useHoverhighlight();
31
33
  const hybridViewerStore = useHybridViewerStore();
32
-
33
34
  interface Emits {
34
35
  "show-menu": [
35
36
  payload: {
@@ -37,7 +38,8 @@ interface Emits {
37
38
  itemId: string;
38
39
  context_type: "model_component" | "model_component_type";
39
40
  modelId: string;
40
- modelComponentType: string | undefined;
41
+ modelComponentType?: string;
42
+ targetComponentIds?: string[];
41
43
  },
42
44
  ];
43
45
  }
@@ -45,15 +47,24 @@ interface Emits {
45
47
  const emit = defineEmits<Emits>();
46
48
 
47
49
  const treeviewStore = useTreeviewStore();
50
+
51
+ const isCollections = computed(() => viewType === "model_collections");
52
+
53
+ const composable = isCollections.value ? useModelCollections(id) : useModelComponents(id);
48
54
  const {
49
55
  items: rawItems,
50
- collectionsCache: componentsCache,
51
56
  localCategories,
52
57
  selection: visibleComponents,
53
58
  updateVisibility,
54
- } = useModelCollections(id);
59
+ } = composable;
55
60
 
56
- const currentView = computed<TreeViewItem | undefined>(() =>
61
+ const cache = computed(
62
+ () =>
63
+ ("componentsCache" in composable ? composable.componentsCache.value : undefined) ??
64
+ ("collectionsCache" in composable ? composable.collectionsCache.value : undefined),
65
+ );
66
+
67
+ const currentView = computed(() =>
57
68
  treeviewStore.opened_views.find((view) => view.id === actualViewId),
58
69
  );
59
70
 
@@ -78,14 +89,16 @@ function onUpdateSelection(newSelection: string[]): void {
78
89
  updateVisibility(finalSelection as string[]);
79
90
  }
80
91
 
81
- const visibleSelection = computed<string[]>(() => applySearchFilter(visibleComponents.value, []));
92
+ const visibleSelection = computed<string[]>(
93
+ () => applySearchFilter(visibleComponents.value, []) as string[],
94
+ );
82
95
 
83
96
  const itemsForTreeView = computed<CollectionTreeItem[]>(() => {
84
- if (search.value && componentsCache.value) {
97
+ if (search.value && cache.value) {
85
98
  const query = search.value.toLowerCase();
86
99
  const result: CollectionTreeItem[] = [];
87
- for (const type of Object.keys(componentsCache.value)) {
88
- const matches = (componentsCache.value[type] ?? []).filter(
100
+ for (const type of Object.keys(cache.value)) {
101
+ const matches = (cache.value[type] ?? []).filter(
89
102
  (component: { title: string; id: string }) =>
90
103
  component.title.toLowerCase().includes(query) ||
91
104
  component.id.toLowerCase().includes(query),
@@ -108,7 +121,7 @@ const itemsForTreeView = computed<CollectionTreeItem[]>(() => {
108
121
  ...category,
109
122
  id: categoryId,
110
123
  children: sortAndFormatItems(
111
- componentsCache.value?.[categoryId],
124
+ cache.value?.[categoryId],
112
125
  sortType.value,
113
126
  ) as unknown as CollectionTreeItem[],
114
127
  });
@@ -118,12 +131,16 @@ const itemsForTreeView = computed<CollectionTreeItem[]>(() => {
118
131
 
119
132
  function showContextMenu(event: unknown, item: CollectionTreeItem): void {
120
133
  const actualItem = item.raw || item;
134
+ const typeId = actualItem.category || actualItem.id;
135
+ const typeItem = itemsForTreeView.value.find((type) => type.id === typeId);
136
+ const targetComponentIds = typeItem?.children?.map((child) => child.id);
121
137
  emit("show-menu", {
122
138
  event,
123
139
  itemId: actualItem.category ? actualItem.id : id,
124
140
  context_type: actualItem.category ? "model_component" : "model_component_type",
125
141
  modelId: id,
126
142
  modelComponentType: actualItem.category ? undefined : actualItem.id,
143
+ targetComponentIds,
127
144
  });
128
145
  }
129
146
 
@@ -151,7 +168,6 @@ function handleHoverEnter({
151
168
  }
152
169
 
153
170
  const viewerIdsToHover = extractIds(actualItem);
154
-
155
171
  onHoverEnter(id, () => viewerIdsToHover, "model", immediate);
156
172
  }
157
173
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geode/opengeodeweb-front",
3
- "version": "10.34.0-rc.16",
3
+ "version": "10.34.0-rc.18",
4
4
  "description": "OpenSource Vue/Nuxt/Pinia/Vuetify framework for web applications",
5
5
  "homepage": "https://github.com/Geode-solutions/OpenGeodeWeb-Front",
6
6
  "bugs": {
@@ -1,277 +0,0 @@
1
- <script setup lang="ts">
2
- import { sortAndFormatItems, useTreeFilter } from "@ogw_front/composables/tree_filter";
3
- import CommonTreeView from "@ogw_front/components/Viewer/ObjectTree/Base/CommonTreeView.vue";
4
- import type { DisplayItem } from "@ogw_front/composables/virtual_tree";
5
- import FetchingData from "@ogw_front/components/FetchingData.vue";
6
- import ObjectTreeControls from "@ogw_front/components/Viewer/ObjectTree/Base/Controls.vue";
7
- import ObjectTreeItemLabel from "@ogw_front/components/Viewer/ObjectTree/Base/ItemLabel.vue";
8
- import { useHoverhighlight } from "@ogw_front/composables/hover_highlight";
9
- import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer";
10
- import { useModelComponents } from "@ogw_front/composables/model_components";
11
- import { useTreeviewStore } from "@ogw_front/stores/treeview";
12
-
13
- interface Props {
14
- id: string;
15
- viewId?: string;
16
- }
17
-
18
- const { id, viewId = undefined } = defineProps<Props>();
19
- const actualViewId = viewId || id;
20
-
21
- interface TreeViewItem {
22
- raw?: TreeViewItem;
23
- id: string;
24
- category?: string;
25
- children?: TreeViewItem[];
26
- viewer_id?: string;
27
- title?: string;
28
- }
29
-
30
- const { onHoverEnter, onHoverLeave } = useHoverhighlight();
31
- const hybridViewerStore = useHybridViewerStore();
32
-
33
- interface Emits {
34
- "show-menu": [
35
- payload: {
36
- event: unknown;
37
- itemId: string;
38
- context_type: "model_component" | "model_component_type";
39
- modelId: string;
40
- modelComponentType?: string;
41
- targetComponentIds?: string[];
42
- },
43
- ];
44
- }
45
-
46
- const emit = defineEmits<Emits>();
47
-
48
- const treeviewStore = useTreeviewStore();
49
- const {
50
- items: rawItems,
51
- componentsCache,
52
- localCategories,
53
- selection: visibleComponents,
54
- updateVisibility,
55
- } = useModelComponents(id);
56
-
57
- const currentView = computed<TreeViewItem | undefined>(() =>
58
- treeviewStore.opened_views.find((view) => view.id === actualViewId),
59
- );
60
-
61
- const opened = computed<string[]>({
62
- get: () => currentView.value?.opened || [],
63
- set: (val) => treeviewStore.setOpened(actualViewId, val),
64
- });
65
-
66
- const {
67
- search,
68
- sortType,
69
- filterOptions,
70
- processedItems: filteredCategories,
71
- availableFilterOptions,
72
- toggleSort,
73
- customFilter,
74
- applySearchFilter,
75
- } = useTreeFilter(localCategories);
76
-
77
- function onUpdateSelection(newSelection: string[]): void {
78
- const finalSelection = applySearchFilter(newSelection, visibleComponents.value);
79
- updateVisibility(finalSelection as string[]);
80
- }
81
-
82
- const visibleSelection = computed<string[]>(() => applySearchFilter(visibleComponents.value, []));
83
-
84
- const itemsForTreeView = computed<TreeViewItem[]>(() => {
85
- if (search.value && componentsCache.value) {
86
- const query = search.value.toLowerCase();
87
- const result: TreeViewItem[] = [];
88
- for (const type of Object.keys(componentsCache.value)) {
89
- const matches = (componentsCache.value[type] ?? []).filter(
90
- (component: { title: string; id: string }) =>
91
- component.title.toLowerCase().includes(query) ||
92
- component.id.toLowerCase().includes(query),
93
- );
94
- if (matches.length > 0) {
95
- result.push({
96
- id: type,
97
- title: `${type}s (${matches.length})`,
98
- children: sortAndFormatItems(matches, sortType.value) as unknown as TreeViewItem[],
99
- });
100
- }
101
- }
102
- return result;
103
- }
104
-
105
- const result: TreeViewItem[] = [];
106
- for (const category of filteredCategories.value) {
107
- const categoryId = category.id as string;
108
- result.push({
109
- ...category,
110
- id: categoryId,
111
- children: sortAndFormatItems(
112
- componentsCache.value?.[categoryId],
113
- sortType.value,
114
- ) as unknown as TreeViewItem[],
115
- });
116
- }
117
- return result;
118
- });
119
-
120
- function showContextMenu(event: unknown, item: TreeViewItem): void {
121
- const actualItem = item.raw || item;
122
- const typeId = actualItem.category || actualItem.id;
123
- const typeItem = itemsForTreeView.value.find((type) => type.id === typeId);
124
- const targetComponentIds = typeItem
125
- ? (typeItem.children ?? []).map((child) => child.id)
126
- : undefined;
127
- emit("show-menu", {
128
- event,
129
- itemId: actualItem.category ? actualItem.id : id,
130
- context_type: actualItem.category ? "model_component" : "model_component_type",
131
- modelId: id,
132
- modelComponentType: actualItem.category ? undefined : actualItem.id,
133
- targetComponentIds,
134
- });
135
- }
136
-
137
- function handleHoverEnter({
138
- item,
139
- immediate = false,
140
- }: {
141
- item: TreeViewItem;
142
- immediate?: boolean;
143
- }): void {
144
- const actualItem = item.raw || item;
145
-
146
- if (!actualItem.category && (!actualItem.children || actualItem.children.length === 0)) {
147
- return;
148
- }
149
-
150
- onHoverEnter(
151
- id,
152
- () =>
153
- actualItem.category
154
- ? [Number(actualItem.viewer_id)]
155
- : (actualItem.children ?? []).map((child) => Number(child.viewer_id)),
156
- "model",
157
- immediate,
158
- );
159
- }
160
-
161
- function handleHoverLeave(): void {
162
- onHoverLeave(id);
163
- }
164
-
165
- // CommonTreeView's #title/#append slots hand back the generic tree item it renders internally (a TreeItem); this view's tree is always built from TreeViewItem nodes (see itemsForTreeView above), so it's safe to view the slot item through that lens here.
166
- function asTreeViewItem(item: unknown): TreeViewItem {
167
- return item as unknown as TreeViewItem;
168
- }
169
-
170
- // The focusCameraOnObject composable's block_ids parameter is declared as string[], but this view (like the sibling ModelCollections view) has always focused the camera using the raw viewer_id values collected here; that pre-dates this typing pass, so the ids are passed through as-is rather than changed here.
171
- function getFocusBlockIds(item: TreeViewItem): string[] {
172
- const ids = item.category
173
- ? [item.viewer_id]
174
- : (item.children ?? []).map((child) => child.viewer_id);
175
- return ids as unknown as string[];
176
- }
177
-
178
- function expandAll(): void {
179
- const allIds: string[] = [];
180
- function traverse(itemsList: TreeViewItem[]): void {
181
- for (const item of itemsList) {
182
- if (item.children && item.children.length > 0) {
183
- allIds.push(item.id);
184
- traverse(item.children);
185
- }
186
- }
187
- }
188
- traverse(itemsForTreeView.value);
189
- opened.value = allIds;
190
- }
191
- </script>
192
-
193
- <template>
194
- <div class="tree-view-container">
195
- <ObjectTreeControls
196
- v-model:search="search"
197
- :sort-type="sortType"
198
- :filter-options="filterOptions"
199
- :available-filter-options="availableFilterOptions"
200
- :is-collapsed="opened.length === 0"
201
- @toggle-sort="toggleSort"
202
- @collapse-all="opened = []"
203
- @expand-all="expandAll"
204
- />
205
-
206
- <FetchingData v-if="rawItems === undefined" :size="48" :width="4" text="" />
207
-
208
- <CommonTreeView
209
- :selected="visibleSelection"
210
- v-model:opened="opened"
211
- v-model:active="treeviewStore.activeItems"
212
- :items="itemsForTreeView"
213
- :options="{
214
- selection: { selectable: true, strategy: 'classic' },
215
- search,
216
- customFilter,
217
- }"
218
- :scroll-top="currentView?.scrollTop || 0"
219
- class="transparent-treeview virtual-tree-height"
220
- @update:selected="(val) => onUpdateSelection(val as string[])"
221
- @click:item="onUpdateSelection([$event.id as string, ...visibleComponents])"
222
- @update:scroll-top="treeviewStore.setScrollTop(actualViewId, $event)"
223
- @hover:enter="({ item }) => handleHoverEnter({ item: item as unknown as TreeViewItem })"
224
- @hover:leave="handleHoverLeave"
225
- @contextmenu="showContextMenu($event.event, $event.item as unknown as TreeViewItem)"
226
- >
227
- <template #title="{ item, isLeaf }">
228
- <ObjectTreeItemLabel
229
- :item="item as unknown as DisplayItem"
230
- :is-leaf="isLeaf"
231
- show-tooltip
232
- class="text-body-1"
233
- @contextmenu="showContextMenu($event, item as unknown as TreeViewItem)"
234
- />
235
- </template>
236
-
237
- <template #append="{ item: rawItem }">
238
- <v-btn
239
- v-if="
240
- asTreeViewItem(rawItem).category ||
241
- (asTreeViewItem(rawItem).children && asTreeViewItem(rawItem).children!.length > 0)
242
- "
243
- icon="mdi-target"
244
- size="medium"
245
- variant="text"
246
- v-tooltip="'Focus camera on object'"
247
- @click.stop="
248
- hybridViewerStore.focusCameraOnObject(id, getFocusBlockIds(asTreeViewItem(rawItem)))
249
- "
250
- />
251
- </template>
252
- </CommonTreeView>
253
- </div>
254
- </template>
255
-
256
- <style scoped>
257
- .tree-view-container {
258
- height: 100%;
259
- display: flex;
260
- flex-direction: column;
261
- overflow: hidden;
262
- min-height: 0;
263
- }
264
-
265
- .virtual-tree-height {
266
- flex-grow: 1;
267
- min-height: 0;
268
- }
269
-
270
- .transparent-treeview {
271
- background-color: transparent;
272
- }
273
-
274
- :deep(.v-list-item__overlay) {
275
- display: none !important;
276
- }
277
- </style>