@momentumcms/admin 0.1.9 → 0.2.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 (24) hide show
  1. package/fesm2022/momentumcms-admin-array-field.component-CT5NlIEv.mjs +316 -0
  2. package/fesm2022/momentumcms-admin-array-field.component-CT5NlIEv.mjs.map +1 -0
  3. package/fesm2022/momentumcms-admin-blocks-field.component-Cz7HmuBK.mjs +362 -0
  4. package/fesm2022/momentumcms-admin-blocks-field.component-Cz7HmuBK.mjs.map +1 -0
  5. package/fesm2022/momentumcms-admin-collapsible-field.component-CtwrGQvg.mjs +120 -0
  6. package/fesm2022/momentumcms-admin-collapsible-field.component-CtwrGQvg.mjs.map +1 -0
  7. package/fesm2022/{momentumcms-admin-global-edit.page-DFDV-uh3.mjs → momentumcms-admin-global-edit.page-BBUtWCSl.mjs} +2 -2
  8. package/fesm2022/{momentumcms-admin-global-edit.page-DFDV-uh3.mjs.map → momentumcms-admin-global-edit.page-BBUtWCSl.mjs.map} +1 -1
  9. package/fesm2022/momentumcms-admin-group-field.component-BZeG8Oqy.mjs +184 -0
  10. package/fesm2022/momentumcms-admin-group-field.component-BZeG8Oqy.mjs.map +1 -0
  11. package/fesm2022/{momentumcms-admin-momentumcms-admin-z82jXEsP.mjs → momentumcms-admin-momentumcms-admin-o0FbJXZN.mjs} +9688 -12241
  12. package/fesm2022/momentumcms-admin-momentumcms-admin-o0FbJXZN.mjs.map +1 -0
  13. package/fesm2022/momentumcms-admin-relationship-field.component-BuxtRs2_.mjs +473 -0
  14. package/fesm2022/momentumcms-admin-relationship-field.component-BuxtRs2_.mjs.map +1 -0
  15. package/fesm2022/momentumcms-admin-rich-text-field.component-DKQ6pwp7.mjs +813 -0
  16. package/fesm2022/momentumcms-admin-rich-text-field.component-DKQ6pwp7.mjs.map +1 -0
  17. package/fesm2022/momentumcms-admin-row-field.component-ks3FXd4B.mjs +98 -0
  18. package/fesm2022/momentumcms-admin-row-field.component-ks3FXd4B.mjs.map +1 -0
  19. package/fesm2022/momentumcms-admin-tabs-field.component-mZ4dpZoD.mjs +189 -0
  20. package/fesm2022/momentumcms-admin-tabs-field.component-mZ4dpZoD.mjs.map +1 -0
  21. package/fesm2022/momentumcms-admin.mjs +1 -1
  22. package/package.json +1 -1
  23. package/types/momentumcms-admin.d.ts +79 -13
  24. package/fesm2022/momentumcms-admin-momentumcms-admin-z82jXEsP.mjs.map +0 -1
