@quazardous/qdadm 2.7.0 → 2.7.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/README.md
CHANGED
|
@@ -98,6 +98,7 @@ Options: `transformRequest`, `transformResponse`, `responseFormat`, `clientSideP
|
|
|
98
98
|
| [QDADM_CREDO](QDADM_CREDO.md) | Philosophy, patterns, concepts |
|
|
99
99
|
| [Page compositions](../../docs/page-compositions.md) | **Start here** — which composition for which need |
|
|
100
100
|
| [CRUD Pages](../../docs/crud.md) | List / form / show / child pages reference |
|
|
101
|
+
| [Navigation](../../docs/navigation.md) | Breadcrumb, sibling navlinks, View↔Edit toggle |
|
|
101
102
|
| [Architecture](../../docs/architecture.md) | PAC pattern, layers |
|
|
102
103
|
| [Extension](../../docs/extension.md) | Decorators, bundles, multi-storage |
|
|
103
104
|
| [Hooks](../../docs/hooks.md) | Alter / invoke registry |
|
package/package.json
CHANGED
|
@@ -31,6 +31,8 @@ import { version as qdadmVersion } from '../../../package.json'
|
|
|
31
31
|
interface FeaturesConfig {
|
|
32
32
|
poweredBy?: boolean
|
|
33
33
|
breadcrumb?: boolean
|
|
34
|
+
/** Opt-in (#1332/#1341): View↔Edit toggle next to the terminal crumb */
|
|
35
|
+
breadcrumbModeToggle?: boolean
|
|
34
36
|
[key: string]: unknown
|
|
35
37
|
}
|
|
36
38
|
|
|
@@ -239,7 +241,7 @@ watch(() => route.fullPath, () => {
|
|
|
239
241
|
|
|
240
242
|
// Navigation context (breadcrumb + navlinks from route config)
|
|
241
243
|
// Entity data comes from activeStack (populated by useEntityItemPage/useEntityItemFormPage)
|
|
242
|
-
const { breadcrumb: defaultBreadcrumb, navlinks: defaultNavlinks } = useNavContext()
|
|
244
|
+
const { breadcrumb: defaultBreadcrumb, navlinks: defaultNavlinks, modeToggle } = useNavContext()
|
|
243
245
|
|
|
244
246
|
// Allow child pages to override breadcrumb/navlinks via provide/inject
|
|
245
247
|
const breadcrumbOverride = ref<BreadcrumbItem[] | null>(null)
|
|
@@ -258,6 +260,12 @@ const showBreadcrumb = computed<boolean>(() => {
|
|
|
258
260
|
if (route.name === 'dashboard') return false
|
|
259
261
|
return true
|
|
260
262
|
})
|
|
263
|
+
|
|
264
|
+
// View↔Edit toggle on the terminal crumb (#1332/#1341) — same opt-in contract
|
|
265
|
+
// as DefaultBreadcrumb; this inline breadcrumb must honor the flag too
|
|
266
|
+
const shownModeToggle = computed(() =>
|
|
267
|
+
features.breadcrumbModeToggle === true ? modeToggle.value : null
|
|
268
|
+
)
|
|
261
269
|
</script>
|
|
262
270
|
|
|
263
271
|
<template>
|
|
@@ -382,8 +390,10 @@ const showBreadcrumb = computed<boolean>(() => {
|
|
|
382
390
|
</template>
|
|
383
391
|
</Breadcrumb>
|
|
384
392
|
|
|
385
|
-
<!-- Navlinks (provided by PageNav for child routes)
|
|
386
|
-
|
|
393
|
+
<!-- Navlinks (provided by PageNav for child routes) + View↔Edit mode
|
|
394
|
+
toggle (#1332/#1341) — plain navigation; the form page's own
|
|
395
|
+
route-leave guard covers unsaved changes on edit→show -->
|
|
396
|
+
<div v-if="navlinks.length > 0 || shownModeToggle" class="layout-navlinks">
|
|
387
397
|
<template v-for="(link, index) in navlinks" :key="link.to?.name || index">
|
|
388
398
|
<span v-if="index > 0" class="layout-navlinks-separator">|</span>
|
|
389
399
|
<RouterLink
|
|
@@ -394,6 +404,15 @@ const showBreadcrumb = computed<boolean>(() => {
|
|
|
394
404
|
{{ link.label }}
|
|
395
405
|
</RouterLink>
|
|
396
406
|
</template>
|
|
407
|
+
<template v-if="shownModeToggle">
|
|
408
|
+
<span v-if="navlinks.length > 0" class="layout-navlinks-separator">|</span>
|
|
409
|
+
<RouterLink
|
|
410
|
+
:to="shownModeToggle.to"
|
|
411
|
+
class="layout-navlink breadcrumb-mode-toggle"
|
|
412
|
+
>
|
|
413
|
+
{{ shownModeToggle.label }}
|
|
414
|
+
</RouterLink>
|
|
415
|
+
</template>
|
|
397
416
|
</div>
|
|
398
417
|
</div>
|
|
399
418
|
|
|
@@ -64,19 +64,10 @@ const shownModeToggle = computed(() =>
|
|
|
64
64
|
</template>
|
|
65
65
|
</Breadcrumb>
|
|
66
66
|
|
|
67
|
-
<!--
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
:to="shownModeToggle.to"
|
|
72
|
-
class="breadcrumb-mode-toggle"
|
|
73
|
-
>
|
|
74
|
-
<i :class="shownModeToggle.target === 'edit' ? 'pi pi-pencil' : 'pi pi-eye'"></i>
|
|
75
|
-
<span>{{ shownModeToggle.label }}</span>
|
|
76
|
-
</RouterLink>
|
|
77
|
-
|
|
78
|
-
<!-- Navlinks (provided by PageNav for child routes) -->
|
|
79
|
-
<div v-if="navlinks.length > 0" class="breadcrumb-navlinks">
|
|
67
|
+
<!-- Navlinks (provided by PageNav for child routes) + View↔Edit mode
|
|
68
|
+
toggle (#1332/#1341) — plain navigation; the form page's own
|
|
69
|
+
route-leave guard covers unsaved changes on edit→show -->
|
|
70
|
+
<div v-if="navlinks.length > 0 || shownModeToggle" class="breadcrumb-navlinks">
|
|
80
71
|
<template v-for="(link, index) in navlinks" :key="link.to?.name || index">
|
|
81
72
|
<span v-if="index > 0" class="navlinks-separator">|</span>
|
|
82
73
|
<RouterLink
|
|
@@ -87,6 +78,15 @@ const shownModeToggle = computed(() =>
|
|
|
87
78
|
{{ link.label }}
|
|
88
79
|
</RouterLink>
|
|
89
80
|
</template>
|
|
81
|
+
<template v-if="shownModeToggle">
|
|
82
|
+
<span v-if="navlinks.length > 0" class="navlinks-separator">|</span>
|
|
83
|
+
<RouterLink
|
|
84
|
+
:to="shownModeToggle.to"
|
|
85
|
+
class="navlink breadcrumb-mode-toggle"
|
|
86
|
+
>
|
|
87
|
+
{{ shownModeToggle.label }}
|
|
88
|
+
</RouterLink>
|
|
89
|
+
</template>
|
|
90
90
|
</div>
|
|
91
91
|
</div>
|
|
92
92
|
</template>
|
|
@@ -111,24 +111,6 @@ const shownModeToggle = computed(() =>
|
|
|
111
111
|
font-size: 0.875rem;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
/* Mode toggle sits right after the crumbs; auto margin keeps navlinks pinned right */
|
|
115
|
-
.breadcrumb-mode-toggle {
|
|
116
|
-
display: inline-flex;
|
|
117
|
-
align-items: center;
|
|
118
|
-
gap: 0.35rem;
|
|
119
|
-
margin-left: 0.75rem;
|
|
120
|
-
margin-right: auto;
|
|
121
|
-
font-size: 0.875rem;
|
|
122
|
-
color: var(--p-primary-500, #3b82f6);
|
|
123
|
-
text-decoration: none;
|
|
124
|
-
transition: color 0.15s;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
.breadcrumb-mode-toggle:hover {
|
|
128
|
-
color: var(--p-primary-700, #1d4ed8);
|
|
129
|
-
text-decoration: underline;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
114
|
.navlinks-separator {
|
|
133
115
|
color: var(--p-surface-400, #94a3b8);
|
|
134
116
|
}
|
|
@@ -39,7 +39,8 @@ export interface ShowFieldDefinition {
|
|
|
39
39
|
type?: string
|
|
40
40
|
schemaType?: string
|
|
41
41
|
label?: string
|
|
42
|
-
|
|
42
|
+
/** Canonical FieldConfig object form, or the entity name as a bare string */
|
|
43
|
+
reference?: string | { entity: string; labelField?: string; valueField?: string }
|
|
43
44
|
referenceRoute?: string | ((value: unknown) => { name: string; params: Record<string, unknown> })
|
|
44
45
|
referenceLabel?: string | ((value: unknown, option?: unknown) => string)
|
|
45
46
|
options?: unknown[]
|
|
@@ -73,6 +74,8 @@ export interface ShowResolvedFieldConfig extends ShowFieldDefinition {
|
|
|
73
74
|
*/
|
|
74
75
|
export interface ShowResolverOrchestrator {
|
|
75
76
|
get: (entityName: string) => ShowResolverManager | undefined
|
|
77
|
+
/** Real Orchestrator.get THROWS on unknown entities — probe first when available */
|
|
78
|
+
has?: (entityName: string) => boolean
|
|
76
79
|
}
|
|
77
80
|
|
|
78
81
|
/**
|
|
@@ -133,13 +136,20 @@ export function createShowFieldResolver(
|
|
|
133
136
|
let resolvedDisplayType = SHOW_TYPE_MAPPINGS[type] || SHOW_TYPE_MAPPINGS[schemaType] || 'text'
|
|
134
137
|
const resolvedLabel = label || snakeCaseToTitle(name)
|
|
135
138
|
|
|
136
|
-
// Auto-set reference route if reference entity is specified
|
|
139
|
+
// Auto-set reference route if reference entity is specified.
|
|
140
|
+
// FieldConfig.reference is canonically { entity, ... }; the bare-string
|
|
141
|
+
// form stays accepted. Passing the object to orchestrator.get produced
|
|
142
|
+
// '[object Object]' lookups that THREW during generateFields (#1341).
|
|
137
143
|
let referenceRoute = rest.referenceRoute
|
|
138
144
|
if (reference && !referenceRoute) {
|
|
139
|
-
const
|
|
145
|
+
const refEntity = typeof reference === 'string' ? reference : reference.entity
|
|
146
|
+
const refManager =
|
|
147
|
+
refEntity && (!orchestrator.has || orchestrator.has(refEntity))
|
|
148
|
+
? orchestrator.get(refEntity)
|
|
149
|
+
: undefined
|
|
140
150
|
if (refManager) {
|
|
141
151
|
referenceRoute = (value: unknown) => ({
|
|
142
|
-
name: `${refManager.routePrefix ||
|
|
152
|
+
name: `${refManager.routePrefix || refEntity}-show`,
|
|
143
153
|
params: { [refManager.idField]: value },
|
|
144
154
|
})
|
|
145
155
|
}
|