@cosmicdrift/kumiko-headless 0.290.0 → 0.291.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.291.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.291.0",
|
|
40
40
|
"temporal-polyfill": "^0.3.2",
|
|
41
41
|
"zod": "^4.4.3"
|
|
42
42
|
},
|
|
@@ -424,6 +424,31 @@ describe("computeEditViewModel — embedded-list cells (#1835)", () => {
|
|
|
424
424
|
refFeature: "orders",
|
|
425
425
|
refLabelField: "name",
|
|
426
426
|
});
|
|
427
|
+
expect(byField["product"]?.refOptionsQuery).toBeUndefined();
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
test("a reference sub-field's optionsQuery reaches the cell view-model (fw#2780)", () => {
|
|
431
|
+
const vm = computeEditViewModel({
|
|
432
|
+
screen: editScreen({ sections: [{ title: "x", fields: ["lines"] }] }),
|
|
433
|
+
entity: embeddedListEntity({
|
|
434
|
+
schema: {
|
|
435
|
+
...lineFieldSchema,
|
|
436
|
+
product: {
|
|
437
|
+
type: "reference",
|
|
438
|
+
entity: "product",
|
|
439
|
+
labelField: "name",
|
|
440
|
+
optionsQuery: "orders:query:product:options",
|
|
441
|
+
},
|
|
442
|
+
},
|
|
443
|
+
}),
|
|
444
|
+
values: { lines: [] },
|
|
445
|
+
translate,
|
|
446
|
+
featureName: "orders",
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
const cells = asFields(vm.sections[0]).fields[0]?.embeddedListCells;
|
|
450
|
+
const product = (cells ?? []).find((cell) => cell.field === "product");
|
|
451
|
+
expect(product?.refOptionsQuery).toBe("orders:query:product:options");
|
|
427
452
|
});
|
|
428
453
|
|
|
429
454
|
test("minItems/maxItems/derived/totals pass through onto the field view-model when set", () => {
|
|
@@ -720,6 +745,34 @@ describe("computeEditViewModel — declared reference metadata (fw#2662)", () =>
|
|
|
720
745
|
expect(field?.refMultiple).toBeUndefined();
|
|
721
746
|
});
|
|
722
747
|
|
|
748
|
+
test("a reference field's optionsQuery reaches the field view-model, labelField untouched (fw#2780)", () => {
|
|
749
|
+
const entity = {
|
|
750
|
+
fields: {
|
|
751
|
+
leaseId: {
|
|
752
|
+
type: "reference",
|
|
753
|
+
entity: "lease",
|
|
754
|
+
labelField: "startDate",
|
|
755
|
+
optionsQuery: "deposits:query:lease:options",
|
|
756
|
+
},
|
|
757
|
+
},
|
|
758
|
+
} as unknown as EntityDefinition;
|
|
759
|
+
const vm = computeEditViewModel({
|
|
760
|
+
screen: editScreen({ sections: [{ fields: ["leaseId"] }] }),
|
|
761
|
+
entity,
|
|
762
|
+
values: {},
|
|
763
|
+
translate,
|
|
764
|
+
featureName: "deposits",
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
const field = asFields(vm.sections[0]).fields[0];
|
|
768
|
+
expect(field).toMatchObject({
|
|
769
|
+
type: "reference",
|
|
770
|
+
refEntity: "lease",
|
|
771
|
+
refLabelField: "startDate",
|
|
772
|
+
refOptionsQuery: "deposits:query:lease:options",
|
|
773
|
+
});
|
|
774
|
+
});
|
|
775
|
+
|
|
723
776
|
test("non-regression: a projectionDetail field without refEntity metadata stays 'text', unchanged (fw#2662)", () => {
|
|
724
777
|
const vm = computeEditViewModel({
|
|
725
778
|
screen: editScreen({ sections: [{ fields: ["ip"] }] }),
|
package/src/view-model/edit.ts
CHANGED
|
@@ -52,6 +52,7 @@ type EmbeddedSubFieldShape = {
|
|
|
52
52
|
readonly options?: readonly string[];
|
|
53
53
|
readonly entity?: string;
|
|
54
54
|
readonly labelField?: string;
|
|
55
|
+
readonly optionsQuery?: string;
|
|
55
56
|
readonly scale?: number;
|
|
56
57
|
};
|
|
57
58
|
|
|
@@ -283,13 +284,17 @@ export function computeEditViewModel<
|
|
|
283
284
|
: fieldDef.type === "reference"
|
|
284
285
|
? ((fieldDef as unknown as { labelField?: string }).labelField ?? "id")
|
|
285
286
|
: undefined;
|
|
286
|
-
// Declared reference metadata has no `multiple`
|
|
287
|
-
// row-meta/derived fields, always single-valued
|
|
288
|
-
//
|
|
289
|
-
|
|
287
|
+
// Declared reference metadata has no `multiple` or `optionsQuery`
|
|
288
|
+
// concept (it targets row-meta/derived fields, always single-valued and
|
|
289
|
+
// resolved through the target entity) — only a real ReferenceFieldDef
|
|
290
|
+
// carries them.
|
|
291
|
+
const ownReferenceDef =
|
|
290
292
|
declaredRefTarget === undefined && fieldDef.type === "reference"
|
|
291
|
-
? (
|
|
293
|
+
? (fieldDef as unknown as { multiple?: boolean; optionsQuery?: string })
|
|
292
294
|
: undefined;
|
|
295
|
+
const refOptionsQuery = ownReferenceDef?.optionsQuery;
|
|
296
|
+
const refMultiple =
|
|
297
|
+
ownReferenceDef === undefined ? undefined : (ownReferenceDef.multiple ?? false);
|
|
293
298
|
// file/image: accept/maxSize ins ViewModel + entityType/fieldName für
|
|
294
299
|
// den Upload-POST (Endpoint validiert gegen die richtige Field-Def).
|
|
295
300
|
const isFileType =
|
|
@@ -356,9 +361,10 @@ export function computeEditViewModel<
|
|
|
356
361
|
cellOptions,
|
|
357
362
|
)
|
|
358
363
|
: undefined;
|
|
364
|
+
const cellRef = subField.type === "reference" ? subField : undefined;
|
|
359
365
|
const cellRefTarget =
|
|
360
|
-
|
|
361
|
-
? parseRefTarget(
|
|
366
|
+
cellRef?.entity !== undefined
|
|
367
|
+
? parseRefTarget(cellRef.entity, featureName)
|
|
362
368
|
: undefined;
|
|
363
369
|
const cell: EmbeddedListCellViewModel = {
|
|
364
370
|
field: subFieldName,
|
|
@@ -369,8 +375,10 @@ export function computeEditViewModel<
|
|
|
369
375
|
...(cellOptionLabels !== undefined && { optionLabels: cellOptionLabels }),
|
|
370
376
|
...(cellRefTarget !== undefined && { refEntity: cellRefTarget.entityName }),
|
|
371
377
|
...(cellRefTarget !== undefined && { refFeature: cellRefTarget.featureName }),
|
|
372
|
-
...(
|
|
373
|
-
|
|
378
|
+
...(cellRef?.labelField !== undefined && { refLabelField: cellRef.labelField }),
|
|
379
|
+
...(cellRef?.optionsQuery !== undefined && {
|
|
380
|
+
refOptionsQuery: cellRef.optionsQuery,
|
|
381
|
+
}),
|
|
374
382
|
...(subField.type === "decimal" &&
|
|
375
383
|
subField.scale !== undefined && { scale: subField.scale }),
|
|
376
384
|
};
|
|
@@ -401,6 +409,7 @@ export function computeEditViewModel<
|
|
|
401
409
|
...(refEntity !== undefined && { refEntity }),
|
|
402
410
|
...(refFeature !== undefined && { refFeature }),
|
|
403
411
|
...(refLabelField !== undefined && { refLabelField }),
|
|
412
|
+
...(refOptionsQuery !== undefined && { refOptionsQuery }),
|
|
404
413
|
...(refMultiple !== undefined && { refMultiple }),
|
|
405
414
|
...(fileDef?.accept !== undefined && { accept: fileDef.accept }),
|
|
406
415
|
...(fileDef?.maxSize !== undefined && { maxSize: fileDef.maxSize }),
|
package/src/view-model/types.ts
CHANGED
|
@@ -112,6 +112,7 @@ export type EmbeddedListCellViewModel = {
|
|
|
112
112
|
readonly refEntity?: string;
|
|
113
113
|
readonly refFeature?: string;
|
|
114
114
|
readonly refLabelField?: string;
|
|
115
|
+
readonly refOptionsQuery?: string;
|
|
115
116
|
/** Only for `type: "decimal"`. */
|
|
116
117
|
readonly scale?: number;
|
|
117
118
|
};
|
|
@@ -190,6 +191,11 @@ export type EditFieldViewModel = {
|
|
|
190
191
|
/** Nur bei `type: "reference"` gesetzt — Welches Feld der referenced
|
|
191
192
|
* Entity als Display-Label im Dropdown erscheint. Default: "id". */
|
|
192
193
|
readonly refLabelField?: string;
|
|
194
|
+
/** Only for `type: "reference"` — query handler QN the picker sources its
|
|
195
|
+
* options from (fw#2780), replacing the referenced entity's list handler.
|
|
196
|
+
* Rows carry `{ id, label }`; `refLabelField` is then unused for the
|
|
197
|
+
* picker but still drives list cells. */
|
|
198
|
+
readonly refOptionsQuery?: string;
|
|
193
199
|
/** Nur bei `type: "reference"` — Multi-Mode (Tier 2.7e-Multi):
|
|
194
200
|
* Wert ist UUID-Array, Renderer mountet Multi-Combobox mit Tags. */
|
|
195
201
|
readonly refMultiple?: boolean;
|