@@ -0,0 +1,362 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, computed, effect, untracked, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { moveItemInArray, CdkDropList, CdkDrag, CdkDragHandle } from '@angular/cdk/drag-drop';
4
+ import { NgIcon, provideIcons } from '@ng-icons/core';
5
+ import { heroBars2, heroTrash, heroPlus } from '@ng-icons/heroicons/outline';
6
+ import { Card, CardHeader, CardTitle, CardContent, CardFooter, Button, Badge } from '@momentumcms/ui';
7
+ import { humanizeFieldName } from '@momentumcms/core';
8
+ import { a as getFieldNodeState, i as isRecord, n as normalizeBlockDefaults, b as getSubNode, c as getFieldDefaultValue, F as FieldRenderer } from './momentumcms-admin-momentumcms-admin-o0FbJXZN.mjs';
9
+
10
+ /**
11
+ * Blocks field renderer.
12
+ *
13
+ * Renders a list of typed blocks. Each block has a `blockType` discriminator
14
+ * and type-specific fields. Users can add, remove, and reorder blocks.
15
+ * A dropdown allows selecting which block type to add.
16
+ *
17
+ * Data container pattern: passes block sub-field FieldTree nodes via
18
+ * getSubNode(getSubNode(formNode, blockIndex), subFieldName).
19
+ * Block mutations use nodeState.value.set(newArray).
20
+ */
21
+ class BlocksFieldRenderer {
22
+ /** Field definition (must be a BlocksField) */
23
+ field = input.required(...(ngDevMode ? [{ debugName: "field" }] : []));
24
+ /** Signal forms FieldTree node for this blocks array */
25
+ formNode = input(null, ...(ngDevMode ? [{ debugName: "formNode" }] : []));
26
+ /** Root signal forms FieldTree (for layout fields that look up child nodes) */
27
+ formTree = input(null, ...(ngDevMode ? [{ debugName: "formTree" }] : []));
28
+ /** Form model data (for condition evaluation and relationship filterOptions) */
29
+ formModel = input({}, ...(ngDevMode ? [{ debugName: "formModel" }] : []));
30
+ /** Form mode */
31
+ mode = input('create', ...(ngDevMode ? [{ debugName: "mode" }] : []));
32
+ /** Field path (e.g., "content") */
33
+ path = input.required(...(ngDevMode ? [{ debugName: "path" }] : []));
34
+ /** Bridge: extract FieldState from formNode */
35
+ nodeState = computed(() => getFieldNodeState(this.formNode()), ...(ngDevMode ? [{ debugName: "nodeState" }] : []));
36
+ /** Computed label */
37
+ label = computed(() => this.field().label || humanizeFieldName(this.field().name), ...(ngDevMode ? [{ debugName: "label" }] : []));
38
+ /** Computed description */
39
+ description = computed(() => this.field().description || '', ...(ngDevMode ? [{ debugName: "description" }] : []));
40
+ /** Block type definitions from the field */
41
+ blockDefinitions = computed(() => {
42
+ const f = this.field();
43
+ if (f.type === 'blocks') {
44
+ return f.blocks;
45
+ }
46
+ return [];
47
+ }, ...(ngDevMode ? [{ debugName: "blockDefinitions" }] : []));
48
+ /** Min rows constraint */
49
+ minRows = computed(() => {
50
+ const f = this.field();
51
+ return f.type === 'blocks' ? (f.minRows ?? 0) : 0;
52
+ }, ...(ngDevMode ? [{ debugName: "minRows" }] : []));
53
+ /** Max rows constraint */
54
+ maxRows = computed(() => {
55
+ const f = this.field();
56
+ return f.type === 'blocks' ? f.maxRows : undefined;
57
+ }, ...(ngDevMode ? [{ debugName: "maxRows" }] : []));
58
+ /** Current blocks as typed items (read from FieldState) */
59
+ blocks = computed(() => {
60
+ const state = this.nodeState();
61
+ if (!state)
62
+ return [];
63
+ const val = state.value();
64
+ if (Array.isArray(val)) {
65
+ return val.filter((item) => isRecord(item) && typeof item['blockType'] === 'string');
66
+ }
67
+ return [];
68
+ }, ...(ngDevMode ? [{ debugName: "blocks" }] : []));
69
+ /**
70
+ * Normalize loaded blocks: ensure every block has defaults for all definition fields.
71
+ * Blocks saved before new fields were added won't have those keys, and
72
+ * signal-forms only creates controls for keys present in the model.
73
+ */
74
+ _normalizeBlocks = effect(() => {
75
+ const state = this.nodeState();
76
+ if (!state)
77
+ return;
78
+ const val = state.value();
79
+ if (!Array.isArray(val))
80
+ return;
81
+ const { normalized, changed } = normalizeBlockDefaults(val, this.blockDefMap());
82
+ if (changed) {
83
+ untracked(() => state.value.set(normalized));
84
+ }
85
+ }, ...(ngDevMode ? [{ debugName: "_normalizeBlocks" }] : []));
86
+ /** Whether the field is disabled (view mode) */
87
+ isDisabled = computed(() => this.mode() === 'view', ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
88
+ /** Whether a new block can be added */
89
+ canAddBlock = computed(() => {
90
+ if (this.isDisabled())
91
+ return false;
92
+ const max = this.maxRows();
93
+ return max === undefined || this.blocks().length < max;
94
+ }, ...(ngDevMode ? [{ debugName: "canAddBlock" }] : []));
95
+ /** Whether blocks can be removed */
96
+ canRemoveBlock = computed(() => {
97
+ if (this.isDisabled())
98
+ return false;
99
+ return this.blocks().length > this.minRows();
100
+ }, ...(ngDevMode ? [{ debugName: "canRemoveBlock" }] : []));
101
+ /** Block definition lookup cache */
102
+ blockDefMap = computed(() => {
103
+ const map = new Map();
104
+ for (const def of this.blockDefinitions()) {
105
+ map.set(def.slug, def);
106
+ }
107
+ return map;
108
+ }, ...(ngDevMode ? [{ debugName: "blockDefMap" }] : []));
109
+ /** Get display label for a block type */
110
+ getBlockLabel(blockType) {
111
+ const def = this.blockDefMap().get(blockType);
112
+ return def?.labels?.singular || blockType;
113
+ }
114
+ /** Get fields for a block type */
115
+ getBlockFields(blockType) {
116
+ const def = this.blockDefMap().get(blockType);
117
+ return def?.fields.filter((f) => !f.admin?.hidden) ?? [];
118
+ }
119
+ /** Get a FieldTree sub-node for a block's sub-field */
120
+ getBlockSubNode(blockIndex, subFieldName) {
121
+ const blockNode = getSubNode(this.formNode(), blockIndex);
122
+ return getSubNode(blockNode, subFieldName);
123
+ }
124
+ /** Get the full path for a block's sub-field */
125
+ getBlockSubFieldPath(blockIndex, subFieldName) {
126
+ return `${this.path()}.${blockIndex}.${subFieldName}`;
127
+ }
128
+ /** Handle drag-drop reorder */
129
+ onDrop(event) {
130
+ const state = this.nodeState();
131
+ if (!state)
132
+ return;
133
+ const blocks = [...this.blocks()];
134
+ moveItemInArray(blocks, event.previousIndex, event.currentIndex);
135
+ state.value.set(blocks);
136
+ }
137
+ /** Add a new block of the given type */
138
+ addBlock(blockType) {
139
+ const def = this.blockDefMap().get(blockType);
140
+ if (!def)
141
+ return;
142
+ const state = this.nodeState();
143
+ if (!state)
144
+ return;
145
+ const newBlock = { blockType };
146
+ for (const field of def.fields) {
147
+ newBlock[field.name] = getFieldDefaultValue(field);
148
+ }
149
+ const blocks = [...this.blocks(), newBlock];
150
+ state.value.set(blocks);
151
+ }
152
+ /** Remove a block at the given index */
153
+ removeBlock(index) {
154
+ const state = this.nodeState();
155
+ if (!state)
156
+ return;
157
+ const blocks = this.blocks().filter((_, i) => i !== index);
158
+ state.value.set(blocks);
159
+ }
160
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: BlocksFieldRenderer, deps: [], target: i0.ɵɵFactoryTarget.Component });
161
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: BlocksFieldRenderer, isStandalone: true, selector: "mcms-blocks-field-renderer", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, formNode: { classPropertyName: "formNode", publicName: "formNode", isSignal: true, isRequired: false, transformFunction: null }, formTree: { classPropertyName: "formTree", publicName: "formTree", isSignal: true, isRequired: false, transformFunction: null }, formModel: { classPropertyName: "formModel", publicName: "formModel", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, path: { classPropertyName: "path", publicName: "path", isSignal: true, isRequired: true, transformFunction: null } }, providers: [provideIcons({ heroPlus, heroTrash, heroBars2 })], ngImport: i0, template: `
162
+ <mcms-card>
163
+ <mcms-card-header>
164
+ <div class="flex items-center justify-between">
165
+ <div>
166
+ <mcms-card-title>{{ label() }}</mcms-card-title>
167
+ @if (description()) {
168
+ <p class="text-sm text-muted-foreground mt-1">{{ description() }}</p>
169
+ }
170
+ </div>
171
+ <span class="text-sm text-muted-foreground">
172
+ {{ blocks().length }}{{ maxRows() ? ' / ' + maxRows() : '' }} blocks
173
+ </span>
174
+ </div>
175
+ </mcms-card-header>
176
+ <mcms-card-content>
177
+ @if (blocks().length === 0) {
178
+ <p class="text-sm text-muted-foreground py-4 text-center">
179
+ No blocks yet. Add a block to get started.
180
+ </p>
181
+ } @else {
182
+ <div
183
+ cdkDropList
184
+ (cdkDropListDropped)="onDrop($event)"
185
+ class="space-y-3"
186
+ role="list"
187
+ aria-label="Content blocks"
188
+ >
189
+ @for (block of blocks(); track $index; let i = $index) {
190
+ <div cdkDrag class="border rounded-lg bg-card" [cdkDragDisabled]="isDisabled()">
191
+ <div class="flex items-center gap-3 px-4 py-2 border-b bg-muted/50 rounded-t-lg">
192
+ <div
193
+ cdkDragHandle
194
+ class="cursor-grab text-muted-foreground hover:text-foreground"
195
+ [class.hidden]="isDisabled()"
196
+ role="button"
197
+ tabindex="0"
198
+ [attr.aria-label]="'Reorder ' + getBlockLabel(block.blockType) + ' block'"
199
+ aria-roledescription="sortable"
200
+ >
201
+ <ng-icon name="heroBars2" size="16" aria-hidden="true" />
202
+ </div>
203
+ <mcms-badge>{{ getBlockLabel(block.blockType) }}</mcms-badge>
204
+ <div class="flex-1"></div>
205
+ @if (canRemoveBlock()) {
206
+ <button
207
+ mcms-button
208
+ variant="ghost"
209
+ size="icon"
210
+ class="h-7 w-7 text-destructive hover:text-destructive"
211
+ (click)="removeBlock(i)"
212
+ [attr.aria-label]="'Remove ' + getBlockLabel(block.blockType) + ' block'"
213
+ >
214
+ <ng-icon name="heroTrash" size="14" aria-hidden="true" />
215
+ </button>
216
+ }
217
+ </div>
218
+ <div class="p-4 space-y-3">
219
+ @for (subField of getBlockFields(block.blockType); track subField.name) {
220
+ <mcms-field-renderer
221
+ [field]="subField"
222
+ [formNode]="getBlockSubNode(i, subField.name)"
223
+ [formTree]="formTree()"
224
+ [formModel]="formModel()"
225
+ [mode]="mode()"
226
+ [path]="getBlockSubFieldPath(i, subField.name)"
227
+ />
228
+ }
229
+ </div>
230
+ </div>
231
+ }
232
+ </div>
233
+ }
234
+ </mcms-card-content>
235
+ @if (canAddBlock()) {
236
+ <mcms-card-footer>
237
+ <div class="flex gap-2">
238
+ @for (blockDef of blockDefinitions(); track blockDef.slug) {
239
+ <button mcms-button variant="outline" (click)="addBlock(blockDef.slug)">
240
+ <ng-icon name="heroPlus" size="16" aria-hidden="true" />
241
+ {{ blockDef.labels?.singular || blockDef.slug }}
242
+ </button>
243
+ }
244
+ </div>
245
+ </mcms-card-footer>
246
+ }
247
+ </mcms-card>
248
+ `, isInline: true, dependencies: [{ kind: "component", type: Card, selector: "mcms-card" }, { kind: "component", type: CardHeader, selector: "mcms-card-header" }, { kind: "component", type: CardTitle, selector: "mcms-card-title" }, { kind: "component", type: CardContent, selector: "mcms-card-content" }, { kind: "component", type: CardFooter, selector: "mcms-card-footer" }, { kind: "component", type: Button, selector: "button[mcms-button], a[mcms-button]", inputs: ["variant", "size", "disabled", "loading", "ariaLabel", "class"] }, { kind: "component", type: Badge, selector: "mcms-badge", inputs: ["variant", "class", "ariaLabel"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "component", type: FieldRenderer, selector: "mcms-field-renderer", inputs: ["field", "formNode", "formTree", "formModel", "mode", "path"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
249
+ }
250
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: BlocksFieldRenderer, decorators: [{
251
+ type: Component,
252
+ args: [{
253
+ selector: 'mcms-blocks-field-renderer',
254
+ imports: [
255
+ Card,
256
+ CardHeader,
257
+ CardTitle,
258
+ CardContent,
259
+ CardFooter,
260
+ Button,
261
+ Badge,
262
+ NgIcon,
263
+ CdkDropList,
264
+ CdkDrag,
265
+ CdkDragHandle,
266
+ FieldRenderer,
267
+ ],
268
+ providers: [provideIcons({ heroPlus, heroTrash, heroBars2 })],
269
+ changeDetection: ChangeDetectionStrategy.OnPush,
270
+ template: `
271
+ <mcms-card>
272
+ <mcms-card-header>
273
+ <div class="flex items-center justify-between">
274
+ <div>
275
+ <mcms-card-title>{{ label() }}</mcms-card-title>
276
+ @if (description()) {
277
+ <p class="text-sm text-muted-foreground mt-1">{{ description() }}</p>
278
+ }
279
+ </div>
280
+ <span class="text-sm text-muted-foreground">
281
+ {{ blocks().length }}{{ maxRows() ? ' / ' + maxRows() : '' }} blocks
282
+ </span>
283
+ </div>
284
+ </mcms-card-header>
285
+ <mcms-card-content>
286
+ @if (blocks().length === 0) {
287
+ <p class="text-sm text-muted-foreground py-4 text-center">
288
+ No blocks yet. Add a block to get started.
289
+ </p>
290
+ } @else {
291
+ <div
292
+ cdkDropList
293
+ (cdkDropListDropped)="onDrop($event)"
294
+ class="space-y-3"
295
+ role="list"
296
+ aria-label="Content blocks"
297
+ >
298
+ @for (block of blocks(); track $index; let i = $index) {
299
+ <div cdkDrag class="border rounded-lg bg-card" [cdkDragDisabled]="isDisabled()">
300
+ <div class="flex items-center gap-3 px-4 py-2 border-b bg-muted/50 rounded-t-lg">
301
+ <div
302
+ cdkDragHandle
303
+ class="cursor-grab text-muted-foreground hover:text-foreground"
304
+ [class.hidden]="isDisabled()"
305
+ role="button"
306
+ tabindex="0"
307
+ [attr.aria-label]="'Reorder ' + getBlockLabel(block.blockType) + ' block'"
308
+ aria-roledescription="sortable"
309
+ >
310
+ <ng-icon name="heroBars2" size="16" aria-hidden="true" />
311
+ </div>
312
+ <mcms-badge>{{ getBlockLabel(block.blockType) }}</mcms-badge>
313
+ <div class="flex-1"></div>
314
+ @if (canRemoveBlock()) {
315
+ <button
316
+ mcms-button
317
+ variant="ghost"
318
+ size="icon"
319
+ class="h-7 w-7 text-destructive hover:text-destructive"
320
+ (click)="removeBlock(i)"
321
+ [attr.aria-label]="'Remove ' + getBlockLabel(block.blockType) + ' block'"
322
+ >
323
+ <ng-icon name="heroTrash" size="14" aria-hidden="true" />
324
+ </button>
325
+ }
326
+ </div>
327
+ <div class="p-4 space-y-3">
328
+ @for (subField of getBlockFields(block.blockType); track subField.name) {
329
+ <mcms-field-renderer
330
+ [field]="subField"
331
+ [formNode]="getBlockSubNode(i, subField.name)"
332
+ [formTree]="formTree()"
333
+ [formModel]="formModel()"
334
+ [mode]="mode()"
335
+ [path]="getBlockSubFieldPath(i, subField.name)"
336
+ />
337
+ }
338
+ </div>
339
+ </div>
340
+ }
341
+ </div>
342
+ }
343
+ </mcms-card-content>
344
+ @if (canAddBlock()) {
345
+ <mcms-card-footer>
346
+ <div class="flex gap-2">
347
+ @for (blockDef of blockDefinitions(); track blockDef.slug) {
348
+ <button mcms-button variant="outline" (click)="addBlock(blockDef.slug)">
349
+ <ng-icon name="heroPlus" size="16" aria-hidden="true" />
350
+ {{ blockDef.labels?.singular || blockDef.slug }}
351
+ </button>
352
+ }
353
+ </div>
354
+ </mcms-card-footer>
355
+ }
356
+ </mcms-card>
357
+ `,
358
+ }]
359
+ }], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], formNode: [{ type: i0.Input, args: [{ isSignal: true, alias: "formNode", required: false }] }], formTree: [{ type: i0.Input, args: [{ isSignal: true, alias: "formTree", required: false }] }], formModel: [{ type: i0.Input, args: [{ isSignal: true, alias: "formModel", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], path: [{ type: i0.Input, args: [{ isSignal: true, alias: "path", required: true }] }] } });
360
+
361
+ export { BlocksFieldRenderer };
362
+ //# sourceMappingURL=momentumcms-admin-blocks-field.component-Cz7HmuBK.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"momentumcms-admin-blocks-field.component-Cz7HmuBK.mjs","sources":["../../../../libs/admin/src/lib/widgets/entity-form/field-renderers/blocks-field.component.ts"],"sourcesContent":["import {\n\tChangeDetectionStrategy,\n\tComponent,\n\tcomputed,\n\teffect,\n\tinput,\n\tuntracked,\n} from '@angular/core';\nimport {\n\tCdkDropList,\n\tCdkDrag,\n\tCdkDragHandle,\n\ttype CdkDragDrop,\n\tmoveItemInArray,\n} from '@angular/cdk/drag-drop';\nimport { NgIcon, provideIcons } from '@ng-icons/core';\nimport { heroPlus, heroTrash, heroBars2 } from '@ng-icons/heroicons/outline';\nimport {\n\tCard,\n\tCardHeader,\n\tCardTitle,\n\tCardContent,\n\tCardFooter,\n\tButton,\n\tBadge,\n} from '@momentumcms/ui';\nimport { humanizeFieldName } from '@momentumcms/core';\nimport type { Field, BlockConfig } from '@momentumcms/core';\nimport type { EntityFormMode } from '../entity-form.types';\nimport {\n\tgetFieldNodeState,\n\tgetSubNode,\n\tisRecord,\n\tgetFieldDefaultValue,\n\tnormalizeBlockDefaults,\n} from '../entity-form.types';\nimport { FieldRenderer } from './field-renderer.component';\n\n/** Shape of a block item in the stored data */\ninterface BlockItem {\n\tblockType: string;\n\t[key: string]: unknown;\n}\n\n/**\n * Blocks field renderer.\n *\n * Renders a list of typed blocks. Each block has a `blockType` discriminator\n * and type-specific fields. Users can add, remove, and reorder blocks.\n * A dropdown allows selecting which block type to add.\n *\n * Data container pattern: passes block sub-field FieldTree nodes via\n * getSubNode(getSubNode(formNode, blockIndex), subFieldName).\n * Block mutations use nodeState.value.set(newArray).\n */\n@Component({\n\tselector: 'mcms-blocks-field-renderer',\n\timports: [\n\t\tCard,\n\t\tCardHeader,\n\t\tCardTitle,\n\t\tCardContent,\n\t\tCardFooter,\n\t\tButton,\n\t\tBadge,\n\t\tNgIcon,\n\t\tCdkDropList,\n\t\tCdkDrag,\n\t\tCdkDragHandle,\n\t\tFieldRenderer,\n\t],\n\tproviders: [provideIcons({ heroPlus, heroTrash, heroBars2 })],\n\tchangeDetection: ChangeDetectionStrategy.OnPush,\n\ttemplate: `\n\t\t<mcms-card>\n\t\t\t<mcms-card-header>\n\t\t\t\t<div class=\"flex items-center justify-between\">\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<mcms-card-title>{{ label() }}</mcms-card-title>\n\t\t\t\t\t\t@if (description()) {\n\t\t\t\t\t\t\t<p class=\"text-sm text-muted-foreground mt-1\">{{ description() }}</p>\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>\n\t\t\t\t\t<span class=\"text-sm text-muted-foreground\">\n\t\t\t\t\t\t{{ blocks().length }}{{ maxRows() ? ' / ' + maxRows() : '' }} blocks\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t</mcms-card-header>\n\t\t\t<mcms-card-content>\n\t\t\t\t@if (blocks().length === 0) {\n\t\t\t\t\t<p class=\"text-sm text-muted-foreground py-4 text-center\">\n\t\t\t\t\t\tNo blocks yet. Add a block to get started.\n\t\t\t\t\t</p>\n\t\t\t\t} @else {\n\t\t\t\t\t<div\n\t\t\t\t\t\tcdkDropList\n\t\t\t\t\t\t(cdkDropListDropped)=\"onDrop($event)\"\n\t\t\t\t\t\tclass=\"space-y-3\"\n\t\t\t\t\t\trole=\"list\"\n\t\t\t\t\t\taria-label=\"Content blocks\"\n\t\t\t\t\t>\n\t\t\t\t\t\t@for (block of blocks(); track $index; let i = $index) {\n\t\t\t\t\t\t\t<div cdkDrag class=\"border rounded-lg bg-card\" [cdkDragDisabled]=\"isDisabled()\">\n\t\t\t\t\t\t\t\t<div class=\"flex items-center gap-3 px-4 py-2 border-b bg-muted/50 rounded-t-lg\">\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\tcdkDragHandle\n\t\t\t\t\t\t\t\t\t\tclass=\"cursor-grab text-muted-foreground hover:text-foreground\"\n\t\t\t\t\t\t\t\t\t\t[class.hidden]=\"isDisabled()\"\n\t\t\t\t\t\t\t\t\t\trole=\"button\"\n\t\t\t\t\t\t\t\t\t\ttabindex=\"0\"\n\t\t\t\t\t\t\t\t\t\t[attr.aria-label]=\"'Reorder ' + getBlockLabel(block.blockType) + ' block'\"\n\t\t\t\t\t\t\t\t\t\taria-roledescription=\"sortable\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<ng-icon name=\"heroBars2\" size=\"16\" aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<mcms-badge>{{ getBlockLabel(block.blockType) }}</mcms-badge>\n\t\t\t\t\t\t\t\t\t<div class=\"flex-1\"></div>\n\t\t\t\t\t\t\t\t\t@if (canRemoveBlock()) {\n\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\tmcms-button\n\t\t\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\t\t\t\t\t\tclass=\"h-7 w-7 text-destructive hover:text-destructive\"\n\t\t\t\t\t\t\t\t\t\t\t(click)=\"removeBlock(i)\"\n\t\t\t\t\t\t\t\t\t\t\t[attr.aria-label]=\"'Remove ' + getBlockLabel(block.blockType) + ' block'\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<ng-icon name=\"heroTrash\" size=\"14\" aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div class=\"p-4 space-y-3\">\n\t\t\t\t\t\t\t\t\t@for (subField of getBlockFields(block.blockType); track subField.name) {\n\t\t\t\t\t\t\t\t\t\t<mcms-field-renderer\n\t\t\t\t\t\t\t\t\t\t\t[field]=\"subField\"\n\t\t\t\t\t\t\t\t\t\t\t[formNode]=\"getBlockSubNode(i, subField.name)\"\n\t\t\t\t\t\t\t\t\t\t\t[formTree]=\"formTree()\"\n\t\t\t\t\t\t\t\t\t\t\t[formModel]=\"formModel()\"\n\t\t\t\t\t\t\t\t\t\t\t[mode]=\"mode()\"\n\t\t\t\t\t\t\t\t\t\t\t[path]=\"getBlockSubFieldPath(i, subField.name)\"\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t</mcms-card-content>\n\t\t\t@if (canAddBlock()) {\n\t\t\t\t<mcms-card-footer>\n\t\t\t\t\t<div class=\"flex gap-2\">\n\t\t\t\t\t\t@for (blockDef of blockDefinitions(); track blockDef.slug) {\n\t\t\t\t\t\t\t<button mcms-button variant=\"outline\" (click)=\"addBlock(blockDef.slug)\">\n\t\t\t\t\t\t\t\t<ng-icon name=\"heroPlus\" size=\"16\" aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\t{{ blockDef.labels?.singular || blockDef.slug }}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>\n\t\t\t\t</mcms-card-footer>\n\t\t\t}\n\t\t</mcms-card>\n\t`,\n})\nexport class BlocksFieldRenderer {\n\t/** Field definition (must be a BlocksField) */\n\treadonly field = input.required<Field>();\n\n\t/** Signal forms FieldTree node for this blocks array */\n\treadonly formNode = input<unknown>(null);\n\n\t/** Root signal forms FieldTree (for layout fields that look up child nodes) */\n\treadonly formTree = input<unknown>(null);\n\n\t/** Form model data (for condition evaluation and relationship filterOptions) */\n\treadonly formModel = input<Record<string, unknown>>({});\n\n\t/** Form mode */\n\treadonly mode = input<EntityFormMode>('create');\n\n\t/** Field path (e.g., \"content\") */\n\treadonly path = input.required<string>();\n\n\t/** Bridge: extract FieldState from formNode */\n\tprivate readonly nodeState = computed(() => getFieldNodeState(this.formNode()));\n\n\t/** Computed label */\n\treadonly label = computed(() => this.field().label || humanizeFieldName(this.field().name));\n\n\t/** Computed description */\n\treadonly description = computed(() => this.field().description || '');\n\n\t/** Block type definitions from the field */\n\treadonly blockDefinitions = computed((): BlockConfig[] => {\n\t\tconst f = this.field();\n\t\tif (f.type === 'blocks') {\n\t\t\treturn f.blocks;\n\t\t}\n\t\treturn [];\n\t});\n\n\t/** Min rows constraint */\n\treadonly minRows = computed((): number => {\n\t\tconst f = this.field();\n\t\treturn f.type === 'blocks' ? (f.minRows ?? 0) : 0;\n\t});\n\n\t/** Max rows constraint */\n\treadonly maxRows = computed((): number | undefined => {\n\t\tconst f = this.field();\n\t\treturn f.type === 'blocks' ? f.maxRows : undefined;\n\t});\n\n\t/** Current blocks as typed items (read from FieldState) */\n\treadonly blocks = computed((): BlockItem[] => {\n\t\tconst state = this.nodeState();\n\t\tif (!state) return [];\n\t\tconst val = state.value();\n\t\tif (Array.isArray(val)) {\n\t\t\treturn val.filter(\n\t\t\t\t(item): item is BlockItem => isRecord(item) && typeof item['blockType'] === 'string',\n\t\t\t);\n\t\t}\n\t\treturn [];\n\t});\n\n\t/**\n\t * Normalize loaded blocks: ensure every block has defaults for all definition fields.\n\t * Blocks saved before new fields were added won't have those keys, and\n\t * signal-forms only creates controls for keys present in the model.\n\t */\n\tprivate readonly _normalizeBlocks = effect(() => {\n\t\tconst state = this.nodeState();\n\t\tif (!state) return;\n\t\tconst val = state.value();\n\t\tif (!Array.isArray(val)) return;\n\n\t\tconst { normalized, changed } = normalizeBlockDefaults(val, this.blockDefMap());\n\t\tif (changed) {\n\t\t\tuntracked(() => state.value.set(normalized));\n\t\t}\n\t});\n\n\t/** Whether the field is disabled (view mode) */\n\treadonly isDisabled = computed(() => this.mode() === 'view');\n\n\t/** Whether a new block can be added */\n\treadonly canAddBlock = computed((): boolean => {\n\t\tif (this.isDisabled()) return false;\n\t\tconst max = this.maxRows();\n\t\treturn max === undefined || this.blocks().length < max;\n\t});\n\n\t/** Whether blocks can be removed */\n\treadonly canRemoveBlock = computed((): boolean => {\n\t\tif (this.isDisabled()) return false;\n\t\treturn this.blocks().length > this.minRows();\n\t});\n\n\t/** Block definition lookup cache */\n\tprivate readonly blockDefMap = computed((): Map<string, BlockConfig> => {\n\t\tconst map = new Map<string, BlockConfig>();\n\t\tfor (const def of this.blockDefinitions()) {\n\t\t\tmap.set(def.slug, def);\n\t\t}\n\t\treturn map;\n\t});\n\n\t/** Get display label for a block type */\n\tgetBlockLabel(blockType: string): string {\n\t\tconst def = this.blockDefMap().get(blockType);\n\t\treturn def?.labels?.singular || blockType;\n\t}\n\n\t/** Get fields for a block type */\n\tgetBlockFields(blockType: string): Field[] {\n\t\tconst def = this.blockDefMap().get(blockType);\n\t\treturn def?.fields.filter((f) => !f.admin?.hidden) ?? [];\n\t}\n\n\t/** Get a FieldTree sub-node for a block's sub-field */\n\tgetBlockSubNode(blockIndex: number, subFieldName: string): unknown {\n\t\tconst blockNode = getSubNode(this.formNode(), blockIndex);\n\t\treturn getSubNode(blockNode, subFieldName);\n\t}\n\n\t/** Get the full path for a block's sub-field */\n\tgetBlockSubFieldPath(blockIndex: number, subFieldName: string): string {\n\t\treturn `${this.path()}.${blockIndex}.${subFieldName}`;\n\t}\n\n\t/** Handle drag-drop reorder */\n\tonDrop(event: CdkDragDrop<unknown>): void {\n\t\tconst state = this.nodeState();\n\t\tif (!state) return;\n\t\tconst blocks = [...this.blocks()];\n\t\tmoveItemInArray(blocks, event.previousIndex, event.currentIndex);\n\t\tstate.value.set(blocks);\n\t}\n\n\t/** Add a new block of the given type */\n\taddBlock(blockType: string): void {\n\t\tconst def = this.blockDefMap().get(blockType);\n\t\tif (!def) return;\n\t\tconst state = this.nodeState();\n\t\tif (!state) return;\n\n\t\tconst newBlock: BlockItem = { blockType };\n\t\tfor (const field of def.fields) {\n\t\t\tnewBlock[field.name] = getFieldDefaultValue(field);\n\t\t}\n\n\t\tconst blocks = [...this.blocks(), newBlock];\n\t\tstate.value.set(blocks);\n\t}\n\n\t/** Remove a block at the given index */\n\tremoveBlock(index: number): void {\n\t\tconst state = this.nodeState();\n\t\tif (!state) return;\n\t\tconst blocks = this.blocks().filter((_, i) => i !== index);\n\t\tstate.value.set(blocks);\n\t}\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,120 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, signal, computed, effect, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from '@momentumcms/ui';
4
+ import { humanizeFieldName } from '@momentumcms/core';
5
+ import { b as getSubNode, F as FieldRenderer } from './momentumcms-admin-momentumcms-admin-o0FbJXZN.mjs';
6
+
7
+ /**
8
+ * Collapsible layout field renderer.
9
+ *
10
+ * Wraps child fields in an expandable/collapsible accordion section.
11
+ * This is a layout-only field; it does not store data itself.
12
+ * Child field FieldTree nodes are looked up from the root formTree
13
+ * using flat field names.
14
+ */
15
+ class CollapsibleFieldRenderer {
16
+ /** Field definition (must be a CollapsibleField) */
17
+ field = input.required(...(ngDevMode ? [{ debugName: "field" }] : []));
18
+ /** Root signal forms FieldTree (for looking up child field nodes) */
19
+ formTree = input(null, ...(ngDevMode ? [{ debugName: "formTree" }] : []));
20
+ /** Form model data (for condition evaluation and relationship filterOptions) */
21
+ formModel = input({}, ...(ngDevMode ? [{ debugName: "formModel" }] : []));
22
+ /** Form mode */
23
+ mode = input('create', ...(ngDevMode ? [{ debugName: "mode" }] : []));
24
+ /** Field path (unused for layout fields, kept for interface consistency) */
25
+ path = input.required(...(ngDevMode ? [{ debugName: "path" }] : []));
26
+ /** Whether the accordion is expanded */
27
+ isExpanded = signal(false, ...(ngDevMode ? [{ debugName: "isExpanded" }] : []));
28
+ /** Unique panel ID for accordion aria linkage */
29
+ panelId = computed(() => `collapsible-${this.path().replace(/\./g, '-')}`, ...(ngDevMode ? [{ debugName: "panelId" }] : []));
30
+ /** Computed label */
31
+ label = computed(() => this.field().label || humanizeFieldName(this.field().name), ...(ngDevMode ? [{ debugName: "label" }] : []));
32
+ /** Computed description */
33
+ description = computed(() => this.field().description || '', ...(ngDevMode ? [{ debugName: "description" }] : []));
34
+ /** Child fields */
35
+ subFields = computed(() => {
36
+ const f = this.field();
37
+ if (f.type === 'collapsible') {
38
+ return f.fields.filter((sf) => !sf.admin?.hidden);
39
+ }
40
+ return [];
41
+ }, ...(ngDevMode ? [{ debugName: "subFields" }] : []));
42
+ constructor() {
43
+ // Set initial expanded state from field config once input is available
44
+ effect(() => {
45
+ const f = this.field();
46
+ if (f.type === 'collapsible' && f.defaultOpen) {
47
+ this.isExpanded.set(true);
48
+ }
49
+ });
50
+ }
51
+ /** Get a FieldTree sub-node for a child field (flat path from root tree) */
52
+ getChildFormNode(fieldName) {
53
+ return getSubNode(this.formTree(), fieldName);
54
+ }
55
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: CollapsibleFieldRenderer, deps: [], target: i0.ɵɵFactoryTarget.Component });
56
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: CollapsibleFieldRenderer, isStandalone: true, selector: "mcms-collapsible-field-renderer", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, formTree: { classPropertyName: "formTree", publicName: "formTree", isSignal: true, isRequired: false, transformFunction: null }, formModel: { classPropertyName: "formModel", publicName: "formModel", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, path: { classPropertyName: "path", publicName: "path", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "block" }, ngImport: i0, template: `
57
+ <mcms-accordion>
58
+ <mcms-accordion-item>
59
+ <mcms-accordion-trigger [panelId]="panelId()" [(expanded)]="isExpanded">
60
+ {{ label() }}
61
+ </mcms-accordion-trigger>
62
+ <mcms-accordion-content [panelId]="panelId()">
63
+ @if (description()) {
64
+ <p class="text-sm text-muted-foreground mb-4">{{ description() }}</p>
65
+ }
66
+ <div class="space-y-4 py-4">
67
+ @for (subField of subFields(); track subField.name) {
68
+ <mcms-field-renderer
69
+ [field]="subField"
70
+ [formNode]="getChildFormNode(subField.name)"
71
+ [formTree]="formTree()"
72
+ [formModel]="formModel()"
73
+ [mode]="mode()"
74
+ [path]="subField.name"
75
+ />
76
+ }
77
+ </div>
78
+ </mcms-accordion-content>
79
+ </mcms-accordion-item>
80
+ </mcms-accordion>
81
+ `, isInline: true, dependencies: [{ kind: "component", type: Accordion, selector: "mcms-accordion", inputs: ["multiExpandable", "disabled", "wrap", "class"] }, { kind: "component", type: AccordionItem, selector: "mcms-accordion-item", inputs: ["class"] }, { kind: "component", type: AccordionTrigger, selector: "mcms-accordion-trigger", inputs: ["panelId", "disabled", "expanded", "class"], outputs: ["expandedChange"] }, { kind: "component", type: AccordionContent, selector: "mcms-accordion-content", inputs: ["panelId", "class"] }, { kind: "component", type: FieldRenderer, selector: "mcms-field-renderer", inputs: ["field", "formNode", "formTree", "formModel", "mode", "path"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
82
+ }
83
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: CollapsibleFieldRenderer, decorators: [{
84
+ type: Component,
85
+ args: [{
86
+ selector: 'mcms-collapsible-field-renderer',
87
+ imports: [Accordion, AccordionItem, AccordionTrigger, AccordionContent, FieldRenderer],
88
+ changeDetection: ChangeDetectionStrategy.OnPush,
89
+ host: { class: 'block' },
90
+ template: `
91
+ <mcms-accordion>
92
+ <mcms-accordion-item>
93
+ <mcms-accordion-trigger [panelId]="panelId()" [(expanded)]="isExpanded">
94
+ {{ label() }}
95
+ </mcms-accordion-trigger>
96
+ <mcms-accordion-content [panelId]="panelId()">
97
+ @if (description()) {
98
+ <p class="text-sm text-muted-foreground mb-4">{{ description() }}</p>
99
+ }
100
+ <div class="space-y-4 py-4">
101
+ @for (subField of subFields(); track subField.name) {
102
+ <mcms-field-renderer
103
+ [field]="subField"
104
+ [formNode]="getChildFormNode(subField.name)"
105
+ [formTree]="formTree()"
106
+ [formModel]="formModel()"
107
+ [mode]="mode()"
108
+ [path]="subField.name"
109
+ />
110
+ }
111
+ </div>
112
+ </mcms-accordion-content>
113
+ </mcms-accordion-item>
114
+ </mcms-accordion>
115
+ `,
116
+ }]
117
+ }], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], formTree: [{ type: i0.Input, args: [{ isSignal: true, alias: "formTree", required: false }] }], formModel: [{ type: i0.Input, args: [{ isSignal: true, alias: "formModel", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], path: [{ type: i0.Input, args: [{ isSignal: true, alias: "path", required: true }] }] } });
118
+
119
+ export { CollapsibleFieldRenderer };
120
+ //# sourceMappingURL=momentumcms-admin-collapsible-field.component-CtwrGQvg.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"momentumcms-admin-collapsible-field.component-CtwrGQvg.mjs","sources":["../../../../libs/admin/src/lib/widgets/entity-form/field-renderers/collapsible-field.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, effect, input, signal } from '@angular/core';\nimport { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from '@momentumcms/ui';\nimport { humanizeFieldName } from '@momentumcms/core';\nimport type { Field } from '@momentumcms/core';\nimport type { EntityFormMode } from '../entity-form.types';\nimport { getSubNode } from '../entity-form.types';\nimport { FieldRenderer } from './field-renderer.component';\n\n/**\n * Collapsible layout field renderer.\n *\n * Wraps child fields in an expandable/collapsible accordion section.\n * This is a layout-only field; it does not store data itself.\n * Child field FieldTree nodes are looked up from the root formTree\n * using flat field names.\n */\n@Component({\n\tselector: 'mcms-collapsible-field-renderer',\n\timports: [Accordion, AccordionItem, AccordionTrigger, AccordionContent, FieldRenderer],\n\tchangeDetection: ChangeDetectionStrategy.OnPush,\n\thost: { class: 'block' },\n\ttemplate: `\n\t\t<mcms-accordion>\n\t\t\t<mcms-accordion-item>\n\t\t\t\t<mcms-accordion-trigger [panelId]=\"panelId()\" [(expanded)]=\"isExpanded\">\n\t\t\t\t\t{{ label() }}\n\t\t\t\t</mcms-accordion-trigger>\n\t\t\t\t<mcms-accordion-content [panelId]=\"panelId()\">\n\t\t\t\t\t@if (description()) {\n\t\t\t\t\t\t<p class=\"text-sm text-muted-foreground mb-4\">{{ description() }}</p>\n\t\t\t\t\t}\n\t\t\t\t\t<div class=\"space-y-4 py-4\">\n\t\t\t\t\t\t@for (subField of subFields(); track subField.name) {\n\t\t\t\t\t\t\t<mcms-field-renderer\n\t\t\t\t\t\t\t\t[field]=\"subField\"\n\t\t\t\t\t\t\t\t[formNode]=\"getChildFormNode(subField.name)\"\n\t\t\t\t\t\t\t\t[formTree]=\"formTree()\"\n\t\t\t\t\t\t\t\t[formModel]=\"formModel()\"\n\t\t\t\t\t\t\t\t[mode]=\"mode()\"\n\t\t\t\t\t\t\t\t[path]=\"subField.name\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>\n\t\t\t\t</mcms-accordion-content>\n\t\t\t</mcms-accordion-item>\n\t\t</mcms-accordion>\n\t`,\n})\nexport class CollapsibleFieldRenderer {\n\t/** Field definition (must be a CollapsibleField) */\n\treadonly field = input.required<Field>();\n\n\t/** Root signal forms FieldTree (for looking up child field nodes) */\n\treadonly formTree = input<unknown>(null);\n\n\t/** Form model data (for condition evaluation and relationship filterOptions) */\n\treadonly formModel = input<Record<string, unknown>>({});\n\n\t/** Form mode */\n\treadonly mode = input<EntityFormMode>('create');\n\n\t/** Field path (unused for layout fields, kept for interface consistency) */\n\treadonly path = input.required<string>();\n\n\t/** Whether the accordion is expanded */\n\treadonly isExpanded = signal(false);\n\n\t/** Unique panel ID for accordion aria linkage */\n\treadonly panelId = computed(() => `collapsible-${this.path().replace(/\\./g, '-')}`);\n\n\t/** Computed label */\n\treadonly label = computed(() => this.field().label || humanizeFieldName(this.field().name));\n\n\t/** Computed description */\n\treadonly description = computed(() => this.field().description || '');\n\n\t/** Child fields */\n\treadonly subFields = computed((): Field[] => {\n\t\tconst f = this.field();\n\t\tif (f.type === 'collapsible') {\n\t\t\treturn f.fields.filter((sf) => !sf.admin?.hidden);\n\t\t}\n\t\treturn [];\n\t});\n\n\tconstructor() {\n\t\t// Set initial expanded state from field config once input is available\n\t\teffect(() => {\n\t\t\tconst f = this.field();\n\t\t\tif (f.type === 'collapsible' && f.defaultOpen) {\n\t\t\t\tthis.isExpanded.set(true);\n\t\t\t}\n\t\t});\n\t}\n\n\t/** Get a FieldTree sub-node for a child field (flat path from root tree) */\n\tgetChildFormNode(fieldName: string): unknown {\n\t\treturn getSubNode(this.formTree(), fieldName);\n\t}\n}\n"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@ import * as i0 from '@angular/core';
2
2
  import { inject, viewChild, computed, ChangeDetectionStrategy, Component } from '@angular/core';
3
3
  import { ActivatedRoute } from '@angular/router';
4
4
  import { humanizeFieldName } from '@momentumcms/core';
5
- import { g as getGlobalsFromRouteData, E as EntityFormWidget } from './momentumcms-admin-momentumcms-admin-z82jXEsP.mjs';
5
+ import { g as getGlobalsFromRouteData, E as EntityFormWidget } from './momentumcms-admin-momentumcms-admin-o0FbJXZN.mjs';
6
6
 
7
7
  /**
8
8
  * Global Edit Page
@@ -87,4 +87,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
87
87
  }], propDecorators: { entityFormRef: [{ type: i0.ViewChild, args: ['entityForm', { isSignal: true }] }] } });
88
88
 
89
89
  export { GlobalEditPage };
90
- //# sourceMappingURL=momentumcms-admin-global-edit.page-DFDV-uh3.mjs.map
90
+ //# sourceMappingURL=momentumcms-admin-global-edit.page-BBUtWCSl.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"momentumcms-admin-global-edit.page-DFDV-uh3.mjs","sources":["../../../../libs/admin/src/lib/pages/global-edit/global-edit.page.ts"],"sourcesContent":["import { Component, ChangeDetectionStrategy, inject, computed, viewChild } from '@angular/core';\nimport { ActivatedRoute } from '@angular/router';\nimport type { CollectionConfig, GlobalConfig } from '@momentumcms/core';\nimport { humanizeFieldName } from '@momentumcms/core';\nimport { getGlobalsFromRouteData } from '../../utils/route-data';\nimport { EntityFormWidget } from '../../widgets/entity-form/entity-form.component';\nimport type { HasUnsavedChanges } from '../../guards/unsaved-changes.guard';\n\n/**\n * Global Edit Page\n *\n * Renders the EntityFormWidget in global (singleton) mode for editing a global document.\n * The global config is converted to a CollectionConfig shape and passed with isGlobal=true.\n */\n@Component({\n\tselector: 'mcms-global-edit',\n\timports: [EntityFormWidget],\n\tchangeDetection: ChangeDetectionStrategy.OnPush,\n\thost: { class: 'block' },\n\ttemplate: `\n\t\t@if (collectionConfig(); as col) {\n\t\t\t<mcms-entity-form\n\t\t\t\t#entityForm\n\t\t\t\t[collection]=\"col\"\n\t\t\t\t[mode]=\"'edit'\"\n\t\t\t\t[basePath]=\"'/admin/globals'\"\n\t\t\t\t[showBreadcrumbs]=\"true\"\n\t\t\t\t[isGlobal]=\"true\"\n\t\t\t\t[globalSlug]=\"globalSlug()\"\n\t\t\t/>\n\t\t} @else {\n\t\t\t<div class=\"p-12 text-center text-muted-foreground\">Global not found</div>\n\t\t}\n\t`,\n})\nexport class GlobalEditPage implements HasUnsavedChanges {\n\tprivate readonly route = inject(ActivatedRoute);\n\n\tprivate readonly entityFormRef = viewChild<EntityFormWidget>('entityForm');\n\n\treadonly globalSlug = computed((): string => {\n\t\treturn this.route.snapshot.paramMap.get('slug') ?? '';\n\t});\n\n\treadonly globalConfig = computed((): GlobalConfig | undefined => {\n\t\tconst slug = this.globalSlug();\n\t\tconst globals = getGlobalsFromRouteData(this.route.parent?.snapshot.data);\n\t\treturn globals.find((g) => g.slug === slug);\n\t});\n\n\t/** Convert GlobalConfig to CollectionConfig shape for EntityFormWidget */\n\treadonly collectionConfig = computed((): CollectionConfig | undefined => {\n\t\tconst global = this.globalConfig();\n\t\tif (!global) return undefined;\n\n\t\treturn {\n\t\t\tslug: global.slug,\n\t\t\tfields: global.fields,\n\t\t\tlabels: {\n\t\t\t\tsingular: global.label ?? humanizeFieldName(global.slug),\n\t\t\t\tplural: global.label ?? humanizeFieldName(global.slug),\n\t\t\t},\n\t\t\tadmin: global.admin,\n\t\t\taccess: global.access\n\t\t\t\t? { read: global.access.read, update: global.access.update }\n\t\t\t\t: undefined,\n\t\t\thooks: global.hooks,\n\t\t\tversions: global.versions,\n\t\t};\n\t});\n\n\thasUnsavedChanges(): boolean {\n\t\treturn this.entityFormRef()?.isDirty() ?? false;\n\t}\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"momentumcms-admin-global-edit.page-BBUtWCSl.mjs","sources":["../../../../libs/admin/src/lib/pages/global-edit/global-edit.page.ts"],"sourcesContent":["import { Component, ChangeDetectionStrategy, inject, computed, viewChild } from '@angular/core';\nimport { ActivatedRoute } from '@angular/router';\nimport type { CollectionConfig, GlobalConfig } from '@momentumcms/core';\nimport { humanizeFieldName } from '@momentumcms/core';\nimport { getGlobalsFromRouteData } from '../../utils/route-data';\nimport { EntityFormWidget } from '../../widgets/entity-form/entity-form.component';\nimport type { HasUnsavedChanges } from '../../guards/unsaved-changes.guard';\n\n/**\n * Global Edit Page\n *\n * Renders the EntityFormWidget in global (singleton) mode for editing a global document.\n * The global config is converted to a CollectionConfig shape and passed with isGlobal=true.\n */\n@Component({\n\tselector: 'mcms-global-edit',\n\timports: [EntityFormWidget],\n\tchangeDetection: ChangeDetectionStrategy.OnPush,\n\thost: { class: 'block' },\n\ttemplate: `\n\t\t@if (collectionConfig(); as col) {\n\t\t\t<mcms-entity-form\n\t\t\t\t#entityForm\n\t\t\t\t[collection]=\"col\"\n\t\t\t\t[mode]=\"'edit'\"\n\t\t\t\t[basePath]=\"'/admin/globals'\"\n\t\t\t\t[showBreadcrumbs]=\"true\"\n\t\t\t\t[isGlobal]=\"true\"\n\t\t\t\t[globalSlug]=\"globalSlug()\"\n\t\t\t/>\n\t\t} @else {\n\t\t\t<div class=\"p-12 text-center text-muted-foreground\">Global not found</div>\n\t\t}\n\t`,\n})\nexport class GlobalEditPage implements HasUnsavedChanges {\n\tprivate readonly route = inject(ActivatedRoute);\n\n\tprivate readonly entityFormRef = viewChild<EntityFormWidget>('entityForm');\n\n\treadonly globalSlug = computed((): string => {\n\t\treturn this.route.snapshot.paramMap.get('slug') ?? '';\n\t});\n\n\treadonly globalConfig = computed((): GlobalConfig | undefined => {\n\t\tconst slug = this.globalSlug();\n\t\tconst globals = getGlobalsFromRouteData(this.route.parent?.snapshot.data);\n\t\treturn globals.find((g) => g.slug === slug);\n\t});\n\n\t/** Convert GlobalConfig to CollectionConfig shape for EntityFormWidget */\n\treadonly collectionConfig = computed((): CollectionConfig | undefined => {\n\t\tconst global = this.globalConfig();\n\t\tif (!global) return undefined;\n\n\t\treturn {\n\t\t\tslug: global.slug,\n\t\t\tfields: global.fields,\n\t\t\tlabels: {\n\t\t\t\tsingular: global.label ?? humanizeFieldName(global.slug),\n\t\t\t\tplural: global.label ?? humanizeFieldName(global.slug),\n\t\t\t},\n\t\t\tadmin: global.admin,\n\t\t\taccess: global.access\n\t\t\t\t? { read: global.access.read, update: global.access.update }\n\t\t\t\t: undefined,\n\t\t\thooks: global.hooks,\n\t\t\tversions: global.versions,\n\t\t};\n\t});\n\n\thasUnsavedChanges(): boolean {\n\t\treturn this.entityFormRef()?.isDirty() ?? false;\n\t}\n}\n"],"names":[],"mappings":"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"}