@cosmicdrift/kumiko-renderer 0.32.1 → 0.34.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 +1 -1
- package/src/app/kumiko-screen.tsx +35 -11
- package/src/components/render-edit.tsx +17 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-renderer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.34.0",
|
|
4
4
|
"description": "Platform-agnostic React renderer for Kumiko screens. Contains the shared logic — primitives-contract, hooks, KumikoScreen, navigation & SSE abstractions — that any platform-specific renderer (web, native) composes. No DOM, no EventSource, no react-dom.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -5,8 +5,10 @@ import type {
|
|
|
5
5
|
EntityDefinition,
|
|
6
6
|
EntityEditScreenDefinition,
|
|
7
7
|
EntityListScreenDefinition,
|
|
8
|
+
FieldCondition,
|
|
8
9
|
RowAction,
|
|
9
10
|
RowActionWriteHandler,
|
|
11
|
+
RowFieldExtractor,
|
|
10
12
|
ScreenDefinition,
|
|
11
13
|
ToolbarAction,
|
|
12
14
|
} from "@cosmicdrift/kumiko-framework/ui-types";
|
|
@@ -34,6 +36,23 @@ import { useNav } from "./nav";
|
|
|
34
36
|
import { lastSegment } from "./qn";
|
|
35
37
|
import { dispatcherErrorText, WriteFailedError } from "./write-failed-error";
|
|
36
38
|
|
|
39
|
+
function evalRowExtractor(
|
|
40
|
+
extractor: RowFieldExtractor,
|
|
41
|
+
row: Record<string, unknown>,
|
|
42
|
+
): Record<string, unknown> {
|
|
43
|
+
if ("pick" in extractor) {
|
|
44
|
+
return Object.fromEntries(extractor.pick.map((f) => [f, row[f]]));
|
|
45
|
+
}
|
|
46
|
+
return Object.fromEntries(Object.entries(extractor.map).map(([to, from]) => [to, row[from]]));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function evalFieldCondition(cond: FieldCondition, values: Record<string, unknown>): boolean {
|
|
50
|
+
if (typeof cond === "boolean") return cond;
|
|
51
|
+
const val = values[cond.field];
|
|
52
|
+
if ("eq" in cond) return val === cond.eq;
|
|
53
|
+
return val !== cond.ne;
|
|
54
|
+
}
|
|
55
|
+
|
|
37
56
|
// KumikoScreen picks up a ScreenDefinition from the schema by qn and
|
|
38
57
|
// routes it to the right renderer based on `screen.type`. Command
|
|
39
58
|
// qualification (`<feature>:write:<entity>:create` etc.) happens here
|
|
@@ -659,11 +678,8 @@ function EntityListBody({
|
|
|
659
678
|
// navigate-Variante braucht keinen Dispatcher; nav ist
|
|
660
679
|
// immer da (Provider von createKumikoApp).
|
|
661
680
|
if (action.kind === "navigate") {
|
|
662
|
-
//
|
|
663
|
-
//
|
|
664
|
-
// Function-Form (action.entityId) JSON-injizierte Schemas
|
|
665
|
-
// (window.__KUMIKO_SCHEMA__) nicht überlebt — silent gedroppt,
|
|
666
|
-
// siehe RowAction-Type-Header.
|
|
681
|
+
// Default entityId für entityEdit-Targets: row["id"] wenn
|
|
682
|
+
// kein expliziter entityId-Feldname gesetzt ist.
|
|
667
683
|
const targetIsEntityEdit = schema.screens.some(
|
|
668
684
|
(s) => s.type === "entityEdit" && lastSegment(s.id) === action.screen,
|
|
669
685
|
);
|
|
@@ -672,14 +688,20 @@ function EntityListBody({
|
|
|
672
688
|
label: effectiveTranslate(action.label),
|
|
673
689
|
...(action.style !== undefined && { style: action.style }),
|
|
674
690
|
onTrigger: (row: ListRowViewModel) => {
|
|
675
|
-
const explicit =
|
|
691
|
+
const explicit =
|
|
692
|
+
action.entityId !== undefined
|
|
693
|
+
? String(row.values[action.entityId] ?? "")
|
|
694
|
+
: undefined;
|
|
676
695
|
const fallback = targetIsEntityEdit ? String(row.values["id"] ?? "") : undefined;
|
|
677
696
|
const entityId = explicit ?? fallback;
|
|
678
697
|
nav.navigate({
|
|
679
698
|
screenId: action.screen,
|
|
680
699
|
...(entityId !== undefined && entityId !== "" && { entityId }),
|
|
681
700
|
});
|
|
682
|
-
const params =
|
|
701
|
+
const params =
|
|
702
|
+
action.params !== undefined
|
|
703
|
+
? evalRowExtractor(action.params, row.values)
|
|
704
|
+
: undefined;
|
|
683
705
|
if (params !== undefined) {
|
|
684
706
|
// setSearchParams nimmt string|null. Komplexe Werte
|
|
685
707
|
// (number/boolean) wandeln wir zu String — der Reader
|
|
@@ -696,7 +718,7 @@ function EntityListBody({
|
|
|
696
718
|
}
|
|
697
719
|
},
|
|
698
720
|
...(action.visible !== undefined && {
|
|
699
|
-
isVisible: (row: ListRowViewModel) => action.visible
|
|
721
|
+
isVisible: (row: ListRowViewModel) => evalFieldCondition(action.visible!, row.values),
|
|
700
722
|
}),
|
|
701
723
|
};
|
|
702
724
|
}
|
|
@@ -716,7 +738,9 @@ function EntityListBody({
|
|
|
716
738
|
onTrigger: async (row: ListRowViewModel) => {
|
|
717
739
|
const buildPayload = writeAction.payload;
|
|
718
740
|
const payload =
|
|
719
|
-
buildPayload !== undefined
|
|
741
|
+
buildPayload !== undefined
|
|
742
|
+
? evalRowExtractor(buildPayload, row.values)
|
|
743
|
+
: { id: row.values["id"] };
|
|
720
744
|
const result = await dispatcher.write(writeAction.handler, payload);
|
|
721
745
|
// write() wirft nicht — Failure-Result MUSS hier zum Error
|
|
722
746
|
// werden, sonst schließt der Confirm-Dialog kommentarlos und
|
|
@@ -730,7 +754,7 @@ function EntityListBody({
|
|
|
730
754
|
},
|
|
731
755
|
isVisible:
|
|
732
756
|
writeAction.visible !== undefined
|
|
733
|
-
? (row: ListRowViewModel) => writeAction.visible
|
|
757
|
+
? (row: ListRowViewModel) => evalFieldCondition(writeAction.visible!, row.values)
|
|
734
758
|
: undefined,
|
|
735
759
|
};
|
|
736
760
|
})
|
|
@@ -766,7 +790,7 @@ function EntityListBody({
|
|
|
766
790
|
confirmLabel: effectiveTranslate(action.confirmLabel),
|
|
767
791
|
}),
|
|
768
792
|
onTrigger: async () => {
|
|
769
|
-
const payload = action.payload
|
|
793
|
+
const payload = action.payload ?? {};
|
|
770
794
|
await dispatcher.write(action.handler, payload);
|
|
771
795
|
},
|
|
772
796
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
EntityDefinition,
|
|
3
3
|
EntityEditScreenDefinition,
|
|
4
|
+
FieldCondition,
|
|
4
5
|
} from "@cosmicdrift/kumiko-framework/ui-types";
|
|
5
6
|
import { isExtensionEditSection, normalizeEditField } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
6
7
|
import type {
|
|
@@ -64,6 +65,18 @@ export type RenderEditProps<TValues extends FormValues, TCtx = unknown> = {
|
|
|
64
65
|
readonly fieldAppendix?: (fieldName: string) => ReactNode | undefined;
|
|
65
66
|
};
|
|
66
67
|
|
|
68
|
+
function toConditionValue<TValues extends FormValues, TCtx>(
|
|
69
|
+
cond: FieldCondition,
|
|
70
|
+
): NonNullable<FieldConditions<TValues, TCtx>["visible"]> {
|
|
71
|
+
if (typeof cond === "boolean") return cond;
|
|
72
|
+
if ("eq" in cond) {
|
|
73
|
+
const { field, eq } = cond;
|
|
74
|
+
return (values: TValues) => (values as Record<string, unknown>)[field] === eq;
|
|
75
|
+
}
|
|
76
|
+
const { field, ne } = cond;
|
|
77
|
+
return (values: TValues) => (values as Record<string, unknown>)[field] !== ne;
|
|
78
|
+
}
|
|
79
|
+
|
|
67
80
|
function deriveFormFields<TValues extends FormValues, TCtx>(
|
|
68
81
|
screen: EntityEditScreenDefinition,
|
|
69
82
|
): Record<string, FieldConditions<TValues, TCtx>> {
|
|
@@ -74,13 +87,13 @@ function deriveFormFields<TValues extends FormValues, TCtx>(
|
|
|
74
87
|
const normalized = normalizeEditField(spec);
|
|
75
88
|
out[normalized.field] = {
|
|
76
89
|
...(normalized.visible !== undefined && {
|
|
77
|
-
visible:
|
|
90
|
+
visible: toConditionValue<TValues, TCtx>(normalized.visible),
|
|
78
91
|
}),
|
|
79
92
|
...(normalized.readOnly !== undefined && {
|
|
80
|
-
readonly:
|
|
93
|
+
readonly: toConditionValue<TValues, TCtx>(normalized.readOnly),
|
|
81
94
|
}),
|
|
82
95
|
...(normalized.required !== undefined && {
|
|
83
|
-
required:
|
|
96
|
+
required: toConditionValue<TValues, TCtx>(normalized.required),
|
|
84
97
|
}),
|
|
85
98
|
};
|
|
86
99
|
}
|
|
@@ -195,9 +208,8 @@ export function RenderEdit<TValues extends FormValues, TCtx = unknown>(
|
|
|
195
208
|
values: snapshot.values,
|
|
196
209
|
translate,
|
|
197
210
|
featureName,
|
|
198
|
-
ctx,
|
|
199
211
|
}),
|
|
200
|
-
[screen, entity, snapshot.values, translate, featureName
|
|
212
|
+
[screen, entity, snapshot.values, translate, featureName],
|
|
201
213
|
);
|
|
202
214
|
|
|
203
215
|
async function handleSubmit(): Promise<void> {
|