@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
@@ -9,50 +9,136 @@ const { search, sortType, filterOptions, availableFilterOptions } = defineProps(
9
9
  availableFilterOptions: { type: Array, required: true },
10
10
  });
11
11
 
12
- const emit = defineEmits(["update:search", "toggle-sort"]);
12
+ const emit = defineEmits(["update:search", "toggle-sort", "collapse-all"]);
13
+
14
+ const showSearch = ref(false);
15
+
16
+ watch(
17
+ () => showSearch.value,
18
+ (val) => {
19
+ if (!val) {
20
+ emit("update:search", "");
21
+ }
22
+ },
23
+ );
13
24
  </script>
14
25
 
15
26
  <template>
16
- <v-row dense align="center" class="pa-2">
17
- <v-col>
18
- <SearchBar
19
- :model-value="search"
20
- label="Search"
21
- color="black"
22
- base-color="black"
23
- @update:model-value="emit('update:search', $event)"
24
- />
25
- </v-col>
26
- <v-col cols="auto" class="d-flex align-center">
27
- <ActionButton
28
- :tooltip="'Sort by ' + (sortType === 'name' ? 'ID' : 'Name')"
29
- :icon="
30
- sortType === 'name' ? 'mdi-sort-alphabetical-ascending' : 'mdi-sort-numeric-ascending'
31
- "
32
- tooltipLocation="bottom"
33
- @click="emit('toggle-sort')"
34
- />
35
- <v-menu :close-on-content-click="false">
36
- <template #activator="{ props: menuProps }">
27
+ <v-row dense align="center" class="pa-2 py-1">
28
+ <v-col cols="12">
29
+ <div
30
+ class="controls-capsule d-flex align-center rounded-pill px-1 overflow-hidden"
31
+ :class="{ 'is-expanded': showSearch }"
32
+ >
33
+ <ActionButton
34
+ :tooltip="showSearch ? 'Hide search' : 'Show search'"
35
+ icon="mdi-magnify"
36
+ tooltipLocation="bottom"
37
+ variant="text"
38
+ color="black"
39
+ @click="showSearch = !showSearch"
40
+ />
41
+
42
+ <v-expand-x-transition>
43
+ <div v-if="showSearch" class="flex-grow-1 ms-1 text-no-wrap overflow-hidden">
44
+ <SearchBar
45
+ :model-value="search"
46
+ placeholder="Search objects..."
47
+ color="black"
48
+ base-color="black"
49
+ variant="plain"
50
+ density="compact"
51
+ prepend-inner-icon=""
52
+ autofocus
53
+ @update:model-value="emit('update:search', $event)"
54
+ />
55
+ </div>
56
+ </v-expand-x-transition>
57
+
58
+ <div class="d-flex align-center">
59
+ <v-fade-transition>
60
+ <v-divider
61
+ v-if="!showSearch"
62
+ vertical
63
+ inset
64
+ class="mx-1 align-self-center"
65
+ style="height: 20px"
66
+ />
67
+ </v-fade-transition>
37
68
  <ActionButton
38
- tooltip="Filter options"
39
- icon="mdi-filter-variant"
69
+ :tooltip="'Sort by ' + (sortType === 'name' ? 'ID' : 'Name')"
70
+ :icon="
71
+ sortType === 'name' ? 'mdi-sort-alphabetical-ascending' : 'mdi-sort-numeric-ascending'
72
+ "
73
+ variant="text"
74
+ color="black"
40
75
  tooltipLocation="bottom"
41
- class="ml-1"
42
- v-bind="menuProps"
76
+ @click="emit('toggle-sort')"
43
77
  />
