@cosmicdrift/kumiko-renderer 0.315.0 → 0.317.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-renderer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.317.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>",
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
}
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
19
|
-
"@cosmicdrift/kumiko-headless": "0.
|
|
20
|
-
"@cosmicdrift/kumiko-types": "0.
|
|
18
|
+
"@cosmicdrift/kumiko-framework": "0.317.0",
|
|
19
|
+
"@cosmicdrift/kumiko-headless": "0.317.0",
|
|
20
|
+
"@cosmicdrift/kumiko-types": "0.317.0",
|
|
21
21
|
"react": "^19.2.6",
|
|
22
22
|
"temporal-polyfill": "^0.3.2",
|
|
23
23
|
"zod": "^4.4.3"
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"@types/react-dom": "^19.2.3",
|
|
29
29
|
"jsdom": "^29.1.1",
|
|
30
30
|
"react-dom": "^19.2.6",
|
|
31
|
-
"@cosmicdrift/kumiko-locale-de": "0.
|
|
31
|
+
"@cosmicdrift/kumiko-locale-de": "0.317.0"
|
|
32
32
|
},
|
|
33
33
|
"repository": {
|
|
34
34
|
"type": "git",
|
package/src/changes.json
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.316.0",
|
|
4
|
+
"type": "fix",
|
|
5
|
+
"title": "An app footer-slot action can opt into the wizard's sticky primary group",
|
|
6
|
+
"detail": "fw#1918 pinned the primary action into a sticky footer on mobile by checking\neach form-action element's `type === \"submit\"` prop. An app's\n`screen.slots.footer` renders through EditSlotMount, which never carries\n`type=\"submit\"` (its button is opaque app code), so that slot always landed\nin the non-sticky secondary group even on a wizard's last step — regressing\napps like offlot-app whose wizard-final \"Publish\" action lives in the footer\nslot. `ScreenSlots.footerPrimary?: boolean` now marks that slot as the\nsticky-primary action; `FormFooter` in kumiko-renderer-web recognizes it via\nthe shared `STICKY_PRIMARY_ACTION_PROP` marker exported from kumiko-renderer.\nBuilt-in submit buttons are unaffected.",
|
|
7
|
+
"migration": "Additive — no action needed unless a footer-slot action must become the\nsticky primary action on mobile. Set `slots.footerPrimary: true` next to\nthat screen's `slots.footer` registration to opt in."
|
|
8
|
+
},
|
|
2
9
|
{
|
|
3
10
|
"version": "0.310.0",
|
|
4
11
|
"type": "improvement",
|
|
@@ -1212,8 +1212,8 @@ describe("RelatedListSection — search + facets (fw#2740)", () => {
|
|
|
1212
1212
|
},
|
|
1213
1213
|
{ timeout: 2000 },
|
|
1214
1214
|
);
|
|
1215
|
-
await waitFor(() => expect(rtlScreen.
|
|
1216
|
-
expect(rtlScreen.
|
|
1215
|
+
await waitFor(() => expect(rtlScreen.queryByTestId("row-r2")).toBeNull());
|
|
1216
|
+
expect(rtlScreen.getByTestId("row-r1")).toBeTruthy();
|
|
1217
1217
|
});
|
|
1218
1218
|
|
|
1219
1219
|
test("without searchable, RenderList shows no search input and no payload ever carries a search key", async () => {
|
|
@@ -1266,8 +1266,8 @@ describe("RelatedListSection — search + facets (fw#2740)", () => {
|
|
|
1266
1266
|
},
|
|
1267
1267
|
{ timeout: 2000 },
|
|
1268
1268
|
);
|
|
1269
|
-
await waitFor(() => expect(rtlScreen.
|
|
1270
|
-
expect(rtlScreen.
|
|
1269
|
+
await waitFor(() => expect(rtlScreen.queryByTestId("row-r2")).toBeNull());
|
|
1270
|
+
expect(rtlScreen.getByTestId("row-r1")).toBeTruthy();
|
|
1271
1271
|
});
|
|
1272
1272
|
|
|
1273
1273
|
const statusSection: EditRelatedListSectionViewModel = {
|
|
@@ -1463,8 +1463,8 @@ describe("RelatedListSection — parentFilter", () => {
|
|
|
1463
1463
|
);
|
|
1464
1464
|
const last = payloads[payloads.length - 1];
|
|
1465
1465
|
expect(last?.["filter"]).toEqual({ field: "parentId", op: "eq", value: "order-1" });
|
|
1466
|
-
await waitFor(() => expect(rtlScreen.
|
|
1467
|
-
expect(rtlScreen.
|
|
1466
|
+
await waitFor(() => expect(rtlScreen.queryByTestId("row-r2")).toBeNull());
|
|
1467
|
+
expect(rtlScreen.getByTestId("row-r1")).toBeTruthy();
|
|
1468
1468
|
});
|
|
1469
1469
|
|
|
1470
1470
|
test("selecting a facet leaves payload.filter unchanged and adds payload.filters", async () => {
|
|
@@ -37,7 +37,12 @@ import { useDraftStorage } from "../context/draft-storage-context";
|
|
|
37
37
|
import { formatWhen } from "../format-when";
|
|
38
38
|
import { useForm } from "../hooks/use-form";
|
|
39
39
|
import { useTranslation } from "../i18n";
|
|
40
|
-
import {
|
|
40
|
+
import {
|
|
41
|
+
STICKY_PRIMARY_ACTION_PROP,
|
|
42
|
+
type StickyPrimaryActionMarker,
|
|
43
|
+
shouldRenderActionsIconOnly,
|
|
44
|
+
usePrimitives,
|
|
45
|
+
} from "../primitives";
|
|
41
46
|
import { GridCellForField } from "./grid-cell-for-field";
|
|
42
47
|
import { RelatedListSection } from "./related-list-section";
|
|
43
48
|
import {
|
|
@@ -253,7 +258,7 @@ function EditSlotMount({
|
|
|
253
258
|
readonly values: Readonly<Record<string, unknown>>;
|
|
254
259
|
readonly hasUnsavedChanges?: boolean;
|
|
255
260
|
readonly wizardStep?: { readonly index: number; readonly isLast: boolean };
|
|
256
|
-
}): ReactNode {
|
|
261
|
+
} & StickyPrimaryActionMarker): ReactNode {
|
|
257
262
|
const name = extensionSectionName(slot);
|
|
258
263
|
const Component = useExtensionSectionComponent(name);
|
|
259
264
|
useEffect(() => {
|
|
@@ -1199,6 +1204,7 @@ export function RenderEdit<TValues extends FormValues, TCtx = unknown>(
|
|
|
1199
1204
|
values={snapshot.values}
|
|
1200
1205
|
hasUnsavedChanges={snapshot.isDirty || extensionDirty}
|
|
1201
1206
|
wizardStep={isWizard ? { index: currentStep, isLast: isLastWizardStep } : undefined}
|
|
1207
|
+
{...(screen.slots?.footerPrimary === true && { [STICKY_PRIMARY_ACTION_PROP]: true })}
|
|
1202
1208
|
/>
|
|
1203
1209
|
)}
|
|
1204
1210
|
{isWizard && !isLastWizardStep && (
|
package/src/index.ts
CHANGED
|
@@ -231,12 +231,14 @@ export type {
|
|
|
231
231
|
StatusBadgeProps,
|
|
232
232
|
StatusTone,
|
|
233
233
|
StepBarProps,
|
|
234
|
+
StickyPrimaryActionMarker,
|
|
234
235
|
TabsProps,
|
|
235
236
|
TextProps,
|
|
236
237
|
WizardStepGroupProps,
|
|
237
238
|
} from "./primitives";
|
|
238
239
|
export {
|
|
239
240
|
PrimitivesProvider,
|
|
241
|
+
STICKY_PRIMARY_ACTION_PROP,
|
|
240
242
|
shouldRenderActionsIconOnly,
|
|
241
243
|
statusToneForValue,
|
|
242
244
|
usePrimitives,
|
package/src/primitives.tsx
CHANGED
|
@@ -66,6 +66,15 @@ export type { RuntimeRenderer };
|
|
|
66
66
|
* element; native impls ignore it (precedent: `className`). */
|
|
67
67
|
export type DataAttributes = Readonly<Record<`data-${string}`, string>>;
|
|
68
68
|
|
|
69
|
+
/** Structural marker a top-level `actions` node can carry so DefaultForm's
|
|
70
|
+
* sticky-footer split (fw#1918) treats it as the pinned primary action even
|
|
71
|
+
* though it isn't a `Button` with `type="submit"` — e.g. RenderEdit's
|
|
72
|
+
* footer-slot mount, whose actual rendered button is opaque app code the
|
|
73
|
+
* split can't inspect (see `ScreenSlots.footerPrimary`). */
|
|
74
|
+
export const STICKY_PRIMARY_ACTION_PROP = "kumikoStickyPrimaryAction" as const;
|
|
75
|
+
|
|
76
|
+
export type StickyPrimaryActionMarker = Partial<Record<typeof STICKY_PRIMARY_ACTION_PROP, boolean>>;
|
|
77
|
+
|
|
69
78
|
/** Standard-Button. `loading` zeigt einen Spinner statt der Children
|
|
70
79
|
* und sollte mit `disabled` kombiniert werden, wenn die Action wirklich
|
|
71
80
|
* blockiert bis das Loading durch ist (z.B. async submit). Native-
|