@momentumcms/core 0.1.10 → 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.
- package/CHANGELOG.md +11 -0
- package/generators/generator.cjs +20 -11
- package/generators/generator.js +20 -11
- package/index.cjs +17 -1
- package/index.js +16 -1
- package/package.json +1 -1
- package/src/generators/generator.d.ts +1 -0
- package/src/lib/fields/field.types.d.ts +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
## 0.2.0 (2026-02-17)
|
|
2
|
+
|
|
3
|
+
### 🚀 Features
|
|
4
|
+
|
|
5
|
+
- add named tabs support with nested data grouping and tab UI improvements ([63ab63e](https://github.com/DonaldMurillo/momentum-cms/commit/63ab63e))
|
|
6
|
+
|
|
7
|
+
### ❤️ Thank You
|
|
8
|
+
|
|
9
|
+
- Claude Opus 4.6
|
|
10
|
+
- Donald Murillo @DonaldMurillo
|
|
11
|
+
|
|
1
12
|
## 0.1.10 (2026-02-17)
|
|
2
13
|
|
|
3
14
|
### 🩹 Fixes
|
package/generators/generator.cjs
CHANGED
|
@@ -36,12 +36,26 @@ var import_node_path = require("node:path");
|
|
|
36
36
|
var import_node_url = require("node:url");
|
|
37
37
|
|
|
38
38
|
// libs/core/src/lib/fields/field.types.ts
|
|
39
|
+
function isNamedTab(tab) {
|
|
40
|
+
return typeof tab.name === "string" && tab.name.length > 0;
|
|
41
|
+
}
|
|
39
42
|
function flattenDataFields(fields) {
|
|
40
43
|
const result = [];
|
|
41
44
|
for (const field of fields) {
|
|
42
45
|
if (field.type === "tabs") {
|
|
43
46
|
for (const tab of field.tabs) {
|
|
44
|
-
|
|
47
|
+
if (isNamedTab(tab)) {
|
|
48
|
+
const syntheticGroup = {
|
|
49
|
+
name: tab.name,
|
|
50
|
+
type: "group",
|
|
51
|
+
label: tab.label,
|
|
52
|
+
description: tab.description,
|
|
53
|
+
fields: tab.fields
|
|
54
|
+
};
|
|
55
|
+
result.push(syntheticGroup);
|
|
56
|
+
} else {
|
|
57
|
+
result.push(...flattenDataFields(tab.fields));
|
|
58
|
+
}
|
|
45
59
|
}
|
|
46
60
|
} else if (field.type === "collapsible" || field.type === "row") {
|
|
47
61
|
result.push(...flattenDataFields(field.fields));
|
|
@@ -262,11 +276,7 @@ function generateTypes(config) {
|
|
|
262
276
|
const hasTimestamps = collection.timestamps !== false;
|
|
263
277
|
for (const field of dataFields) {
|
|
264
278
|
if (field.type === "blocks") {
|
|
265
|
-
const blockResult = generateBlockTypes(
|
|
266
|
-
collection.slug,
|
|
267
|
-
field.name,
|
|
268
|
-
field
|
|
269
|
-
);
|
|
279
|
+
const blockResult = generateBlockTypes(collection.slug, field.name, field);
|
|
270
280
|
blockDeclarations.push(blockResult.declarations);
|
|
271
281
|
}
|
|
272
282
|
}
|
|
@@ -280,11 +290,7 @@ function generateTypes(config) {
|
|
|
280
290
|
const optional = field.required ? "" : "?";
|
|
281
291
|
const propName = needsQuoting2(field.name) ? safeQuote(field.name) : field.name;
|
|
282
292
|
if (field.type === "blocks") {
|
|
283
|
-
const blockResult = generateBlockTypes(
|
|
284
|
-
collection.slug,
|
|
285
|
-
field.name,
|
|
286
|
-
field
|
|
287
|
-
);
|
|
293
|
+
const blockResult = generateBlockTypes(collection.slug, field.name, field);
|
|
288
294
|
lines.push(` ${propName}${optional}: ${blockResult.unionTypeName}[];`);
|
|
289
295
|
} else {
|
|
290
296
|
const tsType = fieldTypeToTS(field);
|
|
@@ -554,6 +560,9 @@ function serializeTabsArray(tabs, indent) {
|
|
|
554
560
|
return "[]";
|
|
555
561
|
const items = tabs.map((tab) => {
|
|
556
562
|
const parts = [];
|
|
563
|
+
if (tab.name) {
|
|
564
|
+
parts.push(`${indent} name: ${JSON.stringify(tab.name)}`);
|
|
565
|
+
}
|
|
557
566
|
parts.push(`${indent} label: ${JSON.stringify(tab.label)}`);
|
|
558
567
|
if (tab.description) {
|
|
559
568
|
parts.push(`${indent} description: ${JSON.stringify(tab.description)}`);
|
package/generators/generator.js
CHANGED
|
@@ -5,12 +5,26 @@ import { dirname, resolve, relative } from "node:path";
|
|
|
5
5
|
import { pathToFileURL } from "node:url";
|
|
6
6
|
|
|
7
7
|
// libs/core/src/lib/fields/field.types.ts
|
|
8
|
+
function isNamedTab(tab) {
|
|
9
|
+
return typeof tab.name === "string" && tab.name.length > 0;
|
|
10
|
+
}
|
|
8
11
|
function flattenDataFields(fields) {
|
|
9
12
|
const result = [];
|
|
10
13
|
for (const field of fields) {
|
|
11
14
|
if (field.type === "tabs") {
|
|
12
15
|
for (const tab of field.tabs) {
|
|
13
|
-
|
|
16
|
+
if (isNamedTab(tab)) {
|
|
17
|
+
const syntheticGroup = {
|
|
18
|
+
name: tab.name,
|
|
19
|
+
type: "group",
|
|
20
|
+
label: tab.label,
|
|
21
|
+
description: tab.description,
|
|
22
|
+
fields: tab.fields
|
|
23
|
+
};
|
|
24
|
+
result.push(syntheticGroup);
|
|
25
|
+
} else {
|
|
26
|
+
result.push(...flattenDataFields(tab.fields));
|
|
27
|
+
}
|
|
14
28
|
}
|
|
15
29
|
} else if (field.type === "collapsible" || field.type === "row") {
|
|
16
30
|
result.push(...flattenDataFields(field.fields));
|
|
@@ -231,11 +245,7 @@ function generateTypes(config) {
|
|
|
231
245
|
const hasTimestamps = collection.timestamps !== false;
|
|
232
246
|
for (const field of dataFields) {
|
|
233
247
|
if (field.type === "blocks") {
|
|
234
|
-
const blockResult = generateBlockTypes(
|
|
235
|
-
collection.slug,
|
|
236
|
-
field.name,
|
|
237
|
-
field
|
|
238
|
-
);
|
|
248
|
+
const blockResult = generateBlockTypes(collection.slug, field.name, field);
|
|
239
249
|
blockDeclarations.push(blockResult.declarations);
|
|
240
250
|
}
|
|
241
251
|
}
|
|
@@ -249,11 +259,7 @@ function generateTypes(config) {
|
|
|
249
259
|
const optional = field.required ? "" : "?";
|
|
250
260
|
const propName = needsQuoting2(field.name) ? safeQuote(field.name) : field.name;
|
|
251
261
|
if (field.type === "blocks") {
|
|
252
|
-
const blockResult = generateBlockTypes(
|
|
253
|
-
collection.slug,
|
|
254
|
-
field.name,
|
|
255
|
-
field
|
|
256
|
-
);
|
|
262
|
+
const blockResult = generateBlockTypes(collection.slug, field.name, field);
|
|
257
263
|
lines.push(` ${propName}${optional}: ${blockResult.unionTypeName}[];`);
|
|
258
264
|
} else {
|
|
259
265
|
const tsType = fieldTypeToTS(field);
|
|
@@ -523,6 +529,9 @@ function serializeTabsArray(tabs, indent) {
|
|
|
523
529
|
return "[]";
|
|
524
530
|
const items = tabs.map((tab) => {
|
|
525
531
|
const parts = [];
|
|
532
|
+
if (tab.name) {
|
|
533
|
+
parts.push(`${indent} name: ${JSON.stringify(tab.name)}`);
|
|
534
|
+
}
|
|
526
535
|
parts.push(`${indent} label: ${JSON.stringify(tab.label)}`);
|
|
527
536
|
if (tab.description) {
|
|
528
537
|
parts.push(`${indent} description: ${JSON.stringify(tab.description)}`);
|
package/index.cjs
CHANGED
|
@@ -53,6 +53,7 @@ __export(src_exports, {
|
|
|
53
53
|
humanizeFieldName: () => humanizeFieldName,
|
|
54
54
|
isAuthenticated: () => isAuthenticated,
|
|
55
55
|
isLayoutField: () => isLayoutField,
|
|
56
|
+
isNamedTab: () => isNamedTab,
|
|
56
57
|
isOwner: () => isOwner,
|
|
57
58
|
json: () => json,
|
|
58
59
|
not: () => not,
|
|
@@ -127,6 +128,9 @@ var ReferentialIntegrityError = class extends Error {
|
|
|
127
128
|
this.constraint = constraint;
|
|
128
129
|
}
|
|
129
130
|
};
|
|
131
|
+
function isNamedTab(tab) {
|
|
132
|
+
return typeof tab.name === "string" && tab.name.length > 0;
|
|
133
|
+
}
|
|
130
134
|
function isLayoutField(field) {
|
|
131
135
|
return LAYOUT_FIELD_TYPES.has(field.type);
|
|
132
136
|
}
|
|
@@ -135,7 +139,18 @@ function flattenDataFields(fields) {
|
|
|
135
139
|
for (const field of fields) {
|
|
136
140
|
if (field.type === "tabs") {
|
|
137
141
|
for (const tab of field.tabs) {
|
|
138
|
-
|
|
142
|
+
if (isNamedTab(tab)) {
|
|
143
|
+
const syntheticGroup = {
|
|
144
|
+
name: tab.name,
|
|
145
|
+
type: "group",
|
|
146
|
+
label: tab.label,
|
|
147
|
+
description: tab.description,
|
|
148
|
+
fields: tab.fields
|
|
149
|
+
};
|
|
150
|
+
result.push(syntheticGroup);
|
|
151
|
+
} else {
|
|
152
|
+
result.push(...flattenDataFields(tab.fields));
|
|
153
|
+
}
|
|
139
154
|
}
|
|
140
155
|
} else if (field.type === "collapsible" || field.type === "row") {
|
|
141
156
|
result.push(...flattenDataFields(field.fields));
|
|
@@ -709,6 +724,7 @@ function createSeedHelpers() {
|
|
|
709
724
|
humanizeFieldName,
|
|
710
725
|
isAuthenticated,
|
|
711
726
|
isLayoutField,
|
|
727
|
+
isNamedTab,
|
|
712
728
|
isOwner,
|
|
713
729
|
json,
|
|
714
730
|
not,
|
package/index.js
CHANGED
|
@@ -51,6 +51,9 @@ var ReferentialIntegrityError = class extends Error {
|
|
|
51
51
|
this.constraint = constraint;
|
|
52
52
|
}
|
|
53
53
|
};
|
|
54
|
+
function isNamedTab(tab) {
|
|
55
|
+
return typeof tab.name === "string" && tab.name.length > 0;
|
|
56
|
+
}
|
|
54
57
|
function isLayoutField(field) {
|
|
55
58
|
return LAYOUT_FIELD_TYPES.has(field.type);
|
|
56
59
|
}
|
|
@@ -59,7 +62,18 @@ function flattenDataFields(fields) {
|
|
|
59
62
|
for (const field of fields) {
|
|
60
63
|
if (field.type === "tabs") {
|
|
61
64
|
for (const tab of field.tabs) {
|
|
62
|
-
|
|
65
|
+
if (isNamedTab(tab)) {
|
|
66
|
+
const syntheticGroup = {
|
|
67
|
+
name: tab.name,
|
|
68
|
+
type: "group",
|
|
69
|
+
label: tab.label,
|
|
70
|
+
description: tab.description,
|
|
71
|
+
fields: tab.fields
|
|
72
|
+
};
|
|
73
|
+
result.push(syntheticGroup);
|
|
74
|
+
} else {
|
|
75
|
+
result.push(...flattenDataFields(tab.fields));
|
|
76
|
+
}
|
|
63
77
|
}
|
|
64
78
|
} else if (field.type === "collapsible" || field.type === "row") {
|
|
65
79
|
result.push(...flattenDataFields(field.fields));
|
|
@@ -632,6 +646,7 @@ export {
|
|
|
632
646
|
humanizeFieldName,
|
|
633
647
|
isAuthenticated,
|
|
634
648
|
isLayoutField,
|
|
649
|
+
isNamedTab,
|
|
635
650
|
isOwner,
|
|
636
651
|
json,
|
|
637
652
|
not,
|
package/package.json
CHANGED
|
@@ -239,10 +239,16 @@ export interface SlugField extends BaseField {
|
|
|
239
239
|
}
|
|
240
240
|
/** Tab definition within a tabs layout field */
|
|
241
241
|
export interface TabConfig {
|
|
242
|
+
/** When present, creates a nested data structure (like a group). Omit for layout-only tabs. */
|
|
243
|
+
name?: string;
|
|
242
244
|
label: string;
|
|
243
245
|
description?: string;
|
|
244
246
|
fields: Field[];
|
|
245
247
|
}
|
|
248
|
+
/** Type guard: returns true if the tab has a non-empty name (stores nested data). */
|
|
249
|
+
export declare function isNamedTab(tab: TabConfig): tab is TabConfig & {
|
|
250
|
+
name: string;
|
|
251
|
+
};
|
|
246
252
|
/** Tabs layout field - organizes fields into tabbed sections */
|
|
247
253
|
export interface TabsField extends BaseField {
|
|
248
254
|
type: 'tabs';
|