44
- </template>
45
- <v-list class="mt-1">
46
- <v-list-item v-for="category_id in availableFilterOptions" :key="category_id">
47
- <v-checkbox
48
- v-model="filterOptions[category_id]"
49
- :label="category_id"
50
- hide-details
51
- density="compact"
52
- />
53
- </v-list-item>
54
- </v-list>
55
- </v-menu>
78
+ <v-menu :close-on-content-click="false">
79
+ <template #activator="{ props: menuProps }">
80
+ <ActionButton
81
+ tooltip="Filter options"
82
+ icon="mdi-filter-variant"
83
+ variant="text"
84
+ color="black"
85
+ tooltipLocation="bottom"
86
+ v-bind="menuProps"
87
+ />
88
+ </template>
89
+ <v-list class="mt-1">
90
+ <v-list-item v-for="category_id in availableFilterOptions" :key="category_id">
91
+ <v-checkbox
92
+ v-model="filterOptions[category_id]"
93
+ :label="category_id"
94
+ hide-details
95
+ density="compact"
96
+ />
97
+ </v-list-item>
98
+ </v-list>
99
+ </v-menu>
100
+ <ActionButton
101
+ tooltip="Collapse All"
102
+ icon="mdi-collapse-all-outline"
103
+ variant="text"
104
+ color="black"
105
+ tooltipLocation="bottom"
106
+ @click="emit('collapse-all')"
107
+ />
108
+ </div>
109
+ </div>
56
110
  </v-col>
57
111
  </v-row>
58
112
  </template>
113
+
114
+ <style scoped>
115
+ .controls-capsule {
116
+ height: 40px;
117
+ border: 1px solid transparent;
118
+ transition: all 0.3s ease;
119
+ width: fit-content;
120
+ }
121
+
122
+ .controls-capsule.is-expanded {
123
+ width: 100%;
124
+ background-color: rgba(0, 0, 0, 0.05);
125
+ border: 1px solid rgba(0, 0, 0, 0.08);
126
+ }
127
+
128
+ .controls-capsule:hover:not(.is-expanded) {
129
+ background-color: rgba(0, 0, 0, 0.05);
130
+ border: 1px solid rgba(0, 0, 0, 0.08);
131
+ }
132
+
133
+ :deep(.v-field__input) {
134
+ padding-top: 0 !important;
135
+ padding-bottom: 0 !important;
136
+ min-height: 40px !important;
137
+ display: flex;
138
+ align-items: center;
139
+ }
140
+
141
+ :deep(.v-field__field) {
142
+ height: 40px !important;
143
+ }
144
+ </style>
@@ -1,37 +1,62 @@
1
1
  <script setup>
