@geode/opengeodeweb-front 10.14.1 → 10.14.2-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 (68) hide show
  1. package/.oxlintrc.json +12 -2
  2. package/app/components/Basic/Slider.vue +7 -0
  3. package/app/components/Basic/Switch.vue +7 -0
  4. package/app/components/HybridRenderingView.vue +1 -1
  5. package/app/components/SearchBar.vue +1 -1
  6. package/app/components/Viewer/ContextMenu.vue +1 -3
  7. package/app/components/Viewer/ContextMenuItem.vue +2 -2
  8. package/app/components/Viewer/EdgedCurve/SpecificEdgesOptions.vue +5 -9
  9. package/app/components/Viewer/Generic/Mesh/CellsOptions.vue +7 -2
  10. package/app/components/Viewer/Generic/Mesh/EdgesOptions.vue +11 -9
  11. package/app/components/Viewer/Generic/Mesh/PointsOptions.vue +14 -9
  12. package/app/components/Viewer/Generic/Mesh/PolygonsOptions.vue +10 -2
  13. package/app/components/Viewer/Generic/Mesh/PolyhedraOptions.vue +10 -2
  14. package/app/components/Viewer/Generic/Model/PointsOptions.vue +2 -14
  15. package/app/components/Viewer/ObjectTree/Base/CommonTreeView.vue +189 -0
  16. package/app/components/Viewer/ObjectTree/Base/Controls.vue +124 -38
  17. package/app/components/Viewer/ObjectTree/Base/ItemLabel.vue +43 -18
  18. package/app/components/Viewer/ObjectTree/Base/StickyHeader.vue +46 -0
  19. package/app/components/Viewer/ObjectTree/Base/TreeRow.vue +77 -0
  20. package/app/components/Viewer/ObjectTree/Box.vue +106 -15
  21. package/app/components/Viewer/ObjectTree/Layout.vue +14 -12
  22. package/app/components/Viewer/ObjectTree/Views/GlobalObjects.vue +48 -33
  23. package/app/components/Viewer/ObjectTree/Views/ModelComponents.vue +102 -66
  24. package/app/components/Viewer/Options/Sliders/Size.vue +8 -0
  25. package/app/components/Viewer/Options/Sliders/Slider.vue +17 -0
  26. package/app/components/Viewer/Options/Sliders/Width.vue +8 -0
  27. package/app/components/Viewer/Options/VisibilitySwitch.vue +3 -1
  28. package/app/components/Viewer/PointSet/SpecificPointsOptions.vue +1 -5
  29. package/app/components/Viewer/Surface/Triangulated/TrianglesOptions.vue +11 -0
  30. package/app/composables/hover_highlight.js +85 -0
  31. package/app/composables/model_components.js +68 -0
  32. package/app/composables/{use_tree_filter.js → tree_filter.js} +49 -31
  33. package/app/composables/tree_keyboard_nav.js +81 -0
  34. package/app/composables/tree_scroll.js +91 -0
  35. package/app/composables/virtual_tree.js +164 -0
  36. package/app/stores/data.js +41 -1
  37. package/app/stores/hybrid_viewer.js +30 -38
  38. package/app/stores/menu.js +8 -14
  39. package/app/utils/hybrid_viewer.js +101 -0
  40. package/package.json +3 -3
  41. package/tests/integration/setup.js +2 -1
  42. package/tests/integration/stores/data_style/mesh/cells.nuxt.test.js +2 -3
  43. package/tests/integration/stores/data_style/mesh/edges.nuxt.test.js +2 -3
  44. package/tests/integration/stores/data_style/mesh/index.nuxt.test.js +2 -3
  45. package/tests/integration/stores/data_style/mesh/points.nuxt.test.js +2 -3
  46. package/tests/integration/stores/data_style/mesh/polygons.nuxt.test.js +2 -3
  47. package/tests/integration/stores/data_style/mesh/polyhedra.nuxt.test.js +2 -3
  48. package/tests/integration/stores/data_style/model/blocks.nuxt.test.js +2 -3
  49. package/tests/integration/stores/data_style/model/corners.nuxt.test.js +2 -3
  50. package/tests/integration/stores/data_style/model/edges.nuxt.test.js +2 -3
  51. package/tests/integration/stores/data_style/model/index.nuxt.test.js +2 -3
  52. package/tests/integration/stores/data_style/model/lines.nuxt.test.js +2 -3
  53. package/tests/integration/stores/data_style/model/points.nuxt.test.js +2 -3
  54. package/tests/integration/stores/data_style/model/surfaces.nuxt.test.js +2 -3
  55. package/tests/integration/stores/viewer.nuxt.test.js +2 -2
  56. package/app/components/Viewer/HybridSolid/EdgesOptions.vue +0 -12
  57. package/app/components/Viewer/HybridSolid/PointsOptions.vue +0 -12
  58. package/app/components/Viewer/HybridSolid/PolygonsOptions.vue +0 -12
  59. package/app/components/Viewer/HybridSolid/PolyhedraOptions.vue +0 -12
  60. package/app/components/Viewer/PolygonalSurface/EdgesOptions.vue +0 -12
  61. package/app/components/Viewer/PolygonalSurface/PointsOptions.vue +0 -12
  62. package/app/components/Viewer/TriangulatedSurface/TrianglesOptions.vue +0 -16
  63. package/app/composables/use_hover_highlight.js +0 -48
  64. package/tests/integration/microservices/back/requirements.txt +0 -7
  65. package/tests/integration/microservices/viewer/requirements.txt +0 -7
  66. /package/app/components/Viewer/{TriangulatedSurface → Surface}/EdgesOptions.vue +0 -0
  67. /package/app/components/Viewer/{TriangulatedSurface → Surface}/PointsOptions.vue +0 -0
  68. /package/app/components/Viewer/{PolygonalSurface/SpecificPolygonsOptions.vue → Surface/PolygonsOptions.vue} +0 -0
