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.
Files changed (58) hide show
  1. package/README.md +153 -1
  2. package/package.json +15 -2
  3. package/src/components/forms/FormField.vue +64 -6
  4. package/src/components/forms/FormPage.vue +276 -0
  5. package/src/components/index.js +11 -0
  6. package/src/components/layout/BaseLayout.vue +183 -0
  7. package/src/components/layout/DashboardLayout.vue +100 -0
  8. package/src/components/layout/FormLayout.vue +261 -0
  9. package/src/components/layout/ListLayout.vue +334 -0
  10. package/src/components/layout/Zone.vue +165 -0
  11. package/src/components/layout/defaults/DefaultBreadcrumb.vue +140 -0
  12. package/src/components/layout/defaults/DefaultFooter.vue +56 -0
  13. package/src/components/layout/defaults/DefaultFormActions.vue +53 -0
  14. package/src/components/layout/defaults/DefaultHeader.vue +69 -0
  15. package/src/components/layout/defaults/DefaultMenu.vue +197 -0
  16. package/src/components/layout/defaults/DefaultPagination.vue +79 -0
  17. package/src/components/layout/defaults/DefaultTable.vue +130 -0
  18. package/src/components/layout/defaults/DefaultToaster.vue +16 -0
  19. package/src/components/layout/defaults/DefaultUserInfo.vue +96 -0
  20. package/src/components/layout/defaults/index.js +17 -0
  21. package/src/composables/index.js +6 -6
  22. package/src/composables/useForm.js +135 -0
  23. package/src/composables/useFormPageBuilder.js +1154 -0
  24. package/src/composables/useHooks.js +53 -0
  25. package/src/composables/useLayoutResolver.js +260 -0
  26. package/src/composables/useListPageBuilder.js +336 -52
  27. package/src/composables/useNavigation.js +38 -2
  28. package/src/composables/useSignals.js +49 -0
  29. package/src/composables/useZoneRegistry.js +162 -0
  30. package/src/core/bundles.js +406 -0
  31. package/src/core/decorator.js +322 -0
  32. package/src/core/extension.js +386 -0
  33. package/src/core/index.js +28 -0
  34. package/src/entity/EntityManager.js +314 -16
  35. package/src/entity/auth/AuthAdapter.js +125 -0
  36. package/src/entity/auth/PermissiveAdapter.js +64 -0
  37. package/src/entity/auth/index.js +11 -0
  38. package/src/entity/index.js +3 -0
  39. package/src/entity/storage/MockApiStorage.js +349 -0
  40. package/src/entity/storage/SdkStorage.js +478 -0
  41. package/src/entity/storage/index.js +2 -0
  42. package/src/hooks/HookRegistry.js +411 -0
  43. package/src/hooks/index.js +12 -0
  44. package/src/index.js +9 -0
  45. package/src/kernel/Kernel.js +136 -4
  46. package/src/kernel/SignalBus.js +180 -0
  47. package/src/kernel/index.js +7 -0
  48. package/src/module/moduleRegistry.js +124 -6
  49. package/src/orchestrator/Orchestrator.js +73 -1
  50. package/src/zones/ZoneRegistry.js +821 -0
  51. package/src/zones/index.js +16 -0
  52. package/src/zones/zones.js +189 -0
  53. package/src/composables/useEntityTitle.js +0 -121
  54. package/src/composables/useManager.js +0 -20
  55. package/src/composables/usePageBuilder.js +0 -334
  56. package/src/composables/useStatus.js +0 -146
  57. package/src/composables/useSubEditor.js +0 -165
  58. 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
+ }