2
- const { item, showTooltip } = defineProps({
2
+ const { item, isLeaf } = defineProps({
3
3
  item: { type: Object, required: true },
4
- showTooltip: { type: Boolean, default: false },
4
+ isLeaf: { type: Boolean, required: false, default: undefined },
5
5
  });
6
6
 
7
- const emit = defineEmits(["contextmenu"]);
7
+ const emit = defineEmits(["contextmenu", "mouseenter", "mouseleave"]);
8
8
 
9
9
  const actualItem = computed(() => item.raw || item);
10
+
11
+ const tooltipDisabled = computed(() => {
12
+ if (isLeaf !== undefined) {
13
+ return !isLeaf;
14
+ }
15
+ return actualItem.value.children && actualItem.value.children.length > 0;
16
+ });
10
17
  </script>
11
18
 
12
19
  <template>
13
- <span
14
- class="tree-item-label"
15
- :class="{ 'inactive-item': actualItem.is_active === false }"
16
- @contextmenu.prevent.stop="emit('contextmenu', $event)"
17
- >
18
- {{ actualItem.title }}
19
- <v-tooltip v-if="showTooltip && actualItem.category" activator="parent" location="right">
20
- <div class="d-flex flex-column pa-1">
21
- <span class="text-caption"><strong>ID:</strong> {{ actualItem.id }}</span>
22
- <span v-if="actualItem.title" class="text-caption"
23
- ><strong>Name:</strong> {{ actualItem.title }}</span
20
+ <div class="tree-item-label-container w-100">
21
+ <v-tooltip :disabled="tooltipDisabled" location="right" open-delay="400">
22
+ <template #activator="{ props: tooltipProps }">
23
+ <span
24
+ v-bind="tooltipProps"
25
+ class="tree-item-label"
26
+ :class="{ 'inactive-item': actualItem.is_active === false }"
27
+ @contextmenu.prevent.stop="emit('contextmenu', $event)"
28
+ @mouseenter="emit('mouseenter')"
29
+ @mouseleave="emit('mouseleave')"
24
30
  >
25
- <span class="text-caption font-italic border-t-sm d-flex align-center">
26
- <strong class="mr-1">Status:</strong>
27
- {{ actualItem.is_active ? "Active" : "Inactive" }}
31
+ {{ actualItem.title }}
32
+ </span>
33
+ </template>
34
+
35
+ <div class="d-flex flex-column ga-1">
36
+ <span class="text-caption">
37
+ <strong class="text-white">ID:</strong> {{ actualItem.id }}
38
+ </span>
39
+ <span v-if="actualItem.title" class="text-caption">
40
+ <strong class="text-white">Name:</strong> {{ actualItem.title }}
41
+ </span>
42
+ <span class="text-caption">
43
+ <strong class="text-white">Status:</strong>
44
+ <i class="ml-1">{{ actualItem.is_active ? "Active" : "Inactive" }}</i>
28
45
  </span>
29
46
  </div>
30
47
  </v-tooltip>
31
- </span>
48
+ </div>
32
49
  </template>
33
50
 
34
51
  <style scoped>
52
+ .tree-item-label-container {
53
+ display: flex;
54
+ align-items: center;
55
+ min-width: 0;
56
+ height: 100%;
57
+ width: 100%;
58
+ }
59
+
35
60
  .tree-item-label {
36
61
  white-space: nowrap;
37
62
  overflow: hidden;
@@ -0,0 +1,46 @@
1
+ <script setup>
2
+ import TreeRow from "@ogw_front/components/Viewer/ObjectTree/Base/TreeRow.vue";
3
+
4
+ const { item, itemProps, selection, isSelected, getIndeterminate } = defineProps({
5
+ item: { type: Object, required: true },
6
+ itemProps: { type: Object, required: true },
7
+ selection: { type: Object, required: true },
8
+ isSelected: { type: Function, required: true },
9
+ getIndeterminate: { type: Function, required: true },
10
+ });
11
+
12
+ const emit = defineEmits(["toggle-open", "toggle-select"]);
13
+ </script>
14
+
15
+ <template>
16
+ <div class="sticky-tree-header tree-row" @click="$emit('toggle-open', item.raw)">
17
+ <TreeRow
18
+ :item="item"
19
+ :item-props="itemProps"
20
+ :selection="selection"
21
+ :is-selected="isSelected"
22
+ :get-indeterminate="getIndeterminate"
23
+ @toggle-open="$emit('toggle-open', $event)"
24
+ @toggle-select="$emit('toggle-select', $event)"
25
+ >
26
+ <template #title="slotProps">
27
+ <slot name="title" v-bind="slotProps" />
28
+ </template>
29
+ </TreeRow>
30
+ </div>
31
+ </template>
32
+
33
+ <style scoped>
34
+ .sticky-tree-header {
35
+ flex-shrink: 0;
36
+ background-color: transparent;
37
+ border-bottom: 2px solid rgba(0, 0, 0, 0.05);
38
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
39
+ cursor: pointer;
40
+ z-index: 20;
41
+ }
42
+
43
+ .sticky-tree-header:hover {
44
+ background-color: rgba(0, 0, 0, 0.04);
45
+ }
46
+ </style>
@@ -0,0 +1,77 @@
1
+ <script setup>
2
+ const { item, itemProps, selection, isSelected, getIndeterminate } = defineProps({
3
+ item: { type: Object, required: true },
4
+ itemProps: { type: Object, required: true },
5
+ selection: { type: Object, required: true },
6
+ isSelected: { type: Function, required: true },
7
+ getIndeterminate: { type: Function, required: true },
8
+ });
9
+
10
+ defineEmits(["toggle-open", "toggle-select"]);
11
+
12
+ const INDENT_STEP = 16;
13
+ </script>
14
+
15
+ <template>
16
+ <div class="tree-row-content d-flex align-center px-4 ps-3 w-100">
17
+ <div
18
+ v-if="item.depth > 0"
19
+ class="flex-shrink-0"
20
+ :style="{ width: `${item.depth * INDENT_STEP}px` }"
21
+ />
22
+
23
+ <div class="d-flex align-center flex-shrink-0">
24
+ <v-icon
25
+ v-if="!item.isLeaf"
26
+ :icon="item.isOpen ? 'mdi-menu-down' : 'mdi-menu-right'"
27
+ class="me-1"
28
+ color="black"
29
+ @click.stop="$emit('toggle-open', item.raw)"
30
+ />
31
+ <div v-else class="icon-placeholder" />
32
+
33
+ <v-btn
34
+ v-if="selection.selectable"
35
+ :icon="
36
+ getIndeterminate(item.raw)
37
+ ? 'mdi-eye-minus'
38
+ : isSelected(item.raw)
39
+ ? 'mdi-eye'
40
+ : 'mdi-eye-off'
41
+ "
42
+ variant="text"
43
+ density="compact"
44
+ color="black"
45
+ class="flex-shrink-0"
46
+ @click.stop="$emit('toggle-select', item.raw)"
47
+ @mousedown.stop
48
+ />
49
+ </div>
50
+
51
+ <div class="tree-title flex-grow-1 overflow-hidden d-flex align-center ms-1 pt-1">
52
+ <slot name="title" :item="item.raw" :is-leaf="item.isLeaf">
53
+ <v-list-item-title :class="{ 'font-weight-bold': !item.isLeaf }" class="text-black">
54
+ {{ item.raw[itemProps.title] || item.id }}
55
+ </v-list-item-title>
56
+ </slot>
57
+ </div>
58
+
59
+ <div class="ms-auto d-flex align-center">
60
+ <slot name="append" :item="item.raw" />
61
+ </div>
62
+ </div>
63
+ </template>
64
+
65
+ <style scoped>
66
+ .tree-row-content {
67
+ min-height: 44px;
68
+ }
69
+
70
+ .icon-placeholder {
71
+ width: 24px;
72
+ }
73
+
74
+ .tree-title {
75
+ min-height: 24px;
76
+ }
77
+ </style>
@@ -1,20 +1,68 @@
1
1
  <script setup>
2
+ import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer";
3
+
2
4
  const SCROLL_SYNC_DELAY = 50;
3
5
  const SCROLL_THRESHOLD = 1;
4
6
  const { title, closable, icon, mdiIcon, scrollTop } = defineProps({
5
7
  title: { type: String, required: true },
6
- closable: { type: Boolean, default: false },
7
- icon: { type: String, default: "" },
8
- mdiIcon: { type: String, default: "" },
9
- scrollTop: { type: Number, default: 0 },
8
+ closable: { type: Boolean, required: false, default: false },
9
+ icon: { type: String, required: false, default: "" },
10
+ mdiIcon: { type: String, required: false, default: "" },
11
+ scrollTop: { type: Number, required: false, default: 0 },
10
12
  });
11
-
12
13
  const emit = defineEmits(["close", "dragstart", "update:scrollTop"]);
13
14
 
14
- const scrollContainer = ref(undefined);
15
+ const scrollContainer = useTemplateRef("scroll-container");
16
+ const treeviewBox = useTemplateRef("treeview-box");
17
+ const hybridViewerStore = useHybridViewerStore();
18
+
19
+ const LUMINANCE_THRESHOLD = 0.65;
20
+ const ADAPTIVE_EXPONENT = 0.3;
21
+
22
+ const MIN_BLUR = 8;
23
+ const MAX_BLUR = 25;
24
+
25
+ const MIN_OPACITY = 0;
26
+ const MAX_OPACITY = 0.5;
27
+
28
+ const MIN_BOOST = 1;
29
+ const MAX_BOOST = 1.2;
30
+ const ADAPTIVE_REFRESH_RATE = 150;
31
+
32
+ const { x, y, width, height } = useElementBounding(treeviewBox);
33
+ const brightness = ref(LUMINANCE_THRESHOLD);
34
+
35
+ const updateBrightness = useThrottleFn(() => {
36
+ brightness.value = hybridViewerStore.getAverageBrightness({
37
+ x: x.value,
38
+ y: y.value,
39
+ width: width.value,
40
+ height: height.value,
41
+ });
42
+ }, ADAPTIVE_REFRESH_RATE);
43
+
15
44
  let isApplyingScroll = false;
16
45
  let resizeObserver = undefined;
17
46
 
47
+ watch([x, y, width, height, () => hybridViewerStore.latestImage], updateBrightness, {
48
+ immediate: true,
49
+ });
50
+
51
+ const adaptiveStyles = computed(() => {
52
+ const normalized = Math.min(1, brightness.value / LUMINANCE_THRESHOLD);
53
+ const darkFactor = (1 - normalized) ** ADAPTIVE_EXPONENT;
54
+
55
+ const blur = MIN_BLUR + darkFactor * (MAX_BLUR - MIN_BLUR);
56
+ const opacity = MIN_OPACITY + darkFactor * (MAX_OPACITY - MIN_OPACITY);
57
+ const brightnessBoost = MIN_BOOST + darkFactor * (MAX_BOOST - MIN_BOOST);
58
+
59
+ return {
60
+ "--adaptive-blur": `${blur}px`,
61
+ "--adaptive-opacity": opacity,
62
+ "--adaptive-brightness": brightnessBoost,
63
+ };
64
+ });
65
+
18
66
  function handleScroll(event) {
19
67
  if (isApplyingScroll) {
20
68
  return;
@@ -69,7 +117,12 @@ watch(
69
117
  </script>
70
118
 
71
119
  <template>
72
- <v-card variant="outlined" class="tree-box d-flex flex-column">
120
+ <v-card
121
+ ref="treeview-box"
122
+ variant="outlined"
123
+ class="tree-box d-flex flex-column"
124
+ :style="adaptiveStyles"
125
+ >
73
126
  <v-card-title
74
127
  class="tree-box-header d-flex align-center"
75
128
  :class="{ 'cursor-grab': closable }"
@@ -89,7 +142,15 @@ watch(
89
142
  <v-icon v-else-if="closable" size="24" class="mr-2">mdi-drag-variant</v-icon>
90
143
  <span
91
144
  class="text-subtitle-2 font-weight-bold d-inline-flex align-center"
92
- style="height: 24px; line-height: 1"
145
+ style="
146
+ height: 24px;
147
+ line-height: 1;
148
+ overflow: hidden;
149
+ text-overflow: ellipsis;
150
+ white-space: nowrap;
151
+ flex-shrink: 1;
152
+ min-width: 0;
153
+ "
93
154
  >
94
155
  {{ title }}
95
156
  </span>
@@ -103,10 +164,11 @@ watch(
103
164
  />
104
165
  </v-card-title>
105
166
  <v-divider />
106
- <v-card-text class="pa-0 flex-grow-1 overflow-hidden d-flex flex-column">
167
+ <v-card-text class="pa-0 flex-grow-1 overflow-hidden d-flex flex-column" style="min-height: 0">
107
168
  <div
108
- ref="scrollContainer"
109
- class="flex-grow-1 overflow-y-auto overflow-x-hidden"
169
+ ref="scroll-container"
170
+ class="flex-grow-1 overflow-y-hidden overflow-x-hidden d-flex flex-column"
171
+ style="min-height: 0"
110
172
  @scroll="handleScroll"
111
173
  >
112
174
  <slot />
@@ -118,15 +180,44 @@ watch(
118
180
  <style scoped>
119
181
  .tree-box {
120
182
  height: 100%;
183
+ min-height: 0;
121
184
  border-radius: 16px;
122
185
  background-color: transparent !important;
123
- backdrop-filter: blur(2px);
124
- border: 1px solid rgba(0, 0, 0, 0.2);
186
+ border: 1px solid rgba(255, 255, 255, 0.2) !important;
187
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
188
+ position: relative;
189
+ overflow: hidden;
190
+ transition:
191
+ background-color 0.3s ease,
192
+ backdrop-filter 0.3s ease;
193
+ }
194
+
195
+ .tree-box::before {
196
+ content: "";
197
+ position: absolute;
198
+ inset: 0;
199
+ background: rgba(255, 255, 255, var(--adaptive-opacity));
200
+ backdrop-filter: blur(var(--adaptive-blur)) brightness(var(--adaptive-brightness));
201
+ -webkit-backdrop-filter: blur(var(--adaptive-blur)) brightness(var(--adaptive-brightness));
202
+ mix-blend-mode: lighten;
203
+ z-index: 0;
204
+ pointer-events: none;
205
+ border-radius: inherit;
206
+ transition:
207
+ background-color 0.3s ease,
208
+ backdrop-filter 0.3s ease;
209
+ }
210
+
211
+ .tree-box > * {
212
+ position: relative;
213
+ z-index: 1;
125
214
  }
126
215
 
127
216
  .tree-box-header {
128
217
  height: 40px !important;
129
- padding: 0 8px !important;
130
- background-color: rgba(255, 255, 255, 0.1);
218
+ padding: 0 12px !important;
219
+ background-color: rgba(255, 255, 255, 0.05);
220
+ border-bottom: 1px solid rgba(0, 0, 0, 0.05);
221
+ flex-shrink: 0;
131
222
  }
132
223
  </style>
@@ -48,7 +48,7 @@ watch(
48
48
  { immediate: true },
49
49
  );
50
50
 
51
- watch(maxWidth, (newMax) => {
51
+ watch([maxWidth, () => additionalViews.value.length], ([newMax]) => {
52
52
  const hasAdditional = additionalViews.value.length > 0;
53
53
  const gap = hasAdditional ? GAP_WIDTH : 0;
54
54
  const total =
@@ -143,20 +143,20 @@ function onVerticalResizeStart(event, index) {
143
143
  const deltaPercent = (deltaY / containerHeight) * PERCENT_100;
144
144
  const minHeightPercent = (HEIGHT_MIN / containerHeight) * PERCENT_100;
145
145
 
146
- let newH1 = startHeight1 + deltaPercent;
147
- let newH2 = startHeight2 - deltaPercent;
146
+ let newHeight1 = startHeight1 + deltaPercent;
147
+ let newHeight2 = startHeight2 - deltaPercent;
148
148
 
149
- if (newH1 < minHeightPercent) {
150
- newH1 = minHeightPercent;
151
- newH2 = startHeight1 + startHeight2 - minHeightPercent;
152
- } else if (newH2 < minHeightPercent) {
153
- newH2 = minHeightPercent;
154
- newH1 = startHeight1 + startHeight2 - minHeightPercent;
149
+ if (newHeight1 < minHeightPercent) {
150
+ newHeight1 = minHeightPercent;
151
+ newHeight2 = startHeight1 + startHeight2 - minHeightPercent;
152
+ } else if (newHeight2 < minHeightPercent) {
153
+ newHeight2 = minHeightPercent;
154
+ newHeight1 = startHeight1 + startHeight2 - minHeightPercent;
155
155
  }
156
156
 
157
157
  const newHeights = [...rowHeights.value];
158
- newHeights[index] = newH1;
159
- newHeights[index + 1] = newH2;
158
+ newHeights[index] = newHeight1;
159
+ newHeights[index + 1] = newHeight2;
160
160
  treeviewStore.setRowHeights(newHeights);
161
161
 
162
162
  document.body.style.userSelect = "none";
@@ -272,7 +272,7 @@ function onVerticalResizeStart(event, index) {
272
272
  display: flex;
273
273
  flex-direction: column;
274
274
  height: 100%;
275
- overflow-y: auto;
275
+ overflow-y: hidden;
276
276
  overflow-x: hidden;
277
277
  flex-shrink: 0;
278
278
  }
@@ -286,6 +286,8 @@ function onVerticalResizeStart(event, index) {
286
286
  }
287
287
 
288
288
  .view-wrapper {
289
+ display: flex;
290
+ flex-direction: column;
289
291
  overflow: hidden;
290
292
  padding: 2px;
291
293
  transition: transform 0.2s;