@geode/opengeodeweb-front 10.20.1 → 10.21.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 (45) hide show
  1. package/app/components/CrsSelector.vue +3 -3
  2. package/app/components/ExtensionSelector.vue +3 -3
  3. package/app/components/FileSelector.vue +3 -3
  4. package/app/components/FileUploader.vue +4 -4
  5. package/app/components/Inspector/InspectionButton.vue +3 -3
  6. package/app/components/Loading.vue +27 -0
  7. package/app/components/MissingFilesSelector.vue +3 -3
  8. package/app/components/ObjectSelector.vue +3 -3
  9. package/app/components/PackagesVersions.vue +4 -4
  10. package/app/components/Viewer/ObjectTree/Layout.vue +6 -3
  11. package/app/components/Viewer/ObjectTree/Views/GlobalObjects.vue +47 -1
  12. package/app/components/Viewer/ObjectTree/Views/ModelCollections.vue +222 -0
  13. package/app/components/Viewer/ObjectTree/Views/ModelComponents.vue +10 -4
  14. package/app/components/Viewer/Options/AttributeSelector.vue +3 -3
  15. package/app/components/Viewer/Options/TextureItem.vue +4 -4
  16. package/app/composables/model_collections.js +72 -0
  17. package/app/composables/model_components.js +5 -1
  18. package/app/composables/project_manager.js +5 -5
  19. package/app/composables/virtual_tree.js +8 -7
  20. package/app/stores/{geode.js → back.js} +16 -1
  21. package/app/stores/data.js +39 -111
  22. package/app/stores/data_helpers/collections.js +102 -0
  23. package/app/stores/data_helpers/mesh.js +122 -0
  24. package/app/stores/treeview.js +18 -8
  25. package/app/stores/viewer.js +18 -0
  26. package/app/utils/extension.js +0 -2
  27. package/app/utils/import_workflow.js +3 -3
  28. package/internal/stores/hybrid_viewer_camera_animation.js +24 -2
  29. package/package.json +3 -3
  30. package/tests/integration/setup.js +3 -3
  31. package/tests/unit/components/CrsSelector.nuxt.test.js +6 -6
  32. package/tests/unit/components/ExtensionSelector.nuxt.test.js +4 -4
  33. package/tests/unit/components/FileSelector.nuxt.test.js +3 -3
  34. package/tests/unit/components/FileUploader.nuxt.test.js +3 -3
  35. package/tests/unit/components/Inspector/InspectionButton.nuxt.test.js +4 -4
  36. package/tests/unit/components/MissingFilesSelector.nuxt.test.js +4 -4
  37. package/tests/unit/components/ObjectSelector.nuxt.test.js +3 -3
  38. package/tests/unit/components/PackagesVersions.nuxt.test.js +3 -3
  39. package/tests/unit/composables/api_fetch.nuxt.test.js +9 -9
  40. package/tests/unit/composables/project_manager.nuxt.test.js +3 -3
  41. package/tests/unit/composables/run_function_when_microservices_connected.nuxt.test.js +7 -7
  42. package/tests/unit/composables/upload_file.nuxt.test.js +7 -7
  43. package/tests/unit/stores/app.nuxt.test.js +9 -9
  44. package/tests/unit/stores/{geode.nuxt.test.js → back.nuxt.test.js} +43 -43
  45. package/tests/unit/stores/infra.nuxt.test.js +34 -34
@@ -1,6 +1,6 @@
1
1
  <script setup>
2
2
  import schemas from "@geode/opengeodeweb-back/opengeodeweb_back_schemas.json";
3
- import { useGeodeStore } from "@ogw_front/stores/geode";
3
+ import { useBackStore } from "@ogw_front/stores/back";
4
4
 
5
5
  const schema = schemas.opengeodeweb_back.geographic_coordinate_systems;
6
6
 
@@ -16,7 +16,7 @@ const data_table_loading = ref(false);
16
16
  const crs_list = ref([]);