package/.oxlintrc.json CHANGED
@@ -92,6 +92,12 @@
92
92
  "ignore": [-1, 0, 1, 2, 3, 4],
93
93
  "ignoreArrayIndexes": true
94
94
  }
95
+ ],
96
+ "eslint/no-underscore-dangle": [
97
+ "error",
98
+ {
99
+ "allow": ["_data", "__dirname", "__VEASE_UTILS__", "__VEASE_STORES__", "__VEASE_SCHEMAS__"]
100
+ }
95
101
  ]
96
102
  },
97
103
  "overrides": [
@@ -117,6 +123,7 @@
117
123
  "rules": {
118
124
  "vitest/require-hook": "off",
119
125
  "vitest/no-hooks": "off",
126
+ "jest/no-hooks": "off",
120
127
  "vitest/no-importing-vitest-globals": "off",
121
128
  "max-lines-per-function": "off",
122
129
  "max-statements": "off",
@@ -124,8 +131,11 @@
124
131
  "vitest/prefer-to-be-falsy": "off",
125
132
  "vitest/require-test-timeout": "warn",
126
133
  "vitest/prefer-importing-vitest-globals": "off",
127
- "jest/consistent-test-it": ["error", { "fn": "test", "withinDescribe": "test" }],
128
- "vitest/prefer-spy-on": "off"
134
+ "jest/consistent-test-it": "off",
135
+ "vitest/consistent-test-it": ["error", { "fn": "test", "withinDescribe": "test" }],
136
+ "vitest/prefer-spy-on": "off",
137
+ "vitest/prefer-expect-assertions": "off",
138
+ "jest/prefer-expect-assertions": "off"
129
139
  }
130
140
  },
