srcdev-nuxt-components 2.1.9 → 2.1.11
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.
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<slot name="summary"></slot>
|
|
6
6
|
</span>
|
|
7
7
|
<slot name="summaryIcon">
|
|
8
|
-
<Icon name="bi:caret-down-fill" class="icon mi-12" :class="
|
|
8
|
+
<Icon name="bi:caret-down-fill" class="icon mi-12" :class="iconSize" />
|
|
9
9
|
</slot>
|
|
10
10
|
</summary>
|
|
11
11
|
<div class="display-details-content" :aria-labelledby="triggerId" :id="contentId" role="region" ref="contentRef">
|
|
@@ -15,7 +15,10 @@
|
|
|
15
15
|
</template>
|
|
16
16
|
|
|
17
17
|
<script lang="ts">
|
|
18
|
-
|
|
18
|
+
// Create a global store to track open details elements by name
|
|
19
|
+
const openDetailsByName = reactive(new Map<string, HTMLDetailsElement>());
|
|
20
|
+
|
|
21
|
+
export const useDetailsTransition = (detailsRef: Ref<HTMLDetailsElement | null>, summaryRef: Ref<HTMLElement | null>, contentRef: Ref<HTMLDivElement | null>, name: string) => {
|
|
19
22
|
// State
|
|
20
23
|
const animation = ref<Animation | null>(null);
|
|
21
24
|
const animationDuration = 400;
|
|
@@ -24,12 +27,42 @@ export const useDetailsTransition = (detailsRef: Ref<HTMLDetailsElement | null>,
|
|
|
24
27
|
|
|
25
28
|
// Check if refs are available
|
|
26
29
|
if (!detailsRef.value || !summaryRef.value || !contentRef.value) {
|
|
27
|
-
console.warn('Details or content ref is null');
|
|
30
|
+
console.warn('Details, summary, or content ref is null');
|
|
28
31
|
return {
|
|
29
32
|
clickAction: () => console.warn('Component not fully initialized'),
|
|
30
33
|
};
|
|
31
34
|
}
|
|
32
35
|
|
|
36
|
+
const closeOtherDetailsWithSameName = () => {
|
|
37
|
+
const currentDetails = detailsRef.value;
|
|
38
|
+
if (!currentDetails || !name) return;
|
|
39
|
+
|
|
40
|
+
// Get the currently open details with the same name
|
|
41
|
+
const openDetails = openDetailsByName.get(name);
|
|
42
|
+
|
|
43
|
+
// If there's an open details with the same name and it's not the current one, close it
|
|
44
|
+
if (openDetails && openDetails !== currentDetails && openDetails.open) {
|
|
45
|
+
// Simulate a click on the other details to close it with animation
|
|
46
|
+
const otherSummary = openDetails.querySelector('summary');
|
|
47
|
+
if (otherSummary) {
|
|
48
|
+
otherSummary.click();
|
|
49
|
+
} else {
|
|
50
|
+
// Fallback: close directly without animation
|
|
51
|
+
openDetails.open = false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Update the map with the current details if it's open
|
|
56
|
+
if (currentDetails.open) {
|
|
57
|
+
openDetailsByName.set(name, currentDetails);
|
|
58
|
+
} else {
|
|
59
|
+
// If it's closed and was the one in the map, remove it
|
|
60
|
+
if (openDetailsByName.get(name) === currentDetails) {
|
|
61
|
+
openDetailsByName.delete(name);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
33
66
|
const clickAction = () => {
|
|
34
67
|
const details = detailsRef.value;
|
|
35
68
|
const summary = summaryRef.value;
|
|
@@ -41,13 +74,15 @@ export const useDetailsTransition = (detailsRef: Ref<HTMLDetailsElement | null>,
|
|
|
41
74
|
details.style.overflow = 'hidden';
|
|
42
75
|
|
|
43
76
|
if (isClosing.value || !details.open) {
|
|
77
|
+
// Close other details with the same name first
|
|
78
|
+
closeOtherDetailsWithSameName();
|
|
79
|
+
|
|
44
80
|
// Open the details
|
|
45
81
|
details.open = true;
|
|
46
82
|
isExpanding.value = true;
|
|
47
83
|
isClosing.value = false;
|
|
48
84
|
|
|
49
85
|
// Get the height of the content
|
|
50
|
-
|
|
51
86
|
const detailsHeight = details.offsetHeight;
|
|
52
87
|
const contentHeight = content.offsetHeight;
|
|
53
88
|
const summaryHeight = summary.offsetHeight;
|
|
@@ -77,6 +112,9 @@ export const useDetailsTransition = (detailsRef: Ref<HTMLDetailsElement | null>,
|
|
|
77
112
|
details.style.overflow = '';
|
|
78
113
|
isExpanding.value = false;
|
|
79
114
|
animation.value = null;
|
|
115
|
+
|
|
116
|
+
// Register this as the open details for this name
|
|
117
|
+
openDetailsByName.set(name, details);
|
|
80
118
|
};
|
|
81
119
|
|
|
82
120
|
animation.value.oncancel = () => {
|
|
@@ -114,6 +152,11 @@ export const useDetailsTransition = (detailsRef: Ref<HTMLDetailsElement | null>,
|
|
|
114
152
|
details.style.overflow = '';
|
|
115
153
|
isClosing.value = false;
|
|
116
154
|
animation.value = null;
|
|
155
|
+
|
|
156
|
+
// Remove this from the open details map if it's there
|
|
157
|
+
if (openDetailsByName.get(name) === details) {
|
|
158
|
+
openDetailsByName.delete(name);
|
|
159
|
+
}
|
|
117
160
|
};
|
|
118
161
|
|
|
119
162
|
animation.value.oncancel = () => {
|
|
@@ -182,7 +225,7 @@ watch(
|
|
|
182
225
|
onMounted(() => {
|
|
183
226
|
// Initialize the composable once the component is mounted and refs are available
|
|
184
227
|
if (detailsRef.value && contentRef.value && summaryRef.value) {
|
|
185
|
-
const details = useDetailsTransition(detailsRef, summaryRef, contentRef);
|
|
228
|
+
const details = useDetailsTransition(detailsRef, summaryRef, contentRef, props.name);
|
|
186
229
|
clickAction = details.clickAction; // Assign the real click handler
|
|
187
230
|
} else {
|
|
188
231
|
console.error('Refs not available after mounting');
|
|
@@ -191,70 +234,72 @@ onMounted(() => {
|
|
|
191
234
|
</script>
|
|
192
235
|
|
|
193
236
|
<style lang="css">
|
|
194
|
-
.display-details {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
237
|
+
@scope (.display-details) {
|
|
238
|
+
.display-details {
|
|
239
|
+
/* Component setup */
|
|
240
|
+
--_display-details-icon-transform: scaleY(1);
|
|
241
|
+
--_display-details-icon-size: 1.2rem;
|
|
242
|
+
|
|
243
|
+
/* Configurable properties */
|
|
244
|
+
--_display-details-border: none;
|
|
245
|
+
--_display-details-outline: none;
|
|
246
|
+
--_display-details-box-shadow: none;
|
|
247
|
+
--_display-details-border-radius: 0;
|
|
248
|
+
--_display-details-mbe: 1em;
|
|
249
|
+
|
|
250
|
+
--_display-details-summary-gap: 12px;
|
|
251
|
+
--_display-details-summary-flex-direction: row;
|
|
252
|
+
|
|
253
|
+
--_display-details-content-padding: 0;
|
|
254
|
+
|
|
255
|
+
&.medium {
|
|
256
|
+
--_display-details-icon-size: 1.8rem;
|
|
257
|
+
}
|
|
258
|
+
&.large {
|
|
259
|
+
--_display-details-icon-size: 2.4rem;
|
|
260
|
+
}
|
|
205
261
|
|
|
206
|
-
|
|
207
|
-
|
|
262
|
+
&[open] {
|
|
263
|
+
--_display-details-icon-transform: scaleY(-1);
|
|
264
|
+
}
|
|
208
265
|
|
|
209
|
-
|
|
266
|
+
border: var(--_display-details-border);
|
|
267
|
+
outline: var(--_display-details-outline);
|
|
268
|
+
box-shadow: var(--_display-details-box-shadow);
|
|
269
|
+
border-radius: var(--_display-details-border-radius);
|
|
270
|
+
margin-block-end: var(--_display-details-mbe);
|
|
210
271
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
214
|
-
&.large {
|
|
215
|
-
--_display-details-icon-size: 2.4rem;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
&[open] {
|
|
219
|
-
--_display-details-icon-transform: scaleY(-1);
|
|
220
|
-
}
|
|
272
|
+
.display-details-summary {
|
|
273
|
+
list-style: none;
|
|
221
274
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
margin-block-end: var(--_display-details-mbe);
|
|
275
|
+
&::-webkit-details-marker,
|
|
276
|
+
&::marker {
|
|
277
|
+
display: none;
|
|
278
|
+
}
|
|
227
279
|
|
|
228
|
-
|
|
229
|
-
|
|
280
|
+
display: flex !important;
|
|
281
|
+
flex-direction: var(--_display-details-summary-flex-direction);
|
|
282
|
+
align-items: center;
|
|
283
|
+
gap: var(--_display-details-summary-gap);
|
|
284
|
+
overflow: clip;
|
|
230
285
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
286
|
+
.label {
|
|
287
|
+
display: block;
|
|
288
|
+
flex-grow: 1;
|
|
289
|
+
}
|
|
235
290
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
align-items: center;
|
|
239
|
-
gap: var(--_display-details-summary-gap);
|
|
240
|
-
overflow: clip;
|
|
291
|
+
.icon {
|
|
292
|
+
display: block;
|
|
241
293
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
294
|
+
font-size: var(--_display-details-icon-size);
|
|
295
|
+
transform: var(--_display-details-icon-transform);
|
|
296
|
+
transition: transform 200ms;
|
|
297
|
+
}
|
|
245
298
|
}
|
|
246
299
|
|
|
247
|
-
.
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
font-size: var(--_display-details-icon-size);
|
|
251
|
-
transform: var(--_display-details-icon-transform);
|
|
252
|
-
transition: transform 200ms;
|
|
300
|
+
.display-details-content {
|
|
301
|
+
padding: var(--_display-details-content-padding);
|
|
253
302
|
}
|
|
254
303
|
}
|
|
255
|
-
|
|
256
|
-
.display-details-content {
|
|
257
|
-
padding: var(--_display-details-content-padding);
|
|
258
|
-
}
|
|
259
304
|
}
|
|
260
305
|
</style>
|
package/package.json
CHANGED