17
17
  const selected_crs = ref([]);
18
18
  const toggle_loading = useToggle(data_table_loading);
19
- const geodeStore = useGeodeStore();
19
+ const backStore = useBackStore();
20
20
 
21
21
  watch(selected_crs, (new_value) => {
22
22
  const crs = get_selected_crs(new_value[0]);
@@ -38,7 +38,7 @@ function get_selected_crs(crs_code) {
38
38
  async function get_crs_table() {
39
39
  const params = { geode_object_type };
40
40
  toggle_loading();
41
- await geodeStore.request(schema, params, {
41
+ await backStore.request(schema, params, {
42
42
  response_function: (response) => {
43
43
  crs_list.value = response.crs_list;
44
44
  },
@@ -2,7 +2,7 @@
2
2
  import schemas from "@geode/opengeodeweb-back/opengeodeweb_back_schemas.json";
3
3
 
4
4
  import FetchingData from "@ogw_front/components/FetchingData";
5
- import { useGeodeStore } from "@ogw_front/stores/geode";
5
+ import { useBackStore } from "@ogw_front/stores/back";
6
6
 
7
7
  const schema = schemas.opengeodeweb_back.geode_objects_and_output_extensions;
8
8
  const emit = defineEmits(["update_values", "increment_step", "decrement_step"]);
@@ -19,10 +19,10 @@ const toggle_loading = useToggle(loading);
19
19
  async function get_output_file_extensions() {
20
20
  toggle_loading();
21
21
  geode_objects_and_output_extensions.value = {};
22
- const geodeStore = useGeodeStore();
22
+ const backStore = useBackStore();
23
23
  const values = await Promise.all(
24
24
  filenames.map(async (filename) => {
25
- const response = await geodeStore.request(schema, {
25
+ const response = await backStore.request(schema, {
26
26
  geode_object_type,
27
27
  filename,
28
28
  });
@@ -3,7 +3,7 @@ import schemas from "@geode/opengeodeweb-back/opengeodeweb_back_schemas.json";
3
3
 
4
4
  import FetchingData from "@ogw_front/components/FetchingData";
5
5
  import FileUploader from "@ogw_front/components/FileUploader";
6
- import { useGeodeStore } from "@ogw_front/stores/geode";
6
+ import { useBackStore } from "@ogw_front/stores/back";
7
7
 
8
8
  const schema = schemas.opengeodeweb_back.allowed_files;
9
9
 
@@ -46,8 +46,8 @@ function files_uploaded_event(value) {
46
46
 
47
47
  async function get_allowed_files() {
48
48
  toggle_loading();
49
- const geodeStore = useGeodeStore();
50
- const response = await geodeStore.request(schema, {});
49
+ const backStore = useBackStore();
50
+ const response = await backStore.request(schema, {});
51
51
  accept.value = response.extensions.map((extension) => `.${extension}`).join(",");
52
52
  toggle_loading();
53
53
  }
@@ -1,5 +1,5 @@
1
1
  <script setup>
2
- import { useGeodeStore } from "@ogw_front/stores/geode";
2
+ import { useBackStore } from "@ogw_front/stores/back";
3
3
 
4
4
  import CsvPreviewer from "@ogw_front/components/csv-preview/CsvPreviewer";
5
5
  import DragAndDrop from "@ogw_front/components/DragAndDrop";
@@ -15,7 +15,7 @@ const { multiple, accept, files, auto_upload, showOverlay, mini } = defineProps(
15
15
  mini: { type: Boolean, default: false },
16
16
  });
17
17
 
18
- const geodeStore = useGeodeStore();
18
+ const backStore = useBackStore();
19
19
  const internal_files = ref(files);
20
20
  const dragAndDropRef = useTemplateRef("dragAndDropRef");
21
21
  const csv_dialog = ref(false);
@@ -49,7 +49,7 @@ async function onCsvConfirm(result) {
49
49
  });
50
50
 
51
51
  current_csv_file.value.isConfigured = true;
52
- await geodeStore.upload(json_file);
52
+ await backStore.upload(json_file);
53
53
  internal_files.value = [...internal_files.value];
54
54
  csv_dialog.value = false;
55
55
  }
@@ -72,7 +72,7 @@ function removeFile(index) {
72
72
 
73
73
  async function upload_files() {
74
74
  toggle_loading();
75
- const promise_array = internal_files.value.map((file) => geodeStore.upload(file));
75
+ const promise_array = internal_files.value.map((file) => backStore.upload(file));
76
76
  await Promise.all(promise_array);
77
77
  files_uploaded.value = true;
78
78
  toggle_loading();
@@ -1,6 +1,6 @@
1
1
  <script setup>
2
2
  import schemas from "@geode/opengeodeweb-back/opengeodeweb_back_schemas.json";
3
- import { useGeodeStore } from "@ogw_front/stores/geode";
3
+ import { useBackStore } from "@ogw_front/stores/back";
4
4
 
5
5
  const schema = schemas.opengeodeweb_back.inspect_file;
6
6
 
@@ -18,9 +18,9 @@ async function get_inspection_results() {
18
18
  geode_object_type,
19
19
  filename,
20
20
  };
21
- const geodeStore = useGeodeStore();
21
+ const backStore = useBackStore();
22
22
 
23
- await geodeStore.request(schema, params, {
23
+ await backStore.request(schema, params, {
24
24
  response_function: (response) => {
25
25
  emit("update_values", {
26
26
  inspection_result: [response.inspection_result],
@@ -1,4 +1,7 @@
1
1
  <script setup>
2
+ import { Status } from "@ogw_front/utils/status";
3
+ import { useInfraStore } from "@ogw_front/stores/infra";
4
+
2
5
  const { logo, appName } = defineProps({
3
6
  logo: {
4
7
  type: String,
@@ -10,6 +13,12 @@ const { logo, appName } = defineProps({
10
13
  },
11
14
  });
12
15
 
16
+ const infraStore = useInfraStore();
17
+
18
+ const extensionStores = computed(() =>
19
+ infraStore.microservices.filter((store) => store.$id !== "back" && store.$id !== "viewer"),
20
+ );
21
+
13
22
  const show = ref(false);
14
23
  const progress = ref(0);
15
24
 
@@ -76,6 +85,24 @@ onUnmounted(() => {
76
85
  <LoadingHeader :logo="logo" />
77
86
  <LoadingEcoMessages :app-name="appName" />
78
87
  <LoadingProgress :progress="progress" />
88
+
89
+ <div class="d-flex flex-wrap justify-center gap-4 w-100 mt-4">
90
+ <v-chip
91
+ v-for="store in extensionStores"
92
+ :key="store.$id"
93
+ :color="store.status === Status.CONNECTED ? 'success' : 'primary'"
94
+ variant="flat"
95
+ >
96
+ <v-icon
97
+ start
98
+ :icon="
99
+ store.status === Status.CONNECTED ? 'mdi-check-circle' : 'mdi-loading mdi-spin'
100
+ "
101
+ />
102
+ {{ store.$id.charAt(0).toUpperCase() + store.$id.slice(1) }}
103
+ </v-chip>
104
+ </div>
105
+
79
106
  <LoadingFooter />
80
107
  </div>
81
108
  </div>
@@ -3,7 +3,7 @@ import schemas from "@geode/opengeodeweb-back/opengeodeweb_back_schemas.json";
3
3
 
4
4
  import FetchingData from "@ogw_front/components/FetchingData";
5
5
  import FileUploader from "@ogw_front/components/FileUploader";
6
- import { useGeodeStore } from "@ogw_front/stores/geode";
6
+ import { useBackStore } from "@ogw_front/stores/back";
7
7
 
8
8
  const schema = schemas.opengeodeweb_back.missing_files;
9
9
 
@@ -33,7 +33,7 @@ async function missing_files() {
33
33
  has_missing_files.value = false;
34
34
  mandatory_files.value = [];
35
35
  additional_files.value = [];
36
- const geodeStore = useGeodeStore();
36
+ const backStore = useBackStore();
37
37
 
38
38
  const promise_array = filenames.map((filename) => {
39
39
  const isCsvFile =
@@ -45,7 +45,7 @@ async function missing_files() {
45
45
  additional_files: [],
46
46
  });
47
47
  }
48
- return geodeStore.request(schema, { geode_object_type, filename });
48
+ return backStore.request(schema, { geode_object_type, filename });
49
49
  });
50
50
  const values = await Promise.all(promise_array);
51
51
  for (const value of values) {
@@ -2,7 +2,7 @@
2
2
  import FetchingData from "@ogw_front/components/FetchingData.vue";
3
3
  import { geode_objects } from "@ogw_front/assets/geode_objects";
4
4
  import schemas from "@geode/opengeodeweb-back/opengeodeweb_back_schemas.json";
5
- import { useGeodeStore } from "@ogw_front/stores/geode";
5
+ import { useBackStore } from "@ogw_front/stores/back";
6
6
 
7
7
  const schema = schemas.opengeodeweb_back.allowed_objects;
8
8
 
@@ -12,7 +12,7 @@ const { filenames } = defineProps({
12
12
  filenames: { type: Array, required: true },
13
13
  });
14
14
 
15
- const geodeStore = useGeodeStore();
15
+ const backStore = useBackStore();
16
16
 
17
17
  const loading = ref(false);
18
18
  const allowed_objects = ref({});
@@ -52,7 +52,7 @@ async function get_allowed_objects() {
52
52
  toggle_loading();
53
53
  allowed_objects.value = {};
54
54
 
55
- const promise_array = filenames.map((filename) => geodeStore.request(schema, { filename }));
55
+ const promise_array = filenames.map((filename) => backStore.request(schema, { filename }));
56
56
  const responses = await Promise.all(promise_array);
57
57
  const allowed_objects_list = responses.map((response) => response.allowed_objects);
58
58
  const all_keys = [...new Set(allowed_objects_list.flatMap((obj) => Object.keys(obj)))];
@@ -1,16 +1,16 @@
1
1
  <script setup>
2
2
  import { Status } from "@ogw_front/utils/status";
3
- import { useGeodeStore } from "@ogw_front/stores/geode";
3
+ import { useBackStore } from "@ogw_front/stores/back";
4
4
 
5
5
  const { schema } = defineProps({
6
6
  schema: { type: Object, required: true },
7
7
  });
8
8
 
9
- const geodeStore = useGeodeStore();
9
+ const backStore = useBackStore();
10
10
  const packages_versions = ref([]);
11
11
 
12
12
  async function get_packages_versions() {
13
- await geodeStore.request(
13
+ await backStore.request(
14
14
  schema,
15
15
  {},
16
16
  {
@@ -22,7 +22,7 @@ async function get_packages_versions() {
22
22
  }
23
23
 
24
24
  watch(
25
- () => geodeStore.status,
25
+ () => backStore.status,
26
26
  (value) => {
27
27
  if (value === Status.CONNECTED) {
28
28
  get_packages_versions();
@@ -1,5 +1,6 @@
1
1
  <script setup>
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";
3
4
  import ModelComponents from "@ogw_front/components/Viewer/ObjectTree/Views/ModelComponents.vue";
4
5
  import ViewerObjectTreeBox from "@ogw_front/components/Viewer/ObjectTree/Box.vue";
5
6
  import { geode_objects } from "@ogw_front/assets/geode_objects";
@@ -263,15 +264,17 @@ function onVerticalResizeStart(event, index) {
263
264
  :icon="geode_objects[view.geode_object_type]?.image"
264
265
  :scroll-top="view.scrollTop"
265
266
  closable
266
- :border-radius="index === additionalViews.length - 1 ? '0 16px 16px 0' : '0'"
267
+ :border-radius="`0 ${index === 0 ? '16px' : '0'} ${index === additionalViews.length - 1 ? '16px' : '0'} 0`"
267
268
  :border-left="false"
268
269
  @close="treeviewStore.closeView(view.id)"
269
270
  @dragstart="onDragStart(index + 1)"
270
271
  @update:scroll-top="treeviewStore.setScrollTop(view.id, $event)"
271
272
  >
272
- <ModelComponents
273
+ <component
274
+ :is="view.viewType === 'model_collections' ? ModelCollections : ModelComponents"
273
275
  data-testid="modelComponentsObjectTree"
274
- :id="view.id"
276
+ :id="view.modelId || view.id"
277
+ :view-id="view.id"
275
278
  @show-menu="emit('show-menu', $event)"
276
279
  />
277
280
  </ViewerObjectTreeBox>
@@ -75,6 +75,31 @@ function isModel(item) {
75
75
  );
76
76
  }
77
77
 
78
+ const hasCollectionsMap = reactive({});
79
+
80
+ watch(
81
+ () => treeviewStore.items,
82
+ async (newItems) => {
83
+ const models = newItems
84
+ .flatMap((group) => group.children || [])
85
+ .filter((item) => isModel(item));
86
+ const fetchPromises = models.map(async (model) => {
87
+ if (hasCollectionsMap[model.id] === undefined) {
88
+ hasCollectionsMap[model.id] = false;
89
+ try {
90
+ const hasCollections = await dataStore.hasCollectionComponents(model.id);
91
+ hasCollectionsMap[model.id] = hasCollections;
92
+ } catch (error) {
93
+ console.error("Failed to check collections", error);
94
+ }
95
+ }
96
+ });
97
+
98
+ await Promise.all(fetchPromises);
99
+ },
100
+ { immediate: true, deep: true },
101
+ );
102
+
78
103
  function handleHoverEnter({ item, immediate = false }) {
79
104
  const actualItem = item.raw || item;
80
105
 
@@ -169,7 +194,28 @@ function expandAll() {
169
194
  variant="text"
170
195
  v-tooltip="'Model\'s mesh components'"
171
196
  @click.stop="
172
- treeviewStore.displayAdditionalTree(item.id, item.title, item.geode_object_type)
197
+ treeviewStore.displayAdditionalTree(
198
+ item.id,
199
+ item.title,
200
+ item.geode_object_type,
201
+ 'model_components',
202
+ )
203
+ "
204
+ />
205
+ <v-btn
206
+ v-if="isModel(item) && hasCollectionsMap[item.id]"
207
+ icon="mdi-format-list-group"
208
+ size="medium"
209
+ class="ml-2"
210
+ variant="text"
211
+ v-tooltip="'Model\'s collections'"
212
+ @click.stop="
213
+ treeviewStore.displayAdditionalTree(
214
+ item.id,
215
+ item.title,
216
+ item.geode_object_type,
217
+ 'model_collections',
218
+ )
173
219
  "
174
220
  />
175
221
  </template>
@@ -0,0 +1,222 @@
1
+ <script setup>
2
+ import { sortAndFormatItems, useTreeFilter } from "@ogw_front/composables/tree_filter";
3
+ import CommonTreeView from "@ogw_front/components/Viewer/ObjectTree/Base/CommonTreeView.vue";
4
+ import FetchingData from "@ogw_front/components/FetchingData.vue";
5
+ import ObjectTreeControls from "@ogw_front/components/Viewer/ObjectTree/Base/Controls.vue";
6
+ import ObjectTreeItemLabel from "@ogw_front/components/Viewer/ObjectTree/Base/ItemLabel.vue";
7
+ import { useHoverhighlight } from "@ogw_front/composables/hover_highlight";
8
+ import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer";
9
+ import { useModelCollections } from "@ogw_front/composables/model_collections";
10
+ import { useTreeviewStore } from "@ogw_front/stores/treeview";
11
+
12
+ const { id, viewId } = defineProps({
13
+ id: { type: String, required: true },
14
+ viewId: { type: String, required: false },
15
+ });
16
+ const actualViewId = viewId || id;
17
+ const { onHoverEnter, onHoverLeave } = useHoverhighlight();
18
+ const hybridViewerStore = useHybridViewerStore();
19
+ const emit = defineEmits(["show-menu"]);
20
+
21
+ const treeviewStore = useTreeviewStore();
22
+ const {
23
+ items: rawItems,
24
+ collectionsCache: componentsCache,
25
+ localCategories,
26
+ selection: visibleComponents,
27
+ updateVisibility,
28
+ } = useModelCollections(id);
29
+
30
+ const currentView = computed(() =>
31
+ treeviewStore.opened_views.find((view) => view.id === actualViewId),
32
+ );
33
+
34
+ const opened = computed({
35
+ get: () => currentView.value?.opened || [],
36
+ set: (val) => treeviewStore.setOpened(actualViewId, val),
37
+ });
38
+
39
+ const {
40
+ search,
41
+ sortType,
42
+ filterOptions,
43
+ processedItems: filteredCategories,
44
+ availableFilterOptions,
45
+ toggleSort,
46
+ customFilter,
47
+ applySearchFilter,
48
+ } = useTreeFilter(localCategories);
49
+
50
+ function onUpdateSelection(newSelection) {
51
+ const finalSelection = applySearchFilter(newSelection, visibleComponents.value);
52
+ updateVisibility(finalSelection);
53
+ }
54
+
55
+ const visibleSelection = computed(() => applySearchFilter(visibleComponents.value, []));
56
+
57
+ const itemsForTreeView = computed(() => {
58
+ if (search.value && componentsCache.value) {
59
+ const query = search.value.toLowerCase();
60
+ const result = [];
61
+ for (const type of Object.keys(componentsCache.value)) {
62
+ const matches = componentsCache.value[type].filter(
63
+ (component) =>
64
+ component.title.toLowerCase().includes(query) ||
65
+ component.id.toLowerCase().includes(query),
66
+ );
67
+ if (matches.length > 0) {
68
+ result.push({
69
+ id: type,
70
+ title: `${type}s (${matches.length})`,
71
+ children: sortAndFormatItems(matches, sortType.value),
72
+ });
73
+ }
74
+ }
75
+ return result;
76
+ }
77
+
78
+ const result = [];
79
+ for (const category of filteredCategories.value) {
80
+ result.push({
81
+ ...category,
82
+ children: sortAndFormatItems(componentsCache.value?.[category.id], sortType.value),
83
+ });
84
+ }
85
+ return result;
86
+ });
87
+
88
+ function showContextMenu(event, item) {
89
+ const actualItem = item.raw || item;
90
+ emit("show-menu", {
91
+ event,
92
+ itemId: actualItem.category ? actualItem.id : id,
93
+ context_type: actualItem.category ? "model_component" : "model_component_type",
94
+ modelId: id,
95
+ modelComponentType: actualItem.category ? undefined : actualItem.id,
96
+ });
97
+ }
98
+
99
+ function handleHoverEnter({ item, immediate = false }) {
100
+ const actualItem = item.raw || item;
101
+
102
+ if (!actualItem.category && (!actualItem.children || actualItem.children.length === 0)) {
103
+ return;
104
+ }
105
+
106
+ const viewerIdsToHover = extractIds(actualItem);
107
+
108
+ onHoverEnter(id, () => viewerIdsToHover, "model", immediate);
109
+ }
110
+
111
+ function handleHoverLeave() {
112
+ onHoverLeave(id);
113
+ }
114
+
115
+ function expandAll() {
116
+ const allIds = [];
117
+ function traverse(itemsList) {
118
+ for (const item of itemsList) {
119
+ if (item.children && item.children.length > 0) {
120
+ allIds.push(item.id);
121
+ traverse(item.children);
122
+ }
123
+ }
124
+ }
125
+ traverse(itemsForTreeView.value);
126
+ opened.value = allIds;
127
+ }
128
+
129
+ function extractIds(node) {
130
+ if (node.children && node.children.length > 0) {
131
+ return node.children.flatMap((child) => extractIds(child));
132
+ }
133
+ if (node.viewer_id !== undefined && node.viewer_id !== null) {
134
+ return [node.viewer_id];
135
+ }
136
+ return [];
137
+ }
138
+
139
+ function getLeafViewerIds(item) {
140
+ const actualItem = item.raw || item;
141
+ return extractIds(actualItem);
142
+ }
143
+ </script>
144
+
145
+ <template>
146
+ <div class="tree-view-container">
147
+ <ObjectTreeControls
148
+ v-model:search="search"
149
+ :sort-type="sortType"
150
+ :filter-options="filterOptions"
151
+ :available-filter-options="availableFilterOptions"
152
+ :is-collapsed="opened.length === 0"
153
+ @toggle-sort="toggleSort"
154
+ @collapse-all="opened = []"
155
+ @expand-all="expandAll"
156
+ />
157
+
158
+ <FetchingData v-if="rawItems === undefined" :size="48" :width="4" text="" />
159
+
160
+ <CommonTreeView
161
+ :selected="visibleSelection"
162
+ v-model:opened="opened"
163
+ :items="itemsForTreeView"
164
+ :options="{
165
+ selection: { selectable: true, strategy: 'classic' },
166
+ search,
167
+ customFilter,
168
+ }"
169
+ :scroll-top="currentView?.scrollTop || 0"
170
+ class="transparent-treeview virtual-tree-height"
171
+ @update:selected="onUpdateSelection"
172
+ @click:item="onUpdateSelection([$event.id, ...visibleComponents])"
173
+ @update:scroll-top="treeviewStore.setScrollTop(actualViewId, $event)"
174
+ @hover:enter="handleHoverEnter"
175
+ @hover:leave="handleHoverLeave"
176
+ >
177
+ <template #title="{ item, isLeaf }">
178
+ <ObjectTreeItemLabel
179
+ :item="item"
180
+ :is-leaf="isLeaf"
181
+ show-tooltip
182
+ class="text-body-1"
183
+ @contextmenu.prevent.stop="showContextMenu($event, item)"
184
+ />
185
+ </template>
186
+
187
+ <template #append="{ item }">
188
+ <v-btn
189
+ v-if="item.category || (item.children && item.children.length > 0)"
190
+ icon="mdi-target"
191
+ size="medium"
192
+ variant="text"
193
+ v-tooltip="'Focus camera on object'"
194
+ @click.stop="hybridViewerStore.focusCameraOnObject(id, getLeafViewerIds(item))"
195
+ />
196
+ </template>
197
+ </CommonTreeView>
198
+ </div>
199
+ </template>
200
+
201
+ <style scoped>
202
+ .tree-view-container {
203
+ height: 100%;
204
+ display: flex;
205
+ flex-direction: column;
206
+ overflow: hidden;
207
+ min-height: 0;
208
+ }
209
+
210
+ .virtual-tree-height {
211
+ flex-grow: 1;
212
+ min-height: 0;
213
+ }
214
+
215
+ .transparent-treeview {
216
+ background-color: transparent;
217
+ }
218
+
219
+ :deep(.v-list-item__overlay) {
220
+ display: none !important;
221
+ }
222
+ </style>
@@ -9,7 +9,11 @@ import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer";
9
9
  import { useModelComponents } from "@ogw_front/composables/model_components";
10
10
  import { useTreeviewStore } from "@ogw_front/stores/treeview";
11
11
 
12
- const { id } = defineProps({ id: { type: String, required: true } });
12
+ const { id, viewId } = defineProps({
13
+ id: { type: String, required: true },
14
+ viewId: { type: String, required: false },
15
+ });
16
+ const actualViewId = viewId || id;
13
17
  const { onHoverEnter, onHoverLeave } = useHoverhighlight();
14
18
  const hybridViewerStore = useHybridViewerStore();
15
19
  const emit = defineEmits(["show-menu"]);
@@ -23,11 +27,13 @@ const {
23
27
  updateVisibility,
24
28
  } = useModelComponents(id);
25
29
 
26
- const currentView = computed(() => treeviewStore.opened_views.find((view) => view.id === id));
30
+ const currentView = computed(() =>
31
+ treeviewStore.opened_views.find((view) => view.id === actualViewId),
32
+ );
27
33
 
28
34
  const opened = computed({
29
35
  get: () => currentView.value?.opened || [],
30
- set: (val) => treeviewStore.setOpened(id, val),
36
+ set: (val) => treeviewStore.setOpened(actualViewId, val),
31
37
  });
32
38
 
33
39
  const {
@@ -155,7 +161,7 @@ function expandAll() {
155
161
  class="transparent-treeview virtual-tree-height"
156
162
  @update:selected="onUpdateSelection"
157
163
  @click:item="onUpdateSelection([$event.id, ...visibleComponents])"
158
- @update:scroll-top="treeviewStore.setScrollTop(id, $event)"
164
+ @update:scroll-top="treeviewStore.setScrollTop(actualViewId, $event)"
159
165
  @hover:enter="handleHoverEnter"
160
166
  @hover:leave="handleHoverLeave"
161
167
  >
@@ -1,8 +1,8 @@
1
1
  <script setup>
2
2
  import ViewerOptionsAttributeColorBar from "@ogw_front/components/Viewer/Options/AttributeColorBar.vue";
3
- import { useGeodeStore } from "@ogw_front/stores/geode";
3
+ import { useBackStore } from "@ogw_front/stores/back";
4
4
 
5
- const geodeStore = useGeodeStore();
5
+ const backStore = useBackStore();
6
6
 
7
7
  const name = defineModel("name", { type: String });
8
8
  const range = defineModel("range", { type: Array });
@@ -49,7 +49,7 @@ function resetRange() {
49
49
  }
50
50
 
51
51
  function getAttributes() {
52
- geodeStore.request(
52
+ backStore.request(
53
53
  schema,
54
54
  { id },
55
55
  {
@@ -1,7 +1,7 @@
1
1
  <script setup>
2
2
  import FileUploader from "@ogw_front/components/FileUploader";
3
3
  import back_schemas from "@geode/opengeodeweb-back/opengeodeweb_back_schemas.json";
4
- import { useGeodeStore } from "@ogw_front/stores/geode";
4
+ import { useBackStore } from "@ogw_front/stores/back";
5
5
 
6
6
  const emit = defineEmits(["update_value"]);
7
7
 
@@ -18,14 +18,14 @@ const texture_id = ref("");
18
18
  texture_id.value = texture_id;
19
19
 
20
20
  const texture_coordinates = ref([]);
21
- const geodeStore = useGeodeStore();
21
+ const backStore = useBackStore();
22
22
 
23
23
  onMounted(() => {
24
24
  getTextureCoordinates();
25
25
  });
26
26
 
27
27
  function getTextureCoordinates() {
28
- geodeStore.request(
28
+ backStore.request(
29
29
  back_schemas.opengeodeweb_back.texture_coordinates,
30
30
  { id },
31
31
  {
@@ -38,7 +38,7 @@ function getTextureCoordinates() {
38
38
 
39
39
  async function files_uploaded_event(value) {
40
40
  if (value.length > 0) {
41
- await geodeStore.request(
41
+ await backStore.request(
42
42
  back_schemas.opengeodeweb_back.save_viewable_file,
43
43
  {
44
44
  schema: back_schemas.opengeodeweb_back.save_viewable_file,