@cosmicdrift/kumiko-renderer 0.333.0 → 0.335.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/dist/app/config-edit-shim.d.ts +7 -0
- package/dist/app/config-edit-shim.js +39 -0
- package/dist/app/content-preview.js +7 -9
- package/dist/app/extension-sections.d.ts +6 -0
- package/dist/app/form-schema.d.ts +2 -1
- package/dist/app/form-schema.js +11 -2
- package/dist/app/kumiko-screen.d.ts +5 -0
- package/dist/app/kumiko-screen.js +259 -101
- package/dist/app/nav.d.ts +5 -0
- package/dist/app/nav.js +17 -4
- package/dist/app/query-state-banners.d.ts +8 -0
- package/dist/app/query-state-banners.js +14 -0
- package/dist/app/reference-facet-bridge.js +8 -1
- package/dist/app/row-actions.d.ts +12 -0
- package/dist/app/row-actions.js +24 -7
- package/dist/app/screen-access.js +13 -9
- package/dist/app/secret-mint-body.js +41 -7
- package/dist/app/secrets-edit-body.js +89 -33
- package/dist/components/embedded-list-field.js +2 -4
- package/dist/components/grid-cell-for-field.d.ts +7 -2
- package/dist/components/grid-cell-for-field.js +2 -2
- package/dist/components/multi-select-checkboxes.js +4 -1
- package/dist/components/query-options-select.d.ts +12 -3
- package/dist/components/query-options-select.js +45 -2
- package/dist/components/reference-create-dialog.js +9 -4
- package/dist/components/related-list-section.js +8 -10
- package/dist/components/render-edit-action-button.d.ts +1 -1
- package/dist/components/render-edit-action-button.js +5 -1
- package/dist/components/render-edit-logic.d.ts +2 -1
- package/dist/components/render-edit-logic.js +8 -5
- package/dist/components/render-edit-types.d.ts +30 -1
- package/dist/components/render-edit.js +248 -76
- package/dist/components/render-field.d.ts +7 -1
- package/dist/components/render-field.js +40 -15
- package/dist/components/render-list.js +6 -6
- package/dist/components/write-form-section.d.ts +5 -1
- package/dist/components/write-form-section.js +29 -25
- package/dist/context/embedded-screen-context.d.ts +5 -0
- package/dist/context/embedded-screen-context.js +12 -0
- package/dist/hooks/use-reference-lookup.d.ts +7 -0
- package/dist/hooks/use-reference-lookup.js +10 -0
- package/dist/i18n-defaults.js +30 -5
- package/dist/i18n.js +8 -4
- package/dist/index.d.ts +5 -1
- package/dist/index.js +3 -0
- package/dist/primitives.d.ts +70 -19
- package/dist/session/session-ended.d.ts +16 -0
- package/dist/session/session-ended.js +73 -0
- package/dist/sort-by-accessor.d.ts +6 -2
- package/dist/sort-by-accessor.js +13 -3
- package/package.json +5 -5
- package/src/changes.json +40 -0
package/dist/primitives.d.ts
CHANGED
|
@@ -123,11 +123,28 @@ export type FieldProps = {
|
|
|
123
123
|
readonly issues?: readonly FieldIssue[];
|
|
124
124
|
readonly labelAppendix?: ReactNode;
|
|
125
125
|
readonly fieldAppendix?: ReactNode;
|
|
126
|
+
/** Where `fieldAppendix` sits relative to the control. Default
|
|
127
|
+
* `above-control` (a disclosure panel belongs under its label-row
|
|
128
|
+
* trigger); `below-control` keeps sibling inputs on one line when the
|
|
129
|
+
* appendix is a status line or an expanded detail. */
|
|
130
|
+
readonly appendixPlacement?: "above-control" | "below-control";
|
|
131
|
+
/** Help text directly under the label, above the control. */
|
|
132
|
+
readonly description?: ReactNode;
|
|
133
|
+
/** Status pill at the right end of the label row (e.g. "Saved", "Not set"). */
|
|
134
|
+
readonly status?: {
|
|
135
|
+
readonly tone: StatusTone;
|
|
136
|
+
readonly label: string;
|
|
137
|
+
readonly testId?: string;
|
|
138
|
+
};
|
|
126
139
|
readonly children: ReactNode;
|
|
127
140
|
/** Label-Layout. `stacked` (Default): Label über dem Control. `inline`:
|
|
128
141
|
* Control + Label nebeneinander (Box links, Label rechts) — für
|
|
129
142
|
* Checkbox/Switch-Felder (boolean), wie das shadcn-Muster. */
|
|
130
|
-
readonly layout?: "stacked" | "inline";
|
|
143
|
+
readonly layout?: "stacked" | "inline" | "row";
|
|
144
|
+
/** Marks a field whose value is set at this level (differs from the
|
|
145
|
+
* inherited one). Only `layout="row"` draws it: a 2px primary line on the
|
|
146
|
+
* row's left edge. Ignored by the other layouts. */
|
|
147
|
+
readonly accent?: boolean;
|
|
131
148
|
/** Collapses the visible label to `sr-only` — stays associated via
|
|
132
149
|
* `htmlFor` (no separate `aria-label` needed). For tables/grids whose
|
|
133
150
|
* column header already carries the label for each row. */
|
|
@@ -141,6 +158,12 @@ export type FieldProps = {
|
|
|
141
158
|
/** Discriminated union — jede Input-Sorte hat ihre eigene value/onChange
|
|
142
159
|
* Signatur. Custom-Impls dispatchen intern, rendern anders (Toggle
|
|
143
160
|
* statt Checkbox), oder nur einzelne kinds unterschiedlich. */
|
|
161
|
+
export type TrailingInputAction = {
|
|
162
|
+
readonly icon: "clear" | "undo";
|
|
163
|
+
readonly label: string;
|
|
164
|
+
readonly onPress: () => void;
|
|
165
|
+
readonly testId?: string;
|
|
166
|
+
};
|
|
144
167
|
export type InputProps = {
|
|
145
168
|
readonly kind: "text";
|
|
146
169
|
readonly id: string;
|
|
@@ -198,6 +221,9 @@ export type InputProps = {
|
|
|
198
221
|
* Browser-Password-Manager nutzen das für die Speicherentscheidung.
|
|
199
222
|
* Native: textContentType="password" / "newPassword". */
|
|
200
223
|
readonly autoComplete?: "current-password" | "new-password";
|
|
224
|
+
/** Icon button inside the field's trailing edge (clear a stored value,
|
|
225
|
+
* undo a pending removal). `label` is its accessible name + tooltip. */
|
|
226
|
+
readonly trailingAction?: TrailingInputAction;
|
|
201
227
|
readonly testId?: string;
|
|
202
228
|
} | {
|
|
203
229
|
readonly kind: "number";
|
|
@@ -299,8 +325,13 @@ export type InputProps = {
|
|
|
299
325
|
readonly options: readonly string[] | readonly {
|
|
300
326
|
readonly value: string;
|
|
301
327
|
readonly label: string;
|
|
302
|
-
/**
|
|
328
|
+
/** Muted second line under the label (radio list and dropdown items;
|
|
329
|
+
* the dropdown search matches it too). */
|
|
303
330
|
readonly description?: string;
|
|
331
|
+
/** Group heading. Groups form in order of first occurrence; options
|
|
332
|
+
* without a group stay ungrouped and come before all groups. Options
|
|
333
|
+
* with description or group never render as a segmented control. */
|
|
334
|
+
readonly group?: string;
|
|
304
335
|
}[];
|
|
305
336
|
readonly disabled?: boolean;
|
|
306
337
|
readonly required?: boolean;
|
|
@@ -837,8 +868,8 @@ export type FormProps = {
|
|
|
837
868
|
* detail screens (which also fill the height) do not set it. Native impls
|
|
838
869
|
* may ignore this prop. */
|
|
839
870
|
readonly screenForm?: boolean;
|
|
840
|
-
/** Number of unsaved changes, shown in the pinned footer
|
|
841
|
-
*
|
|
871
|
+
/** Number of unsaved changes, shown in the pinned footer; 0 shows the quiet
|
|
872
|
+
* "No unsaved changes" status. Omitted (create mode) — no status at all. */
|
|
842
873
|
readonly unsavedCount?: number;
|
|
843
874
|
/** Drawer-hosted forms: context box above the fields. */
|
|
844
875
|
readonly summary?: {
|
|
@@ -870,6 +901,9 @@ export type SectionProps = {
|
|
|
870
901
|
* title-less/subtitle-less Section still draws the row when `actions`
|
|
871
902
|
* is set, so a hideTitle tabs-Section with actions isn't stranded. */
|
|
872
903
|
readonly actions?: ReactNode;
|
|
904
|
+
/** "settings-list": title + subtitle form a left header column and the
|
|
905
|
+
* children (field rows) the right one on `md:` and up. Default "stacked". */
|
|
906
|
+
readonly layout?: "stacked" | "settings-list";
|
|
873
907
|
/** "destructive" marks the Section as a warning/danger area (e.g. account
|
|
874
908
|
* deletion, restrict processing) — border color only, no content change.
|
|
875
909
|
* Default "default" (normal card border). */
|
|
@@ -920,6 +954,13 @@ export type GridProps = {
|
|
|
920
954
|
/** Fields flow left to right in wrapping rows sized by `GridCell.width`
|
|
921
955
|
* instead of equal-width tracks (screen forms and drawers). */
|
|
922
956
|
readonly flow?: boolean;
|
|
957
|
+
/** One full-width row per child, separated by hairlines — the field rows of
|
|
958
|
+
* a `settings-list` section. Wins over `columns`/`flow`. */
|
|
959
|
+
readonly list?: boolean;
|
|
960
|
+
/** Id of the element naming this grid (a Field label). Turns the grid into
|
|
961
|
+
* a `role="group"` so a Field whose control is a set of inputs (checkbox
|
|
962
|
+
* group) still gives screen readers a group name. */
|
|
963
|
+
readonly ariaLabelledBy?: string;
|
|
923
964
|
};
|
|
924
965
|
/** Width class of a field cell inside a `flow` grid: sized by what the field holds. */
|
|
925
966
|
export type FieldCellWidth = "text" | "number" | "money" | "date" | "timestamp" | "select" | "full" | "toggle";
|
|
@@ -1036,19 +1077,19 @@ export type ConfigSourceBadgeProps = {
|
|
|
1036
1077
|
* rule as ConfigCascadeView's toDisplayLevels. */
|
|
1037
1078
|
readonly screenScope?: ConfigScope;
|
|
1038
1079
|
};
|
|
1039
|
-
/**
|
|
1040
|
-
*
|
|
1041
|
-
*
|
|
1042
|
-
* split mode the screen owns the `expanded` state (both slots share it);
|
|
1043
|
-
* without `slot` the component renders both with its own state. */
|
|
1080
|
+
/** Cascade view for a config-edit field: one origin line ("Set for this
|
|
1081
|
+
* tenant", "Uses the default", …), a reset action for an own value, and a
|
|
1082
|
+
* disclosure listing every level. Owns its disclosure state. */
|
|
1044
1083
|
export type ConfigCascadeViewProps = {
|
|
1045
1084
|
readonly cascade: ConfigCascade;
|
|
1046
1085
|
readonly screenScope: ConfigScope;
|
|
1047
1086
|
readonly onReset?: (key: string, scope: ConfigScope) => void;
|
|
1048
1087
|
readonly qualifiedKey?: string;
|
|
1049
|
-
|
|
1050
|
-
readonly
|
|
1051
|
-
|
|
1088
|
+
/** The key declares `required: true` — a missing value reads "Not set. Required.". */
|
|
1089
|
+
readonly required?: boolean;
|
|
1090
|
+
/** Display form of a level value (e.g. a select's label instead of its raw
|
|
1091
|
+
* value). Absent = the raw value; a missing value keeps the no-value text. */
|
|
1092
|
+
readonly renderValue?: (value: string | number | boolean) => ReactNode;
|
|
1052
1093
|
};
|
|
1053
1094
|
/** Card-Slots — benannte Inhalts-Regionen, alle optional. Neue Slots später =
|
|
1054
1095
|
* rein additiv → Consumer müssen NIE migriert werden. `header` ist der Escape-
|
|
@@ -1058,9 +1099,9 @@ export type CardSlots = {
|
|
|
1058
1099
|
readonly header?: ReactNode;
|
|
1059
1100
|
readonly title?: ReactNode;
|
|
1060
1101
|
readonly subtitle?: ReactNode;
|
|
1061
|
-
/** Free-form block content
|
|
1062
|
-
*
|
|
1063
|
-
*
|
|
1102
|
+
/** Free-form block content in its own full-width row below the
|
|
1103
|
+
* title/subtitle/`headerActions` row, within the same header block — for a
|
|
1104
|
+
* host-supplied header (projectionDetail `slots.header`). */
|
|
1064
1105
|
readonly headerContent?: ReactNode;
|
|
1065
1106
|
/** Rechtsbündiger Header-Slot (Range-Umschalter, Filter, Badge …). */
|
|
1066
1107
|
readonly headerActions?: ReactNode;
|
|
@@ -1082,6 +1123,11 @@ export type CardOptions = {
|
|
|
1082
1123
|
/** false drops border, background, shadow and radius but keeps padding and
|
|
1083
1124
|
* header slots — a panel inside a host surface such as a tab panel. Default true. */
|
|
1084
1125
|
readonly framed?: boolean;
|
|
1126
|
+
/** The card is the whole screen body (secretMint reveal and done phase):
|
|
1127
|
+
* no frame, the shared screen insets and width of form screens, children
|
|
1128
|
+
* stacked with a gap. Without it such a screen sits raw against the
|
|
1129
|
+
* shell edge. Default false. */
|
|
1130
|
+
readonly screenBody?: boolean;
|
|
1085
1131
|
};
|
|
1086
1132
|
/** Generische Card-Chrome (border/bg/radius/shadow + Header/Body/Footer) als
|
|
1087
1133
|
* Primitive — eine Quelle für alle Card-Flächen, statt pro App nachgebaut.
|
|
@@ -1107,10 +1153,9 @@ export type ProgressProps = {
|
|
|
1107
1153
|
readonly testId?: string;
|
|
1108
1154
|
};
|
|
1109
1155
|
/** Wizard step overview — numbered chips, one per step label, with the
|
|
1110
|
-
* active step highlighted. With `onStepSelect`, completed steps
|
|
1111
|
-
*
|
|
1112
|
-
*
|
|
1113
|
-
* (kumiko-framework#1966). Implementations render a
|
|
1156
|
+
* active step highlighted. With `onStepSelect`, completed steps become
|
|
1157
|
+
* buttons (every non-current step with `selectableSteps: "all"`); the caller
|
|
1158
|
+
* owns the validation gate for forward jumps. Implementations render a
|
|
1114
1159
|
* narrow-viewport fallback showing `compactLabel` instead (caller
|
|
1115
1160
|
* supplies it pre-translated, e.g. "Step 2 of 5 · Industry") — which of
|
|
1116
1161
|
* the two is visible is a responsive layout choice owned by the
|
|
@@ -1120,6 +1165,12 @@ export type StepBarProps = {
|
|
|
1120
1165
|
readonly currentIndex: number;
|
|
1121
1166
|
readonly compactLabel: string;
|
|
1122
1167
|
readonly onStepSelect?: (index: number) => void;
|
|
1168
|
+
/** Explicit done state per step index. Absent: a step is done when its
|
|
1169
|
+
* index is below `currentIndex`. The current step is never done. */
|
|
1170
|
+
readonly doneSteps?: readonly boolean[];
|
|
1171
|
+
/** "done" (default): only done steps are jump targets. "all": every
|
|
1172
|
+
* non-current step is, when `onStepSelect` is set. */
|
|
1173
|
+
readonly selectableSteps?: "done" | "all";
|
|
1123
1174
|
/** "label" (default) swaps the step row for `compactLabel` on narrow
|
|
1124
1175
|
* viewports; "steps" keeps the row there too — for short wizards whose
|
|
1125
1176
|
* labels fit a phone and whose done steps must stay tappable. */
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Dispatcher, DispatcherError } from "@cosmicdrift/kumiko-headless";
|
|
2
|
+
import { type ReactNode } from "react";
|
|
3
|
+
export declare const SESSION_ENDED_ERROR_CODES: readonly ["session_invalid", "invalid_token", "missing_token", "unauthenticated"];
|
|
4
|
+
export declare function isSessionEndedError(error: Pick<DispatcherError, "code" | "httpStatus">): boolean;
|
|
5
|
+
export type SessionEndedSignal = {
|
|
6
|
+
notify(): void;
|
|
7
|
+
subscribe(listener: () => void): () => void;
|
|
8
|
+
};
|
|
9
|
+
export declare function createSessionEndedSignal(): SessionEndedSignal;
|
|
10
|
+
export declare function withSessionEndedDetection(dispatcher: Dispatcher, signal: SessionEndedSignal): Dispatcher;
|
|
11
|
+
export type SessionEndedSignalProviderProps = {
|
|
12
|
+
readonly signal: SessionEndedSignal;
|
|
13
|
+
readonly children: ReactNode;
|
|
14
|
+
};
|
|
15
|
+
export declare function SessionEndedSignalProvider({ signal, children, }: SessionEndedSignalProviderProps): ReactNode;
|
|
16
|
+
export declare function useSessionEndedSignal(): SessionEndedSignal | undefined;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext } from "react";
|
|
3
|
+
// Only auth-layer 401s end a session. Handlers never throw 401 otherwise and
|
|
4
|
+
// 403 means "authenticated but not allowed", which must not log the user out.
|
|
5
|
+
export const SESSION_ENDED_ERROR_CODES = [
|
|
6
|
+
"session_invalid",
|
|
7
|
+
"invalid_token",
|
|
8
|
+
"missing_token",
|
|
9
|
+
"unauthenticated",
|
|
10
|
+
];
|
|
11
|
+
export function isSessionEndedError(error) {
|
|
12
|
+
return (error.httpStatus === 401 &&
|
|
13
|
+
SESSION_ENDED_ERROR_CODES.some((sessionEndedCode) => sessionEndedCode === error.code));
|
|
14
|
+
}
|
|
15
|
+
function isSessionEndedThrow(thrown) {
|
|
16
|
+
if (typeof thrown !== "object" || thrown === null)
|
|
17
|
+
return false;
|
|
18
|
+
if (!("code" in thrown) || !("httpStatus" in thrown))
|
|
19
|
+
return false;
|
|
20
|
+
const { code, httpStatus } = thrown;
|
|
21
|
+
return typeof code === "string" && typeof httpStatus === "number"
|
|
22
|
+
? isSessionEndedError({ code, httpStatus })
|
|
23
|
+
: false;
|
|
24
|
+
}
|
|
25
|
+
export function createSessionEndedSignal() {
|
|
26
|
+
const listeners = new Set();
|
|
27
|
+
return {
|
|
28
|
+
notify() {
|
|
29
|
+
for (const listener of [...listeners])
|
|
30
|
+
listener();
|
|
31
|
+
},
|
|
32
|
+
subscribe(listener) {
|
|
33
|
+
listeners.add(listener);
|
|
34
|
+
return () => {
|
|
35
|
+
listeners.delete(listener);
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function withSessionEndedDetection(dispatcher, signal) {
|
|
41
|
+
async function detect(pending) {
|
|
42
|
+
const result = await pending;
|
|
43
|
+
if (!result.isSuccess && result.error !== undefined && isSessionEndedError(result.error)) {
|
|
44
|
+
signal.notify();
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
write: (type, payload, opts) => detect(dispatcher.write(type, payload, opts)),
|
|
50
|
+
query: (type, payload, opts) => detect(dispatcher.query(type, payload, opts)),
|
|
51
|
+
batch: (commands, opts) => detect(dispatcher.batch(commands, opts)),
|
|
52
|
+
async *stream(type, payload, opts) {
|
|
53
|
+
try {
|
|
54
|
+
yield* dispatcher.stream(type, payload, opts);
|
|
55
|
+
}
|
|
56
|
+
catch (thrown) {
|
|
57
|
+
if (isSessionEndedThrow(thrown))
|
|
58
|
+
signal.notify();
|
|
59
|
+
throw thrown;
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
statusStore: dispatcher.statusStore,
|
|
63
|
+
pendingWrites: () => dispatcher.pendingWrites(),
|
|
64
|
+
pendingFiles: () => dispatcher.pendingFiles(),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const SessionEndedSignalContext = createContext(undefined);
|
|
68
|
+
export function SessionEndedSignalProvider({ signal, children, }) {
|
|
69
|
+
return _jsx(SessionEndedSignalContext, { value: signal, children: children });
|
|
70
|
+
}
|
|
71
|
+
export function useSessionEndedSignal() {
|
|
72
|
+
return useContext(SessionEndedSignalContext);
|
|
73
|
+
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { DataTableSort } from "./primitives.js";
|
|
2
|
+
type SortValue = string | number | null;
|
|
2
3
|
/** Sorts `rows` by a `DataTableSort` against a field->accessor map. Unknown
|
|
3
4
|
* field or `sort === null` returns `rows` unchanged (no-op, not an error —
|
|
4
|
-
* callers pass whatever the DataTable reports).
|
|
5
|
-
|
|
5
|
+
* callers pass whatever the DataTable reports). `null` values sort last in
|
|
6
|
+
* both directions; strings compare locale-aware with numeric collation, so
|
|
7
|
+
* numeric strings (Postgres numeric/bigint) order by value. */
|
|
8
|
+
export declare function sortByAccessor<TRow>(rows: readonly TRow[], sort: DataTableSort | null, accessors: Readonly<Record<string, (row: TRow) => SortValue>>, locale?: string): readonly TRow[];
|
|
9
|
+
export {};
|
package/dist/sort-by-accessor.js
CHANGED
|
@@ -1,16 +1,26 @@
|
|
|
1
|
+
function compareNonNull(a, b, collator) {
|
|
2
|
+
if (typeof a === "number" && typeof b === "number")
|
|
3
|
+
return a - b;
|
|
4
|
+
return collator.compare(String(a), String(b));
|
|
5
|
+
}
|
|
1
6
|
/** Sorts `rows` by a `DataTableSort` against a field->accessor map. Unknown
|
|
2
7
|
* field or `sort === null` returns `rows` unchanged (no-op, not an error —
|
|
3
|
-
* callers pass whatever the DataTable reports).
|
|
4
|
-
|
|
8
|
+
* callers pass whatever the DataTable reports). `null` values sort last in
|
|
9
|
+
* both directions; strings compare locale-aware with numeric collation, so
|
|
10
|
+
* numeric strings (Postgres numeric/bigint) order by value. */
|
|
11
|
+
export function sortByAccessor(rows, sort, accessors, locale) {
|
|
5
12
|
if (sort === null)
|
|
6
13
|
return rows;
|
|
7
14
|
const accessor = accessors[sort.field];
|
|
8
15
|
if (accessor === undefined)
|
|
9
16
|
return rows;
|
|
10
17
|
const factor = sort.dir === "asc" ? 1 : -1;
|
|
18
|
+
const collator = new Intl.Collator(locale, { numeric: true, sensitivity: "base" });
|
|
11
19
|
return [...rows].sort((a, b) => {
|
|
12
20
|
const av = accessor(a);
|
|
13
21
|
const bv = accessor(b);
|
|
14
|
-
|
|
22
|
+
if (av === null || bv === null)
|
|
23
|
+
return av === bv ? 0 : av === null ? 1 : -1;
|
|
24
|
+
return factor * compareNonNull(av, bv, collator);
|
|
15
25
|
});
|
|
16
26
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-renderer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.335.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.335.0",
|
|
19
|
+
"@cosmicdrift/kumiko-headless": "0.335.0",
|
|
20
|
+
"@cosmicdrift/kumiko-types": "0.335.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.335.0"
|
|
32
32
|
},
|
|
33
33
|
"repository": {
|
|
34
34
|
"type": "git",
|
package/src/changes.json
CHANGED
|
@@ -1,4 +1,44 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.335.0",
|
|
4
|
+
"type": "fix",
|
|
5
|
+
"title": "secretMint reveal and confirm render as one screen form with the translated screen title; new RenderEdit props leadContent and i18nScreenId"
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"version": "0.335.0",
|
|
9
|
+
"type": "fix",
|
|
10
|
+
"title": "Router paths encode and decode each segment, so entity ids with slashes or spaces round-trip"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"version": "0.335.0",
|
|
14
|
+
"type": "fix",
|
|
15
|
+
"title": "Icon-only list and edit action buttons set a hover title"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"version": "0.335.0",
|
|
19
|
+
"type": "fix",
|
|
20
|
+
"title": "Secret delete confirmation, grouped-field conditions, scoped valid flag, tenant currency in reference-create dialog",
|
|
21
|
+
"detail": "Renderer review fixes. Deleting a secret on a secretsEdit screen now asks for confirmation first (new `config.secrets.deleteConfirm` key) and the delete button is disabled while a save or delete is running. The multiSelect checkbox group is exposed as a labelled group (`GridProps.ariaLabelledBy`). The inline reference-create dialog seeds `currency: { kind: \"tenant\" }` money fields from the tenant currency. Fields declared only inside a section `groups` entry now get their `visible`/`readOnly`/`required` conditions registered. `onChange`'s `valid` ignores issues on hidden fields and outside the `fields` scope, like submit does (shared `relevantFieldIssues` helper). Copy-link in the form footer keeps a 44px touch target on narrow viewports. A free-text sibling-field number unit longer than 8 characters is no longer rendered as a suffix."
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"version": "0.335.0",
|
|
25
|
+
"type": "fix",
|
|
26
|
+
"title": "Scoped validation matches exact paths, root refine issues are visible, tabs keep the active tab by id, update redirects use the saved FK, related list sorting handles numeric strings, nulls and locales",
|
|
27
|
+
"detail": "Form and list fixes. A scoped `validate()` entry such as `address.city` no longer reports sibling issues like `address.zip`. Root-level `.refine()` issues now show as a banner in `RenderEdit` instead of blocking submit silently. In tabs mode the active tab follows its id when a field change hides an earlier tab, and an explicit subtitle override is kept. An entityEdit update redirect with `idFrom` reads the saved projection, so a changed parent FK redirects to the new parent. A row-extractor key missing from the row no longer wipes the target field's default. `relatedList` client-side sorting orders numeric strings by value, puts empty values last and compares text locale-aware. A reference list facet warns in dev when the lookup hits its row cap."
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"version": "0.335.0",
|
|
31
|
+
"type": "fix",
|
|
32
|
+
"title": "writeForm section shows the error banner for issues no rendered field can display",
|
|
33
|
+
"detail": "A writeForm section keeps its error banner when the server reports an issue no rendered field can show (`version`, `id`, a root-level refine, a hidden field), instead of failing the save without any visible message."
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"version": "0.335.0",
|
|
37
|
+
"type": "breaking",
|
|
38
|
+
"title": "Wizard editing an existing record shows data-based done state and allows jumping to any step; delete record actions leave the screen",
|
|
39
|
+
"detail": "Update-mode wizards compute done per step from the record (valid, non-empty fields, or passed via Next) and make every non-current step a jump target; forward jumps run the current step's validate gate. `ExtensionSectionProps.reportStepComplete` lets extension steps report completeness. `StepBar` gets `doneSteps` and `selectableSteps`. A writeHandler record action with `redirect`, or a delete of the shown record, navigates away (returnTo, redirect, listScreenId, entity list) instead of refetching.",
|
|
40
|
+
"migration": "Extension wizard steps in an entityEdit that edits an existing record should call `reportStepComplete(true)` (usually from an effect) once they hold their data, otherwise the step bar shows them as not done until the user passes them with Next. This also applies to singleton wizards (e.g. a company-setup wizard), whose steps now show done by data and are all jump targets; tests that assumed back-only chips must be updated. A delete writeHandler action on a projectionDetail or entityEdit now navigates away after success (returnTo, else listScreenId or the entity's list) instead of refetching into \"record not found\"; set `redirect` to choose another target. List row actions are unchanged."
|
|
41
|
+
},
|
|
2
42
|
{
|
|
3
43
|
"version": "0.333.0",
|
|
4
44
|
"type": "improvement",
|