@quazardous/qdadm 2.24.0 → 2.25.0
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/package.json
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* </FieldGroups>
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
|
-
import { computed, type PropType } from 'vue'
|
|
23
|
+
import { computed, ref, watch, type PropType } from 'vue'
|
|
24
24
|
import Tabs from 'primevue/tabs'
|
|
25
25
|
import TabList from 'primevue/tablist'
|
|
26
26
|
import Tab from 'primevue/tab'
|
|
@@ -84,8 +84,21 @@ const props = defineProps({
|
|
|
84
84
|
type: String as PropType<LayoutMode>,
|
|
85
85
|
default: 'sections',
|
|
86
86
|
},
|
|
87
|
+
/**
|
|
88
|
+
* The active group (`tabs`: a group name) or the open ones (`accordion`:
|
|
89
|
+
* names), for `v-model:active` (#2435). Optional: without it the component
|
|
90
|
+
* keeps its own, starting on the first group.
|
|
91
|
+
*/
|
|
92
|
+
active: {
|
|
93
|
+
type: [String, Array] as PropType<string | string[] | null>,
|
|
94
|
+
default: undefined,
|
|
95
|
+
},
|
|
87
96
|
})
|
|
88
97
|
|
|
98
|
+
const emit = defineEmits<{
|
|
99
|
+
'update:active': [value: string | string[]]
|
|
100
|
+
}>()
|
|
101
|
+
|
|
89
102
|
// Expose slots for field rendering
|
|
90
103
|
defineSlots<{
|
|
91
104
|
field: (props: { field: BaseFieldConfig; value: unknown; groupName: string }) => unknown
|
|
@@ -105,6 +118,34 @@ const displayGroups = computed(() => {
|
|
|
105
118
|
})
|
|
106
119
|
})
|
|
107
120
|
|
|
121
|
+
// The active tab / open panels are state, not a constant: a re-render must not
|
|
122
|
+
// send the user back to the first group (#2435). An app owning it passes
|
|
123
|
+
// v-model:active; otherwise this is the only copy.
|
|
124
|
+
const localActive = ref<string | string[] | null | undefined>(props.active)
|
|
125
|
+
watch(
|
|
126
|
+
() => props.active,
|
|
127
|
+
(value) => {
|
|
128
|
+
if (value !== undefined) localActive.value = value
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
const firstGroupName = () => displayGroups.value[0]?.name || '0'
|
|
132
|
+
const activeTab = computed(() => (typeof localActive.value === 'string' ? localActive.value : firstGroupName()))
|
|
133
|
+
const openPanels = computed(() => {
|
|
134
|
+
const value = localActive.value
|
|
135
|
+
if (Array.isArray(value)) return value
|
|
136
|
+
return typeof value === 'string' ? [value] : [firstGroupName()]
|
|
137
|
+
})
|
|
138
|
+
function setActive(value: string | string[]) {
|
|
139
|
+
localActive.value = value
|
|
140
|
+
emit('update:active', value)
|
|
141
|
+
}
|
|
142
|
+
function onTabChange(value: string | number) {
|
|
143
|
+
setActive(String(value))
|
|
144
|
+
}
|
|
145
|
+
function onPanelsChange(value: unknown) {
|
|
146
|
+
setActive(Array.isArray(value) ? value.map(String) : value == null ? [] : [String(value)])
|
|
147
|
+
}
|
|
148
|
+
|
|
108
149
|
// Get value from data
|
|
109
150
|
function getValue(fieldName: string): unknown {
|
|
110
151
|
return props.data?.[fieldName]
|
|
@@ -227,7 +268,7 @@ function getBadgeValue(group: FieldGroup): string | number | null {
|
|
|
227
268
|
|
|
228
269
|
<!-- Tabs layout -->
|
|
229
270
|
<template v-else-if="layout === 'tabs'">
|
|
230
|
-
<Tabs :value="
|
|
271
|
+
<Tabs :value="activeTab" @update:value="onTabChange">
|
|
231
272
|
<TabList>
|
|
232
273
|
<Tab
|
|
233
274
|
v-for="group in displayGroups"
|
|
@@ -275,7 +316,7 @@ function getBadgeValue(group: FieldGroup): string | number | null {
|
|
|
275
316
|
|
|
276
317
|
<!-- Accordion layout -->
|
|
277
318
|
<template v-else-if="layout === 'accordion'">
|
|
278
|
-
<Accordion :value="
|
|
319
|
+
<Accordion :value="openPanels" multiple @update:value="onPanelsChange">
|
|
279
320
|
<AccordionPanel
|
|
280
321
|
v-for="group in displayGroups"
|
|
281
322
|
:key="group.name"
|
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
* - #fields: Field content (required) - renders in right column (or full width if no media)
|
|
36
36
|
* - #footer: Custom footer (replaces ShowActions if provided)
|
|
37
37
|
* - #error: Custom error display
|
|
38
|
-
* - #loading: Custom
|
|
38
|
+
* - #loading: Custom display for the first load. A reload of a record already
|
|
39
|
+
* on screen keeps it there, marked busy (#2435).
|
|
39
40
|
*/
|
|
40
41
|
import { computed, type PropType } from 'vue'
|
|
41
42
|
import PageHeader from '../layout/PageHeader.vue'
|
|
@@ -203,8 +204,11 @@ type ShowFieldValue = string | number | boolean | Date | Record<string, unknown>
|
|
|
203
204
|
<!-- Toolbar slot (between header and content) -->
|
|
204
205
|
<slot name="toolbar" />
|
|
205
206
|
|
|
206
|
-
<!--
|
|
207
|
-
|
|
207
|
+
<!-- First load only. A reload of a record already shown keeps the content
|
|
208
|
+
mounted (#2435): unmounting it threw away the active tab, open panels,
|
|
209
|
+
scroll and any local state — on every action reload and every live
|
|
210
|
+
update of the record. -->
|
|
211
|
+
<template v-if="loading && !data">
|
|
208
212
|
<slot name="loading">
|
|
209
213
|
<div class="loading-state">
|
|
210
214
|
<i class="pi pi-spin pi-spinner" style="font-size: 2rem"></i>
|
|
@@ -223,6 +227,7 @@ type ShowFieldValue = string | number | boolean | Date | Record<string, unknown>
|
|
|
223
227
|
|
|
224
228
|
<!-- Content -->
|
|
225
229
|
<template v-else>
|
|
230
|
+
<div v-if="loading" class="show-refreshing" role="progressbar" aria-label="Refreshing" />
|
|
226
231
|
<!-- Single content body; Card wrapper is conditional (#1193) -->
|
|
227
232
|
<CardShell :card="cardWrapper">
|
|
228
233
|
<!-- Grid layout with optional media zone -->
|
|
@@ -230,6 +235,7 @@ type ShowFieldValue = string | number | boolean | Date | Record<string, unknown>
|
|
|
230
235
|
class="show-content"
|
|
231
236
|
:class="{ 'show-content--with-media': slots.media }"
|
|
232
237
|
:style="slots.media ? { '--media-width': mediaWidth } : {}"
|
|
238
|
+
:aria-busy="loading ? 'true' : undefined"
|
|
233
239
|
>
|
|
234
240
|
<!-- Media zone (optional) -->
|
|
235
241
|
<div v-if="slots.media" class="show-media">
|
|
@@ -304,6 +310,29 @@ type ShowFieldValue = string | number | boolean | Date | Record<string, unknown>
|
|
|
304
310
|
padding: 3rem;
|
|
305
311
|
}
|
|
306
312
|
|
|
313
|
+
/* A record being refreshed stays on screen: a thin bar says so (#2435). */
|
|
314
|
+
.show-refreshing {
|
|
315
|
+
height: 3px;
|
|
316
|
+
margin-bottom: -1rem; /* inside the page gap: the content does not move */
|
|
317
|
+
background: linear-gradient(90deg, transparent, var(--p-primary-color, #3b82f6), transparent);
|
|
318
|
+
background-size: 40% 100%;
|
|
319
|
+
background-repeat: no-repeat;
|
|
320
|
+
animation: show-refreshing 1.1s ease-in-out infinite;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
@keyframes show-refreshing {
|
|
324
|
+
from { background-position: -40% 0; }
|
|
325
|
+
to { background-position: 140% 0; }
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
@media (prefers-reduced-motion: reduce) {
|
|
329
|
+
.show-refreshing {
|
|
330
|
+
animation: none;
|
|
331
|
+
background: var(--p-primary-color, #3b82f6);
|
|
332
|
+
opacity: 0.4;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
307
336
|
.show-error-message {
|
|
308
337
|
margin: 1rem 0;
|
|
309
338
|
}
|