131
141
  {
@@ -0,0 +1,7 @@
1
+ <script setup>
2
+ const model = defineModel();
3
+ </script>
4
+
5
+ <template>
6
+ <v-slider v-model="model" hide-details min="0" max="20" step="2" />
7
+ </template>
@@ -0,0 +1,7 @@
1
+ <script setup>
2
+ const model = defineModel();
3
+ </script>
4
+
5
+ <template>
6
+ <v-switch v-model="model" inset hide-details />
7
+ </template>
@@ -46,7 +46,7 @@ function debounce(func, wait) {
46
46
 
47
47
  <template>
48
48
  <ClientOnly>
49
- <div class="fill-height" style="position: relative; height: 100%">
49
+ <div data-testid="hybridViewer" class="fill-height" style="position: relative; height: 100%">
50
50
  <VeaseViewToolbar />
51
51
  <slot name="ui"></slot>
52
52
  <v-col
@@ -1,7 +1,7 @@
1
1
  <script setup>
2
2
  const { modelValue, label } = defineProps({
3
3
  modelValue: { type: String, default: "" },
4
- label: { type: String, default: "Search..." },
4
+ label: { type: String, default: "" },
5
5
  });
6
6
 
7
7
  const emit = defineEmits(["update:modelValue"]);
@@ -4,7 +4,6 @@ import { useMenuStore } from "@ogw_front/stores/menu";
4
4
 
5
5
  const RADIUS = 80;
6
6
  const MARGIN_OFFSET = 40;
7
- const Z_INDEX_MENU = 1000;
8
7
  const Z_INDEX_ACTIVE_ITEM = 10;
9
8
  const Z_INDEX_BASE_ITEM = 1;
10
9
  const FULL_ANGLE = 360;
@@ -34,7 +33,7 @@ const menuX = ref(x);
34
33
  const menuY = ref(y);
35
34
 
36
35
  watch(
37
- () => [x, y],
36
+ () => [x, y, containerWidth, containerHeight],
38
37
  ([newX, newY]) => {
39
38
  const { x: clampedX, y: clampedY } = clampPosition(newX, newY);
40
39
  menuX.value = clampedX;
@@ -110,7 +109,6 @@ function getMenuStyle() {
110
109
  position: "fixed",
111
110
  left: `${menuStore.containerLeft + menuX.value - RADIUS}px`,
112
111
  top: `${menuStore.containerTop + menuY.value - RADIUS}px`,
113
- zIndex: Z_INDEX_MENU,
114
112
  };
115
113
  }
116
114
 
@@ -29,7 +29,7 @@ const optionsStyle = computed(() => {
29
29
  if (!is_active.value || !optionsHeight.value) {
30
30
  return {};
31
31
  }
32
- const angle = (itemProps.index / itemProps.totalItems) * 2 * Math.PI;
32
+ const angle = (index / itemProps.totalItems) * 2 * Math.PI;
33
33
  const radius = RADIUS;
34
34
  const absoluteButtonY = menuStore.menuY + Math.sin(angle) * radius;
35
35
  const height = optionsHeight.value;
@@ -89,7 +89,6 @@ function toggleOptions() {
89
89
  >
90
90
  <GlassCard
91
91
  @click.stop
92
- :title="tooltip"
93
92
  width="320"
94
93
  :max-height="maxCardHeight"
95
94
  :ripple="false"
@@ -98,6 +97,7 @@ function toggleOptions() {
98
97
  class="elevation-24"
99
98
  style="overflow: hidden; display: flex; flex-direction: column"
100
99
  >
100
+ <v-card-title>{{ tooltip }}</v-card-title>
101
101
  <v-card-text class="pa-5" style="overflow-y: auto; flex: 1; min-height: 0">
102
102
  <slot name="options" />
103
103
  </v-card-text>
@@ -3,6 +3,7 @@ import EdgedCurveEdges from "@ogw_front/assets/viewer_svgs/edged_curve_edges.svg
3
3
  import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenuItem";
4
4
  import ViewerOptionsColoringTypeSelector from "@ogw_front/components/Viewer/Options/ColoringTypeSelector";
5
5
  import ViewerOptionsVisibilitySwitch from "@ogw_front/components/Viewer/Options/VisibilitySwitch";
6
+ import ViewerOptionsWidthSlider from "@ogw_front/components/Viewer/Options/Sliders/Width";
6
7
 
7
8
  import { useDataStyleStore } from "@ogw_front/stores/data_style";
8
9
  import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer";
@@ -23,7 +24,7 @@ const visibility = computed({
23
24
  hybridViewerStore.remoteRender();
24
25
  },
25
26
  });
26
- const size = computed({
27
+ const width = computed({
27
28
  get: () => dataStyleStore.meshEdgesWidth(id.value),
28
29
  set: async (newValue) => {
29
30
  await dataStyleStore.setMeshEdgesWidth(id.value, newValue);
@@ -90,20 +91,15 @@ const edge_attribute_color_map = computed({
90
91
  <template>
91
92
  <ViewerContextMenuItem
92
93
  :itemProps="itemProps"
93
- tooltip="Edges options"
94
94
  :btn_image="EdgedCurveEdges"
95
+ tooltip="Edges options"
95
96
  >
96
97
  <template #options>
97
- <ViewerOptionsVisibilitySwitch v-model="visibility" />
98
+ <ViewerOptionsVisibilitySwitch data-testid="meshEdgesVisibilitySwitch" v-model="visibility" />
98
99
  <template v-if="visibility">
99
100
  <v-row class="pa-0" align="center">
100
101
  <v-divider />
101
- <v-col cols="auto" justify="center">
102
- <v-icon size="30" icon="mdi-ruler" v-tooltip:left="'Width'" />
103
- </v-col>
104
- <v-col justify="center">
105
- <v-slider v-model="size" hide-details min="0" max="20" step="2" />
106
- </v-col>
102
+ <ViewerOptionsWidthSlider v-model="width" />
107
103
  </v-row>
108
104
  <v-row>
109
105
  <v-col>
@@ -90,9 +90,14 @@ const cell_attribute_color_map = computed({
90
90
  </script>
91
91
 
92
92
  <template>
93
- <ViewerContextMenuItem :itemProps="itemProps" :tooltip="tooltip" :btn_image="btn_image">
93
+ <ViewerContextMenuItem
94
+ data-testid="meshCellsMenu"
95
+ :itemProps="itemProps"
96
+ :tooltip="tooltip"
97
+ :btn_image="btn_image"
98
+ >
94
99
  <template #options>
95
- <ViewerOptionsVisibilitySwitch v-model="visibility" />
100
+ <ViewerOptionsVisibilitySwitch data-testid="meshCellsVisibilitySwitch" v-model="visibility" />
96
101
  <template v-if="visibility">
97
102
  <ViewerOptionsColoringTypeSelector
98
103
  :id="id"
@@ -2,6 +2,7 @@
2
2
  import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenuItem";
3
3
  import ViewerOptionsColoringTypeSelector from "@ogw_front/components/Viewer/Options/ColoringTypeSelector";
4
4
  import ViewerOptionsVisibilitySwitch from "@ogw_front/components/Viewer/Options/VisibilitySwitch";
5
+ import ViewerOptionsWidthSlider from "@ogw_front/components/Viewer/Options/Sliders/Width";
5
6
 
6
7
  import { useDataStyleStore } from "@ogw_front/stores/data_style";
7
8
  import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer";
@@ -9,9 +10,10 @@ import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer";
9
10
  const dataStyleStore = useDataStyleStore();
10
11
  const hybridViewerStore = useHybridViewerStore();
11
12
 
12
- const { itemProps, btn_image } = defineProps({
13
+ const { itemProps, btn_image, tooltip } = defineProps({
13
14
  itemProps: { type: Object, required: true },
14
15
  btn_image: { type: String, required: true },
16
+ tooltip: { type: String, required: false, default: "Edges options" },
15
17
  });
16
18
 
17
19
  const id = toRef(() => itemProps.id);
@@ -89,18 +91,18 @@ const edge_attribute_color_map = computed({
89
91
  </script>
90
92
 
91
93
  <template>
92
- <ViewerContextMenuItem :itemProps="itemProps" tooltip="Edges options" :btn_image="btn_image">
94
+ <ViewerContextMenuItem
95
+ data-testid="meshEdgesMenu"
96
+ :itemProps="itemProps"
97
+ :tooltip="tooltip"
98
+ :btn_image="btn_image"
99
+ >
93
100
  <template #options>
94
- <ViewerOptionsVisibilitySwitch v-model="visibility" />
101
+ <ViewerOptionsVisibilitySwitch data-testid="meshEdgesVisibilitySwitch" v-model="visibility" />
95
102
  <template v-if="visibility">
96
103
  <v-row class="pa-0" align="center">
97
104
  <v-divider />
98
- <v-col cols="auto" justify="center">
99
- <v-icon size="30" icon="mdi-ruler" v-tooltip:left="'Width'" />
100
- </v-col>
101
- <v-col justify="center">
102
- <v-slider v-model="size" hide-details min="0" max="20" step="2" />
103
- </v-col>
105
+ <ViewerOptionsWidthSlider data-testid="meshEdgesWidthSlider" v-model="size" />
104
106
  </v-row>
105
107
  <v-row>
106
108
  <v-col>
@@ -1,6 +1,7 @@
1
1
  <script setup>
2
2
  import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenuItem";
3
3
  import ViewerOptionsColoringTypeSelector from "@ogw_front/components/Viewer/Options/ColoringTypeSelector";
4
+ import ViewerOptionsSizeSlider from "@ogw_front/components/Viewer/Options/Sliders/Size";
4
5
  import ViewerOptionsVisibilitySwitch from "@ogw_front/components/Viewer/Options/VisibilitySwitch";
5
6
 
6
7
  import { useDataStyleStore } from "@ogw_front/stores/data_style";
@@ -9,9 +10,10 @@ import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer";
9
10
  const dataStyleStore = useDataStyleStore();
10
11
  const hybridViewerStore = useHybridViewerStore();
11
12
 
12
- const { itemProps, btn_image } = defineProps({
13
+ const { itemProps, btn_image, tooltip } = defineProps({
13
14
  itemProps: { type: Object, required: true },
14
15
  btn_image: { type: String, required: true },
16
+ tooltip: { type: String, required: false, default: "Points options" },
15
17
  });
16
18
 
17
19
  const id = toRef(() => itemProps.id);
@@ -68,18 +70,21 @@ const vertex_attribute_color_map = computed({
68
70
  </script>
69
71
 
70
72
  <template>
71
- <ViewerContextMenuItem :itemProps="itemProps" tooltip="Points options" :btn_image="btn_image">
73
+ <ViewerContextMenuItem
74
+ data-testid="meshPointsMenu"
75
+ :itemProps="itemProps"
76
+ :tooltip="tooltip"
77
+ :btn_image="btn_image"
78
+ >
72
79
  <template #options>
73
- <ViewerOptionsVisibilitySwitch v-model="visibility" />
80
+ <ViewerOptionsVisibilitySwitch
81
+ data-testid="meshPointsVisibilitySwitch"
82
+ v-model="visibility"
83
+ />
74
84
  <template v-if="visibility">
75
85
  <v-row class="pa-0" align="center">
76
86
  <v-divider />
77
- <v-col cols="auto" justify="center">
78
- <v-icon size="30" icon="mdi-ruler" v-tooltip:left="'Size'" />
79
- </v-col>
80
- <v-col justify="center">
81
- <v-slider v-model="size" hide-details min="0" max="20" step="2" />
82
- </v-col>
87
+ <ViewerOptionsSizeSlider data-testid="meshPointsSizeSlider" v-model="size" />
83
88
  </v-row>
84
89
  <v-row>
85
90
  <v-col>
@@ -90,9 +90,17 @@ const polygon_attribute_color_map = computed({
90
90
  </script>
91
91
 
92
92
  <template>
93
- <ViewerContextMenuItem :itemProps="itemProps" :tooltip="tooltip" :btn_image="btn_image">
93
+ <ViewerContextMenuItem
94
+ data-testid="meshPolygonsMenu"
95
+ :itemProps="itemProps"
96
+ :tooltip="tooltip"
97
+ :btn_image="btn_image"
98
+ >
94
99
  <template #options>
95
- <ViewerOptionsVisibilitySwitch v-model="visibility" />
100
+ <ViewerOptionsVisibilitySwitch
101
+ data-testid="meshPolygonsVisibilitySwitch"
102
+ v-model="visibility"
103
+ />
96
104
  <template v-if="visibility">
97
105
  <ViewerOptionsColoringTypeSelector
98
106
  :id="id"
@@ -87,9 +87,17 @@ const polyhedron_attribute_color_map = computed({
87
87
  </script>
88
88
 
89
89
  <template>
90
- <ViewerContextMenuItem :itemProps="itemProps" :tooltip="tooltip" :btn_image="btn_image">
90
+ <ViewerContextMenuItem
91
+ data-testid="meshPolyhedraMenu"
92
+ :itemProps="itemProps"
93
+ :tooltip="tooltip"
94
+ :btn_image="btn_image"
95
+ >
91
96
  <template #options>
92
- <ViewerOptionsVisibilitySwitch v-model="visibility" />
97
+ <ViewerOptionsVisibilitySwitch
98
+ data-testid="meshPolyhedraVisibilitySwitch"
99
+ v-model="visibility"
100
+ />
93
101
  <template v-if="visibility">
94
102
  <ViewerOptionsColoringTypeSelector
95
103
  :id="id"
@@ -1,6 +1,7 @@
1
1
  <script setup>
2
2
  import SurfacePoints from "@ogw_front/assets/viewer_svgs/surface_points.svg";
3
3
  import ViewerContextMenuItem from "@ogw_front/components/Viewer/ContextMenuItem";
4
+ import ViewerOptionsSizeSlider from "@ogw_front/components/Viewer/Options/Sliders/Size";
4
5
  import ViewerOptionsVisibilitySwitch from "@ogw_front/components/Viewer/Options/VisibilitySwitch";
5
6
 
6
7
  import { useDataStyleStore } from "@ogw_front/stores/data_style";
@@ -46,20 +47,7 @@ const size = computed({
46
47
  <template v-if="visibility">
47
48
  <v-row class="pa-0" align="center">
48
49
  <v-divider />
49
- <v-col cols="auto" justify="center">
50
- <v-icon size="30" icon="mdi-ruler" v-tooltip:left="'Size'" />
51
- </v-col>
52
- <v-col justify="center">
53
- <v-slider
54
- v-model="size"
55
- hide-details
56
- min="0"
57
- max="20"
58
- step="2"
59
- thumb-color="black"
60
- ticks
61
- />
62
- </v-col>
50
+ <ViewerOptionsSizeSlider data-testid="modelPointsSizeSlider" v-model="size" />
63
51
  </v-row>
64
52
  </template>
65
53
  </template>
@@ -0,0 +1,189 @@
1
+ <script setup>
2
+ import StickyHeader from "@ogw_front/components/Viewer/ObjectTree/Base/StickyHeader.vue";
3
+ import TreeRow from "@ogw_front/components/Viewer/ObjectTree/Base/TreeRow.vue";
4
+ import { useTreeKeyboardNav } from "@ogw_front/composables/tree_keyboard_nav";
5
+ import { useTreeScroll } from "@ogw_front/composables/tree_scroll";
6
+ import { useVirtualTree } from "@ogw_front/composables/virtual_tree";
7
+
8
+ const { items, opened, selected, scrollTop, options } = defineProps({
9
+ items: { type: Array, required: true },
10
+ opened: { type: Array, required: false, default: () => [] },
11
+ selected: { type: Array, required: false, default: () => [] },
12
+ scrollTop: { type: Number, required: false, default: 0 },
13
+ options: { type: Object, required: false, default: () => ({}) },
14
+ });
15
+
16
+ const treeWrapper = ref(undefined);
17
+
18
+ const emit = defineEmits([
19
+ "update:opened",
20
+ "update:selected",
21
+ "click:item",
22
+ "update:scrollTop",
23
+ "hover:enter",
24
+ "hover:leave",
25
+ ]);
26
+
27
+ const {
28
+ actualItemProps,
29
+ actualSelection,
30
+ displayItems,
31
+ toggleOpen,
32
+ toggleSelect,
33
+ isSelected,
34
+ getIndeterminate,
35
+ } = useVirtualTree(
36
+ computed(() => ({
37
+ items,
38
+ opened,
39
+ selected,
40
+ ...options,
41
+ })),
42
+ emit,
43
+ );
44
+
45
+ const { virtualScrollRef, stickyHeader, handleScroll, scrollToIndex } = useTreeScroll(
46
+ computed(() => ({ scrollTop })),
47
+ emit,
48
+ displayItems,
49
+ actualItemProps,
50
+ );
51
+
52
+ function handleItemClick(item, index) {
53
+ if (index !== undefined) {
54
+ focusedIndex.value = index;
55
+ }
56
+ if (item.isLeaf) {
57
+ toggleSelect(item.raw);
58
+ emit("click:item", item.raw);
59
+ } else {
60
+ toggleOpen(item.raw);
61
+ }
62
+ }
63
+
64
+ const { focusedIndex, handleKeyDown } = useTreeKeyboardNav(
65
+ displayItems,
66
+ emit,
67
+ scrollToIndex,
68
+ toggleOpen,
69
+ handleItemClick,
70
+ );
71
+ </script>
72
+
73
+ <template>
74
+ <div
75
+ ref="treeWrapper"
76
+ class="common-tree-view-wrapper"
77
+ tabindex="0"
78
+ @keydown="handleKeyDown"
79
+ @mousedown="treeWrapper.focus()"
80
+ >
81
+ <StickyHeader
82
+ v-if="stickyHeader"
83
+ :item="stickyHeader"
84
+ :item-props="actualItemProps"
85
+ :selection="actualSelection"
86
+ :is-selected="isSelected"
87
+ :get-indeterminate="getIndeterminate"
88
+ @toggle-open="toggleOpen"
89
+ @toggle-select="toggleSelect"
90
+ >
91
+ <template #title="slotProps">
92
+ <slot name="title" v-bind="slotProps" />
93
+ </template>
94
+ </StickyHeader>
95
+
96
+ <v-virtual-scroll
97
+ ref="virtualScrollRef"
98
+ :items="displayItems"
99
+ :item-height="actualItemProps.height"
100
+ class="common-tree-view"
101
+ @scroll="handleScroll"
102
+ >
103
+ <template #default="{ item, index }">
104
+ <v-list-item
105
+ :class="[
106
+ 'tree-row-wrapper',
107
+ { 'leaf-row': item.isLeaf, 'is-focused': focusedIndex === index },
108
+ ]"
109
+ class="pa-0"
110
+ tabindex="-1"
111
+ @mousedown.prevent
112
+ @click="
113
+ handleItemClick(item, index);
114
+ treeWrapper.focus();
115
+ "
116
+ @mouseenter="emit('hover:enter', { item })"
117
+ @mouseleave="emit('hover:leave', { item })"
118
+ >
119
+ <TreeRow
120
+ :item="item"
121
+ :item-props="actualItemProps"
122
+ :selection="actualSelection"
123
+ :is-selected="isSelected"
124
+ :get-indeterminate="getIndeterminate"
125
+ @toggle-open="toggleOpen"
126
+ @toggle-select="toggleSelect"
127
+ >
128
+ <template #title="slotProps">
129
+ <slot name="title" v-bind="slotProps" />
130
+ </template>
131
+ <template #append="slotProps">
132
+ <slot name="append" v-bind="slotProps" />
133
+ </template>
134
+ </TreeRow>
135
+ </v-list-item>
136
+ </template>
137
+ </v-virtual-scroll>
138
+ </div>
139
+ </template>
140
+
141
+ <style scoped>
142
+ .common-tree-view-wrapper {
143
+ height: 100%;
144
+ position: relative;
145
+ display: flex;
146
+ flex-direction: column;
147
+ min-height: 0;
148
+ }
149
+
150
+ .common-tree-view-wrapper:focus {
151
+ outline: none;
152
+ }
153
+
154
+ .common-tree-view {
155
+ flex-grow: 1;
156
+ min-height: 0;
157
+ overflow-y: auto !important;
158
+ }
159
+
160
+ .v-list-item {
161
+ background-color: transparent !important;
162
+ transition: none !important;
163
+ }
164
+
165
+ .tree-row-wrapper {
166
+ min-height: 44px !important;
167
+ cursor: pointer;
168
+ border-radius: 8px;
169
+ margin: 1px 4px;
170
+ }
171
+
172
+ .tree-row-wrapper.is-focused {
173
+ background-color: rgba(0, 0, 0, 0.08) !important;
174
+ box-shadow: inset 0 0 0 2px rgba(0, 0, 0, 0.15);
175
+ }
176
+
177
+ .tree-row-wrapper:hover:not(.is-focused) {
178
+ background-color: rgba(0, 0, 0, 0.04) !important;
179
+ }
180
+
181
+ :deep(.v-list-item__content) {
182
+ padding: 0 !important;
183
+ display: block !important;
184
+ }
185
+
186
+ :deep(.v-list-item__overlay) {
187
+ display: none !important;
188
+ }
189
+ </style>