@cosmicdrift/kumiko-renderer-web 0.67.0 → 0.67.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-web",
|
|
3
|
-
"version": "0.67.
|
|
3
|
+
"version": "0.67.1",
|
|
4
4
|
"description": "Web-platform bindings for @cosmicdrift/kumiko-renderer. HTML default-primitives, browser history-based navigation, EventSource-backed live events, and a one-call createKumikoApp that mounts the whole stack via react-dom.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
"./styles.css": "./src/styles.css"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@cosmicdrift/kumiko-dispatcher-live": "0.67.
|
|
20
|
-
"@cosmicdrift/kumiko-headless": "0.67.
|
|
21
|
-
"@cosmicdrift/kumiko-renderer": "0.67.
|
|
19
|
+
"@cosmicdrift/kumiko-dispatcher-live": "0.67.1",
|
|
20
|
+
"@cosmicdrift/kumiko-headless": "0.67.1",
|
|
21
|
+
"@cosmicdrift/kumiko-renderer": "0.67.1",
|
|
22
22
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
23
23
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
24
24
|
"@radix-ui/react-label": "^2.1.8",
|
|
@@ -9,7 +9,7 @@ import type {
|
|
|
9
9
|
EntityEditScreenDefinition,
|
|
10
10
|
} from "@cosmicdrift/kumiko-framework/ui-types";
|
|
11
11
|
import { DispatcherProvider, RenderEdit } from "@cosmicdrift/kumiko-renderer";
|
|
12
|
-
import { defaultPrimitives } from "../primitives";
|
|
12
|
+
import { BareFormProvider, defaultPrimitives } from "../primitives";
|
|
13
13
|
import { createMockDispatcher, render, screen } from "./test-utils";
|
|
14
14
|
|
|
15
15
|
const { Form, Section, Button } = defaultPrimitives;
|
|
@@ -55,11 +55,12 @@ describe("Form = eine Card, Sections als innere Abschnitte", () => {
|
|
|
55
55
|
</Section>
|
|
56
56
|
</Form>,
|
|
57
57
|
);
|
|
58
|
-
// Inner-Region: keine eigene bg-card;
|
|
59
|
-
//
|
|
58
|
+
// Inner-Region: keine eigene bg-card; beide Sections sind Geschwister im
|
|
59
|
+
// selben Body-Wrapper (der die Trennlinie ZWISCHEN ihnen per CSS macht).
|
|
60
60
|
const s1 = screen.getByTestId("s1");
|
|
61
|
+
const s2 = screen.getByTestId("s2");
|
|
61
62
|
expect(s1.className).not.toContain("bg-card");
|
|
62
|
-
expect(s1.
|
|
63
|
+
expect(s1.parentElement).toBe(s2.parentElement);
|
|
63
64
|
// Das Form wrappt alles in GENAU eine Card-Fläche.
|
|
64
65
|
const card = screen.getByTestId("f").querySelector(".bg-card");
|
|
65
66
|
expect(card).toBeTruthy();
|
|
@@ -69,6 +70,21 @@ describe("Form = eine Card, Sections als innere Abschnitte", () => {
|
|
|
69
70
|
expect(card?.querySelector("[data-testid='f-actions']")).toBeTruthy();
|
|
70
71
|
});
|
|
71
72
|
|
|
73
|
+
test("BareFormProvider: Form rendert nackt (keine eigene Card) — gegen Card-in-Card im AuthCard", () => {
|
|
74
|
+
render(
|
|
75
|
+
<BareFormProvider>
|
|
76
|
+
<Form onSubmit={() => {}} testId="bare">
|
|
77
|
+
<div>a</div>
|
|
78
|
+
</Form>
|
|
79
|
+
</BareFormProvider>,
|
|
80
|
+
);
|
|
81
|
+
const form = screen.getByTestId("bare");
|
|
82
|
+
expect(form.tagName).toBe("FORM");
|
|
83
|
+
expect(form.className).not.toContain("max-w-3xl");
|
|
84
|
+
expect(form.querySelector(".bg-card")).toBeNull();
|
|
85
|
+
expect(form.className).toContain("gap-4");
|
|
86
|
+
});
|
|
87
|
+
|
|
72
88
|
test("Section standalone (außerhalb Form) bleibt eine eigene Card", () => {
|
|
73
89
|
render(
|
|
74
90
|
<Section testId="solo" title="Solo">
|
|
@@ -207,6 +207,23 @@ describe("NavTree", () => {
|
|
|
207
207
|
expect(container.querySelectorAll("svg").length).toBe(1);
|
|
208
208
|
});
|
|
209
209
|
|
|
210
|
+
test("layers + building (money-horse Kredit-Gruppen/Mandanten) lösen auf ein Icon auf", () => {
|
|
211
|
+
const schema = {
|
|
212
|
+
featureName: "showcase",
|
|
213
|
+
entities: {},
|
|
214
|
+
screens: [
|
|
215
|
+
{ id: "groups", type: "entityList", entity: "x", columns: [] },
|
|
216
|
+
{ id: "tenants", type: "entityList", entity: "x", columns: [] },
|
|
217
|
+
],
|
|
218
|
+
navs: [
|
|
219
|
+
{ id: "groups", label: "Gruppen", screen: "groups", order: 10, icon: "layers" },
|
|
220
|
+
{ id: "tenants", label: "Mandanten", screen: "tenants", order: 20, icon: "building" },
|
|
221
|
+
],
|
|
222
|
+
} as FeatureSchema;
|
|
223
|
+
const { container } = render(<NavTree schema={schema} />);
|
|
224
|
+
expect(container.querySelectorAll("svg").length).toBe(2);
|
|
225
|
+
});
|
|
226
|
+
|
|
210
227
|
test("unbekannter icon-Key fällt sauber auf den Dot zurück (kein svg)", () => {
|
|
211
228
|
const schema = {
|
|
212
229
|
featureName: "showcase",
|
package/src/index.ts
CHANGED
|
@@ -122,7 +122,7 @@ export { filterByAccess, resolveDefaultId, WorkspaceShell } from "./layout/works
|
|
|
122
122
|
export type { WorkspaceSwitcherProps } from "./layout/workspace-switcher";
|
|
123
123
|
export { WorkspaceSwitcher } from "./layout/workspace-switcher";
|
|
124
124
|
export { cn } from "./lib/cn";
|
|
125
|
-
export { defaultPrimitives } from "./primitives";
|
|
125
|
+
export { BareFormProvider, defaultPrimitives } from "./primitives";
|
|
126
126
|
export type { ActionMenuProps, MenuItemDef } from "./primitives/action-menu";
|
|
127
127
|
export { ActionMenu } from "./primitives/action-menu";
|
|
128
128
|
export {
|
package/src/layout/nav-tree.tsx
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
import {
|
|
27
27
|
BarChart3,
|
|
28
28
|
Bell,
|
|
29
|
+
Building,
|
|
29
30
|
Calculator,
|
|
30
31
|
CalendarDays,
|
|
31
32
|
ChevronDown,
|
|
@@ -36,6 +37,7 @@ import {
|
|
|
36
37
|
Folder,
|
|
37
38
|
Gauge,
|
|
38
39
|
Home,
|
|
40
|
+
Layers,
|
|
39
41
|
LayoutDashboard,
|
|
40
42
|
LineChart,
|
|
41
43
|
List,
|
|
@@ -82,6 +84,8 @@ const NAV_ICONS: Readonly<Record<string, typeof Folder>> = {
|
|
|
82
84
|
gauge: Gauge,
|
|
83
85
|
list: List,
|
|
84
86
|
table: Table,
|
|
87
|
+
layers: Layers,
|
|
88
|
+
building: Building,
|
|
85
89
|
calculator: Calculator,
|
|
86
90
|
wallet: Wallet,
|
|
87
91
|
coins: Coins,
|
package/src/primitives/index.tsx
CHANGED
|
@@ -1292,6 +1292,16 @@ function DataTableCell({
|
|
|
1292
1292
|
// eigene Card.
|
|
1293
1293
|
const InsideFormContext = createContext(false);
|
|
1294
1294
|
|
|
1295
|
+
// Eingebettete Forms (z.B. im AuthCard) tragen ihre Card-Fläche schon vom
|
|
1296
|
+
// Container — der self-cardende DefaultForm würde sonst eine Card-in-Card
|
|
1297
|
+
// erzeugen. BareFormProvider schaltet DefaultForm auf ein nacktes <form>
|
|
1298
|
+
// (gestapelte Felder, kein eigener Rahmen/max-width).
|
|
1299
|
+
const BareFormContext = createContext(false);
|
|
1300
|
+
|
|
1301
|
+
export function BareFormProvider({ children }: { children: ReactNode }): ReactNode {
|
|
1302
|
+
return <BareFormContext.Provider value={true}>{children}</BareFormContext.Provider>;
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1295
1305
|
function DefaultForm({
|
|
1296
1306
|
onSubmit,
|
|
1297
1307
|
children,
|
|
@@ -1300,6 +1310,26 @@ function DefaultForm({
|
|
|
1300
1310
|
actions,
|
|
1301
1311
|
testId,
|
|
1302
1312
|
}: FormProps): ReactNode {
|
|
1313
|
+
// Eingebettet (AuthCard etc.): nacktes <form>, gestapelte Felder mit gap —
|
|
1314
|
+
// der Container trägt Card/Titel selbst, sonst Card-in-Card.
|
|
1315
|
+
if (useContext(BareFormContext)) {
|
|
1316
|
+
return (
|
|
1317
|
+
<form
|
|
1318
|
+
onSubmit={(e) => {
|
|
1319
|
+
e.preventDefault();
|
|
1320
|
+
onSubmit(e);
|
|
1321
|
+
}}
|
|
1322
|
+
data-testid={testId}
|
|
1323
|
+
className="flex flex-col gap-4"
|
|
1324
|
+
>
|
|
1325
|
+
<InsideFormContext.Provider value={true}>{children}</InsideFormContext.Provider>
|
|
1326
|
+
{actions !== undefined && (
|
|
1327
|
+
<div className="flex items-center justify-end gap-2">{actions}</div>
|
|
1328
|
+
)}
|
|
1329
|
+
</form>
|
|
1330
|
+
);
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1303
1333
|
// shadcn-Form-Muster: das Formular ist EINE Card — Titel als Card-Header
|
|
1304
1334
|
// OHNE Trennlinie darunter (Titel fließt in die erste Section). Sections
|
|
1305
1335
|
// sind divide-y-getrennt (Linien nur ZWISCHEN ihnen). Action-Footer mit
|
|
@@ -1336,7 +1366,17 @@ function DefaultForm({
|
|
|
1336
1366
|
)}
|
|
1337
1367
|
</div>
|
|
1338
1368
|
)}
|
|
1339
|
-
<div
|
|
1369
|
+
<div
|
|
1370
|
+
className={cn(
|
|
1371
|
+
"flex flex-col",
|
|
1372
|
+
// Section-Children (Auto-UI-Edit) trennt eine Linie ZWISCHEN
|
|
1373
|
+
// ihnen — sie padden sich selbst. Flache Felder (Custom-Screens)
|
|
1374
|
+
// kriegen Padding + Rhythmus, keine Linie zwischen jedem Feld.
|
|
1375
|
+
"[&>section:not(:first-child)]:border-t",
|
|
1376
|
+
"[&>:not(section)]:px-6 [&>:not(section)]:py-3",
|
|
1377
|
+
"[&>:not(section):first-child]:pt-6 [&>:not(section):last-child]:pb-6",
|
|
1378
|
+
)}
|
|
1379
|
+
>
|
|
1340
1380
|
<InsideFormContext.Provider value={true}>{children}</InsideFormContext.Provider>
|
|
1341
1381
|
</div>
|
|
1342
1382
|
{actions !== undefined && (
|