@geode/opengeodeweb-front 10.18.0 → 10.18.1-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.
- package/app/components/Loading/EcoMessages.vue +3 -3
- package/app/components/Loading/Header.vue +15 -6
- package/app/components/Loading/Progress.vue +1 -1
- package/app/components/Loading.vue +13 -10
- package/app/components/Viewer/ObjectTree/Base/CommonTreeView.vue +3 -3
- package/app/components/Viewer/ObjectTree/Base/Controls.vue +17 -6
- package/app/components/Viewer/ObjectTree/Base/ItemLabel.vue +36 -2
- package/app/components/Viewer/ObjectTree/Base/TreeRow.vue +10 -6
- package/app/components/Viewer/ObjectTree/Box.vue +8 -50
- package/app/components/Viewer/ObjectTree/Layout.vue +156 -74
- package/app/components/Viewer/ObjectTree/Views/GlobalObjects.vue +17 -1
- package/app/components/Viewer/ObjectTree/Views/ModelComponents.vue +16 -0
- package/app/composables/use_adaptive_styles.js +55 -0
- package/app/composables/virtual_tree.js +28 -6
- package/app/stores/treeview.js +37 -4
- package/app/utils/string.js +26 -0
- package/package.json +3 -3
|
@@ -40,18 +40,18 @@ onUnmounted(() => {
|
|
|
40
40
|
</script>
|
|
41
41
|
|
|
42
42
|
<template>
|
|
43
|
-
<v-sheet color="transparent" height="160" class="position-relative overflow-visible mb-4">
|
|
43
|
+
<v-sheet color="transparent" min-height="160" class="position-relative overflow-visible mb-4">
|
|
44
44
|
<v-scroll-y-reverse-transition mode="out-in">
|
|
45
45
|
<v-card
|
|
46
46
|
:key="currentMessage"
|
|
47
47
|
rounded="lg"
|
|
48
|
-
class="pa-6 border mx-auto"
|
|
48
|
+
class="pa-4 pa-sm-6 border mx-auto"
|
|
49
49
|
color="rgba(255, 255, 255, 0.05)"
|
|
50
50
|
elevation="0"
|
|
51
51
|
style="border-color: rgba(255, 255, 255, 0.1) !important"
|
|
52
52
|
>
|
|
53
53
|
<v-card-title
|
|
54
|
-
class="d-flex align-center ga-3 pa-0 mb-3 text-subtitle-1 font-weight-bold text-white text-wrap"
|
|
54
|
+
class="d-flex align-center ga-3 pa-0 mb-2 mb-sm-3 text-body-1 text-sm-subtitle-1 font-weight-bold text-white text-wrap"
|
|
55
55
|
>
|
|
56
56
|
<v-icon
|
|
57
57
|
:icon="ecoMessages[currentMessage].icon"
|
|
@@ -8,18 +8,17 @@ const { logo } = defineProps({
|
|
|
8
8
|
</script>
|
|
9
9
|
|
|
10
10
|
<template>
|
|
11
|
-
<v-row justify="center" class="mb-8">
|
|
11
|
+
<v-row justify="center" class="mb-4 mb-sm-8">
|
|
12
12
|
<v-img
|
|
13
13
|
:src="logo"
|
|
14
|
-
|
|
15
|
-
width="180"
|
|
14
|
+
class="responsive-logo"
|
|
16
15
|
style="filter: drop-shadow(0 0 20px rgba(var(--v-theme-primary), 0.4))"
|
|
17
16
|
/>
|
|
18
17
|
</v-row>
|
|
19
18
|
|
|
20
|
-
<v-card color="transparent" elevation="0" class="mb-8 overflow-visible">
|
|
19
|
+
<v-card color="transparent" elevation="0" class="mb-4 mb-sm-8 overflow-visible">
|
|
21
20
|
<v-card-title
|
|
22
|
-
class="
|
|
21
|
+
class="font-weight-black text-white text-wrap pa-0 d-block responsive-title"
|
|
23
22
|
style="text-shadow: 0 0 20px rgba(255, 255, 255, 0.3)"
|
|
24
23
|
>
|
|
25
24
|
STARTING UP
|
|
@@ -35,7 +34,17 @@ const { logo } = defineProps({
|
|
|
35
34
|
</template>
|
|
36
35
|
|
|
37
36
|
<style scoped>
|
|
37
|
+
.responsive-logo {
|
|
38
|
+
width: clamp(100px, 20vw, 180px);
|
|
39
|
+
height: clamp(100px, 20vw, 180px);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.responsive-title {
|
|
43
|
+
font-size: clamp(2rem, 8vw, 4rem) !important;
|
|
44
|
+
line-height: 1.1 !important;
|
|
45
|
+
}
|
|
46
|
+
|
|
38
47
|
.ls-widest {
|
|
39
|
-
letter-spacing: 0.6em !important;
|
|
48
|
+
letter-spacing: clamp(0.2em, 2vw, 0.6em) !important;
|
|
40
49
|
}
|
|
41
50
|
</style>
|
|
@@ -44,7 +44,7 @@ onUnmounted(() => {
|
|
|
44
44
|
<Teleport to="body">
|
|
45
45
|
<div
|
|
46
46
|
v-if="show"
|
|
47
|
-
class="
|
|
47
|
+
class="transition-swing overflow-y-auto"
|
|
48
48
|
style="
|
|
49
49
|
position: fixed;
|
|
50
50
|
inset: 0;
|
|
@@ -58,23 +58,26 @@ onUnmounted(() => {
|
|
|
58
58
|
>
|
|
59
59
|
<div
|
|
60
60
|
style="
|
|
61
|
-
position:
|
|
61
|
+
position: fixed;
|
|
62
62
|
inset: 0;
|
|
63
63
|
background-image: radial-gradient(rgba(255, 255, 255, 0.08) 1px, transparent 0);
|
|
64
64
|
background-size: 40px 40px;
|
|
65
65
|
background-position: center;
|
|
66
66
|
pointer-events: none;
|
|
67
|
+
z-index: -1;
|
|
67
68
|
"
|
|
68
69
|
/>
|
|
69
70
|
|
|
70
|
-
<div
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
71
|
+
<div class="d-flex align-center justify-center pa-6" style="min-height: 100%">
|
|
72
|
+
<div
|
|
73
|
+
class="d-flex flex-column align-center text-center w-100"
|
|
74
|
+
style="max-width: 650px; gap: clamp(1rem, 4vh, 2rem)"
|
|
75
|
+
>
|
|
76
|
+
<LoadingHeader :logo="logo" />
|
|
77
|
+
<LoadingEcoMessages :app-name="appName" />
|
|
78
|
+
<LoadingProgress :progress="progress" />
|
|
79
|
+
<LoadingFooter />
|
|
80
|
+
</div>
|
|
78
81
|
</div>
|
|
79
82
|
</div>
|
|
80
83
|
</Teleport>
|
|
@@ -163,10 +163,10 @@ const { focusedIndex, handleKeyDown } = useTreeKeyboardNav(
|
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
.tree-row-wrapper {
|
|
166
|
-
min-height:
|
|
166
|
+
min-height: 28px !important;
|
|
167
167
|
cursor: pointer;
|
|
168
|
-
border-radius:
|
|
169
|
-
margin:
|
|
168
|
+
border-radius: 4px;
|
|
169
|
+
margin: 0 2px;
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
.tree-row-wrapper.is-focused {
|
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
import ActionButton from "@ogw_front/components/ActionButton.vue";
|
|
3
3
|
import SearchBar from "@ogw_front/components/SearchBar.vue";
|
|
4
4
|
|
|
5
|
-
const { search, sortType, filterOptions, availableFilterOptions } = defineProps({
|
|
5
|
+
const { search, sortType, filterOptions, availableFilterOptions, isCollapsed } = defineProps({
|
|
6
6
|
search: { type: String, required: true },
|
|
7
7
|
sortType: { type: String, required: true },
|
|
8
8
|
filterOptions: { type: Object, required: true },
|
|
9
9
|
availableFilterOptions: { type: Array, required: true },
|
|
10
|
+
isCollapsed: { type: Boolean, required: false, default: false },
|
|
10
11
|
});
|
|
11
12
|
|
|
12
|
-
const emit = defineEmits(["update:search", "toggle-sort", "collapse-all"]);
|
|
13
|
+
const emit = defineEmits(["update:search", "toggle-sort", "collapse-all", "expand-all"]);
|
|
13
14
|
|
|
14
15
|
const showSearch = ref(false);
|
|
15
16
|
|
|
@@ -24,7 +25,7 @@ watch(
|
|
|
24
25
|
</script>
|
|
25
26
|
|
|
26
27
|
<template>
|
|
27
|
-
<v-row dense align="center" class="pa-
|
|
28
|
+
<v-row dense align="center" class="pa-1 py-0">
|
|
28
29
|
<v-col cols="12">
|
|
29
30
|
<div
|
|
30
31
|
class="controls-capsule d-flex align-center rounded-pill px-1 overflow-hidden"
|
|
@@ -98,6 +99,7 @@ watch(
|
|
|
98
99
|
</v-list>
|
|
99
100
|
</v-menu>
|
|
100
101
|
<ActionButton
|
|
102
|
+
v-if="!isCollapsed"
|
|
101
103
|
tooltip="Collapse All"
|
|
102
104
|
icon="mdi-collapse-all-outline"
|
|
103
105
|
variant="text"
|
|
@@ -105,6 +107,15 @@ watch(
|
|
|
105
107
|
tooltipLocation="bottom"
|
|
106
108
|
@click="emit('collapse-all')"
|
|
107
109
|
/>
|
|
110
|
+
<ActionButton
|
|
111
|
+
v-else
|
|
112
|
+
tooltip="Expand All"
|
|
113
|
+
icon="mdi-expand-all-outline"
|
|
114
|
+
variant="text"
|
|
115
|
+
color="black"
|
|
116
|
+
tooltipLocation="bottom"
|
|
117
|
+
@click="emit('expand-all')"
|
|
118
|
+
/>
|
|
108
119
|
</div>
|
|
109
120
|
</div>
|
|
110
121
|
</v-col>
|
|
@@ -113,7 +124,7 @@ watch(
|
|
|
113
124
|
|
|
114
125
|
<style scoped>
|
|
115
126
|
.controls-capsule {
|
|
116
|
-
height:
|
|
127
|
+
height: 32px;
|
|
117
128
|
border: 1px solid transparent;
|
|
118
129
|
transition: all 0.3s ease;
|
|
119
130
|
width: fit-content;
|
|
@@ -133,12 +144,12 @@ watch(
|
|
|
133
144
|
:deep(.v-field__input) {
|
|
134
145
|
padding-top: 0 !important;
|
|
135
146
|
padding-bottom: 0 !important;
|
|
136
|
-
min-height:
|
|
147
|
+
min-height: 32px !important;
|
|
137
148
|
display: flex;
|
|
138
149
|
align-items: center;
|
|
139
150
|
}
|
|
140
151
|
|
|
141
152
|
:deep(.v-field__field) {
|
|
142
|
-
height:
|
|
153
|
+
height: 32px !important;
|
|
143
154
|
}
|
|
144
155
|
</style>
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
+
import { middleTruncate } from "@ogw_front/utils/string";
|
|
3
|
+
|
|
2
4
|
const { item, isLeaf } = defineProps({
|
|
3
5
|
item: { type: Object, required: true },
|
|
4
6
|
isLeaf: { type: Boolean, required: false, default: undefined },
|
|
@@ -6,8 +8,39 @@ const { item, isLeaf } = defineProps({
|
|
|
6
8
|
|
|
7
9
|
const emit = defineEmits(["contextmenu", "mouseenter", "mouseleave"]);
|
|
8
10
|
|
|
11
|
+
const labelContainer = useTemplateRef("label-container");
|
|
12
|
+
const { width: containerWidth } = useElementSize(labelContainer);
|
|
13
|
+
|
|
9
14
|
const actualItem = computed(() => item.raw || item);
|
|
10
15
|
|
|
16
|
+
const UUID_END_CHARS = 12;
|
|
17
|
+
const ELLIPSIS_LENGTH = 3;
|
|
18
|
+
const MIN_START_CHARS = 4;
|
|
19
|
+
|
|
20
|
+
const displayTitle = computed(() => {
|
|
21
|
+
const { title } = actualItem.value;
|
|
22
|
+
if (!title) {
|
|
23
|
+
return "";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Estimate max characters based on width (approx 9px per char for typical font)
|
|
27
|
+
// We subtract some padding/icon space
|
|
28
|
+
const estimatedCharWidth = 8.5;
|
|
29
|
+
const maxChars = Math.floor(containerWidth.value / estimatedCharWidth);
|
|
30
|
+
|
|
31
|
+
// Only truncate if the text is longer than what fits
|
|
32
|
+
if (title.length <= maxChars) {
|
|
33
|
+
return title;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Calculate dynamic start/end based on available space
|
|
37
|
+
// For UUIDs, showing the last 12 characters is often useful
|
|
38
|
+
const endChars = Math.min(UUID_END_CHARS, Math.floor(maxChars / ELLIPSIS_LENGTH));
|
|
39
|
+
const startChars = Math.max(MIN_START_CHARS, maxChars - endChars - ELLIPSIS_LENGTH);
|
|
40
|
+
|
|
41
|
+
return middleTruncate(title, maxChars, startChars, endChars);
|
|
42
|
+
});
|
|
43
|
+
|
|
11
44
|
const tooltipDisabled = computed(() => {
|
|
12
45
|
if (isLeaf !== undefined) {
|
|
13
46
|
return !isLeaf;
|
|
@@ -17,7 +50,7 @@ const tooltipDisabled = computed(() => {
|
|
|
17
50
|
</script>
|
|
18
51
|
|
|
19
52
|
<template>
|
|
20
|
-
<div class="tree-item-label-container w-100">
|
|
53
|
+
<div ref="label-container" class="tree-item-label-container w-100">
|
|
21
54
|
<v-tooltip :disabled="tooltipDisabled" location="right" open-delay="400">
|
|
22
55
|
<template #activator="{ props: tooltipProps }">
|
|
23
56
|
<span
|
|
@@ -28,7 +61,7 @@ const tooltipDisabled = computed(() => {
|
|
|
28
61
|
@mouseenter="emit('mouseenter')"
|
|
29
62
|
@mouseleave="emit('mouseleave')"
|
|
30
63
|
>
|
|
31
|
-
{{
|
|
64
|
+
{{ displayTitle }}
|
|
32
65
|
</span>
|
|
33
66
|
</template>
|
|
34
67
|
|
|
@@ -65,6 +98,7 @@ const tooltipDisabled = computed(() => {
|
|
|
65
98
|
display: inline-flex;
|
|
66
99
|
align-items: center;
|
|
67
100
|
cursor: pointer;
|
|
101
|
+
font-size: 0.8rem;
|
|
68
102
|
}
|
|
69
103
|
|
|
70
104
|
.inactive-item {
|
|
@@ -9,11 +9,11 @@ const { item, itemProps, selection, isSelected, getIndeterminate } = defineProps
|
|
|
9
9
|
|
|
10
10
|
defineEmits(["toggle-open", "toggle-select"]);
|
|
11
11
|
|
|
12
|
-
const INDENT_STEP =
|
|
12
|
+
const INDENT_STEP = 10;
|
|
13
13
|
</script>
|
|
14
14
|
|
|
15
15
|
<template>
|
|
16
|
-
<div class="tree-row-content d-flex align-center px-
|
|
16
|
+
<div class="tree-row-content d-flex align-center px-2 ps-2 w-100">
|
|
17
17
|
<div
|
|
18
18
|
v-if="item.depth > 0"
|
|
19
19
|
class="flex-shrink-0"
|
|
@@ -34,10 +34,10 @@ const INDENT_STEP = 16;
|
|
|
34
34
|
v-if="selection.selectable"
|
|
35
35
|
:icon="
|
|
36
36
|
getIndeterminate(item.raw)
|
|
37
|
-
? 'mdi-eye-minus'
|
|
37
|
+
? 'mdi-eye-minus-outline'
|
|
38
38
|
: isSelected(item.raw)
|
|
39
39
|
? 'mdi-eye'
|
|
40
|
-
: 'mdi-eye-off'
|
|
40
|
+
: 'mdi-eye-off-outline'
|
|
41
41
|
"
|
|
42
42
|
variant="text"
|
|
43
43
|
density="compact"
|
|
@@ -50,7 +50,11 @@ const INDENT_STEP = 16;
|
|
|
50
50
|
|
|
51
51
|
<div class="tree-title flex-grow-1 overflow-hidden d-flex align-center ms-1 pt-1">
|
|
52
52
|
<slot name="title" :item="item.raw" :is-leaf="item.isLeaf">
|
|
53
|
-
<v-list-item-title
|
|
53
|
+
<v-list-item-title
|
|
54
|
+
:class="{ 'font-weight-bold': !item.isLeaf }"
|
|
55
|
+
class="text-black"
|
|
56
|
+
style="font-size: 0.8rem !important"
|
|
57
|
+
>
|
|
54
58
|
{{ item.raw[itemProps.title] || item.id }}
|
|
55
59
|
</v-list-item-title>
|
|
56
60
|
</slot>
|
|
@@ -64,7 +68,7 @@ const INDENT_STEP = 16;
|
|
|
64
68
|
|
|
65
69
|
<style scoped>
|
|
66
70
|
.tree-row-content {
|
|
67
|
-
min-height:
|
|
71
|
+
min-height: 28px;
|
|
68
72
|
}
|
|
69
73
|
|
|
70
74
|
.icon-placeholder {
|
|
@@ -1,68 +1,27 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import {
|
|
2
|
+
import { useAdaptiveStyles } from "@ogw_front/composables/use_adaptive_styles";
|
|
3
3
|
|
|
4
4
|
const SCROLL_SYNC_DELAY = 50;
|
|
5
5
|
const SCROLL_THRESHOLD = 1;
|
|
6
|
-
const { title, closable, icon, mdiIcon, scrollTop } = defineProps({
|
|
6
|
+
const { title, closable, icon, mdiIcon, scrollTop, borderRadius } = defineProps({
|
|
7
7
|
title: { type: String, required: true },
|
|
8
8
|
closable: { type: Boolean, required: false, default: false },
|
|
9
9
|
icon: { type: String, required: false, default: "" },
|
|
10
10
|
mdiIcon: { type: String, required: false, default: "" },
|
|
11
11
|
scrollTop: { type: Number, required: false, default: 0 },
|
|
12
|
+
borderRadius: { type: String, required: false, default: "16px" },
|
|
13
|
+
borderLeft: { type: Boolean, required: false, default: true },
|
|
12
14
|
});
|
|
13
15
|
const emit = defineEmits(["close", "dragstart", "update:scrollTop"]);
|
|
14
16
|
|
|
15
17
|
const scrollContainer = useTemplateRef("scroll-container");
|
|
16
18
|
const treeviewBox = useTemplateRef("treeview-box");
|
|
17
|
-
const hybridViewerStore = useHybridViewerStore();
|
|
18
19
|
|
|
19
|
-
const
|
|
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);
|
|
20
|
+
const { adaptiveStyles } = useAdaptiveStyles(treeviewBox);
|
|
43
21
|
|
|
44
22
|
let isApplyingScroll = false;
|
|
45
23
|
let resizeObserver = undefined;
|
|
46
24
|
|
|
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
|
-
|
|
66
25
|
function handleScroll(event) {
|
|
67
26
|
if (isApplyingScroll) {
|
|
68
27
|
return;
|
|
@@ -121,7 +80,7 @@ watch(
|
|
|
121
80
|
ref="treeview-box"
|
|
122
81
|
variant="outlined"
|
|
123
82
|
class="tree-box d-flex flex-column"
|
|
124
|
-
:style="adaptiveStyles"
|
|
83
|
+
:style="[adaptiveStyles, { borderRadius, borderLeft: borderLeft ? undefined : 'none' }]"
|
|
125
84
|
>
|
|
126
85
|
<v-card-title
|
|
127
86
|
class="tree-box-header d-flex align-center"
|
|
@@ -181,7 +140,6 @@ watch(
|
|
|
181
140
|
.tree-box {
|
|
182
141
|
height: 100%;
|
|
183
142
|
min-height: 0;
|
|
184
|
-
border-radius: 16px;
|
|
185
143
|
background-color: transparent !important;
|
|
186
144
|
border: 1px solid rgba(255, 255, 255, 0.2) !important;
|
|
187
145
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
|
|
@@ -214,8 +172,8 @@ watch(
|
|
|
214
172
|
}
|
|
215
173
|
|
|
216
174
|
.tree-box-header {
|
|
217
|
-
height:
|
|
218
|
-
padding: 0
|
|
175
|
+
height: 32px !important;
|
|
176
|
+
padding: 0 10px !important;
|
|
219
177
|
background-color: rgba(255, 255, 255, 0.05);
|
|
220
178
|
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
|
221
179
|
flex-shrink: 0;
|
|
@@ -3,15 +3,17 @@ import GlobalObjects from "@ogw_front/components/Viewer/ObjectTree/Views/GlobalO
|
|
|
3
3
|
import ModelComponents from "@ogw_front/components/Viewer/ObjectTree/Views/ModelComponents.vue";
|
|
4
4
|
import ViewerObjectTreeBox from "@ogw_front/components/Viewer/ObjectTree/Box.vue";
|
|
5
5
|
import { geode_objects } from "@ogw_front/assets/geode_objects";
|
|
6
|
+
import { useAdaptiveStyles } from "@ogw_front/composables/use_adaptive_styles";
|
|
6
7
|
import { useTreeviewStore } from "@ogw_front/stores/treeview";
|
|
7
8
|
|
|
8
|
-
const WIDTH_MIN =
|
|
9
|
+
const WIDTH_MIN = 100;
|
|
9
10
|
const HEIGHT_MIN = 150;
|
|
10
|
-
const GAP_WIDTH =
|
|
11
|
+
const GAP_WIDTH = 0;
|
|
11
12
|
const PERCENT_100 = 100;
|
|
12
13
|
|
|
13
14
|
const TOTAL_PERCENT = 100;
|
|
14
15
|
const MAX_PANEL_WIDTH_RATIO = 0.8;
|
|
16
|
+
const AUTO_CLOSE_THRESHOLD = 80;
|
|
15
17
|
|
|
16
18
|
const { containerWidth } = defineProps({
|
|
17
19
|
containerWidth: { type: Number, required: true },
|
|
@@ -20,16 +22,23 @@ const { containerWidth } = defineProps({
|
|
|
20
22
|
const treeviewStore = useTreeviewStore();
|
|
21
23
|
const emit = defineEmits(["show-menu"]);
|
|
22
24
|
|
|
25
|
+
const activityBar = useTemplateRef("activity-bar");
|
|
26
|
+
const { adaptiveStyles: activityBarAdaptiveStyles } = useAdaptiveStyles(activityBar);
|
|
27
|
+
|
|
23
28
|
const maxWidth = computed(() => containerWidth * MAX_PANEL_WIDTH_RATIO);
|
|
24
29
|
|
|
25
|
-
const mainView = computed(() => treeviewStore.opened_views
|
|
26
|
-
const additionalViews = computed(() =>
|
|
30
|
+
const mainView = computed(() => treeviewStore.opened_views.find((view) => view.id === "main"));
|
|
31
|
+
const additionalViews = computed(() =>
|
|
32
|
+
treeviewStore.opened_views.filter((view) => view.id !== "main"),
|
|
33
|
+
);
|
|
27
34
|
|
|
28
35
|
const totalWidth = computed(() => {
|
|
29
36
|
const hasAdditional = additionalViews.value.length > 0;
|
|
30
|
-
const
|
|
37
|
+
const hasMain = Boolean(mainView.value);
|
|
38
|
+
const gap = hasAdditional && hasMain ? GAP_WIDTH : 0;
|
|
39
|
+
const firstColWidth = hasMain ? treeviewStore.panelWidth : 0;
|
|
31
40
|
const secondColWidth = hasAdditional ? treeviewStore.additionalPanelWidth : 0;
|
|
32
|
-
return `${
|
|
41
|
+
return `${firstColWidth + secondColWidth + gap}px`;
|
|
33
42
|
});
|
|
34
43
|
|
|
35
44
|
const rowHeights = computed({
|
|
@@ -90,7 +99,7 @@ function onResizeStart(event) {
|
|
|
90
99
|
const startX = event.clientX;
|
|
91
100
|
function resize(move_event) {
|
|
92
101
|
const deltaX = move_event.clientX - startX;
|
|
93
|
-
let newWidth =
|
|
102
|
+
let newWidth = startWidth + deltaX;
|
|
94
103
|
const hasAdditional = additionalViews.value.length > 0;
|
|
95
104
|
const gap = hasAdditional ? GAP_WIDTH : 0;
|
|
96
105
|
const currentTotalWidth =
|
|
@@ -98,6 +107,12 @@ function onResizeStart(event) {
|
|
|
98
107
|
if (currentTotalWidth > maxWidth.value) {
|
|
99
108
|
newWidth = maxWidth.value - (hasAdditional ? treeviewStore.additionalPanelWidth : 0) - gap;
|
|
100
109
|
}
|
|
110
|
+
|
|
111
|
+
if (newWidth < AUTO_CLOSE_THRESHOLD) {
|
|
112
|
+
treeviewStore.closeView("main");
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
101
116
|
treeviewStore.setPanelWidth(Math.max(WIDTH_MIN, newWidth));
|
|
102
117
|
document.body.style.userSelect = "none";
|
|
103
118
|
}
|
|
@@ -115,11 +130,13 @@ function onAdditionalResizeStart(event) {
|
|
|
115
130
|
const startX = event.clientX;
|
|
116
131
|
function resize(move_event) {
|
|
117
132
|
const deltaX = move_event.clientX - startX;
|
|
118
|
-
|
|
133
|
+
const newWidth = startWidth + deltaX;
|
|
119
134
|
const currentTotalWidth = treeviewStore.panelWidth + newWidth + GAP_WIDTH;
|
|
120
|
-
if (
|
|
121
|
-
|
|
135
|
+
if (newWidth < AUTO_CLOSE_THRESHOLD) {
|
|
136
|
+
treeviewStore.closeView(additionalViews.value.at(-1).id);
|
|
137
|
+
return;
|
|
122
138
|
}
|
|
139
|
+
|
|
123
140
|
treeviewStore.setAdditionalPanelWidth(Math.max(WIDTH_MIN, newWidth));
|
|
124
141
|
document.body.style.userSelect = "none";
|
|
125
142
|
}
|
|
@@ -177,88 +194,154 @@ function onVerticalResizeStart(event, index) {
|
|
|
177
194
|
<template>
|
|
178
195
|
<div
|
|
179
196
|
v-if="treeviewStore.items.length > 0"
|
|
180
|
-
class="treeview-
|
|
181
|
-
:style="{ width: totalWidth }"
|
|
197
|
+
class="treeview-layout d-flex"
|
|
182
198
|
@contextmenu.prevent
|
|
183
199
|
@mousedown.stop
|
|
184
200
|
>
|
|
185
201
|
<div
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}"
|
|
202
|
+
ref="activity-bar"
|
|
203
|
+
class="activity-bar d-flex flex-column align-center py-2"
|
|
204
|
+
:style="activityBarAdaptiveStyles"
|
|
190
205
|
>
|
|
191
|
-
<
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
:
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
206
|
+
<v-btn
|
|
207
|
+
icon="mdi-file-tree-outline"
|
|
208
|
+
variant="text"
|
|
209
|
+
:color="mainView ? 'primary' : 'black'"
|
|
210
|
+
class="mb-2"
|
|
211
|
+
v-tooltip="'Toggle Objects'"
|
|
212
|
+
@click="treeviewStore.toggleView('main')"
|
|
213
|
+
/>
|
|
199
214
|
</div>
|
|
200
215
|
|
|
201
|
-
<div
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
@
|
|
218
|
-
@drop="onDrop(index + 1)"
|
|
216
|
+
<div class="treeview-container d-flex" :style="{ width: totalWidth }">
|
|
217
|
+
<div
|
|
218
|
+
v-if="mainView"
|
|
219
|
+
class="column main-column"
|
|
220
|
+
:style="{
|
|
221
|
+
width: `${treeviewStore.panelWidth}px`,
|
|
222
|
+
}"
|
|
223
|
+
>
|
|
224
|
+
<ViewerObjectTreeBox
|
|
225
|
+
:title="mainView?.title || 'Objects'"
|
|
226
|
+
mdi-icon="mdi-file-tree-outline"
|
|
227
|
+
:scroll-top="mainView?.scrollTop || 0"
|
|
228
|
+
closable
|
|
229
|
+
:border-radius="additionalViews.length > 0 ? '0' : '0 16px 16px 0'"
|
|
230
|
+
:border-left="false"
|
|
231
|
+
@close="treeviewStore.closeView('main')"
|
|
232
|
+
@update:scroll-top="mainView && treeviewStore.setScrollTop(mainView.id, $event)"
|
|
219
233
|
>
|
|
220
|
-
<
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
234
|
+
<GlobalObjects data-testid="mainObjectTree" @show-menu="emit('show-menu', $event)" />
|
|
235
|
+
</ViewerObjectTreeBox>
|
|
236
|
+
</div>
|
|
237
|
+
|
|
238
|
+
<div
|
|
239
|
+
v-if="mainView && additionalViews.length > 0"
|
|
240
|
+
class="column-separator"
|
|
241
|
+
@mousedown="onResizeStart"
|
|
242
|
+
/>
|
|
243
|
+
|
|
244
|
+
<div
|
|
245
|
+
v-if="additionalViews.length > 0"
|
|
246
|
+
class="column additional-column"
|
|
247
|
+
:style="{
|
|
248
|
+
width: `${treeviewStore.additionalPanelWidth}px`,
|
|
249
|
+
}"
|
|
250
|
+
>
|
|
251
|
+
<template v-for="(view, index) in additionalViews" :key="view.id">
|
|
252
|
+
<div
|
|
253
|
+
class="view-wrapper"
|
|
254
|
+
:class="{
|
|
255
|
+
'drag-over': draggedIndex !== undefined && draggedIndex !== index + 1,
|
|
256
|
+
}"
|
|
257
|
+
:style="{ flex: `0 0 ${rowHeights[index]}%` }"
|
|
258
|
+
@dragover="onDragOver"
|
|
259
|
+
@drop="onDrop(index + 1)"
|
|
228
260
|
>
|
|
229
|
-
<
|
|
230
|
-
|
|
231
|
-
:
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
261
|
+
<ViewerObjectTreeBox
|
|
262
|
+
:title="view.title"
|
|
263
|
+
:icon="geode_objects[view.geode_object_type]?.image"
|
|
264
|
+
:scroll-top="view.scrollTop"
|
|
265
|
+
closable
|
|
266
|
+
:border-radius="index === additionalViews.length - 1 ? '0 16px 16px 0' : '0'"
|
|
267
|
+
:border-left="false"
|
|
268
|
+
@close="treeviewStore.closeView(view.id)"
|
|
269
|
+
@dragstart="onDragStart(index + 1)"
|
|
270
|
+
@update:scroll-top="treeviewStore.setScrollTop(view.id, $event)"
|
|
271
|
+
>
|
|
272
|
+
<ModelComponents
|
|
273
|
+
data-testid="modelComponentsObjectTree"
|
|
274
|
+
:id="view.id"
|
|
275
|
+
@show-menu="emit('show-menu', $event)"
|
|
276
|
+
/>
|
|
277
|
+
</ViewerObjectTreeBox>
|
|
278
|
+
</div>
|
|
279
|
+
<div
|
|
280
|
+
v-if="index < additionalViews.length - 1"
|
|
281
|
+
class="v-split-resizer"
|
|
282
|
+
@mousedown="onVerticalResizeStart($event, index)"
|
|
283
|
+
/>
|
|
284
|
+
</template>
|
|
285
|
+
</div>
|
|
286
|
+
<div
|
|
287
|
+
v-if="treeviewStore.opened_views.length > 0"
|
|
288
|
+
class="total-resizer"
|
|
289
|
+
@mousedown="
|
|
290
|
+
additionalViews.length > 0
|
|
291
|
+
? onAdditionalResizeStart($event)
|
|
292
|
+
: mainView
|
|
293
|
+
? onResizeStart($event)
|
|
294
|
+
: undefined
|
|
295
|
+
"
|
|
296
|
+
/>
|
|
242
297
|
</div>
|
|
243
|
-
<div
|
|
244
|
-
class="total-resizer"
|
|
245
|
-
@mousedown="
|
|
246
|
-
additionalViews.length > 0 ? onAdditionalResizeStart($event) : onResizeStart($event)
|
|
247
|
-
"
|
|
248
|
-
/>
|
|
249
298
|
</div>
|
|
250
299
|
</template>
|
|
251
300
|
|
|
252
301
|
<style scoped>
|
|
253
|
-
.treeview-
|
|
302
|
+
.treeview-layout {
|
|
254
303
|
position: absolute;
|
|
255
304
|
z-index: 2;
|
|
256
305
|
left: 0;
|
|
257
306
|
top: 0;
|
|
258
307
|
height: calc(100vh - 100px);
|
|
259
|
-
margin-top:
|
|
260
|
-
margin-left: 10px;
|
|
308
|
+
margin-top: 8px;
|
|
261
309
|
pointer-events: auto;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.activity-bar {
|
|
313
|
+
width: 48px;
|
|
314
|
+
height: 100%;
|
|
315
|
+
border-radius: 16px 0 0 16px;
|
|
316
|
+
margin-left: 10px;
|
|
317
|
+
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
|
|
318
|
+
position: relative;
|
|
319
|
+
overflow: hidden;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.activity-bar::before {
|
|
323
|
+
content: "";
|
|
324
|
+
position: absolute;
|
|
325
|
+
inset: 0;
|
|
326
|
+
background: rgba(255, 255, 255, var(--adaptive-opacity));
|
|
327
|
+
backdrop-filter: blur(var(--adaptive-blur)) brightness(var(--adaptive-brightness));
|
|
328
|
+
-webkit-backdrop-filter: blur(var(--adaptive-blur)) brightness(var(--adaptive-brightness));
|
|
329
|
+
mix-blend-mode: lighten;
|
|
330
|
+
z-index: 0;
|
|
331
|
+
pointer-events: none;
|
|
332
|
+
border-radius: inherit;
|
|
333
|
+
transition:
|
|
334
|
+
background-color 0.3s ease,
|
|
335
|
+
backdrop-filter 0.3s ease;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.activity-bar > * {
|
|
339
|
+
position: relative;
|
|
340
|
+
z-index: 1;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.treeview-container {
|
|
344
|
+
height: 100%;
|
|
262
345
|
width: max-content;
|
|
263
346
|
min-width: min-content;
|
|
264
347
|
}
|
|
@@ -289,7 +372,6 @@ function onVerticalResizeStart(event, index) {
|
|
|
289
372
|
display: flex;
|
|
290
373
|
flex-direction: column;
|
|
291
374
|
overflow: hidden;
|
|
292
|
-
padding: 2px;
|
|
293
375
|
transition: transform 0.2s;
|
|
294
376
|
min-height: 150px;
|
|
295
377
|
}
|
|
@@ -361,6 +443,6 @@ function onVerticalResizeStart(event, index) {
|
|
|
361
443
|
}
|
|
362
444
|
|
|
363
445
|
.total-resizer:hover {
|
|
364
|
-
background-color:
|
|
446
|
+
background-color: transparent;
|
|
365
447
|
}
|
|
366
448
|
</style>
|
|
@@ -99,6 +99,20 @@ function handleHoverLeave({ item }) {
|
|
|
99
99
|
}
|
|
100
100
|
onHoverLeave(actualItem.id);
|
|
101
101
|
}
|
|
102
|
+
|
|
103
|
+
function expandAll() {
|
|
104
|
+
const allIds = [];
|
|
105
|
+
function traverse(itemsList) {
|
|
106
|
+
for (const item of itemsList) {
|
|
107
|
+
if (item.children && item.children.length > 0) {
|
|
108
|
+
allIds.push(item.id);
|
|
109
|
+
traverse(item.children);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
traverse(treeviewStore.items);
|
|
114
|
+
opened.value = allIds;
|
|
115
|
+
}
|
|
102
116
|
</script>
|
|
103
117
|
|
|
104
118
|
<template>
|
|
@@ -108,8 +122,10 @@ function handleHoverLeave({ item }) {
|
|
|
108
122
|
:sort-type="sortType"
|
|
109
123
|
:filter-options="filterOptions"
|
|
110
124
|
:available-filter-options="availableFilterOptions"
|
|
125
|
+
:is-collapsed="opened.length === 0"
|
|
111
126
|
@toggle-sort="toggleSort"
|
|
112
127
|
@collapse-all="opened = []"
|
|
128
|
+
@expand-all="expandAll"
|
|
113
129
|
/>
|
|
114
130
|
|
|
115
131
|
<CommonTreeView
|
|
@@ -177,6 +193,6 @@ function handleHoverLeave({ item }) {
|
|
|
177
193
|
|
|
178
194
|
.transparent-treeview {
|
|
179
195
|
background-color: transparent;
|
|
180
|
-
margin:
|
|
196
|
+
margin: 2px 0;
|
|
181
197
|
}
|
|
182
198
|
</style>
|
|
@@ -111,6 +111,20 @@ function handleHoverEnter({ item, immediate = false }) {
|
|
|
111
111
|
function handleHoverLeave() {
|
|
112
112
|
onHoverLeave(id);
|
|
113
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
|
+
}
|
|
114
128
|
</script>
|
|
115
129
|
|
|
116
130
|
<template>
|
|
@@ -120,8 +134,10 @@ function handleHoverLeave() {
|
|
|
120
134
|
:sort-type="sortType"
|
|
121
135
|
:filter-options="filterOptions"
|
|
122
136
|
:available-filter-options="availableFilterOptions"
|
|
137
|
+
:is-collapsed="opened.length === 0"
|
|
123
138
|
@toggle-sort="toggleSort"
|
|
124
139
|
@collapse-all="opened = []"
|
|
140
|
+
@expand-all="expandAll"
|
|
125
141
|
/>
|
|
126
142
|
|
|
127
143
|
<FetchingData v-if="rawItems === undefined" :size="48" :width="4" text="" />
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { computed, ref, watch } from "vue";
|
|
2
|
+
import { useElementBounding, useThrottleFn } from "@vueuse/core";
|
|
3
|
+
import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer";
|
|
4
|
+
|
|
5
|
+
const LUMINANCE_THRESHOLD = 0.65;
|
|
6
|
+
const ADAPTIVE_EXPONENT = 0.3;
|
|
7
|
+
|
|
8
|
+
const MIN_BLUR = 8;
|
|
9
|
+
const MAX_BLUR = 25;
|
|
10
|
+
|
|
11
|
+
const MIN_OPACITY = 0;
|
|
12
|
+
const MAX_OPACITY = 0.5;
|
|
13
|
+
|
|
14
|
+
const MIN_BOOST = 1;
|
|
15
|
+
const MAX_BOOST = 1.2;
|
|
16
|
+
const ADAPTIVE_REFRESH_RATE = 150;
|
|
17
|
+
|
|
18
|
+
export function useAdaptiveStyles(targetRef) {
|
|
19
|
+
const hybridViewerStore = useHybridViewerStore();
|
|
20
|
+
const { x, y, width, height } = useElementBounding(targetRef);
|
|
21
|
+
const brightness = ref(LUMINANCE_THRESHOLD);
|
|
22
|
+
|
|
23
|
+
const updateBrightness = useThrottleFn(() => {
|
|
24
|
+
brightness.value = hybridViewerStore.getAverageBrightness({
|
|
25
|
+
x: x.value,
|
|
26
|
+
y: y.value,
|
|
27
|
+
width: width.value,
|
|
28
|
+
height: height.value,
|
|
29
|
+
});
|
|
30
|
+
}, ADAPTIVE_REFRESH_RATE);
|
|
31
|
+
|
|
32
|
+
watch([x, y, width, height, () => hybridViewerStore.latestImage], updateBrightness, {
|
|
33
|
+
immediate: true,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const adaptiveStyles = computed(() => {
|
|
37
|
+
const normalized = Math.min(1, brightness.value / LUMINANCE_THRESHOLD);
|
|
38
|
+
const darkFactor = (1 - normalized) ** ADAPTIVE_EXPONENT;
|
|
39
|
+
|
|
40
|
+
const blur = MIN_BLUR + darkFactor * (MAX_BLUR - MIN_BLUR);
|
|
41
|
+
const opacity = MIN_OPACITY + darkFactor * (MAX_OPACITY - MIN_OPACITY);
|
|
42
|
+
const brightnessBoost = MIN_BOOST + darkFactor * (MAX_BOOST - MIN_BOOST);
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
"--adaptive-blur": `${blur}px`,
|
|
46
|
+
"--adaptive-opacity": opacity,
|
|
47
|
+
"--adaptive-brightness": brightnessBoost,
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
adaptiveStyles,
|
|
53
|
+
brightness,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -112,10 +112,6 @@ export function useVirtualTree(propsIn, emit) {
|
|
|
112
112
|
: (item[actualItemProps.value.title] || "").toLowerCase().includes(lowerSearch) ||
|
|
113
113
|
String(id).toLowerCase().includes(lowerSearch);
|
|
114
114
|
|
|
115
|
-
if (!hasChildren && !matches) {
|
|
116
|
-
continue;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
115
|
if (hasChildren) {
|
|
120
116
|
const subtree = [];
|
|
121
117
|
flattenTree(children, depth + 1, subtree);
|
|
@@ -127,10 +123,15 @@ export function useVirtualTree(propsIn, emit) {
|
|
|
127
123
|
raw: item,
|
|
128
124
|
id,
|
|
129
125
|
depth,
|
|
130
|
-
isOpen
|
|
126
|
+
isOpen,
|
|
131
127
|
isLeaf: false,
|
|
132
128
|
});
|
|
133
|
-
|
|
129
|
+
if (isOpen) {
|
|
130
|
+
result.push(...subtree);
|
|
131
|
+
}
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (!matches) {
|
|
134
135
|
continue;
|
|
135
136
|
}
|
|
136
137
|
}
|
|
@@ -150,8 +151,29 @@ export function useVirtualTree(propsIn, emit) {
|
|
|
150
151
|
return result;
|
|
151
152
|
}
|
|
152
153
|
|
|
154
|
+
function traverse(itemsList, allIds) {
|
|
155
|
+
for (const item of itemsList) {
|
|
156
|
+
const children = item[actualItemProps.value.children];
|
|
157
|
+
if (children && children.length > 0) {
|
|
158
|
+
allIds.push(item[actualItemProps.value.value]);
|
|
159
|
+
traverse(children, allIds);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
153
164
|
const displayItems = computed(() => flattenTree(props.value.items || []));
|
|
154
165
|
|
|
166
|
+
watch(
|
|
167
|
+
() => props.value.search,
|
|
168
|
+
(newSearch, oldSearch) => {
|
|
169
|
+
if (newSearch && !oldSearch) {
|
|
170
|
+
const allIds = [];
|
|
171
|
+
traverse(props.value.items || [], allIds);
|
|
172
|
+
emit("update:opened", [...new Set([...(props.value.opened || []), ...allIds])]);
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
);
|
|
176
|
+
|
|
155
177
|
return {
|
|
156
178
|
actualItemProps,
|
|
157
179
|
actualSelection,
|
package/app/stores/treeview.js
CHANGED
|
@@ -68,9 +68,22 @@ export const useTreeviewStore = defineStore("treeview", () => {
|
|
|
68
68
|
}
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
-
function closeView(
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
function closeView(id) {
|
|
72
|
+
opened_views.value = opened_views.value.filter((view) => view.id !== id);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function toggleView(id) {
|
|
76
|
+
const index = opened_views.value.findIndex((view) => view.id === id);
|
|
77
|
+
if (index !== -1) {
|
|
78
|
+
closeView(id);
|
|
79
|
+
} else if (id === "main") {
|
|
80
|
+
opened_views.value.unshift({
|
|
81
|
+
type: "object",
|
|
82
|
+
id: "main",
|
|
83
|
+
title: "Objects",
|
|
84
|
+
scrollTop: 0,
|
|
85
|
+
opened: [],
|
|
86
|
+
});
|
|
74
87
|
}
|
|
75
88
|
}
|
|
76
89
|
|
|
@@ -116,7 +129,7 @@ export const useTreeviewStore = defineStore("treeview", () => {
|
|
|
116
129
|
function displayAdditionalTree(id, title, geodeObjectType) {
|
|
117
130
|
const index = opened_views.value.findIndex((view) => view.id === id);
|
|
118
131
|
if (index !== -1) {
|
|
119
|
-
return closeView(
|
|
132
|
+
return closeView(id);
|
|
120
133
|
}
|
|
121
134
|
additionalPanelWidth.value = panelWidth.value;
|
|
122
135
|
opened_views.value.push({
|
|
@@ -216,6 +229,24 @@ export const useTreeviewStore = defineStore("treeview", () => {
|
|
|
216
229
|
};
|
|
217
230
|
}
|
|
218
231
|
|
|
232
|
+
function renameItem(id, newName) {
|
|
233
|
+
for (const group of items.value) {
|
|
234
|
+
const child = group.children.find((childItem) => childItem.id === id);
|
|
235
|
+
if (child) {
|
|
236
|
+
child.title = newName;
|
|
237
|
+
const options = { numeric: true, sensitivity: "base" };
|
|
238
|
+
group.children.sort((childA, childB) =>
|
|
239
|
+
childA.title.localeCompare(childB.title, undefined, options),
|
|
240
|
+
);
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
const view = opened_views.value.find((openedView) => openedView.id === id);
|
|
245
|
+
if (view) {
|
|
246
|
+
view.title = newName;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
219
250
|
return {
|
|
220
251
|
items,
|
|
221
252
|
selection,
|
|
@@ -226,8 +257,10 @@ export const useTreeviewStore = defineStore("treeview", () => {
|
|
|
226
257
|
rowHeights,
|
|
227
258
|
addItem,
|
|
228
259
|
removeItem,
|
|
260
|
+
renameItem,
|
|
229
261
|
displayAdditionalTree,
|
|
230
262
|
closeView,
|
|
263
|
+
toggleView,
|
|
231
264
|
moveView,
|
|
232
265
|
importStores,
|
|
233
266
|
displayFileTree,
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const DEFAULT_MAX_LENGTH = 20;
|
|
2
|
+
const DEFAULT_START_CHARS = 8;
|
|
3
|
+
const DEFAULT_END_CHARS = 6;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Truncates a string in the middle.
|
|
7
|
+
* @param {string} text - The string to truncate.
|
|
8
|
+
* @param {number} maxLength - The maximum length of the string before truncation.
|
|
9
|
+
* @param {number} startChars - Number of characters to keep at the beginning.
|
|
10
|
+
* @param {number} endChars - Number of characters to keep at the end.
|
|
11
|
+
* @returns {string} The truncated string.
|
|
12
|
+
*/
|
|
13
|
+
export function middleTruncate(
|
|
14
|
+
text,
|
|
15
|
+
maxLength = DEFAULT_MAX_LENGTH,
|
|
16
|
+
startChars = DEFAULT_START_CHARS,
|
|
17
|
+
endChars = DEFAULT_END_CHARS,
|
|
18
|
+
) {
|
|
19
|
+
if (!text || text.length <= maxLength) {
|
|
20
|
+
return text;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const start = text.slice(0, startChars);
|
|
24
|
+
const end = text.slice(-endChars);
|
|
25
|
+
return `${start}...${end}`;
|
|
26
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geode/opengeodeweb-front",
|
|
3
|
-
"version": "10.18.
|
|
3
|
+
"version": "10.18.1-rc.2",
|
|
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": {
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"build": ""
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@geode/opengeodeweb-back": "
|
|
38
|
-
"@geode/opengeodeweb-viewer": "
|
|
37
|
+
"@geode/opengeodeweb-back": "next",
|
|
38
|
+
"@geode/opengeodeweb-viewer": "next",
|
|
39
39
|
"@google-cloud/run": "3.2.0",
|
|
40
40
|
"@kitware/vtk.js": "33.3.0",
|
|
41
41
|
"@mdi/font": "7.4.47",
|