@quazardous/qdadm 2.7.3 → 2.8.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
|
@@ -241,7 +241,7 @@ watch(() => route.fullPath, () => {
|
|
|
241
241
|
|
|
242
242
|
// Navigation context (breadcrumb + navlinks from route config)
|
|
243
243
|
// Entity data comes from activeStack (populated by useEntityItemPage/useEntityItemFormPage)
|
|
244
|
-
const { breadcrumb: defaultBreadcrumb, navlinks: defaultNavlinks,
|
|
244
|
+
const { breadcrumb: defaultBreadcrumb, navlinks: defaultNavlinks, modeLinks } = useNavContext()
|
|
245
245
|
|
|
246
246
|
// Allow child pages to override breadcrumb/navlinks via provide/inject
|
|
247
247
|
const breadcrumbOverride = ref<BreadcrumbItem[] | null>(null)
|
|
@@ -261,10 +261,11 @@ const showBreadcrumb = computed<boolean>(() => {
|
|
|
261
261
|
return true
|
|
262
262
|
})
|
|
263
263
|
|
|
264
|
-
// View↔Edit
|
|
265
|
-
//
|
|
266
|
-
|
|
267
|
-
|
|
264
|
+
// View↔Edit mode links (#1332/#1341/#1353) — same opt-in contract as
|
|
265
|
+
// DefaultBreadcrumb; single opposite-mode link on item pages, the parent's
|
|
266
|
+
// View/Edit pair on child pages
|
|
267
|
+
const shownModeLinks = computed(() =>
|
|
268
|
+
features.breadcrumbModeToggle === true ? modeLinks.value : []
|
|
268
269
|
)
|
|
269
270
|
</script>
|
|
270
271
|
|
|
@@ -391,26 +392,27 @@ const shownModeToggle = computed(() =>
|
|
|
391
392
|
</Breadcrumb>
|
|
392
393
|
|
|
393
394
|
<!-- Navlinks (provided by PageNav for child routes) + View↔Edit mode
|
|
394
|
-
|
|
395
|
+
links (#1332/#1341/#1353) — plain navigation; the form page's own
|
|
395
396
|
route-leave guard covers unsaved changes on edit→show -->
|
|
396
|
-
<div v-if="navlinks.length > 0 ||
|
|
397
|
-
|
|
397
|
+
<div v-if="navlinks.length > 0 || shownModeLinks.length > 0" class="layout-navlinks">
|
|
398
|
+
<!-- mode links lead the group (#j7udkh), sibling tabs follow -->
|
|
399
|
+
<template v-for="(mode, index) in shownModeLinks" :key="`mode-${mode.target}`">
|
|
398
400
|
<span v-if="index > 0" class="layout-navlinks-separator">|</span>
|
|
399
401
|
<RouterLink
|
|
400
|
-
:to="
|
|
401
|
-
class="layout-navlink"
|
|
402
|
-
:class="{ 'layout-navlink--active': link.active }"
|
|
402
|
+
:to="mode.to"
|
|
403
|
+
class="layout-navlink breadcrumb-mode-toggle"
|
|
403
404
|
>
|
|
404
|
-
{{
|
|
405
|
+
{{ mode.label }}
|
|
405
406
|
</RouterLink>
|
|
406
407
|
</template>
|
|
407
|
-
<template v-
|
|
408
|
-
<span v-if="
|
|
408
|
+
<template v-for="(link, index) in navlinks" :key="link.to?.name || index">
|
|
409
|
+
<span v-if="shownModeLinks.length > 0 || index > 0" class="layout-navlinks-separator">|</span>
|
|
409
410
|
<RouterLink
|
|
410
|
-
:to="
|
|
411
|
-
class="layout-navlink
|
|
411
|
+
:to="link.to"
|
|
412
|
+
class="layout-navlink"
|
|
413
|
+
:class="{ 'layout-navlink--active': link.active }"
|
|
412
414
|
>
|
|
413
|
-
{{
|
|
415
|
+
{{ link.label }}
|
|
414
416
|
</RouterLink>
|
|
415
417
|
</template>
|
|
416
418
|
</div>
|
|
@@ -24,7 +24,7 @@ const route = useRoute()
|
|
|
24
24
|
const features = inject<FeaturesConfig>('qdadmFeatures', { breadcrumb: true })
|
|
25
25
|
|
|
26
26
|
// Navigation context (breadcrumb + navlinks from route config)
|
|
27
|
-
const { breadcrumb: defaultBreadcrumb, navlinks: defaultNavlinks,
|
|
27
|
+
const { breadcrumb: defaultBreadcrumb, navlinks: defaultNavlinks, modeLinks } = useNavContext()
|
|
28
28
|
|
|
29
29
|
// Allow child pages to override breadcrumb/navlinks via provide/inject
|
|
30
30
|
const breadcrumbOverride = inject<Ref<BreadcrumbItem[] | null>>('qdadmBreadcrumbOverride', ref(null))
|
|
@@ -42,10 +42,10 @@ const showBreadcrumb = computed<boolean>(() => {
|
|
|
42
42
|
return true
|
|
43
43
|
})
|
|
44
44
|
|
|
45
|
-
// View↔Edit
|
|
46
|
-
//
|
|
47
|
-
const
|
|
48
|
-
features.breadcrumbModeToggle === true ?
|
|
45
|
+
// View↔Edit mode links (#1332/#1353) — opt-in; single opposite-mode link on
|
|
46
|
+
// item pages, the parent's View/Edit pair on child pages
|
|
47
|
+
const shownModeLinks = computed(() =>
|
|
48
|
+
features.breadcrumbModeToggle === true ? modeLinks.value : []
|
|
49
49
|
)
|
|
50
50
|
</script>
|
|
51
51
|
|
|
@@ -65,26 +65,27 @@ const shownModeToggle = computed(() =>
|
|
|
65
65
|
</Breadcrumb>
|
|
66
66
|
|
|
67
67
|
<!-- Navlinks (provided by PageNav for child routes) + View↔Edit mode
|
|
68
|
-
|
|
68
|
+
links (#1332/#1341/#1353) — plain navigation; the form page's own
|
|
69
69
|
route-leave guard covers unsaved changes on edit→show -->
|
|
70
|
-
<div v-if="navlinks.length > 0 ||
|
|
71
|
-
|
|
70
|
+
<div v-if="navlinks.length > 0 || shownModeLinks.length > 0" class="breadcrumb-navlinks">
|
|
71
|
+
<!-- mode links lead the group (#j7udkh), sibling tabs follow -->
|
|
72
|
+
<template v-for="(mode, index) in shownModeLinks" :key="`mode-${mode.target}`">
|
|
72
73
|
<span v-if="index > 0" class="navlinks-separator">|</span>
|
|
73
74
|
<RouterLink
|
|
74
|
-
:to="
|
|
75
|
-
class="navlink"
|
|
76
|
-
:class="{ 'navlink--active': link.active }"
|
|
75
|
+
:to="mode.to"
|
|
76
|
+
class="navlink breadcrumb-mode-toggle"
|
|
77
77
|
>
|
|
78
|
-
{{
|
|
78
|
+
{{ mode.label }}
|
|
79
79
|
</RouterLink>
|
|
80
80
|
</template>
|
|
81
|
-
<template v-
|
|
82
|
-
<span v-if="
|
|
81
|
+
<template v-for="(link, index) in navlinks" :key="link.to?.name || index">
|
|
82
|
+
<span v-if="shownModeLinks.length > 0 || index > 0" class="navlinks-separator">|</span>
|
|
83
83
|
<RouterLink
|
|
84
|
-
:to="
|
|
85
|
-
class="navlink
|
|
84
|
+
:to="link.to"
|
|
85
|
+
class="navlink"
|
|
86
|
+
:class="{ 'navlink--active': link.active }"
|
|
86
87
|
>
|
|
87
|
-
{{
|
|
88
|
+
{{ link.label }}
|
|
88
89
|
</RouterLink>
|
|
89
90
|
</template>
|
|
90
91
|
</div>
|
|
@@ -76,9 +76,10 @@ export interface BreadcrumbItem {
|
|
|
76
76
|
* permissions are honored once the item is loaded.
|
|
77
77
|
*/
|
|
78
78
|
export interface BreadcrumbModeToggle {
|
|
79
|
-
/** Mode of the current route
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
/** Mode of the current route — absent on child pages (#1353), where the
|
|
80
|
+
* page is in neither mode of the parent item */
|
|
81
|
+
current?: 'show' | 'edit'
|
|
82
|
+
/** Mode the link navigates to */
|
|
82
83
|
target: 'show' | 'edit'
|
|
83
84
|
/** Twin-route location — a plain router.push; any dirty-form protection
|
|
84
85
|
* comes from the form page's own onBeforeRouteLeave guard */
|
|
@@ -130,6 +131,7 @@ export interface UseNavContextReturn {
|
|
|
130
131
|
breadcrumb: ComputedRef<BreadcrumbItem[]>
|
|
131
132
|
navlinks: ComputedRef<NavLinkItem[]>
|
|
132
133
|
modeToggle: ComputedRef<BreadcrumbModeToggle | null>
|
|
134
|
+
modeLinks: ComputedRef<BreadcrumbModeToggle[]>
|
|
133
135
|
|
|
134
136
|
// Helpers
|
|
135
137
|
parentConfig: ComputedRef<ParentConfig | undefined>
|
|
@@ -443,7 +445,7 @@ export function useNavContext(_options: UseNavContextOptions = {}): UseNavContex
|
|
|
443
445
|
})
|
|
444
446
|
|
|
445
447
|
// ============================================================================
|
|
446
|
-
// MODE TOGGLE (#1332)
|
|
448
|
+
// MODE TOGGLE / MODE LINKS (#1332, #1353)
|
|
447
449
|
// ============================================================================
|
|
448
450
|
|
|
449
451
|
function toggleLabel(target: 'show' | 'edit'): string {
|
|
@@ -452,6 +454,26 @@ export function useNavContext(_options: UseNavContextOptions = {}): UseNavContex
|
|
|
452
454
|
return target === 'edit' ? 'Edit' : 'View'
|
|
453
455
|
}
|
|
454
456
|
|
|
457
|
+
/** Build one mode link if its route exists and (for edit) permission allows. */
|
|
458
|
+
function buildModeLink(
|
|
459
|
+
manager: EntityManager,
|
|
460
|
+
target: 'show' | 'edit',
|
|
461
|
+
id: unknown,
|
|
462
|
+
permissionEntity: unknown
|
|
463
|
+
): BreadcrumbModeToggle | null {
|
|
464
|
+
const routeName = `${manager.routePrefix}-${target}`
|
|
465
|
+
if (!router.hasRoute(routeName)) return null
|
|
466
|
+
if (target === 'edit' && !manager.canUpdate(permissionEntity ?? undefined)) return null
|
|
467
|
+
return {
|
|
468
|
+
target,
|
|
469
|
+
to: { name: routeName, params: { [manager.idField || 'id']: id as string } },
|
|
470
|
+
label: toggleLabel(target),
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Item pages (#1332): the terminal is entity-show/entity-edit — offer the
|
|
475
|
+
// OPPOSITE mode (getDefaultItemRoute's edit-preference is a landing
|
|
476
|
+
// default, not a toggle).
|
|
455
477
|
const modeToggle = computed<BreadcrumbModeToggle | null>(() => {
|
|
456
478
|
void i18nLocale.value // label re-resolves on locale change
|
|
457
479
|
|
|
@@ -464,20 +486,29 @@ export function useNavContext(_options: UseNavContextOptions = {}): UseNavContex
|
|
|
464
486
|
if (!manager?.routePrefix) return null
|
|
465
487
|
|
|
466
488
|
const current = terminal.kind === 'entity-edit' ? 'edit' : 'show'
|
|
467
|
-
// Target is the OPPOSITE of the current mode — getDefaultItemRoute's
|
|
468
|
-
// edit-preference is a landing default, not a toggle.
|
|
469
489
|
const target = current === 'edit' ? 'show' : 'edit'
|
|
470
|
-
const
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
if (target === 'edit' && !manager.canUpdate(entityData.value ?? undefined)) return null
|
|
490
|
+
const link = buildModeLink(manager, target, terminal.id, entityData.value)
|
|
491
|
+
return link ? { ...link, current } : null
|
|
492
|
+
})
|
|
474
493
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
494
|
+
// Uniform list for rendering (#1353). Item pages: the single opposite-mode
|
|
495
|
+
// entry (modeToggle). Child pages (route carries meta.parent): the PARENT
|
|
496
|
+
// item's applicable pair — the page is in neither mode, so both View and
|
|
497
|
+
// Edit are offered, each under the same existence/permission rules. On a
|
|
498
|
+
// child page, `entityData` resolves to the parent item (last item segment
|
|
499
|
+
// of the chain), so the canUpdate gate is ownership-aware once hydrated.
|
|
500
|
+
const modeLinks = computed<BreadcrumbModeToggle[]>(() => {
|
|
501
|
+
const itemToggle = modeToggle.value
|
|
502
|
+
if (itemToggle) return [itemToggle]
|
|
503
|
+
|
|
504
|
+
void i18nLocale.value
|
|
505
|
+
if (!parentConfig.value || parentId.value == null) return []
|
|
506
|
+
const manager = getManager(parentConfig.value.entity)
|
|
507
|
+
if (!manager?.routePrefix) return []
|
|
508
|
+
|
|
509
|
+
return (['show', 'edit'] as const)
|
|
510
|
+
.map((target) => buildModeLink(manager, target, parentId.value, entityData.value))
|
|
511
|
+
.filter((link): link is BreadcrumbModeToggle => link !== null)
|
|
481
512
|
})
|
|
482
513
|
|
|
483
514
|
return {
|
|
@@ -494,6 +525,7 @@ export function useNavContext(_options: UseNavContextOptions = {}): UseNavContex
|
|
|
494
525
|
breadcrumb,
|
|
495
526
|
navlinks,
|
|
496
527
|
modeToggle,
|
|
528
|
+
modeLinks,
|
|
497
529
|
|
|
498
530
|
// Helpers
|
|
499
531
|
parentConfig,
|