@geode/opengeodeweb-front 10.33.2 → 10.33.3-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.
|
@@ -21,6 +21,11 @@ const TRUNCATE_START_CHARS = 12;
|
|
|
21
21
|
const TRUNCATE_END_CHARS = 7;
|
|
22
22
|
|
|
23
23
|
const { copy, copied } = useClipboard({ copiedDuring: COPIED_TIMEOUT });
|
|
24
|
+
const copiedId = ref("");
|
|
25
|
+
|
|
26
|
+
function isCopied(id) {
|
|
27
|
+
return copied.value && copiedId.value === id;
|
|
28
|
+
}
|
|
24
29
|
|
|
25
30
|
const menuStore = useMenuStore();
|
|
26
31
|
const dataStore = useDataStore();
|
|
@@ -87,6 +92,7 @@ async function copyId(targetId) {
|
|
|
87
92
|
}
|
|
88
93
|
try {
|
|
89
94
|
await copy(targetId);
|
|
95
|
+
copiedId.value = targetId;
|
|
90
96
|
} catch (error) {
|
|
91
97
|
console.error("Failed to copy ID:", error);
|
|
92
98
|
}
|
|
@@ -102,7 +108,7 @@ function formatId(id) {
|
|
|
102
108
|
return `${id.slice(0, ID_SLICE_START)}...${id.slice(id.length - ID_SLICE_END_OFFSET)}`;
|
|
103
109
|
}
|
|
104
110
|
|
|
105
|
-
const formattedId = computed(() => formatId(metaData
|
|
111
|
+
const formattedId = computed(() => formatId(metaData.id));
|
|
106
112
|
</script>
|
|
107
113
|
|
|
108
114
|
<template>
|
|
@@ -136,12 +142,12 @@ const formattedId = computed(() => formatId(metaData?.id));
|
|
|
136
142
|
@click.stop="copyId(metaData.id)"
|
|
137
143
|
>
|
|
138
144
|
<span class="id-text">
|
|
139
|
-
{{
|
|
145
|
+
{{ isCopied(metaData.id) ? "COPIED!" : formattedId }}
|
|
140
146
|
</span>
|
|
141
147
|
<v-icon
|
|
142
|
-
:icon="
|
|
148
|
+
:icon="isCopied(metaData.id) ? 'mdi-check' : 'mdi-content-copy'"
|
|
143
149
|
size="10"
|
|
144
|
-
:color="
|
|
150
|
+
:color="isCopied(metaData.id) ? 'success' : 'white'"
|
|
145
151
|
class="ml-1"
|
|
146
152
|
/>
|
|
147
153
|
</v-col>
|
|
@@ -166,9 +172,14 @@ const formattedId = computed(() => formatId(metaData?.id));
|
|
|
166
172
|
@click.stop="copyId(componentItem.id)"
|
|
167
173
|
>
|
|
168
174
|
<span class="id-text">
|
|
169
|
-
{{ formatId(componentItem.id) }}
|
|
175
|
+
{{ isCopied(componentItem.id) ? "COPIED!" : formatId(componentItem.id) }}
|
|
170
176
|
</span>
|
|
171
|
-
<v-icon
|
|
177
|
+
<v-icon
|
|
178
|
+
:icon="isCopied(componentItem.id) ? 'mdi-check' : 'mdi-content-copy'"
|
|
179
|
+
size="10"
|
|
180
|
+
:color="isCopied(componentItem.id) ? 'success' : 'white'"
|
|
181
|
+
class="ml-1"
|
|
182
|
+
/>
|
|
172
183
|
</v-col>
|
|
173
184
|
</template>
|
|
174
185
|
</v-row>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { middleTruncate } from "@ogw_front/utils/string";
|
|
3
3
|
import { useClipboard } from "@vueuse/core";
|
|
4
4
|
import { useFeedbackStore } from "@ogw_front/stores/feedback";
|
|
5
|
+
import { useResponsiveMiddleTruncate } from "@ogw_front/composables/responsive_middle_truncate";
|
|
5
6
|
|
|
6
7
|
const feedbackStore = useFeedbackStore();
|
|
7
8
|
const { copy } = useClipboard();
|
|
@@ -18,33 +19,20 @@ const { width: containerWidth } = useElementSize(labelContainer);
|
|
|
18
19
|
|
|
19
20
|
const actualItem = computed(() => item.raw || item);
|
|
20
21
|
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
const
|
|
22
|
+
const TOOLTIP_NAME_MAX_LENGTH = 40;
|
|
23
|
+
const TOOLTIP_NAME_START_CHARS = 10;
|
|
24
|
+
const TOOLTIP_NAME_END_CHARS = 8;
|
|
24
25
|
|
|
25
|
-
const displayTitle =
|
|
26
|
-
const { title } = actualItem.value;
|
|
27
|
-
if (!title) {
|
|
28
|
-
return "";
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Estimate max characters based on width (approx 9px per char for typical font)
|
|
32
|
-
// We subtract some padding/icon space
|
|
33
|
-
const estimatedCharWidth = 8.5;
|
|
34
|
-
const maxChars = Math.floor(containerWidth.value / estimatedCharWidth);
|
|
35
|
-
|
|
36
|
-
// Only truncate if the text is longer than what fits
|
|
37
|
-
if (title.length <= maxChars) {
|
|
38
|
-
return title;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Calculate dynamic start/end based on available space
|
|
42
|
-
// For UUIDs, showing the last 12 characters is often useful
|
|
43
|
-
const endChars = Math.min(UUID_END_CHARS, Math.floor(maxChars / ELLIPSIS_LENGTH));
|
|
44
|
-
const startChars = Math.max(MIN_START_CHARS, maxChars - endChars - ELLIPSIS_LENGTH);
|
|
26
|
+
const displayTitle = useResponsiveMiddleTruncate(() => actualItem.value.title, containerWidth);
|
|
45
27
|
|
|
46
|
-
|
|
47
|
-
|
|
28
|
+
const tooltipTitle = computed(() =>
|
|
29
|
+
middleTruncate(
|
|
30
|
+
actualItem.value.title,
|
|
31
|
+
TOOLTIP_NAME_MAX_LENGTH,
|
|
32
|
+
TOOLTIP_NAME_START_CHARS,
|
|
33
|
+
TOOLTIP_NAME_END_CHARS,
|
|
34
|
+
),
|
|
35
|
+
);
|
|
48
36
|
|
|
49
37
|
const tooltipDisabled = computed(() => {
|
|
50
38
|
if (isLeaf !== undefined) {
|
|
@@ -75,6 +63,7 @@ async function copyToClipboard(text, label) {
|
|
|
75
63
|
<template #activator="{ props: tooltipProps }">
|
|
76
64
|
<span
|
|
77
65
|
v-bind="tooltipProps"
|
|
66
|
+
data-testid="treeItemLabel"
|
|
78
67
|
class="tree-item-label"
|
|
79
68
|
:class="{ 'inactive-item': actualItem.is_active === false }"
|
|
80
69
|
@contextmenu.prevent.stop="emit('contextmenu', $event)"
|
|
@@ -90,6 +79,7 @@ async function copyToClipboard(text, label) {
|
|
|
90
79
|
<strong class="text-white mr-1">ID:</strong>
|
|
91
80
|
<span>{{ actualItem.id }}</span>
|
|
92
81
|
<v-btn
|
|
82
|
+
data-testid="copyIdBtn"
|
|
93
83
|
icon="mdi-content-copy"
|
|
94
84
|
variant="text"
|
|
95
85
|
size="x-small"
|
|
@@ -100,8 +90,9 @@ async function copyToClipboard(text, label) {
|
|
|
100
90
|
</span>
|
|
101
91
|
<span v-if="actualItem.title" class="text-caption d-flex align-center">
|
|
102
92
|
<strong class="text-white mr-1">Name:</strong>
|
|
103
|
-
<span>{{
|
|
93
|
+
<span>{{ tooltipTitle }}</span>
|
|
104
94
|
<v-btn
|
|
95
|
+
data-testid="copyNameBtn"
|
|
105
96
|
icon="mdi-content-copy"
|
|
106
97
|
variant="text"
|
|
107
98
|
size="x-small"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { middleTruncate } from "@ogw_front/utils/string";
|
|
2
|
+
|
|
3
|
+
const UUID_END_CHARS = 12;
|
|
4
|
+
const ELLIPSIS_LENGTH = 3;
|
|
5
|
+
const MIN_START_CHARS = 4;
|
|
6
|
+
const DEFAULT_ESTIMATED_CHAR_WIDTH = 8.5;
|
|
7
|
+
|
|
8
|
+
export function useResponsiveMiddleTruncate(textRef, containerWidthRef, options = {}) {
|
|
9
|
+
const {
|
|
10
|
+
estimatedCharWidth = DEFAULT_ESTIMATED_CHAR_WIDTH,
|
|
11
|
+
uuidEndChars = UUID_END_CHARS,
|
|
12
|
+
ellipsisLength = ELLIPSIS_LENGTH,
|
|
13
|
+
minStartChars = MIN_START_CHARS,
|
|
14
|
+
} = options;
|
|
15
|
+
|
|
16
|
+
return computed(() => {
|
|
17
|
+
const text = toValue(textRef);
|
|
18
|
+
if (!text) {
|
|
19
|
+
return "";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const width = toValue(containerWidthRef) || 0;
|
|
23
|
+
const maxChars = Math.floor(width / estimatedCharWidth);
|
|
24
|
+
|
|
25
|
+
if (maxChars <= 0 || text.length <= maxChars) {
|
|
26
|
+
return text;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const endChars = Math.min(uuidEndChars, Math.floor(maxChars / ellipsisLength));
|
|
30
|
+
const startChars = Math.max(minStartChars, maxChars - endChars - ellipsisLength);
|
|
31
|
+
|
|
32
|
+
return middleTruncate(text, maxChars, startChars, endChars);
|
|
33
|
+
});
|
|
34
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geode/opengeodeweb-front",
|
|
3
|
-
"version": "10.33.2",
|
|
3
|
+
"version": "10.33.3-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": {
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"build": ""
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@geode/opengeodeweb-back": "
|
|
43
|
-
"@geode/opengeodeweb-viewer": "
|
|
42
|
+
"@geode/opengeodeweb-back": "next",
|
|
43
|
+
"@geode/opengeodeweb-viewer": "next",
|
|
44
44
|
"@google-cloud/run": "3.2.0",
|
|
45
45
|
"@kitware/vtk.js": "33.3.0",
|
|
46
46
|
"@mdi/font": "7.4.47",
|