qdadm 0.16.0 → 0.17.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/README.md +153 -1
- package/package.json +15 -2
- package/src/components/forms/FormField.vue +64 -6
- package/src/components/forms/FormPage.vue +276 -0
- package/src/components/index.js +11 -0
- package/src/components/layout/BaseLayout.vue +183 -0
- package/src/components/layout/DashboardLayout.vue +100 -0
- package/src/components/layout/FormLayout.vue +261 -0
- package/src/components/layout/ListLayout.vue +334 -0
- package/src/components/layout/Zone.vue +165 -0
- package/src/components/layout/defaults/DefaultBreadcrumb.vue +140 -0
- package/src/components/layout/defaults/DefaultFooter.vue +56 -0
- package/src/components/layout/defaults/DefaultFormActions.vue +53 -0
- package/src/components/layout/defaults/DefaultHeader.vue +69 -0
- package/src/components/layout/defaults/DefaultMenu.vue +197 -0
- package/src/components/layout/defaults/DefaultPagination.vue +79 -0
- package/src/components/layout/defaults/DefaultTable.vue +130 -0
- package/src/components/layout/defaults/DefaultToaster.vue +16 -0
- package/src/components/layout/defaults/DefaultUserInfo.vue +96 -0
- package/src/components/layout/defaults/index.js +17 -0
- package/src/composables/index.js +6 -6
- package/src/composables/useForm.js +135 -0
- package/src/composables/useFormPageBuilder.js +1154 -0
- package/src/composables/useHooks.js +53 -0
- package/src/composables/useLayoutResolver.js +260 -0
- package/src/composables/useListPageBuilder.js +336 -52
- package/src/composables/useNavigation.js +38 -2
- package/src/composables/useSignals.js +49 -0
- package/src/composables/useZoneRegistry.js +162 -0
- package/src/core/bundles.js +406 -0
- package/src/core/decorator.js +322 -0
- package/src/core/extension.js +386 -0
- package/src/core/index.js +28 -0
- package/src/entity/EntityManager.js +314 -16
- package/src/entity/auth/AuthAdapter.js +125 -0
- package/src/entity/auth/PermissiveAdapter.js +64 -0
- package/src/entity/auth/index.js +11 -0
- package/src/entity/index.js +3 -0
- package/src/entity/storage/MockApiStorage.js +349 -0
- package/src/entity/storage/SdkStorage.js +478 -0
- package/src/entity/storage/index.js +2 -0
- package/src/hooks/HookRegistry.js +411 -0
- package/src/hooks/index.js +12 -0
- package/src/index.js +9 -0
- package/src/kernel/Kernel.js +136 -4
- package/src/kernel/SignalBus.js +180 -0
- package/src/kernel/index.js +7 -0
- package/src/module/moduleRegistry.js +124 -6
- package/src/orchestrator/Orchestrator.js +73 -1
- package/src/zones/ZoneRegistry.js +821 -0
- package/src/zones/index.js +16 -0
- package/src/zones/zones.js +189 -0
- package/src/composables/useEntityTitle.js +0 -121
- package/src/composables/useManager.js +0 -20
- package/src/composables/usePageBuilder.js +0 -334
- package/src/composables/useStatus.js +0 -146
- package/src/composables/useSubEditor.js +0 -165
- package/src/composables/useTabSync.js +0 -110
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useHooks - Access the hook registry for Drupal-inspired extensibility
|
|
3
|
+
*
|
|
4
|
+
* Provides access to the HookRegistry created by Kernel during bootstrap.
|
|
5
|
+
* Components can register and invoke hooks without direct imports.
|
|
6
|
+
*
|
|
7
|
+
* @returns {HookRegistry|null} - The hook registry instance or null if not available
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // Register a lifecycle hook
|
|
11
|
+
* const hooks = useHooks()
|
|
12
|
+
* hooks.register('entity:presave', async (event) => {
|
|
13
|
+
* event.data.entity.updated_at = Date.now()
|
|
14
|
+
* }, { priority: 10 })
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* // Register an alter hook
|
|
18
|
+
* const hooks = useHooks()
|
|
19
|
+
* hooks.register('list:alter', (config) => {
|
|
20
|
+
* config.columns.push({ field: 'custom' })
|
|
21
|
+
* return config
|
|
22
|
+
* })
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* // Invoke hooks
|
|
26
|
+
* const hooks = useHooks()
|
|
27
|
+
*
|
|
28
|
+
* // Lifecycle hook (fire-and-forget)
|
|
29
|
+
* await hooks.invoke('entity:presave', { entity, manager })
|
|
30
|
+
*
|
|
31
|
+
* // Alter hook (chained transforms)
|
|
32
|
+
* const config = await hooks.alter('list:alter', baseConfig)
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // Auto-cleanup on unmount
|
|
36
|
+
* import { onUnmounted } from 'vue'
|
|
37
|
+
*
|
|
38
|
+
* const hooks = useHooks()
|
|
39
|
+
* const unbind = hooks.register('entity:created', handler)
|
|
40
|
+
* onUnmounted(() => unbind())
|
|
41
|
+
*/
|
|
42
|
+
import { inject } from 'vue'
|
|
43
|
+
|
|
44
|
+
export function useHooks() {
|
|
45
|
+
const hooks = inject('qdadmHooks')
|
|
46
|
+
|
|
47
|
+
if (!hooks) {
|
|
48
|
+
console.warn('[qdadm] useHooks: hook registry not available. Ensure Kernel is initialized.')
|
|
49
|
+
return null
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return hooks
|
|
53
|
+
}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useLayoutResolver - Automatic layout resolution for entity pages
|
|
3
|
+
*
|
|
4
|
+
* Resolves the appropriate layout component based on:
|
|
5
|
+
* 1. Route meta: `{ meta: { layout: 'list' } }` (highest priority)
|
|
6
|
+
* 2. Page name convention: `*ListPage` -> ListLayout, `*EditPage` -> FormLayout
|
|
7
|
+
* 3. Explicit prop: `layout="list"` passed to component
|
|
8
|
+
*
|
|
9
|
+
* Layout types:
|
|
10
|
+
* - 'list' -> ListLayout (entity list pages)
|
|
11
|
+
* - 'form' -> FormLayout (create/edit pages)
|
|
12
|
+
* - 'dashboard' -> DashboardLayout (dashboard pages)
|
|
13
|
+
* - 'base' -> BaseLayout (generic pages)
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* ```js
|
|
17
|
+
* const { layoutComponent, layoutType, resolveLayout } = useLayoutResolver()
|
|
18
|
+
*
|
|
19
|
+
* // In template:
|
|
20
|
+
* <component :is="layoutComponent">
|
|
21
|
+
* <template #main>...</template>
|
|
22
|
+
* </component>
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* Or with explicit layout prop:
|
|
26
|
+
* ```js
|
|
27
|
+
* const { layoutComponent } = useLayoutResolver({ layout: 'list' })
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
import { computed, inject, getCurrentInstance } from 'vue'
|
|
31
|
+
import { useRoute } from 'vue-router'
|
|
32
|
+
|
|
33
|
+
// Layout type constants
|
|
34
|
+
export const LAYOUT_TYPES = {
|
|
35
|
+
LIST: 'list',
|
|
36
|
+
FORM: 'form',
|
|
37
|
+
DASHBOARD: 'dashboard',
|
|
38
|
+
BASE: 'base'
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Page name patterns for auto-detection
|
|
42
|
+
const PAGE_NAME_PATTERNS = [
|
|
43
|
+
{ pattern: /List(Page)?$/i, layout: LAYOUT_TYPES.LIST },
|
|
44
|
+
{ pattern: /(Edit|Create|Form)(Page)?$/i, layout: LAYOUT_TYPES.FORM },
|
|
45
|
+
{ pattern: /Dashboard(Page)?$/i, layout: LAYOUT_TYPES.DASHBOARD }
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
// Route name patterns for auto-detection
|
|
49
|
+
const ROUTE_NAME_PATTERNS = [
|
|
50
|
+
{ pattern: /-list$/i, layout: LAYOUT_TYPES.LIST },
|
|
51
|
+
{ pattern: /-(edit|create)$/i, layout: LAYOUT_TYPES.FORM },
|
|
52
|
+
{ pattern: /-dashboard$/i, layout: LAYOUT_TYPES.DASHBOARD },
|
|
53
|
+
// Common patterns without suffix
|
|
54
|
+
{ pattern: /^dashboard$/i, layout: LAYOUT_TYPES.DASHBOARD }
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Resolve layout type from component name
|
|
59
|
+
* @param {string} componentName - Vue component name
|
|
60
|
+
* @returns {string|null} Layout type or null
|
|
61
|
+
*/
|
|
62
|
+
function resolveFromComponentName(componentName) {
|
|
63
|
+
if (!componentName) return null
|
|
64
|
+
|
|
65
|
+
for (const { pattern, layout } of PAGE_NAME_PATTERNS) {
|
|
66
|
+
if (pattern.test(componentName)) {
|
|
67
|
+
return layout
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return null
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Resolve layout type from route name
|
|
75
|
+
* @param {string} routeName - Route name
|
|
76
|
+
* @returns {string|null} Layout type or null
|
|
77
|
+
*/
|
|
78
|
+
function resolveFromRouteName(routeName) {
|
|
79
|
+
if (!routeName) return null
|
|
80
|
+
|
|
81
|
+
for (const { pattern, layout } of ROUTE_NAME_PATTERNS) {
|
|
82
|
+
if (pattern.test(routeName)) {
|
|
83
|
+
return layout
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return null
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* useLayoutResolver composable
|
|
91
|
+
*
|
|
92
|
+
* @param {object} options - Configuration options
|
|
93
|
+
* @param {string} [options.layout] - Explicit layout type override
|
|
94
|
+
* @param {string} [options.default] - Default layout if none detected (default: 'base')
|
|
95
|
+
* @returns {object} Layout resolution utilities
|
|
96
|
+
*/
|
|
97
|
+
export function useLayoutResolver(options = {}) {
|
|
98
|
+
const route = useRoute()
|
|
99
|
+
const instance = getCurrentInstance()
|
|
100
|
+
|
|
101
|
+
// Get layout components from injection (provided by Kernel)
|
|
102
|
+
const layoutComponents = inject('qdadmLayoutComponents', null)
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Resolve the layout type based on priority:
|
|
106
|
+
* 1. Explicit option.layout
|
|
107
|
+
* 2. Route meta.layout
|
|
108
|
+
* 3. Component name pattern
|
|
109
|
+
* 4. Route name pattern
|
|
110
|
+
* 5. Default
|
|
111
|
+
*/
|
|
112
|
+
const layoutType = computed(() => {
|
|
113
|
+
// Priority 1: Explicit layout prop/option
|
|
114
|
+
if (options.layout) {
|
|
115
|
+
return options.layout
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Priority 2: Route meta.layout
|
|
119
|
+
if (route.meta?.layout) {
|
|
120
|
+
return route.meta.layout
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Priority 3: Component name pattern
|
|
124
|
+
const componentName = instance?.type?.name || instance?.type?.__name
|
|
125
|
+
const fromComponentName = resolveFromComponentName(componentName)
|
|
126
|
+
if (fromComponentName) {
|
|
127
|
+
return fromComponentName
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Priority 4: Route name pattern
|
|
131
|
+
const fromRouteName = resolveFromRouteName(route.name)
|
|
132
|
+
if (fromRouteName) {
|
|
133
|
+
return fromRouteName
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Priority 5: Default
|
|
137
|
+
return options.default || LAYOUT_TYPES.BASE
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Get the layout component for the resolved type
|
|
142
|
+
*/
|
|
143
|
+
const layoutComponent = computed(() => {
|
|
144
|
+
if (!layoutComponents) {
|
|
145
|
+
// No components injected, return null (caller handles fallback)
|
|
146
|
+
return null
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const type = layoutType.value
|
|
150
|
+
return layoutComponents[type] || layoutComponents.base || null
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Check if a specific layout type is active
|
|
155
|
+
* @param {string} type - Layout type to check
|
|
156
|
+
* @returns {boolean}
|
|
157
|
+
*/
|
|
158
|
+
function isLayout(type) {
|
|
159
|
+
return layoutType.value === type
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Manually resolve layout for a given context
|
|
164
|
+
* Useful for programmatic layout determination
|
|
165
|
+
*
|
|
166
|
+
* @param {object} context - Resolution context
|
|
167
|
+
* @param {string} [context.routeMeta] - Route meta object
|
|
168
|
+
* @param {string} [context.componentName] - Component name
|
|
169
|
+
* @param {string} [context.routeName] - Route name
|
|
170
|
+
* @param {string} [context.explicit] - Explicit layout override
|
|
171
|
+
* @returns {string} Resolved layout type
|
|
172
|
+
*/
|
|
173
|
+
function resolveLayout(context = {}) {
|
|
174
|
+
// Priority 1: Explicit
|
|
175
|
+
if (context.explicit) {
|
|
176
|
+
return context.explicit
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Priority 2: Route meta
|
|
180
|
+
if (context.routeMeta?.layout) {
|
|
181
|
+
return context.routeMeta.layout
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Priority 3: Component name
|
|
185
|
+
const fromComponent = resolveFromComponentName(context.componentName)
|
|
186
|
+
if (fromComponent) {
|
|
187
|
+
return fromComponent
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Priority 4: Route name
|
|
191
|
+
const fromRoute = resolveFromRouteName(context.routeName)
|
|
192
|
+
if (fromRoute) {
|
|
193
|
+
return fromRoute
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Default
|
|
197
|
+
return options.default || LAYOUT_TYPES.BASE
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
/** Current resolved layout type */
|
|
202
|
+
layoutType,
|
|
203
|
+
/** Layout component for current type (if injected) */
|
|
204
|
+
layoutComponent,
|
|
205
|
+
/** Check if specific layout type is active */
|
|
206
|
+
isLayout,
|
|
207
|
+
/** Manually resolve layout for given context */
|
|
208
|
+
resolveLayout,
|
|
209
|
+
/** Layout type constants */
|
|
210
|
+
LAYOUT_TYPES
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Create layout components map for Kernel injection
|
|
216
|
+
*
|
|
217
|
+
* @param {object} components - Layout component imports
|
|
218
|
+
* @returns {object} Layout components map
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* ```js
|
|
222
|
+
* import ListLayout from './components/layout/ListLayout.vue'
|
|
223
|
+
* import FormLayout from './components/layout/FormLayout.vue'
|
|
224
|
+
*
|
|
225
|
+
* const layouts = createLayoutComponents({
|
|
226
|
+
* list: ListLayout,
|
|
227
|
+
* form: FormLayout
|
|
228
|
+
* })
|
|
229
|
+
* ```
|
|
230
|
+
*/
|
|
231
|
+
export function createLayoutComponents(components = {}) {
|
|
232
|
+
return {
|
|
233
|
+
[LAYOUT_TYPES.LIST]: components.list || components.ListLayout || null,
|
|
234
|
+
[LAYOUT_TYPES.FORM]: components.form || components.FormLayout || null,
|
|
235
|
+
[LAYOUT_TYPES.DASHBOARD]: components.dashboard || components.DashboardLayout || null,
|
|
236
|
+
[LAYOUT_TYPES.BASE]: components.base || components.BaseLayout || null
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Get route meta configuration for layout
|
|
242
|
+
*
|
|
243
|
+
* Helper to add layout meta to route definitions
|
|
244
|
+
*
|
|
245
|
+
* @param {string} layoutType - Layout type
|
|
246
|
+
* @returns {object} Route meta object
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* ```js
|
|
250
|
+
* {
|
|
251
|
+
* path: 'books',
|
|
252
|
+
* name: 'book-list',
|
|
253
|
+
* component: BookList,
|
|
254
|
+
* meta: layoutMeta('list')
|
|
255
|
+
* }
|
|
256
|
+
* ```
|
|
257
|
+
*/
|
|
258
|
+
export function layoutMeta(layoutType) {
|
|
259
|
+
return { layout: layoutType }
|
|
260
|
+
}
|