@cosmicdrift/kumiko-headless 0.270.0 → 0.274.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-headless",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.274.0",
|
|
4
4
|
"description": "Headless UI logic for Kumiko — Dispatcher contract, Form-Controller, View-Model, Nav-Resolver. Plattform- und React-frei; jeder Renderer (renderer, renderer-web, renderer-native, …) komponiert darauf.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
39
|
+
"@cosmicdrift/kumiko-framework": "0.274.0",
|
|
40
40
|
"temporal-polyfill": "^0.3.2",
|
|
41
41
|
"zod": "^4.4.3"
|
|
42
42
|
},
|
|
@@ -847,6 +847,46 @@ describe("computeEditViewModel — relatedList searchable/facets passthrough (fw
|
|
|
847
847
|
});
|
|
848
848
|
});
|
|
849
849
|
|
|
850
|
+
describe("computeEditViewModel — relatedList parentFilter passthrough", () => {
|
|
851
|
+
test("parentFilter passes through onto the relatedList section view-model verbatim", () => {
|
|
852
|
+
const vm = computeEditViewModel({
|
|
853
|
+
screen: editScreen({
|
|
854
|
+
sections: [
|
|
855
|
+
{
|
|
856
|
+
kind: "relatedList",
|
|
857
|
+
title: "History",
|
|
858
|
+
query: "app:query:history",
|
|
859
|
+
columns: ["name"],
|
|
860
|
+
parentFilter: { field: "orderId" },
|
|
861
|
+
},
|
|
862
|
+
],
|
|
863
|
+
}),
|
|
864
|
+
entity: orderEntity,
|
|
865
|
+
values: {},
|
|
866
|
+
translate,
|
|
867
|
+
featureName: "orders",
|
|
868
|
+
});
|
|
869
|
+
|
|
870
|
+
expect(asRelatedList(vm.sections[0]).parentFilter).toEqual({ field: "orderId" });
|
|
871
|
+
});
|
|
872
|
+
|
|
873
|
+
test("parentFilter is absent from the view-model when the section spec has none", () => {
|
|
874
|
+
const vm = computeEditViewModel({
|
|
875
|
+
screen: editScreen({
|
|
876
|
+
sections: [
|
|
877
|
+
{ kind: "relatedList", title: "History", query: "app:query:history", columns: ["name"] },
|
|
878
|
+
],
|
|
879
|
+
}),
|
|
880
|
+
entity: orderEntity,
|
|
881
|
+
values: {},
|
|
882
|
+
translate,
|
|
883
|
+
featureName: "orders",
|
|
884
|
+
});
|
|
885
|
+
|
|
886
|
+
expect(asRelatedList(vm.sections[0]).parentFilter).toBeUndefined();
|
|
887
|
+
});
|
|
888
|
+
});
|
|
889
|
+
|
|
850
890
|
describe("computeEditViewModel — writeForm sections (fw editable-detail-screens)", () => {
|
|
851
891
|
test("resolves the section's own fieldDefs/fields through the same per-field pipeline as a fields section", () => {
|
|
852
892
|
const vm = computeEditViewModel({
|
package/src/view-model/edit.ts
CHANGED
|
@@ -123,6 +123,7 @@ function computeRelatedListSectionViewModel(
|
|
|
123
123
|
title: translate(sectionSpec.title),
|
|
124
124
|
query: sectionSpec.query,
|
|
125
125
|
...(sectionSpec.parentParam !== undefined && { parentParam: sectionSpec.parentParam }),
|
|
126
|
+
...(sectionSpec.parentFilter !== undefined && { parentFilter: sectionSpec.parentFilter }),
|
|
126
127
|
columns: sectionSpec.columns,
|
|
127
128
|
...(sectionSpec.pageSize !== undefined && { pageSize: sectionSpec.pageSize }),
|
|
128
129
|
...(sectionSpec.defaultSort !== undefined && { defaultSort: sectionSpec.defaultSort }),
|
|
@@ -130,6 +131,7 @@ function computeRelatedListSectionViewModel(
|
|
|
130
131
|
...(sectionSpec.facets !== undefined && { facets: sectionSpec.facets }),
|
|
131
132
|
...(sectionSpec.rowClick !== undefined && { rowClick: sectionSpec.rowClick }),
|
|
132
133
|
...(sectionSpec.rowActions !== undefined && { rowActions: sectionSpec.rowActions }),
|
|
134
|
+
...(sectionSpec.toolbarActions !== undefined && { toolbarActions: sectionSpec.toolbarActions }),
|
|
133
135
|
};
|
|
134
136
|
}
|
|
135
137
|
|
|
@@ -163,7 +165,13 @@ export function computeEditViewModel<
|
|
|
163
165
|
if (!isFieldsEditSection(sectionSpec)) {
|
|
164
166
|
return computeRelatedListSectionViewModel(sectionSpec, translate);
|
|
165
167
|
}
|
|
166
|
-
|
|
168
|
+
// `groups` flattens into the same per-field pipeline as plain `fields`;
|
|
169
|
+
// the group structure below just re-groups the computed views by name.
|
|
170
|
+
const flatFieldSpecs =
|
|
171
|
+
sectionSpec.groups !== undefined
|
|
172
|
+
? sectionSpec.groups.flatMap((group) => group.fields)
|
|
173
|
+
: sectionSpec.fields;
|
|
174
|
+
const fields: EditFieldViewModel[] = flatFieldSpecs.map((fieldSpec) => {
|
|
167
175
|
const normalized = normalizeEditField(fieldSpec);
|
|
168
176
|
const fieldDef = entity.fields[normalized.field];
|
|
169
177
|
if (!fieldDef) {
|
|
@@ -424,9 +432,27 @@ export function computeEditViewModel<
|
|
|
424
432
|
};
|
|
425
433
|
return view;
|
|
426
434
|
});
|
|
427
|
-
// Boot-validator rejects fields.length === 0 (screens.ts),
|
|
428
|
-
// section never reaches this code.
|
|
435
|
+
// Boot-validator rejects fields.length === 0 with no groups (screens.ts),
|
|
436
|
+
// so an empty section never reaches this code.
|
|
429
437
|
const visible = fields.some((field) => field.visible);
|
|
438
|
+
// Tabs mode renders this section as its own card, where two columns reads
|
|
439
|
+
// better than the single-column default a stacked form keeps.
|
|
440
|
+
const defaultColumns = screen.layout.mode === "tabs" ? 2 : 1;
|
|
441
|
+
const groups = sectionSpec.groups?.map((group) => ({
|
|
442
|
+
title: translate(group.title),
|
|
443
|
+
columns: group.columns ?? 2,
|
|
444
|
+
fields: group.fields.map((fieldSpec) => {
|
|
445
|
+
const fieldName = normalizeEditField(fieldSpec).field;
|
|
446
|
+
const fieldView = fields.find((f) => f.field === fieldName);
|
|
447
|
+
if (fieldView === undefined) {
|
|
448
|
+
throw new Error(
|
|
449
|
+
`computeEditViewModel: screen "${screen.id}" group "${group.title}" references field ` +
|
|
450
|
+
`"${fieldName}" that failed to resolve.`,
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
return fieldView;
|
|
454
|
+
}),
|
|
455
|
+
}));
|
|
430
456
|
return {
|
|
431
457
|
kind: "fields" as const,
|
|
432
458
|
visible,
|
|
@@ -435,8 +461,9 @@ export function computeEditViewModel<
|
|
|
435
461
|
...(sectionSpec.description !== undefined && {
|
|
436
462
|
description: translate(sectionSpec.description),
|
|
437
463
|
}),
|
|
438
|
-
columns: sectionSpec.columns ??
|
|
464
|
+
columns: sectionSpec.columns ?? defaultColumns,
|
|
439
465
|
fields,
|
|
466
|
+
...(groups !== undefined && { groups }),
|
|
440
467
|
...(sectionSpec.icon !== undefined && { icon: sectionSpec.icon }),
|
|
441
468
|
};
|
|
442
469
|
});
|
package/src/view-model/types.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
ListFacetSpec,
|
|
9
9
|
ListSortSpec,
|
|
10
10
|
PlatformComponent,
|
|
11
|
+
RelatedListToolbarAction,
|
|
11
12
|
RowAction,
|
|
12
13
|
ScreenSlots,
|
|
13
14
|
} from "@cosmicdrift/kumiko-framework/ui-types";
|
|
@@ -270,6 +271,13 @@ export type EditFieldsSectionViewModel = {
|
|
|
270
271
|
readonly description?: string;
|
|
271
272
|
readonly columns: number;
|
|
272
273
|
readonly fields: readonly EditFieldViewModel[];
|
|
274
|
+
/** From `EditFieldsSection.groups` — each entry's `fields` are the same
|
|
275
|
+
* view-model objects as `fields` above, re-grouped (not recomputed). */
|
|
276
|
+
readonly groups?: readonly {
|
|
277
|
+
readonly title: string;
|
|
278
|
+
readonly columns: number;
|
|
279
|
+
readonly fields: readonly EditFieldViewModel[];
|
|
280
|
+
}[];
|
|
273
281
|
/** From `EditFieldsSection.icon` — closed IconKey vocabulary, renders
|
|
274
282
|
* left of the title. No effect without `title`. */
|
|
275
283
|
readonly icon?: IconKey;
|
|
@@ -292,6 +300,7 @@ export type EditRelatedListSectionViewModel = {
|
|
|
292
300
|
readonly title: string;
|
|
293
301
|
readonly query: string;
|
|
294
302
|
readonly parentParam?: string;
|
|
303
|
+
readonly parentFilter?: { readonly field: string };
|
|
295
304
|
readonly columns: readonly ListColumnSpec[];
|
|
296
305
|
readonly pageSize?: number;
|
|
297
306
|
readonly defaultSort?: ListSortSpec;
|
|
@@ -299,6 +308,7 @@ export type EditRelatedListSectionViewModel = {
|
|
|
299
308
|
readonly facets?: readonly ListFacetSpec[];
|
|
300
309
|
readonly rowClick?: { readonly entity: string; readonly idColumn?: string };
|
|
301
310
|
readonly rowActions?: readonly RowAction[];
|
|
311
|
+
readonly toolbarActions?: readonly RelatedListToolbarAction[];
|
|
302
312
|
};
|
|
303
313
|
|
|
304
314
|
// Mirrors EditWriteFormSection, except `fields` is already resolved through
|