@cosmicdrift/kumiko-renderer 0.352.0 → 0.353.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/kumiko-screen.js +3 -2
- package/dist/app/projection-list-shim.d.ts +4 -3
- package/dist/app/projection-list-shim.js +9 -4
- package/dist/app/secret-mint-body.js +31 -8
- package/dist/components/render-edit.js +10 -7
- package/dist/hooks/use-reference-lookup.js +2 -1
- package/dist/i18n-defaults.js +3 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/primitives.d.ts +6 -3
- package/dist/primitives.js +8 -1
- package/package.json +5 -5
- package/src/changes.json +6 -0
|
@@ -319,7 +319,7 @@ export function literalCurrencyOverrides(fields) {
|
|
|
319
319
|
// names themselves because an edit form only needs the fetch for fields the
|
|
320
320
|
// record has no stored value for.
|
|
321
321
|
export function useMoneyCurrencyOverrides(fields, tenantFieldNames, fallback) {
|
|
322
|
-
const query = useQuery("config:query:values", {}, { enabled: tenantFieldNames.length > 0 });
|
|
322
|
+
const query = useQuery("config:query:values", { keys: [TENANT_CURRENCY_CONFIG_KEY] }, { enabled: tenantFieldNames.length > 0 });
|
|
323
323
|
const resolution = resolveTenantCurrency(tenantFieldNames, query, fallback);
|
|
324
324
|
const resolvedTenantCurrency = resolution.status === "ready" ? resolution.currency : undefined;
|
|
325
325
|
const overrides = useMemo(() => {
|
|
@@ -1830,7 +1830,8 @@ function headerStatusTone(header, record) {
|
|
|
1830
1830
|
const declared = header.statusTones !== undefined && Object.hasOwn(header.statusTones, value)
|
|
1831
1831
|
? header.statusTones[value]
|
|
1832
1832
|
: undefined;
|
|
1833
|
-
|
|
1833
|
+
const declaredTone = declared !== undefined ? statusToneForOptionTone(declared) : undefined;
|
|
1834
|
+
return declaredTone ?? statusToneForValue(value);
|
|
1834
1835
|
}
|
|
1835
1836
|
function resolveSubtitleHref(header, record) {
|
|
1836
1837
|
if (header?.subtitleHref === undefined)
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { EntityDefinition, EntityListScreenDefinition, ListColumnSpec, ProjectionListScreenDefinition } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
2
2
|
/** Minimal EntityDefinition from the column list: every field is a text
|
|
3
|
-
* field. `sortable` applies
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* field. `sortable` applies to all columns — buildAppSchema derives it from
|
|
4
|
+
* the query's Zod schema (`screen.sortable`, fw#2165); the query itself has
|
|
5
|
+
* no per-field server-sort guarantee, so a column opts out with
|
|
6
|
+
* `sortable: false` when the handler cannot order by it.
|
|
6
7
|
* computeListViewModel only reads `fields[<col>].type` — text is enough,
|
|
7
8
|
* presentation comes from the column renderer + explicit label. */
|
|
8
9
|
export declare function synthesizeProjectionEntity(columns: readonly ListColumnSpec[], sortable: boolean): EntityDefinition;
|
|
@@ -16,15 +16,20 @@
|
|
|
16
16
|
import { normalizeListColumn } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
17
17
|
const PROJECTION_PSEUDO_ENTITY = "__projection__";
|
|
18
18
|
/** Minimal EntityDefinition from the column list: every field is a text
|
|
19
|
-
* field. `sortable` applies
|
|
20
|
-
*
|
|
21
|
-
*
|
|
19
|
+
* field. `sortable` applies to all columns — buildAppSchema derives it from
|
|
20
|
+
* the query's Zod schema (`screen.sortable`, fw#2165); the query itself has
|
|
21
|
+
* no per-field server-sort guarantee, so a column opts out with
|
|
22
|
+
* `sortable: false` when the handler cannot order by it.
|
|
22
23
|
* computeListViewModel only reads `fields[<col>].type` — text is enough,
|
|
23
24
|
* presentation comes from the column renderer + explicit label. */
|
|
24
25
|
export function synthesizeProjectionEntity(columns, sortable) {
|
|
25
26
|
const fields = {};
|
|
26
27
|
for (const col of columns) {
|
|
27
|
-
|
|
28
|
+
const normalized = normalizeListColumn(col);
|
|
29
|
+
fields[normalized.field] = {
|
|
30
|
+
type: "text",
|
|
31
|
+
sortable: sortable && normalized.sortable !== false,
|
|
32
|
+
};
|
|
28
33
|
}
|
|
29
34
|
return { fields };
|
|
30
35
|
}
|
|
@@ -4,7 +4,7 @@ import { RenderEdit } from "../components/render-edit.js";
|
|
|
4
4
|
import { useTranslation } from "../i18n.js";
|
|
5
5
|
import { usePrimitives } from "../primitives.js";
|
|
6
6
|
import { synthesizeActionFormEntity, synthesizeActionFormScreen, synthesizeSecretMintConfirmScreen, } from "./action-form-shim.js";
|
|
7
|
-
import { buildInitialValues,
|
|
7
|
+
import { buildInitialValues, mergeSearchParamsIntoInitial, tenantCurrencyMoneyFieldNames, useMoneyCurrencyOverrides, } from "./kumiko-screen.js";
|
|
8
8
|
import { layoutFieldNames } from "./layout-fields.js";
|
|
9
9
|
import { useInitialValuesHandoff, useNav } from "./nav.js";
|
|
10
10
|
import { lastSegment } from "./qn.js";
|
|
@@ -26,6 +26,9 @@ function extractCarriedValues(data, carry) {
|
|
|
26
26
|
}
|
|
27
27
|
return carried;
|
|
28
28
|
}
|
|
29
|
+
// Entity-less forms have no `entity.defaultCurrency`; same fallback as actionForm.
|
|
30
|
+
const SECRET_MINT_CURRENCY_FALLBACK = "EUR";
|
|
31
|
+
const NO_FIELDS = {};
|
|
29
32
|
function isBlank(value) {
|
|
30
33
|
if (value === undefined || value === null || value === "")
|
|
31
34
|
return true;
|
|
@@ -50,17 +53,26 @@ export function SecretMintBody({ schema, screen, translate }) {
|
|
|
50
53
|
const synthEntity = useMemo(() => synthesizeActionFormEntity(screen.fields), [screen.fields]);
|
|
51
54
|
const synthScreen = useMemo(() => synthesizeActionFormScreen(screen), [screen]);
|
|
52
55
|
const handoffValues = useInitialValuesHandoff(screen.id);
|
|
56
|
+
const mintTenantCurrencyFields = useMemo(() => tenantCurrencyMoneyFieldNames(screen.fields), [screen.fields]);
|
|
57
|
+
const { overrides: mintCurrencyOverrides, loading: mintCurrencyLoading } = useMoneyCurrencyOverrides(screen.fields, mintTenantCurrencyFields, SECRET_MINT_CURRENCY_FALLBACK);
|
|
53
58
|
const initial = useMemo(() => mergeSearchParamsIntoInitial(screen.fields, {
|
|
54
59
|
searchParams: nav.searchParams,
|
|
55
60
|
urlPrefillFields: screen.urlPrefillFields,
|
|
56
61
|
renderableFields: layoutFieldNames(synthScreen),
|
|
57
62
|
// A mint form has no entity either, so its money fields name their own
|
|
58
|
-
// currency source (fw#2839).
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
63
|
+
// currency source (fw#2839, fw#2933).
|
|
64
|
+
...(mintCurrencyOverrides !== undefined && {
|
|
65
|
+
moneyCurrencyOverrides: mintCurrencyOverrides,
|
|
66
|
+
}),
|
|
62
67
|
...(handoffValues !== undefined && { handoffValues }),
|
|
63
|
-
}), [
|
|
68
|
+
}), [
|
|
69
|
+
screen.fields,
|
|
70
|
+
screen.urlPrefillFields,
|
|
71
|
+
nav.searchParams,
|
|
72
|
+
synthScreen,
|
|
73
|
+
handoffValues,
|
|
74
|
+
mintCurrencyOverrides,
|
|
75
|
+
]);
|
|
64
76
|
const [revealed, setRevealed] = useState(null);
|
|
65
77
|
const [done, setDone] = useState(false);
|
|
66
78
|
// The secret was minted server-side but none of the declared reveal fields
|
|
@@ -72,9 +84,12 @@ export function SecretMintBody({ schema, screen, translate }) {
|
|
|
72
84
|
const confirm = screen.confirm;
|
|
73
85
|
const confirmEntity = useMemo(() => (confirm !== undefined ? synthesizeActionFormEntity(confirm.fields) : undefined), [confirm]);
|
|
74
86
|
const confirmScreen = useMemo(() => (confirm !== undefined ? synthesizeSecretMintConfirmScreen(screen, confirm) : undefined), [screen, confirm]);
|
|
87
|
+
const confirmFields = confirm?.fields ?? NO_FIELDS;
|
|
88
|
+
const confirmTenantCurrencyFields = useMemo(() => tenantCurrencyMoneyFieldNames(confirmFields), [confirmFields]);
|
|
89
|
+
const { overrides: confirmCurrencyOverrides, loading: confirmCurrencyLoading } = useMoneyCurrencyOverrides(confirmFields, confirmTenantCurrencyFields, SECRET_MINT_CURRENCY_FALLBACK);
|
|
75
90
|
const confirmInitial = useMemo(() => confirm !== undefined
|
|
76
|
-
? buildInitialValues(confirm.fields, undefined,
|
|
77
|
-
: undefined, [confirm]);
|
|
91
|
+
? buildInitialValues(confirm.fields, undefined, confirmCurrencyOverrides)
|
|
92
|
+
: undefined, [confirm, confirmCurrencyOverrides]);
|
|
78
93
|
const handleSubmitted = useCallback((result) => {
|
|
79
94
|
if (!result.isSuccess)
|
|
80
95
|
return;
|
|
@@ -166,9 +181,17 @@ export function SecretMintBody({ schema, screen, translate }) {
|
|
|
166
181
|
// keys stop the mint and confirm forms sharing one instance (and its
|
|
167
182
|
// submit state) across phases.
|
|
168
183
|
if (confirm !== undefined && confirmEntity !== undefined && confirmScreen !== undefined) {
|
|
184
|
+
// Hold until a tenant-declared currency lands, else the form would seed (and
|
|
185
|
+
// on a fast click submit) the fallback currency (fw#2933).
|
|
186
|
+
if (confirmCurrencyLoading) {
|
|
187
|
+
return (_jsx(Banner, { padded: true, variant: "loading", testId: "kumiko-screen-loading", children: "Loading\u2026" }));
|
|
188
|
+
}
|
|
169
189
|
return (_jsx(RenderEdit, { screen: confirmScreen, i18nScreenId: screen.id, entity: confirmEntity, featureName: schema.featureName, initial: confirmInitial ?? {}, writeCommand: confirm.handler, payloadMode: "values", buildPayload: (snapshot) => ({ ...snapshot.values, ...carriedRef.current }), onSubmit: handleConfirmSubmitted, onCancel: handleCancel ?? restartMint, fillScreenHeight: true, leadContent: _jsx(Section, { testId: "kumiko-screen-secret-mint-card", children: revealContent }), ...(translate !== undefined && { translate }), ...(confirm.submitLabel !== undefined && { submitLabel: confirm.submitLabel }) }, "confirm"));
|
|
170
190
|
}
|
|
171
191
|
return (_jsxs(Card, { options: { screenBody: true }, testId: "kumiko-screen-secret-mint-card", children: [revealContent, _jsx(Button, { type: "button", variant: "primary", onClick: finishMint, testId: "kumiko-screen-secret-mint-confirm", children: effectiveTranslate(screen.reveal.confirmLabel ?? "kumiko.secretMint.confirm") })] }));
|
|
172
192
|
}
|
|
193
|
+
if (mintCurrencyLoading) {
|
|
194
|
+
return (_jsx(Banner, { padded: true, variant: "loading", testId: "kumiko-screen-loading", children: "Loading\u2026" }));
|
|
195
|
+
}
|
|
173
196
|
return (_jsx(RenderEdit, { screen: synthScreen, entity: synthEntity, featureName: schema.featureName, initial: initial, extensionInitialValues: initial, writeCommand: screen.handler, payloadMode: "values", onSubmit: handleSubmitted, fillScreenHeight: true, ...(handleCancel !== undefined && { onCancel: handleCancel }), ...(translate !== undefined && { translate }), ...(screen.submitLabel !== undefined && { submitLabel: screen.submitLabel }) }, "mint"));
|
|
174
197
|
}
|
|
@@ -752,10 +752,10 @@ export function RenderEdit(props) {
|
|
|
752
752
|
}
|
|
753
753
|
const secretFieldNames = new Set(vm.sections.flatMap((section) => section.kind === "fields"
|
|
754
754
|
? section.fields
|
|
755
|
-
.filter((f) => f.format === "password" || f.writeOnly === true)
|
|
755
|
+
.filter((f) => f.format === "password" || f.writeOnly === true || f.sensitive === true)
|
|
756
756
|
.map((f) => f.field)
|
|
757
757
|
: []));
|
|
758
|
-
// A password or
|
|
758
|
+
// A password, writeOnly or sensitive field never enters the persisted draft blob — it would be stored in the clear and restored on resume.
|
|
759
759
|
const draftValues = Object.fromEntries(Object.entries(controller.getSnapshot().values).filter(([field]) => !secretFieldNames.has(field)));
|
|
760
760
|
void dispatcher
|
|
761
761
|
.write(FORM_DRAFT_SAVE, {
|
|
@@ -1131,7 +1131,7 @@ export function RenderEdit(props) {
|
|
|
1131
1131
|
footerActions?.map((action) => (_jsx(Button, { type: "button", variant: action.variant ?? "secondary", ...(action.icon !== undefined && { icon: action.icon }), disabled: isSubmitting || disabled, onClick: () => {
|
|
1132
1132
|
controller.setValues(action.patch); // @cast-boundary form-values
|
|
1133
1133
|
void handleSubmit();
|
|
1134
|
-
}, testId: `render-edit-footer-action-${action.id}`, children: translate(action.label) }, action.id))), hasDirtyFooter && showsSubmit && unsavedCount !== undefined && unsavedCount > 0 && (_jsx(Button, { type: "button", variant: "ghost", disabled: isSubmitting || disabled, onClick: () => controller.reset(), testId: "render-edit-discard", [STICKY_PRIMARY_ACTION_PROP]: true, children: translate("kumiko.form.discard") })), showSubmit && (_jsx(Button, { type: "submit", disabled: (snapshot.isUnchanged && !extensionDirty && !
|
|
1134
|
+
}, testId: `render-edit-footer-action-${action.id}`, children: translate(action.label) }, action.id))), hasDirtyFooter && showsSubmit && unsavedCount !== undefined && unsavedCount > 0 && (_jsx(Button, { type: "button", variant: "ghost", disabled: isSubmitting || disabled, onClick: () => controller.reset(), testId: "render-edit-discard", [STICKY_PRIMARY_ACTION_PROP]: true, children: translate("kumiko.form.discard") })), showSubmit && (_jsx(Button, { type: "submit", disabled: (snapshot.isUnchanged && !extensionDirty && !isFieldlessSubmitForm && !insideDrawer) ||
|
|
1135
1135
|
isSubmitting ||
|
|
1136
1136
|
disabled, loading: isSubmitting, variant: submitVariant ?? "primary", testId: "render-edit-submit", children: translate(submitLabel ??
|
|
1137
1137
|
(isWizard
|
|
@@ -1367,10 +1367,13 @@ export function RenderEdit(props) {
|
|
|
1367
1367
|
}
|
|
1368
1368
|
// No section title here (fw#3218) — the Tab strip right above
|
|
1369
1369
|
// already names this panel, so a repeated Card title would just
|
|
1370
|
-
// duplicate it. The
|
|
1371
|
-
//
|
|
1372
|
-
const cardEl = (_jsx(Card, { options: { framed: false }, ...(sectionActionsEl !== undefined && {
|
|
1373
|
-
slots: {
|
|
1370
|
+
// duplicate it. The header row only exists to carry the section
|
|
1371
|
+
// description and actions, never the title text.
|
|
1372
|
+
const cardEl = (_jsx(Card, { options: { framed: false }, ...((sectionActionsEl !== undefined || section.description !== undefined) && {
|
|
1373
|
+
slots: {
|
|
1374
|
+
...(section.description !== undefined && { subtitle: section.description }),
|
|
1375
|
+
...(sectionActionsEl !== undefined && { headerActions: sectionActionsEl }),
|
|
1376
|
+
},
|
|
1374
1377
|
}), testId: `section-${sectionKey}`, children: renderFieldGrid(section.fields, section.columns, `${sectionKey}-grid`) }, sectionKey));
|
|
1375
1378
|
return wrapWizardStep(sectionKey, cardEl);
|
|
1376
1379
|
}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
// internen Cache shared zwischen List + Edit-Form für die gleiche
|
|
18
18
|
// Entity, Live-Updates kommen via SSE (use-query-live).
|
|
19
19
|
import { REFERENCE_LOOKUP_SOURCES, SYSTEM_REFERENCE_LABELS, } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
20
|
+
import { PII_ERASED_SENTINEL } from "@cosmicdrift/kumiko-types/kms-adapter-types";
|
|
20
21
|
import { useMemo } from "react";
|
|
21
22
|
import { toKebab } from "../app/qn.js";
|
|
22
23
|
import { useTranslation } from "../i18n.js";
|
|
@@ -61,7 +62,7 @@ export function useReferenceLookup(featureName, refEntity, labelField) {
|
|
|
61
62
|
continue;
|
|
62
63
|
const idStr = String(id);
|
|
63
64
|
const label = row[labelKey] ?? id;
|
|
64
|
-
out.set(idStr, String(label));
|
|
65
|
+
out.set(idStr, label === PII_ERASED_SENTINEL ? translate("kumiko.pii.erased") : String(label));
|
|
65
66
|
}
|
|
66
67
|
// System-scope ids (e.g. SYSTEM_TENANT_ID) never have a backing row, so
|
|
67
68
|
// the bulk lookup above never covers them — inject the label directly.
|
package/dist/i18n-defaults.js
CHANGED
|
@@ -41,6 +41,7 @@ export const kumikoDefaultTranslations = {
|
|
|
41
41
|
"kumiko.field.unsupported": "This field type can't be edited here yet.",
|
|
42
42
|
"kumiko.field.writeOnly.remove": "Remove stored value",
|
|
43
43
|
"kumiko.field.writeOnly.set": "Set",
|
|
44
|
+
"kumiko.pii.erased": "Deleted",
|
|
44
45
|
"kumiko.field.writeOnly.setPlaceholder": "Set — leave empty to keep it",
|
|
45
46
|
"kumiko.field.writeOnly.undo": "Undo",
|
|
46
47
|
"kumiko.field.writeOnly.willRemove": "Removed on save",
|
|
@@ -107,6 +108,8 @@ export const kumikoDefaultTranslations = {
|
|
|
107
108
|
"kumiko.dashboard.panel.error.title": "{label} could not be loaded",
|
|
108
109
|
"kumiko.dashboard.time-range": "Time range",
|
|
109
110
|
"kumiko.screen.unavailable": "This page is not available right now.",
|
|
111
|
+
"kumiko.dashboard.brush-end": "Window end",
|
|
112
|
+
"kumiko.dashboard.brush-start": "Window start",
|
|
110
113
|
"kumiko.dashboard.today": "Today",
|
|
111
114
|
"kumiko.dashboard.updated-at": "As of {time}",
|
|
112
115
|
"kumiko.app-boot.loading": "Loading…",
|
package/dist/index.d.ts
CHANGED
|
@@ -72,7 +72,7 @@ export { kumikoDefaultTranslations } from "./i18n-defaults.js";
|
|
|
72
72
|
export { InsideDrawerProvider, useInsideDrawer } from "./inside-drawer.js";
|
|
73
73
|
export { PageHeaderCompactProvider, PageHeaderSlotAvailableProvider, usePageHeaderCompact, usePageHeaderSlotAvailable, } from "./page-header-slot.js";
|
|
74
74
|
export type { ActionMenuItemSpec, ActionOverflowMenuProps, AppPrimitives, BannerProps, ButtonProps, CardOptions, CardProps, CardSlots, CopyButtonProps, CorePrimitives, DataTableDateRangeFacet, DataTableFacet, DataTableFacetChip, DataTableProps, DataTableRowAction, DataTableRowActionMode, DataTableRowGrouping, DataTableSort, DataTableSortDir, DialogProps, DrawerProps, EmbeddedListCellType, EmbeddedListColumn, EmbeddedListInputProps, EmbeddedListTotal, FieldCellWidth, FieldProps, FillContainerProps, FooterActionMarker, FooterActionRole, FormProps, FormSectionNavItem, FormWidth, GridCellProps, GridProps, HeadingProps, InputProps, JsonViewProps, LightboxImage, LightboxProps, LinkProps, MetricBandProps, MetricProps, ModalProps, PageHeaderProps, PrimitivesProviderProps, PrimitivesRegistry, ProgressProps, ProgressTone, PromoPanelAction, PromoPanelProps, RuntimeRenderer, SecretRevealProps, SecretRevealValue, SectionProps, ShareButtonProps, StatusBadgeProps, StatusTone, StepBarProps, StickyActionBarProps, StickyPrimaryActionMarker, TabsProps, TextProps, TrailingInputAction, WizardStepGroupProps, } from "./primitives.js";
|
|
75
|
-
export { FOOTER_ACTION_ROLE_PROP, NARROW_LABEL_PROP, PrimitivesProvider, STICKY_PRIMARY_ACTION_PROP, shouldRenderActionsIconOnly, statusToneForOptionTone, statusToneForValue, usePrimitives, } from "./primitives.js";
|
|
75
|
+
export { FOOTER_ACTION_ROLE_PROP, NARROW_LABEL_PROP, PrimitivesProvider, STICKY_PRIMARY_ACTION_PROP, shouldRenderActionsIconOnly, statusToneForOptionTone, statusToneForValue, useOptionalPrimitives, usePrimitives, } from "./primitives.js";
|
|
76
76
|
export type { SessionEndedSignal, SessionEndedSignalProviderProps, } from "./session/session-ended.js";
|
|
77
77
|
export { createSessionEndedSignal, isSessionEndedError, SESSION_ENDED_ERROR_CODES, SessionEndedSignalProvider, useSessionEndedSignal, withSessionEndedDetection, } from "./session/session-ended.js";
|
|
78
78
|
export { buildWhatsAppShareUrl } from "./share-links.js";
|
package/dist/index.js
CHANGED
|
@@ -52,7 +52,7 @@ export { createStaticLocaleResolver, FormalityProvider, LocaleProvider, mergeTra
|
|
|
52
52
|
export { kumikoDefaultTranslations } from "./i18n-defaults.js";
|
|
53
53
|
export { InsideDrawerProvider, useInsideDrawer } from "./inside-drawer.js";
|
|
54
54
|
export { PageHeaderCompactProvider, PageHeaderSlotAvailableProvider, usePageHeaderCompact, usePageHeaderSlotAvailable, } from "./page-header-slot.js";
|
|
55
|
-
export { FOOTER_ACTION_ROLE_PROP, NARROW_LABEL_PROP, PrimitivesProvider, STICKY_PRIMARY_ACTION_PROP, shouldRenderActionsIconOnly, statusToneForOptionTone, statusToneForValue, usePrimitives, } from "./primitives.js";
|
|
55
|
+
export { FOOTER_ACTION_ROLE_PROP, NARROW_LABEL_PROP, PrimitivesProvider, STICKY_PRIMARY_ACTION_PROP, shouldRenderActionsIconOnly, statusToneForOptionTone, statusToneForValue, useOptionalPrimitives, usePrimitives, } from "./primitives.js";
|
|
56
56
|
export { createSessionEndedSignal, isSessionEndedError, SESSION_ENDED_ERROR_CODES, SessionEndedSignalProvider, useSessionEndedSignal, withSessionEndedDetection, } from "./session/session-ended.js";
|
|
57
57
|
export { buildWhatsAppShareUrl } from "./share-links.js";
|
|
58
58
|
export { sortByAccessor } from "./sort-by-accessor.js";
|
package/dist/primitives.d.ts
CHANGED
|
@@ -1034,8 +1034,9 @@ export type DialogProps = {
|
|
|
1034
1034
|
readonly cancelLabel?: string;
|
|
1035
1035
|
/** `default` = Confirm primary, `danger` = Confirm danger. */
|
|
1036
1036
|
readonly variant?: "default" | "danger";
|
|
1037
|
-
/** Which button takes focus on open. Default: Confirm for a bare dialog
|
|
1038
|
-
*
|
|
1037
|
+
/** Which button takes focus on open. Default: Confirm for a bare dialog
|
|
1038
|
+
* (Cancel when `variant` is `danger`), the browser/Radix default when
|
|
1039
|
+
* `children` are present. `"cancel"` for
|
|
1039
1040
|
* dialogs where the safe choice must be the one Enter triggers. */
|
|
1040
1041
|
readonly initialFocus?: "confirm" | "cancel";
|
|
1041
1042
|
/** Wird gefeuert wenn der User Confirm drückt. Async-Funktion ist
|
|
@@ -1270,7 +1271,7 @@ export type StatusBadgeProps = {
|
|
|
1270
1271
|
readonly tone?: StatusTone;
|
|
1271
1272
|
readonly testId?: string;
|
|
1272
1273
|
};
|
|
1273
|
-
export declare function statusToneForOptionTone(tone: SelectOptionTone): StatusTone;
|
|
1274
|
+
export declare function statusToneForOptionTone(tone: SelectOptionTone): StatusTone | undefined;
|
|
1274
1275
|
export declare function statusToneForValue(value: string): StatusTone | undefined;
|
|
1275
1276
|
/** Compact label/value tile (record-detail metrics band). `testId` is the
|
|
1276
1277
|
* tile's own id — the impl derives `${testId}-label`/`${testId}-value` for
|
|
@@ -1523,4 +1524,6 @@ export type PrimitivesProviderProps = {
|
|
|
1523
1524
|
readonly value: PrimitivesRegistry;
|
|
1524
1525
|
};
|
|
1525
1526
|
export declare function PrimitivesProvider({ children, value }: PrimitivesProviderProps): ReactNode;
|
|
1527
|
+
/** Like `usePrimitives`, but `undefined` outside a provider — for components that also render standalone. */
|
|
1528
|
+
export declare function useOptionalPrimitives(): PrimitivesRegistry | undefined;
|
|
1526
1529
|
export declare function usePrimitives(): PrimitivesRegistry;
|
package/dist/primitives.js
CHANGED
|
@@ -52,8 +52,11 @@ const STATUS_TONE_BY_OPTION_TONE = {
|
|
|
52
52
|
bad: "bad",
|
|
53
53
|
neutral: "muted",
|
|
54
54
|
};
|
|
55
|
+
// Own-key check: an unknown tone from an untyped definition must yield undefined so callers fall back to the value heuristic.
|
|
55
56
|
export function statusToneForOptionTone(tone) {
|
|
56
|
-
return STATUS_TONE_BY_OPTION_TONE
|
|
57
|
+
return Object.hasOwn(STATUS_TONE_BY_OPTION_TONE, tone)
|
|
58
|
+
? STATUS_TONE_BY_OPTION_TONE[tone]
|
|
59
|
+
: undefined;
|
|
57
60
|
}
|
|
58
61
|
export function statusToneForValue(value) {
|
|
59
62
|
const slug = value
|
|
@@ -69,6 +72,10 @@ const PrimitivesContext = createContext(undefined);
|
|
|
69
72
|
export function PrimitivesProvider({ children, value }) {
|
|
70
73
|
return _jsx(PrimitivesContext.Provider, { value: value, children: children });
|
|
71
74
|
}
|
|
75
|
+
/** Like `usePrimitives`, but `undefined` outside a provider — for components that also render standalone. */
|
|
76
|
+
export function useOptionalPrimitives() {
|
|
77
|
+
return useContext(PrimitivesContext);
|
|
78
|
+
}
|
|
72
79
|
export function usePrimitives() {
|
|
73
80
|
const registry = useContext(PrimitivesContext);
|
|
74
81
|
if (registry === undefined) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-renderer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.353.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.353.0",
|
|
19
|
+
"@cosmicdrift/kumiko-headless": "0.353.0",
|
|
20
|
+
"@cosmicdrift/kumiko-types": "0.353.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.353.0"
|
|
32
32
|
},
|
|
33
33
|
"repository": {
|
|
34
34
|
"type": "git",
|
package/src/changes.json
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.353.0",
|
|
4
|
+
"type": "fix",
|
|
5
|
+
"title": "secretMint with tenant currency waits for the tenant currency in mint and confirm step",
|
|
6
|
+
"detail": "Number inputs no longer keep a typed prefix, secretMint honours tenant currency, refEntity respects real field types\nA `NumberInput` draft that does not parse (\"12abc\", \"12,5\" on an integer field) now clears the value instead of leaving the last parseable prefix (\"12\") to be submitted. A `secretMint` screen with `currency: { kind: \"tenant\" }` money fields now waits for the tenant currency in both the mint and the confirm step, like `actionForm`, instead of seeding a bare `0`. `refEntity` on an `entityList` column or `entityEdit` field no longer overrides a real non-text entity field (a multi-reference stays multi, a sortable column stays sortable). The Tailwind scan skips compiled `__tests__` in `dist`."
|
|
7
|
+
},
|
|
2
8
|
{
|
|
3
9
|
"version": "0.352.0",
|
|
4
10
|
"type": "improvement",
|