qdadm 0.16.0 → 0.18.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 +359 -16
- package/src/entity/auth/AuthAdapter.js +184 -0
- package/src/entity/auth/PermissiveAdapter.js +64 -0
- package/src/entity/auth/RoleHierarchy.js +153 -0
- package/src/entity/auth/SecurityChecker.js +167 -0
- package/src/entity/auth/index.js +18 -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 +13 -0
- package/src/kernel/Kernel.js +206 -5
- package/src/kernel/SignalBus.js +180 -0
- package/src/kernel/index.js +7 -0
- package/src/module/moduleRegistry.js +155 -28
- package/src/orchestrator/Orchestrator.js +73 -1
- package/src/zones/ZoneRegistry.js +828 -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
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* - Unsaved changes guard (via useBareForm)
|
|
8
8
|
* - Toast notifications
|
|
9
9
|
* - Navigation helpers
|
|
10
|
+
* - form:alter hook for extensibility
|
|
10
11
|
*
|
|
11
12
|
* Usage:
|
|
12
13
|
* ```js
|
|
@@ -25,9 +26,45 @@
|
|
|
25
26
|
* entityName: 'User' // Override manager.label
|
|
26
27
|
* })
|
|
27
28
|
* ```
|
|
29
|
+
*
|
|
30
|
+
* ## form:alter Hook
|
|
31
|
+
*
|
|
32
|
+
* The composable invokes `form:alter` and `{entity}:form:alter` hooks after
|
|
33
|
+
* form initialization (load for edit, or initial data for create). This allows
|
|
34
|
+
* modules to modify form configuration dynamically.
|
|
35
|
+
*
|
|
36
|
+
* Hook context structure:
|
|
37
|
+
* @typedef {object} FormAlterConfig
|
|
38
|
+
* @property {string} entity - Entity name
|
|
39
|
+
* @property {Array} fields - Field definitions from manager
|
|
40
|
+
* @property {boolean} isEdit - Whether form is in edit mode
|
|
41
|
+
* @property {*} entityId - Entity ID (null for create)
|
|
42
|
+
* @property {object} form - Current form data
|
|
43
|
+
* @property {object} manager - EntityManager reference
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* // Register a hook to modify form fields
|
|
47
|
+
* hooks.register('form:alter', (config) => {
|
|
48
|
+
* if (config.entity === 'books') {
|
|
49
|
+
* // Add a computed field
|
|
50
|
+
* config.fields.push({ name: 'fullTitle', computed: true, readonly: true })
|
|
51
|
+
* }
|
|
52
|
+
* return config
|
|
53
|
+
* })
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* // Entity-specific hook
|
|
57
|
+
* hooks.register('books:form:alter', (config) => {
|
|
58
|
+
* // Modify field visibility based on edit mode
|
|
59
|
+
* if (!config.isEdit) {
|
|
60
|
+
* config.fields = config.fields.filter(f => f.name !== 'internal_id')
|
|
61
|
+
* }
|
|
62
|
+
* return config
|
|
63
|
+
* })
|
|
28
64
|
*/
|
|
29
65
|
import { ref, computed, watch, onMounted, inject, provide } from 'vue'
|
|
30
66
|
import { useBareForm } from './useBareForm'
|
|
67
|
+
import { useHooks } from './useHooks'
|
|
31
68
|
import { deepClone } from '../utils/transformers'
|
|
32
69
|
|
|
33
70
|
export function useForm(options = {}) {
|
|
@@ -53,6 +90,9 @@ export function useForm(options = {}) {
|
|
|
53
90
|
}
|
|
54
91
|
const manager = orchestrator.get(entity)
|
|
55
92
|
|
|
93
|
+
// Get HookRegistry for form:alter hook (optional, may not exist in tests)
|
|
94
|
+
const hooks = useHooks()
|
|
95
|
+
|
|
56
96
|
// Read config from manager with option overrides
|
|
57
97
|
const routePrefix = options.routePrefix ?? manager.routePrefix
|
|
58
98
|
const entityName = options.entityName ?? manager.label
|
|
@@ -62,6 +102,23 @@ export function useForm(options = {}) {
|
|
|
62
102
|
const form = ref(deepClone(initialData))
|
|
63
103
|
const originalData = ref(null)
|
|
64
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Altered fields configuration after form:alter hook processing
|
|
107
|
+
*
|
|
108
|
+
* Contains the field definitions modified by registered hooks.
|
|
109
|
+
* Modules can register form:alter hooks to modify field visibility,
|
|
110
|
+
* add/remove fields, change labels, or modify validation rules.
|
|
111
|
+
*
|
|
112
|
+
* @type {import('vue').Ref<Array<object>>}
|
|
113
|
+
*/
|
|
114
|
+
const alteredFields = ref([])
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Whether form:alter hooks have been invoked
|
|
118
|
+
* @type {import('vue').Ref<boolean>}
|
|
119
|
+
*/
|
|
120
|
+
const hooksInvoked = ref(false)
|
|
121
|
+
|
|
65
122
|
// Dirty state getter
|
|
66
123
|
const dirtyStateGetter = getDirtyState || (() => ({ form: form.value }))
|
|
67
124
|
|
|
@@ -98,11 +155,81 @@ export function useForm(options = {}) {
|
|
|
98
155
|
// Watch for changes
|
|
99
156
|
watch(form, checkDirty, { deep: true })
|
|
100
157
|
|
|
158
|
+
// ============ FORM:ALTER HOOK ============
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Invoke form:alter hooks to allow modules to modify form configuration
|
|
162
|
+
*
|
|
163
|
+
* Builds a config snapshot from current state, passes it through the hook chain,
|
|
164
|
+
* and stores the altered fields configuration.
|
|
165
|
+
*
|
|
166
|
+
* Hook context structure:
|
|
167
|
+
* @typedef {object} FormAlterConfig
|
|
168
|
+
* @property {string} entity - Entity name
|
|
169
|
+
* @property {Array} fields - Field definitions from manager.getFormFields()
|
|
170
|
+
* @property {boolean} isEdit - Whether form is in edit mode
|
|
171
|
+
* @property {*} entityId - Entity ID (null for create)
|
|
172
|
+
* @property {object} form - Current form data
|
|
173
|
+
* @property {object} manager - EntityManager reference
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* // Register a hook to add a custom field
|
|
177
|
+
* hooks.register('form:alter', (config) => {
|
|
178
|
+
* if (config.entity === 'books') {
|
|
179
|
+
* config.fields.push({ name: 'custom', type: 'text', label: 'Custom' })
|
|
180
|
+
* }
|
|
181
|
+
* return config
|
|
182
|
+
* })
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* // Register entity-specific hook
|
|
186
|
+
* hooks.register('books:form:alter', (config) => {
|
|
187
|
+
* // Hide internal_id in create mode
|
|
188
|
+
* if (!config.isEdit) {
|
|
189
|
+
* config.fields = config.fields.filter(f => f.name !== 'internal_id')
|
|
190
|
+
* }
|
|
191
|
+
* return config
|
|
192
|
+
* })
|
|
193
|
+
*/
|
|
194
|
+
async function invokeFormAlterHook() {
|
|
195
|
+
if (!hooks) return
|
|
196
|
+
|
|
197
|
+
// Get fields from manager (if available)
|
|
198
|
+
const managerFields = typeof manager.getFormFields === 'function'
|
|
199
|
+
? manager.getFormFields()
|
|
200
|
+
: (manager.fields || [])
|
|
201
|
+
|
|
202
|
+
// Build config snapshot
|
|
203
|
+
const configSnapshot = {
|
|
204
|
+
entity,
|
|
205
|
+
fields: deepClone(managerFields),
|
|
206
|
+
isEdit: isEdit.value,
|
|
207
|
+
entityId: entityId.value,
|
|
208
|
+
form: form.value,
|
|
209
|
+
manager,
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Invoke generic form:alter hook
|
|
213
|
+
let alteredConfig = await hooks.alter('form:alter', configSnapshot)
|
|
214
|
+
|
|
215
|
+
// Invoke entity-specific hook: {entity}:form:alter
|
|
216
|
+
const entityHookName = `${entity}:form:alter`
|
|
217
|
+
if (hooks.hasHook(entityHookName)) {
|
|
218
|
+
alteredConfig = await hooks.alter(entityHookName, alteredConfig)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Store altered fields for consumption by FormPage
|
|
222
|
+
alteredFields.value = alteredConfig.fields || []
|
|
223
|
+
hooksInvoked.value = true
|
|
224
|
+
}
|
|
225
|
+
|
|
101
226
|
// Load entity
|
|
102
227
|
async function load() {
|
|
103
228
|
if (!isEdit.value) {
|
|
104
229
|
form.value = deepClone(initialData)
|
|
105
230
|
takeSnapshot()
|
|
231
|
+
// Invoke form:alter hooks for create mode
|
|
232
|
+
await invokeFormAlterHook()
|
|
106
233
|
return
|
|
107
234
|
}
|
|
108
235
|
|
|
@@ -114,6 +241,9 @@ export function useForm(options = {}) {
|
|
|
114
241
|
originalData.value = deepClone(data)
|
|
115
242
|
takeSnapshot()
|
|
116
243
|
|
|
244
|
+
// Invoke form:alter hooks after data is loaded
|
|
245
|
+
await invokeFormAlterHook()
|
|
246
|
+
|
|
117
247
|
if (onLoadSuccess) {
|
|
118
248
|
await onLoadSuccess(data)
|
|
119
249
|
}
|
|
@@ -276,6 +406,11 @@ export function useForm(options = {}) {
|
|
|
276
406
|
entityId,
|
|
277
407
|
originalData,
|
|
278
408
|
|
|
409
|
+
// form:alter hook results
|
|
410
|
+
alteredFields,
|
|
411
|
+
hooksInvoked,
|
|
412
|
+
invokeFormAlterHook,
|
|
413
|
+
|
|
279
414
|
// Actions
|
|
280
415
|
load,
|
|
281
416
|
submit,
|