@cosmicdrift/kumiko-renderer 0.2.2 → 0.3.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 +65 -0
- package/package.json +11 -8
- package/src/__tests__/i18n.test.tsx +4 -4
- package/src/app/kumiko-screen.tsx +57 -57
- package/src/components/render-edit.tsx +3 -2
- package/src/components/render-field.tsx +1 -1
- package/src/components/render-list.tsx +20 -14
- package/src/i18n.tsx +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,70 @@
|
|
|
1
1
|
# @cosmicdrift/kumiko-renderer
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 0.3.0 bringt zwei neue Subsysteme (Step-Engine Tier-3 + Visual-Tree) plus
|
|
8
|
+
eine AST-Codemod-Pipeline als Vorarbeit für den L2-AI-Layer.
|
|
9
|
+
|
|
10
|
+
### Breaking Changes
|
|
11
|
+
|
|
12
|
+
- `skipTransitionGuard` → `unsafeSkipTransitionGuard` (Rename in
|
|
13
|
+
feature-ast + engine). Der `unsafe`-Prefix macht die Tragweite des
|
|
14
|
+
Casts sichtbar und ist konsistent zur `unsafeProjectionUpsert`- und
|
|
15
|
+
`r.rawTable`-Konvention. Migration: 1:1-Ersetzung, keine Verhaltens-Änderung.
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
- **Step-Engine M.4 — Tier-3 Workflow-Engine.** Neue Step-Vocabulary
|
|
20
|
+
`wait`, `waitForEvent`, `retry` ermöglicht persistierte Long-Running-Flows
|
|
21
|
+
über Job-Boundaries hinweg. Q7 Snapshot-at-Start hängt jedem Step-Run
|
|
22
|
+
einen SHA-256-Fingerprint des Aggregat-Zustands an, sodass Replays
|
|
23
|
+
deterministisch gegen den ursprünglichen Eingangszustand laufen.
|
|
24
|
+
- **Visual-Tree V.1.x — Tree-API + Editor-Panel.** Neue `VisualTree`-
|
|
25
|
+
Component plus TreeProvider-Pattern; erste TreeProviders für
|
|
26
|
+
`text-content` und `legal-pages` (CMS-light + Impressum/Privacy).
|
|
27
|
+
Fundament für den späteren No-Code-Designer (~3000 LOC, 98 Tests).
|
|
28
|
+
- **Codemod-Pipeline.** AST-basierte Patcher-Module für strukturelle
|
|
29
|
+
Feature-Edits — wird vom kommenden L2-AI-Layer als Tool-Surface
|
|
30
|
+
verwendet, ist aber eigenständig nutzbar für ts-morph-style Migrationen.
|
|
31
|
+
- **user-data-rights Sample-Recipe.** DSGVO Art. 15/17/18/20 vollständig
|
|
32
|
+
als Sample-Recipe (`samples/recipes/`) inklusive README — zeigt die
|
|
33
|
+
Export- und Forget-Pipeline gegen den `compliance-profiles`-Default
|
|
34
|
+
(`eu-dsgvo`).
|
|
35
|
+
|
|
36
|
+
### Fixes
|
|
37
|
+
|
|
38
|
+
- `tier-engine`: auto-default-tier-Hook benutzt jetzt `ctx.db.raw` für
|
|
39
|
+
Event-Store-Operationen (#37, vorher: stiller Bug, 22 Tage live).
|
|
40
|
+
- `engine`: unsafe-projection-upsert nutzt `as never` statt `as any` —
|
|
41
|
+
schmaler Cast-Surface, weniger Compiler-Knebel.
|
|
42
|
+
- `visual-tree`: runtime-isolation marker für client-konsumierte Files,
|
|
43
|
+
damit der Multi-Entry-Build den richtigen Bundle-Split bekommt.
|
|
44
|
+
- `feature-ast`: vollständiger `unsafeSkipTransitionGuard`-Rename (war
|
|
45
|
+
in zwei Modulen noch der alte Name).
|
|
46
|
+
- `framework`: Error-Reasons + `noConsole`-Lint + No-Date-API-Guard
|
|
47
|
+
wieder push-ready.
|
|
48
|
+
|
|
49
|
+
### Library-Updates
|
|
50
|
+
|
|
51
|
+
hono 4.12, jose 6.2, stripe 22.1, meilisearch 0.58, marked 18,
|
|
52
|
+
bun-types 1.3.13, lucide-react 1.14, bullmq 5.76, ioredis 5.10,
|
|
53
|
+
i18next 26.0, react + radix-ui-primitives auf aktuelle Minors.
|
|
54
|
+
|
|
55
|
+
### Patch Changes
|
|
56
|
+
|
|
57
|
+
- Updated dependencies
|
|
58
|
+
- @cosmicdrift/kumiko-framework@0.3.0
|
|
59
|
+
- @cosmicdrift/kumiko-headless@0.3.0
|
|
60
|
+
|
|
61
|
+
## 0.2.3
|
|
62
|
+
|
|
63
|
+
### Patch Changes
|
|
64
|
+
|
|
65
|
+
- @cosmicdrift/kumiko-framework@0.2.3
|
|
66
|
+
- @cosmicdrift/kumiko-headless@0.2.3
|
|
67
|
+
|
|
3
68
|
## 0.2.2
|
|
4
69
|
|
|
5
70
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-renderer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.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>",
|
|
@@ -9,17 +9,20 @@
|
|
|
9
9
|
"runtime": "client"
|
|
10
10
|
},
|
|
11
11
|
"exports": {
|
|
12
|
-
".":
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./src/index.ts",
|
|
14
|
+
"default": "./src/index.ts"
|
|
15
|
+
}
|
|
13
16
|
},
|
|
14
17
|
"dependencies": {
|
|
15
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
16
|
-
"@cosmicdrift/kumiko-headless": "0.
|
|
17
|
-
"react": "^19.2.
|
|
18
|
+
"@cosmicdrift/kumiko-framework": "0.3.0",
|
|
19
|
+
"@cosmicdrift/kumiko-headless": "0.3.0",
|
|
20
|
+
"react": "^19.2.6"
|
|
18
21
|
},
|
|
19
22
|
"devDependencies": {
|
|
20
|
-
"@testing-library/react": "^16.3.
|
|
21
|
-
"@types/react": "^19.2.
|
|
22
|
-
"jsdom": "^29.1.
|
|
23
|
+
"@testing-library/react": "^16.3.2",
|
|
24
|
+
"@types/react": "^19.2.14",
|
|
25
|
+
"jsdom": "^29.1.1"
|
|
23
26
|
},
|
|
24
27
|
"repository": {
|
|
25
28
|
"type": "git",
|
|
@@ -17,16 +17,16 @@ function makeStatefulResolver(initial: string): LocaleResolver {
|
|
|
17
17
|
let current = initial;
|
|
18
18
|
const listeners = new Set<() => void>();
|
|
19
19
|
return {
|
|
20
|
-
translate: (key) => key,
|
|
20
|
+
translate: (key: string) => key,
|
|
21
21
|
locale: () => current,
|
|
22
22
|
timeZone: () => "UTC",
|
|
23
|
-
subscribe: (l) => {
|
|
23
|
+
subscribe: (l: () => void) => {
|
|
24
24
|
listeners.add(l);
|
|
25
25
|
return () => {
|
|
26
26
|
listeners.delete(l);
|
|
27
27
|
};
|
|
28
28
|
},
|
|
29
|
-
setLocale: (next) => {
|
|
29
|
+
setLocale: (next: string) => {
|
|
30
30
|
current = next;
|
|
31
31
|
for (const l of listeners) l();
|
|
32
32
|
},
|
|
@@ -45,7 +45,7 @@ describe("useTranslation — lookup order", () => {
|
|
|
45
45
|
test("App-Resolver wins when it returns a non-key value", () => {
|
|
46
46
|
const resolver: LocaleResolver = {
|
|
47
47
|
...createStaticLocaleResolver({ locale: "de" }),
|
|
48
|
-
translate: (key) => (key === "hello" ? "Resolved by app" : key),
|
|
48
|
+
translate: (key: string) => (key === "hello" ? "Resolved by app" : key),
|
|
49
49
|
};
|
|
50
50
|
const { result } = renderHook(() => useTranslation(), { wrapper: wrap(resolver) });
|
|
51
51
|
expect(result.current("hello")).toBe("Resolved by app");
|
|
@@ -4,7 +4,10 @@ import type {
|
|
|
4
4
|
EntityDefinition,
|
|
5
5
|
EntityEditScreenDefinition,
|
|
6
6
|
EntityListScreenDefinition,
|
|
7
|
+
RowAction,
|
|
8
|
+
RowActionWriteHandler,
|
|
7
9
|
ScreenDefinition,
|
|
10
|
+
ToolbarAction,
|
|
8
11
|
} from "@cosmicdrift/kumiko-framework/ui-types";
|
|
9
12
|
import type {
|
|
10
13
|
Command,
|
|
@@ -16,12 +19,12 @@ import type {
|
|
|
16
19
|
} from "@cosmicdrift/kumiko-headless";
|
|
17
20
|
import { type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
18
21
|
import { RenderEdit } from "../components/render-edit";
|
|
19
|
-
import { RenderList } from "../components/render-list";
|
|
22
|
+
import { RenderList, type ToolbarActionButton } from "../components/render-list";
|
|
20
23
|
import { useDispatcher, useOptionalDispatcher } from "../context/dispatcher-context";
|
|
21
24
|
import { useListUrlState } from "../hooks/use-list-url-state";
|
|
22
25
|
import { useQuery } from "../hooks/use-query";
|
|
23
26
|
import { useTranslation } from "../i18n";
|
|
24
|
-
import { usePrimitives } from "../primitives";
|
|
27
|
+
import { type DataTableRowAction, usePrimitives } from "../primitives";
|
|
25
28
|
import { synthesizeActionFormEntity, synthesizeActionFormScreen } from "./action-form-shim";
|
|
26
29
|
import { synthesizeConfigEditEntity, synthesizeConfigEditScreen } from "./config-edit-shim";
|
|
27
30
|
import { useCustomScreenComponent } from "./custom-screens";
|
|
@@ -82,7 +85,10 @@ export function KumikoScreen({
|
|
|
82
85
|
}: KumikoScreenProps): ReactNode {
|
|
83
86
|
const { Banner, Text } = usePrimitives();
|
|
84
87
|
const screen = useMemo(
|
|
85
|
-
() =>
|
|
88
|
+
() =>
|
|
89
|
+
schema.screens.find(
|
|
90
|
+
(s: ScreenDefinition) => qualifyScreenId(schema.featureName, s.id) === qn,
|
|
91
|
+
),
|
|
86
92
|
[schema.featureName, schema.screens, qn],
|
|
87
93
|
);
|
|
88
94
|
|
|
@@ -162,7 +168,9 @@ function entityWriteCommand(
|
|
|
162
168
|
function useNavigateToListAfter(schema: FeatureSchema, entityName: string): () => void {
|
|
163
169
|
const nav = useNav();
|
|
164
170
|
return useCallback(() => {
|
|
165
|
-
const list = schema.screens.find(
|
|
171
|
+
const list = schema.screens.find(
|
|
172
|
+
(s: ScreenDefinition) => s.type === "entityList" && s.entity === entityName,
|
|
173
|
+
);
|
|
166
174
|
if (!list) return;
|
|
167
175
|
// schema.screens.id ist QN-form (registry-stamped); nav.navigate
|
|
168
176
|
// erwartet Short-Form. Sonst landet die URL doppelt-qualifiziert.
|
|
@@ -184,7 +192,9 @@ function useNavigateToCreateFor(
|
|
|
184
192
|
): (() => void) | undefined {
|
|
185
193
|
const nav = useNav();
|
|
186
194
|
const editScreenId = useMemo(() => {
|
|
187
|
-
const edit = schema.screens.find(
|
|
195
|
+
const edit = schema.screens.find(
|
|
196
|
+
(s: ScreenDefinition) => s.type === "entityEdit" && s.entity === entityName,
|
|
197
|
+
);
|
|
188
198
|
return edit !== undefined ? lastSegment(edit.id) : undefined;
|
|
189
199
|
}, [schema.screens, entityName]);
|
|
190
200
|
const navigate = useCallback(() => {
|
|
@@ -643,7 +653,7 @@ function EntityListBody({
|
|
|
643
653
|
const rowActions = useMemo(() => {
|
|
644
654
|
if (screen.rowActions === undefined) return undefined;
|
|
645
655
|
return screen.rowActions
|
|
646
|
-
.map((action) => {
|
|
656
|
+
.map((action: RowAction): DataTableRowAction | null => {
|
|
647
657
|
// navigate-Variante braucht keinen Dispatcher; nav ist
|
|
648
658
|
// immer da (Provider von createKumikoApp).
|
|
649
659
|
if (action.kind === "navigate") {
|
|
@@ -671,31 +681,32 @@ function EntityListBody({
|
|
|
671
681
|
}),
|
|
672
682
|
};
|
|
673
683
|
}
|
|
674
|
-
// writeHandler-Variante (default kind, Backwards-Compat).
|
|
675
|
-
// Braucht Dispatcher — null returnen → filter unten dropt es,
|
|
676
|
-
// damit das useEffect-Warning oben einmal feuert + die Action
|
|
677
|
-
// einfach nicht rendert (statt Crash).
|
|
678
684
|
if (dispatcher === undefined) return null;
|
|
685
|
+
if (action.kind !== "writeHandler" && action.kind !== undefined) return null;
|
|
686
|
+
const writeAction = action as RowActionWriteHandler;
|
|
679
687
|
return {
|
|
680
|
-
id:
|
|
681
|
-
label: effectiveTranslate(
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
688
|
+
id: writeAction.id,
|
|
689
|
+
label: effectiveTranslate(writeAction.label),
|
|
690
|
+
style: writeAction.style,
|
|
691
|
+
confirm:
|
|
692
|
+
writeAction.confirm !== undefined ? effectiveTranslate(writeAction.confirm) : undefined,
|
|
693
|
+
confirmLabel:
|
|
694
|
+
writeAction.confirmLabel !== undefined
|
|
695
|
+
? effectiveTranslate(writeAction.confirmLabel)
|
|
696
|
+
: undefined,
|
|
687
697
|
onTrigger: async (row: ListRowViewModel) => {
|
|
688
|
-
const buildPayload =
|
|
698
|
+
const buildPayload = writeAction.payload;
|
|
689
699
|
const payload =
|
|
690
700
|
buildPayload !== undefined ? buildPayload(row.values) : { id: row.values["id"] };
|
|
691
|
-
await dispatcher.write(
|
|
701
|
+
await dispatcher.write(writeAction.handler, payload);
|
|
692
702
|
},
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
703
|
+
isVisible:
|
|
704
|
+
writeAction.visible !== undefined
|
|
705
|
+
? (row: ListRowViewModel) => writeAction.visible?.(row.values, undefined) ?? true
|
|
706
|
+
: undefined,
|
|
696
707
|
};
|
|
697
708
|
})
|
|
698
|
-
.filter((a): a is
|
|
709
|
+
.filter((a: DataTableRowAction | null): a is DataTableRowAction => a !== null);
|
|
699
710
|
}, [screen.rowActions, effectiveTranslate, dispatcher, nav]);
|
|
700
711
|
|
|
701
712
|
// ToolbarActions: Schema → Resolved-Form (analog rowActions).
|
|
@@ -705,45 +716,34 @@ function EntityListBody({
|
|
|
705
716
|
const toolbarActions = useMemo(() => {
|
|
706
717
|
if (screen.toolbarActions === undefined) return undefined;
|
|
707
718
|
return screen.toolbarActions
|
|
708
|
-
.map(
|
|
709
|
-
(
|
|
710
|
-
action,
|
|
711
|
-
): {
|
|
712
|
-
id: string;
|
|
713
|
-
label: string;
|
|
714
|
-
style?: "primary" | "secondary" | "danger";
|
|
715
|
-
confirm?: string;
|
|
716
|
-
confirmLabel?: string;
|
|
717
|
-
onTrigger: () => Promise<void> | void;
|
|
718
|
-
} | null => {
|
|
719
|
-
if (action.kind === "navigate") {
|
|
720
|
-
return {
|
|
721
|
-
id: action.id,
|
|
722
|
-
label: effectiveTranslate(action.label),
|
|
723
|
-
...(action.style !== undefined && { style: action.style }),
|
|
724
|
-
onTrigger: () => nav.navigate({ screenId: action.screen }),
|
|
725
|
-
};
|
|
726
|
-
}
|
|
727
|
-
// writeHandler — braucht Dispatcher. Wenn keiner mounted ist,
|
|
728
|
-
// skippen wir die Action statt zu crashen (gleiche Logik wie
|
|
729
|
-
// bei rowActions; einmaliger Warn-Log dort reicht).
|
|
730
|
-
if (dispatcher === undefined) return null;
|
|
719
|
+
.map((action: ToolbarAction): ToolbarActionButton | null => {
|
|
720
|
+
if (action.kind === "navigate") {
|
|
731
721
|
return {
|
|
732
722
|
id: action.id,
|
|
733
723
|
label: effectiveTranslate(action.label),
|
|
734
724
|
...(action.style !== undefined && { style: action.style }),
|
|
735
|
-
|
|
736
|
-
...(action.confirmLabel !== undefined && {
|
|
737
|
-
confirmLabel: effectiveTranslate(action.confirmLabel),
|
|
738
|
-
}),
|
|
739
|
-
onTrigger: async () => {
|
|
740
|
-
const payload = action.payload?.() ?? {};
|
|
741
|
-
await dispatcher.write(action.handler, payload);
|
|
742
|
-
},
|
|
725
|
+
onTrigger: () => nav.navigate({ screenId: action.screen }),
|
|
743
726
|
};
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
|
|
727
|
+
}
|
|
728
|
+
// writeHandler — braucht Dispatcher. Wenn keiner mounted ist,
|
|
729
|
+
// skippen wir die Action statt zu crashen (gleiche Logik wie
|
|
730
|
+
// bei rowActions; einmaliger Warn-Log dort reicht).
|
|
731
|
+
if (dispatcher === undefined) return null;
|
|
732
|
+
return {
|
|
733
|
+
id: action.id,
|
|
734
|
+
label: effectiveTranslate(action.label),
|
|
735
|
+
...(action.style !== undefined && { style: action.style }),
|
|
736
|
+
...(action.confirm !== undefined && { confirm: effectiveTranslate(action.confirm) }),
|
|
737
|
+
...(action.confirmLabel !== undefined && {
|
|
738
|
+
confirmLabel: effectiveTranslate(action.confirmLabel),
|
|
739
|
+
}),
|
|
740
|
+
onTrigger: async () => {
|
|
741
|
+
const payload = action.payload?.() ?? {};
|
|
742
|
+
await dispatcher.write(action.handler, payload);
|
|
743
|
+
},
|
|
744
|
+
};
|
|
745
|
+
})
|
|
746
|
+
.filter((a: ToolbarActionButton | null): a is ToolbarActionButton => a !== null);
|
|
747
747
|
}, [screen.toolbarActions, effectiveTranslate, nav, dispatcher]);
|
|
748
748
|
|
|
749
749
|
if (rowsQuery.loading && rowsQuery.data === null) {
|
|
@@ -6,6 +6,7 @@ import { normalizeEditField } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
|
6
6
|
import type {
|
|
7
7
|
DispatcherError,
|
|
8
8
|
EditFieldViewModel,
|
|
9
|
+
EditSectionViewModel,
|
|
9
10
|
FieldConditions,
|
|
10
11
|
FieldIssue,
|
|
11
12
|
FormSnapshot,
|
|
@@ -252,10 +253,10 @@ export function RenderEdit<TValues extends FormValues, TCtx = unknown>(
|
|
|
252
253
|
actions={formActions}
|
|
253
254
|
testId="render-edit-form"
|
|
254
255
|
>
|
|
255
|
-
{vm.sections.map((section) => (
|
|
256
|
+
{vm.sections.map((section: EditSectionViewModel) => (
|
|
256
257
|
<Section key={section.title} title={section.title} testId={`section-${section.title}`}>
|
|
257
258
|
<Grid columns={section.columns}>
|
|
258
|
-
{section.fields.map((field) => (
|
|
259
|
+
{section.fields.map((field: EditFieldViewModel) => (
|
|
259
260
|
<GridCellForField
|
|
260
261
|
key={field.field}
|
|
261
262
|
field={field}
|
|
@@ -249,7 +249,7 @@ function renderInput({
|
|
|
249
249
|
const labels = field.optionLabels;
|
|
250
250
|
const selectOptions =
|
|
251
251
|
labels !== undefined
|
|
252
|
-
? rawOptions.map((value) => ({ value, label: labels[value] ?? value }))
|
|
252
|
+
? rawOptions.map((value: string) => ({ value, label: labels[value] ?? value }))
|
|
253
253
|
: rawOptions;
|
|
254
254
|
return (
|
|
255
255
|
<Input
|
|
@@ -3,7 +3,11 @@ import type {
|
|
|
3
3
|
EntityDefinition,
|
|
4
4
|
EntityListScreenDefinition,
|
|
5
5
|
} from "@cosmicdrift/kumiko-framework/ui-types";
|
|
6
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
ListColumnViewModel,
|
|
8
|
+
ListRowViewModel,
|
|
9
|
+
Translate,
|
|
10
|
+
} from "@cosmicdrift/kumiko-headless";
|
|
7
11
|
import { computeListViewModel } from "@cosmicdrift/kumiko-headless";
|
|
8
12
|
import { type ReactNode, useCallback, useEffect, useMemo, useState } from "react";
|
|
9
13
|
import type { ListSort } from "../hooks/use-list-url-state";
|
|
@@ -160,8 +164,8 @@ export function RenderList(props: RenderListProps): ReactNode {
|
|
|
160
164
|
const referenceColumns = useMemo(
|
|
161
165
|
() =>
|
|
162
166
|
vm.columns
|
|
163
|
-
.filter((c) => c.type === "reference" && c.refEntity !== undefined)
|
|
164
|
-
.map((c) => ({
|
|
167
|
+
.filter((c: ListColumnViewModel) => c.type === "reference" && c.refEntity !== undefined)
|
|
168
|
+
.map((c: ListColumnViewModel) => ({
|
|
165
169
|
field: c.field,
|
|
166
170
|
refEntity: c.refEntity ?? "",
|
|
167
171
|
// Tier 2.7e Cross-Feature: refFeature kommt aus parseRefTarget
|
|
@@ -183,7 +187,7 @@ export function RenderList(props: RenderListProps): ReactNode {
|
|
|
183
187
|
}, []);
|
|
184
188
|
const enrichedColumns = useMemo(() => {
|
|
185
189
|
if (referenceColumns.length === 0) return vm.columns;
|
|
186
|
-
return vm.columns.map((col) => {
|
|
190
|
+
return vm.columns.map((col: ListColumnViewModel) => {
|
|
187
191
|
if (col.type !== "reference") return col;
|
|
188
192
|
// Author-deklarierter Renderer übersteuert immer — Default greift
|
|
189
193
|
// nur wenn keiner gesetzt ist.
|
|
@@ -284,16 +288,18 @@ export function RenderList(props: RenderListProps): ReactNode {
|
|
|
284
288
|
// ListSort = DataTableSort (use-list-url-state aliased) — kein Cast nötig.
|
|
285
289
|
return (
|
|
286
290
|
<>
|
|
287
|
-
{referenceColumns.map(
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
291
|
+
{referenceColumns.map(
|
|
292
|
+
(rc: { field: string; refEntity: string; refFeature: string; labelField: string }) => (
|
|
293
|
+
<ReferenceLookupBridge
|
|
294
|
+
key={rc.field}
|
|
295
|
+
field={rc.field}
|
|
296
|
+
refEntity={rc.refEntity}
|
|
297
|
+
labelField={rc.labelField}
|
|
298
|
+
featureName={rc.refFeature}
|
|
299
|
+
onMap={handleLookupMap}
|
|
300
|
+
/>
|
|
301
|
+
),
|
|
302
|
+
)}
|
|
297
303
|
<DataTable
|
|
298
304
|
columns={enrichedVm.columns}
|
|
299
305
|
rows={enrichedVm.rows}
|
package/src/i18n.tsx
CHANGED
|
@@ -148,7 +148,7 @@ export function createStaticLocaleResolver(
|
|
|
148
148
|
const locale = options.locale ?? "en";
|
|
149
149
|
const timeZone = options.timeZone ?? "UTC";
|
|
150
150
|
return {
|
|
151
|
-
translate: (key) => key,
|
|
151
|
+
translate: (key: string) => key,
|
|
152
152
|
locale: () => locale,
|
|
153
153
|
timeZone: () => timeZone,
|
|
154
154
|
// No-op subscribe: unsere Locale ist statisch, es gibt nie ein
|