@cosmicdrift/kumiko-renderer 0.39.0 → 0.40.1
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-renderer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.1",
|
|
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,13 +5,13 @@ import type {
|
|
|
5
5
|
EntityDefinition,
|
|
6
6
|
EntityEditScreenDefinition,
|
|
7
7
|
EntityListScreenDefinition,
|
|
8
|
-
FieldCondition,
|
|
9
8
|
RowAction,
|
|
10
9
|
RowActionWriteHandler,
|
|
11
10
|
RowFieldExtractor,
|
|
12
11
|
ScreenDefinition,
|
|
13
12
|
ToolbarAction,
|
|
14
13
|
} from "@cosmicdrift/kumiko-framework/ui-types";
|
|
14
|
+
import { evalFieldCondition } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
15
15
|
import type {
|
|
16
16
|
Command,
|
|
17
17
|
FormSnapshot,
|
|
@@ -46,13 +46,6 @@ function evalRowExtractor(
|
|
|
46
46
|
return Object.fromEntries(Object.entries(extractor.map).map(([to, from]) => [to, row[from]]));
|
|
47
47
|
}
|
|
48
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
|
-
|
|
56
49
|
// KumikoScreen picks up a ScreenDefinition from the schema by qn and
|
|
57
50
|
// routes it to the right renderer based on `screen.type`. Command
|
|
58
51
|
// qualification (`<feature>:write:<entity>:create` etc.) happens here
|
|
@@ -695,9 +688,16 @@ function EntityListBody({
|
|
|
695
688
|
// immer da (Provider von createKumikoApp).
|
|
696
689
|
if (action.kind === "navigate") {
|
|
697
690
|
// Default entityId für entityEdit-Targets: row["id"] wenn
|
|
698
|
-
// kein expliziter entityId-Feldname gesetzt ist.
|
|
691
|
+
// kein expliziter entityId-Feldname gesetzt ist. Nur für Targets
|
|
692
|
+
// DERSELBEN Entity — ein Cross-Entity-Edit-Screen bekäme sonst die
|
|
693
|
+
// falsche row.id injiziert, und "Duplicate → Create"-Patterns
|
|
694
|
+
// würden in den Update-Mode gezwungen. Cross-Entity-Navigation
|
|
695
|
+
// setzt action.entityId explizit.
|
|
699
696
|
const targetIsEntityEdit = schema.screens.some(
|
|
700
|
-
(s) =>
|
|
697
|
+
(s) =>
|
|
698
|
+
s.type === "entityEdit" &&
|
|
699
|
+
s.entity === screen.entity &&
|
|
700
|
+
lastSegment(s.id) === action.screen,
|
|
701
701
|
);
|
|
702
702
|
const actionVisible = action.visible;
|
|
703
703
|
return {
|
|
@@ -731,6 +731,11 @@ function EntityListBody({
|
|
|
731
731
|
// NACH navigate: pushState trägt keine Query — Params die
|
|
732
732
|
// vor dem Push gesetzt werden, kleben an der ALTEN URL und
|
|
733
733
|
// sind auf dem Ziel-Screen weg (actionForm-Prefill leer).
|
|
734
|
+
// Bekannte Kante (bewusst offen): zielt die Action auf den
|
|
735
|
+
// AKTUELLEN pathname, short-circuit't pushPath ohne die Query
|
|
736
|
+
// zu leeren — die neuen Params mergen dann auf den alten
|
|
737
|
+
// ?-String. Für Row-Actions praktisch nicht erreichbar
|
|
738
|
+
// (Pfad differiert über entityId/screen).
|
|
734
739
|
nav.setSearchParams(stringified);
|
|
735
740
|
}
|
|
736
741
|
},
|
|
@@ -777,7 +782,7 @@ function EntityListBody({
|
|
|
777
782
|
};
|
|
778
783
|
})
|
|
779
784
|
.filter((a: DataTableRowAction | null): a is DataTableRowAction => a !== null);
|
|
780
|
-
}, [screen.rowActions, effectiveTranslate, dispatcher, nav, schema.screens]);
|
|
785
|
+
}, [screen.rowActions, screen.entity, effectiveTranslate, dispatcher, nav, schema.screens]);
|
|
781
786
|
|
|
782
787
|
// ToolbarActions: Schema → Resolved-Form (analog rowActions).
|
|
783
788
|
// navigate-kind → useNav().navigate({ screenId }), writeHandler-kind
|
|
@@ -1147,7 +1152,9 @@ function ConfigEditBody({
|
|
|
1147
1152
|
{...(translate !== undefined && { translate })}
|
|
1148
1153
|
labelAppendix={(fieldName: string) => {
|
|
1149
1154
|
const source = sources[fieldName];
|
|
1150
|
-
return source ?
|
|
1155
|
+
return source ? (
|
|
1156
|
+
<ConfigSourceBadge source={source} screenScope={screen.scope} />
|
|
1157
|
+
) : undefined;
|
|
1151
1158
|
}}
|
|
1152
1159
|
fieldAppendix={(fieldName: string) => {
|
|
1153
1160
|
const cascade = cascades[fieldName];
|
|
@@ -3,7 +3,11 @@ import type {
|
|
|
3
3
|
EntityEditScreenDefinition,
|
|
4
4
|
FieldCondition,
|
|
5
5
|
} from "@cosmicdrift/kumiko-framework/ui-types";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
evalFieldCondition,
|
|
8
|
+
isExtensionEditSection,
|
|
9
|
+
normalizeEditField,
|
|
10
|
+
} from "@cosmicdrift/kumiko-framework/ui-types";
|
|
7
11
|
import type {
|
|
8
12
|
DispatcherError,
|
|
9
13
|
EditExtensionSectionViewModel,
|
|
@@ -84,12 +88,8 @@ function toConditionValue<TValues extends FormValues, TCtx>(
|
|
|
84
88
|
cond: FieldCondition,
|
|
85
89
|
): NonNullable<FieldConditions<TValues, TCtx>["visible"]> {
|
|
86
90
|
if (typeof cond === "boolean") return cond;
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
return (values: TValues) => (values as Record<string, unknown>)[field] === eq;
|
|
90
|
-
}
|
|
91
|
-
const { field, ne } = cond;
|
|
92
|
-
return (values: TValues) => (values as Record<string, unknown>)[field] !== ne;
|
|
91
|
+
// @cast-boundary form-values: TValues ist strukturell ein Record.
|
|
92
|
+
return (values: TValues) => evalFieldCondition(cond, values as Record<string, unknown>);
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
function deriveFormFields<TValues extends FormValues, TCtx>(
|
|
@@ -39,8 +39,9 @@ export function RenderField({
|
|
|
39
39
|
const { Field, Input } = usePrimitives();
|
|
40
40
|
// App-Locale (i18n) für money/date-Inputs — sonst fielen sie auf
|
|
41
41
|
// navigator.language (Browser-Sprache) zurück statt der gewählten
|
|
42
|
-
// App-Sprache.
|
|
43
|
-
//
|
|
42
|
+
// App-Sprache. BEWUSSTE API-Verschärfung (seit 0.38): RenderField ist
|
|
43
|
+
// public exportiert und verlangt jetzt einen LocaleProvider —
|
|
44
|
+
// Standalone-Consumer/Tests müssen wrappen (createKumikoApp tut es).
|
|
44
45
|
const appLocale = useLocale().locale();
|
|
45
46
|
if (!field.visible) return null;
|
|
46
47
|
|
package/src/primitives.tsx
CHANGED
|
@@ -486,9 +486,16 @@ export type DialogProps = {
|
|
|
486
486
|
};
|
|
487
487
|
|
|
488
488
|
/** Source-badge for one cascade step (User / Tenant / System / …).
|
|
489
|
-
* Used inline next to a config value to indicate where it came from.
|
|
489
|
+
* Used inline next to a config value to indicate where it came from.
|
|
490
|
+
* Requires a LocaleProvider above it (labels run through useTranslation)
|
|
491
|
+
* — createKumikoApp wires one; standalone consumers must wrap. */
|
|
490
492
|
export type ConfigSourceBadgeProps = {
|
|
491
493
|
readonly source: ConfigValueSource;
|
|
494
|
+
/** Scope of the hosting screen. Non-system screens collapse sources
|
|
495
|
+
* ABOVE their scope (system-row/app-override/computed) into the neutral
|
|
496
|
+
* default badge — operator internals stay invisible to tenants, same
|
|
497
|
+
* rule as ConfigCascadeView's toDisplayLevels. */
|
|
498
|
+
readonly screenScope?: ConfigScope;
|
|
492
499
|
};
|
|
493
500
|
|
|
494
501
|
/** Collapsible cascade-view that lives under a config-edit input.
|