@cosmicdrift/kumiko-renderer 0.389.0 → 0.390.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/dist/app/projection-detail-body.js +59 -394
- package/dist/app/projection-detail-header-model.d.ts +11 -0
- package/dist/app/projection-detail-header-model.js +33 -0
- package/dist/app/projection-detail-header-region.d.ts +28 -0
- package/dist/app/projection-detail-header-region.js +81 -0
- package/dist/app/projection-detail-header.d.ts +29 -0
- package/dist/app/projection-detail-header.js +84 -0
- package/dist/app/projection-detail-navigation.d.ts +13 -0
- package/dist/app/projection-detail-navigation.js +91 -0
- package/dist/app/projection-detail-states.d.ts +22 -0
- package/dist/app/projection-detail-states.js +26 -0
- package/dist/app/projection-drawer-host.d.ts +12 -0
- package/dist/app/projection-drawer-host.js +10 -0
- package/dist/app/projection-list-body.d.ts +1 -1
- package/dist/app/projection-list-body.js +69 -189
- package/dist/app/projection-list-render-props.d.ts +56 -0
- package/dist/app/projection-list-render-props.js +47 -0
- package/dist/app/projection-list-render.d.ts +26 -0
- package/dist/app/projection-list-render.js +20 -0
- package/dist/app/projection-list-row-click.d.ts +12 -0
- package/dist/app/projection-list-row-click.js +14 -0
- package/dist/app/use-projection-detail-actions.d.ts +35 -0
- package/dist/app/use-projection-detail-actions.js +156 -0
- package/dist/app/use-projection-detail-data.d.ts +19 -0
- package/dist/app/use-projection-detail-data.js +47 -0
- package/dist/app/use-projection-detail-layout.d.ts +17 -0
- package/dist/app/use-projection-detail-layout.js +23 -0
- package/dist/app/use-projection-list-actions.d.ts +31 -0
- package/dist/app/use-projection-list-actions.js +73 -0
- package/dist/app/use-projection-list-data.d.ts +20 -0
- package/dist/app/use-projection-list-data.js +26 -0
- package/dist/app/use-projection-list-definitions.d.ts +11 -0
- package/dist/app/use-projection-list-definitions.js +20 -0
- package/dist/app/use-projection-list-query.d.ts +28 -0
- package/dist/app/use-projection-list-query.js +83 -0
- package/dist/components/render-edit-banners.d.ts +12 -0
- package/dist/components/render-edit-banners.js +90 -0
- package/dist/components/render-edit-draft-save-helpers.d.ts +19 -0
- package/dist/components/render-edit-draft-save-helpers.js +42 -0
- package/dist/components/render-edit-field-grid.d.ts +4 -0
- package/dist/components/render-edit-field-grid.js +40 -0
- package/dist/components/render-edit-fields-section.d.ts +4 -0
- package/dist/components/render-edit-fields-section.js +76 -0
- package/dist/components/render-edit-form-actions.d.ts +4 -0
- package/dist/components/render-edit-form-actions.js +66 -0
- package/dist/components/render-edit-header-actions.d.ts +11 -0
- package/dist/components/render-edit-header-actions.js +120 -0
- package/dist/components/render-edit-layout-flags.d.ts +29 -0
- package/dist/components/render-edit-layout-flags.js +106 -0
- package/dist/components/render-edit-layout.d.ts +4 -0
- package/dist/components/render-edit-layout.js +79 -0
- package/dist/components/render-edit-render-model.d.ts +63 -0
- package/dist/components/render-edit-render-model.js +14 -0
- package/dist/components/render-edit-section-kinds.d.ts +4 -0
- package/dist/components/render-edit-section-kinds.js +115 -0
- package/dist/components/render-edit-submit-result.d.ts +8 -0
- package/dist/components/render-edit-submit-result.js +77 -0
- package/dist/components/render-edit-submit.d.ts +7 -0
- package/dist/components/render-edit-submit.js +113 -0
- package/dist/components/render-edit-titles.d.ts +17 -0
- package/dist/components/render-edit-titles.js +99 -0
- package/dist/components/render-edit-wizard-navigation.d.ts +11 -0
- package/dist/components/render-edit-wizard-navigation.js +115 -0
- package/dist/components/render-edit.d.ts +2 -2
- package/dist/components/render-edit.js +63 -1128
- package/dist/components/render-field-choice-inputs.d.ts +7 -0
- package/dist/components/render-field-choice-inputs.js +52 -0
- package/dist/components/render-field-control.d.ts +12 -0
- package/dist/components/render-field-control.js +93 -0
- package/dist/components/render-field-display-text.d.ts +4 -0
- package/dist/components/render-field-display-text.js +48 -0
- package/dist/components/render-field-fill-action.d.ts +13 -0
- package/dist/components/render-field-fill-action.js +63 -0
- package/dist/components/render-field-input-shared.d.ts +39 -0
- package/dist/components/render-field-input-shared.js +20 -0
- package/dist/components/render-field-input.d.ts +3 -0
- package/dist/components/render-field-input.js +34 -0
- package/dist/components/render-field-meta.d.ts +6 -0
- package/dist/components/render-field-meta.js +51 -0
- package/dist/components/render-field-read-only.d.ts +11 -0
- package/dist/components/render-field-read-only.js +75 -0
- package/dist/components/render-field-reference.d.ts +15 -0
- package/dist/components/render-field-reference.js +103 -0
- package/dist/components/render-field-scalar-inputs.d.ts +10 -0
- package/dist/components/render-field-scalar-inputs.js +35 -0
- package/dist/components/render-field-text-inputs.d.ts +3 -0
- package/dist/components/render-field-text-inputs.js +34 -0
- package/dist/components/render-field-values.d.ts +18 -0
- package/dist/components/render-field-values.js +111 -0
- package/dist/components/render-field-write-only.d.ts +9 -0
- package/dist/components/render-field-write-only.js +29 -0
- package/dist/components/render-field.d.ts +2 -2
- package/dist/components/render-field.js +39 -670
- package/dist/components/render-list-header-slot.d.ts +5 -0
- package/dist/components/render-list-header-slot.js +30 -0
- package/dist/components/render-list-reference-bridge.d.ts +3 -0
- package/dist/components/render-list-reference-bridge.js +25 -0
- package/dist/components/render-list-reference-columns.d.ts +25 -0
- package/dist/components/render-list-reference-columns.js +137 -0
- package/dist/components/render-list-table-props.d.ts +52 -0
- package/dist/components/render-list-table-props.js +53 -0
- package/dist/components/render-list-toolbar-model.d.ts +42 -0
- package/dist/components/render-list-toolbar-model.js +82 -0
- package/dist/components/render-list-toolbar.d.ts +38 -0
- package/dist/components/render-list-toolbar.js +40 -0
- package/dist/components/render-list-types.d.ts +113 -0
- package/dist/components/render-list-types.js +1 -0
- package/dist/components/render-list.d.ts +4 -113
- package/dist/components/render-list.js +91 -307
- package/dist/components/use-reference-input-state.d.ts +20 -0
- package/dist/components/use-reference-input-state.js +85 -0
- package/dist/components/use-render-edit-controlled-sync.d.ts +3 -0
- package/dist/components/use-render-edit-controlled-sync.js +26 -0
- package/dist/components/use-render-edit-draft-save.d.ts +29 -0
- package/dist/components/use-render-edit-draft-save.js +141 -0
- package/dist/components/use-render-edit-form.d.ts +10 -0
- package/dist/components/use-render-edit-form.js +101 -0
- package/dist/components/use-render-edit-sections.d.ts +15 -0
- package/dist/components/use-render-edit-sections.js +100 -0
- package/dist/components/use-render-edit-state.d.ts +65 -0
- package/dist/components/use-render-edit-state.js +158 -0
- package/dist/components/use-render-edit-steps.d.ts +13 -0
- package/dist/components/use-render-edit-steps.js +52 -0
- package/dist/components/use-render-list-columns.d.ts +18 -0
- package/dist/components/use-render-list-columns.js +41 -0
- package/dist/components/use-render-list-search.d.ts +4 -0
- package/dist/components/use-render-list-search.js +50 -0
- package/dist/primitives.d.ts +1 -1
- package/dist/web/app/dashboard-body.d.ts +1 -1
- package/dist/web/app/dashboard-body.js +42 -246
- package/dist/web/app/dashboard-header.d.ts +34 -0
- package/dist/web/app/dashboard-header.js +58 -0
- package/dist/web/app/dashboard-screen-controls.d.ts +11 -0
- package/dist/web/app/dashboard-screen-controls.js +42 -0
- package/dist/web/app/dashboard-screen-tiles.d.ts +8 -0
- package/dist/web/app/dashboard-screen-tiles.js +42 -0
- package/dist/web/app/dashboard-visibility.d.ts +42 -0
- package/dist/web/app/dashboard-visibility.js +102 -0
- package/dist/web/app/use-dashboard-screen-state.d.ts +35 -0
- package/dist/web/app/use-dashboard-screen-state.js +69 -0
- package/dist/web/primitives/data-table/actions-column.js +6 -5
- package/dist/web/primitives/data-table/card-body.d.ts +6 -0
- package/dist/web/primitives/data-table/card-body.js +32 -0
- package/dist/web/primitives/data-table/card-row.d.ts +5 -0
- package/dist/web/primitives/data-table/card-row.js +31 -0
- package/dist/web/primitives/data-table/card-view.d.ts +3 -0
- package/dist/web/primitives/data-table/card-view.js +13 -0
- package/dist/web/primitives/data-table/column-resize-handle.js +3 -1
- package/dist/web/primitives/data-table/data-cell-props.d.ts +7 -0
- package/dist/web/primitives/data-table/data-cell-props.js +8 -0
- package/dist/web/primitives/data-table/row-render-parts.d.ts +8 -0
- package/dist/web/primitives/data-table/row-render-parts.js +35 -0
- package/dist/web/primitives/data-table/table-body.d.ts +3 -0
- package/dist/web/primitives/data-table/table-body.js +32 -0
- package/dist/web/primitives/data-table/table-content.d.ts +3 -0
- package/dist/web/primitives/data-table/table-content.js +41 -0
- package/dist/web/primitives/data-table/table-data-cell.d.ts +4 -0
- package/dist/web/primitives/data-table/table-data-cell.js +25 -0
- package/dist/web/primitives/data-table/table-header-row.d.ts +3 -0
- package/dist/web/primitives/data-table/table-header-row.js +21 -0
- package/dist/web/primitives/data-table/table-view.d.ts +3 -0
- package/dist/web/primitives/data-table/table-view.js +25 -0
- package/dist/web/primitives/data-table/toolbar.d.ts +6 -0
- package/dist/web/primitives/data-table/toolbar.js +53 -0
- package/dist/web/primitives/data-table/use-data-table-state.d.ts +44 -0
- package/dist/web/primitives/data-table/use-data-table-state.js +123 -0
- package/dist/web/primitives/data-table.d.ts +3 -4
- package/dist/web/primitives/data-table.js +32 -252
- package/dist/web/primitives/file-value-list.js +1 -1
- package/dist/web/primitives/form-footer-parts.d.ts +25 -0
- package/dist/web/primitives/form-footer-parts.js +55 -0
- package/dist/web/primitives/form.js +33 -43
- package/dist/web/primitives/input-choice-kinds.d.ts +4 -0
- package/dist/web/primitives/input-choice-kinds.js +67 -0
- package/dist/web/primitives/input-file-kinds.d.ts +3 -0
- package/dist/web/primitives/input-file-kinds.js +6 -0
- package/dist/web/primitives/input-number-kinds.d.ts +4 -0
- package/dist/web/primitives/input-number-kinds.js +16 -0
- package/dist/web/primitives/input-optional-props.d.ts +20 -0
- package/dist/web/primitives/input-optional-props.js +24 -0
- package/dist/web/primitives/input-select-presentation.d.ts +13 -0
- package/dist/web/primitives/input-select-presentation.js +19 -0
- package/dist/web/primitives/input-shared.d.ts +22 -0
- package/dist/web/primitives/input-shared.js +10 -0
- package/dist/web/primitives/input-temporal-kinds.d.ts +5 -0
- package/dist/web/primitives/input-temporal-kinds.js +10 -0
- package/dist/web/primitives/input-text-kinds.d.ts +7 -0
- package/dist/web/primitives/input-text-kinds.js +33 -0
- package/dist/web/primitives/input.js +35 -161
- package/dist/web/widgets/chart-data.d.ts +35 -0
- package/dist/web/widgets/chart-data.js +37 -0
- package/dist/web/widgets/chart-markers.d.ts +26 -0
- package/dist/web/widgets/chart-markers.js +64 -0
- package/dist/web/widgets/chart-parts.d.ts +20 -0
- package/dist/web/widgets/chart-parts.js +15 -0
- package/dist/web/widgets/charts.d.ts +5 -78
- package/dist/web/widgets/charts.js +7 -485
- package/dist/web/widgets/drawer-panel-class.d.ts +3 -0
- package/dist/web/widgets/drawer-panel-class.js +40 -0
- package/dist/web/widgets/drawer-resize-controls.d.ts +11 -0
- package/dist/web/widgets/drawer-resize-controls.js +12 -0
- package/dist/web/widgets/drawer-sizing.d.ts +19 -0
- package/dist/web/widgets/drawer-sizing.js +60 -0
- package/dist/web/widgets/drawer.js +17 -116
- package/dist/web/widgets/stacked-area-brush.d.ts +13 -0
- package/dist/web/widgets/stacked-area-brush.js +91 -0
- package/dist/web/widgets/stacked-area-chart.d.ts +6 -0
- package/dist/web/widgets/stacked-area-chart.js +45 -0
- package/dist/web/widgets/stacked-area-chrome.d.ts +7 -0
- package/dist/web/widgets/stacked-area-chrome.js +77 -0
- package/dist/web/widgets/stacked-area-geometry.d.ts +54 -0
- package/dist/web/widgets/stacked-area-geometry.js +83 -0
- package/dist/web/widgets/stacked-area-model.d.ts +11 -0
- package/dist/web/widgets/stacked-area-model.js +1 -0
- package/dist/web/widgets/stacked-area-plot.d.ts +3 -0
- package/dist/web/widgets/stacked-area-plot.js +51 -0
- package/dist/web/widgets/stacked-area-props.d.ts +83 -0
- package/dist/web/widgets/stacked-area-props.js +16 -0
- package/dist/web/widgets/stacked-area-svg.d.ts +3 -0
- package/dist/web/widgets/stacked-area-svg.js +38 -0
- package/dist/web/widgets/stacked-area-tooltip.d.ts +3 -0
- package/dist/web/widgets/stacked-area-tooltip.js +86 -0
- package/dist/web/widgets/use-drawer-resize.d.ts +22 -0
- package/dist/web/widgets/use-drawer-resize.js +52 -0
- package/dist/web/widgets/use-stacked-area-tooltip.d.ts +9 -0
- package/dist/web/widgets/use-stacked-area-tooltip.js +34 -0
- package/dist/web/widgets/use-stacked-area-window.d.ts +20 -0
- package/dist/web/widgets/use-stacked-area-window.js +76 -0
- package/package.json +10 -10
- package/src/changes.json +54 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { SYSTEM_REFERENCE_LABELS, UNRESOLVED_REFERENCE_LABEL_KEYS, } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
3
|
+
import { Fragment } from "react";
|
|
4
|
+
import { useReferenceLinkResolver } from "../app/reference-link.js";
|
|
5
|
+
import { EMPTY_VALUE_PLACEHOLDER } from "../headless/index.js";
|
|
6
|
+
import { REFERENCE_COMBOBOX_LIMIT } from "../hooks/reference-limits.js";
|
|
7
|
+
import { useQuery } from "../hooks/use-query.js";
|
|
8
|
+
import { isCompleteRowPage, referenceOptionSource } from "../hooks/use-reference-lookup.js";
|
|
9
|
+
import { useTranslation } from "../i18n.js";
|
|
10
|
+
import { usePrimitives } from "../primitives.js";
|
|
11
|
+
import { ReferenceCreateDialog } from "./reference-create-dialog.js";
|
|
12
|
+
import { stringArrayOf } from "./render-field-values.js";
|
|
13
|
+
import { useReferenceInputState } from "./use-reference-input-state.js";
|
|
14
|
+
// ReferenceInput renders a searchable combobox populated by a live query for
|
|
15
|
+
// the referenced entity. With the default limit of 200, larger datasets miss
|
|
16
|
+
// the tail in the dropdown; a server-side debounced search query comes later.
|
|
17
|
+
// - Display = row[refLabelField], default labelField "id".
|
|
18
|
+
// - During loading, the dropdown is empty and the field is disabled.
|
|
19
|
+
// - In multiple mode (field.refMultiple), value is a UUID array and the
|
|
20
|
+
// combobox renders tags.
|
|
21
|
+
//
|
|
22
|
+
// Storage: the UI value is a UUID (row.id), or a UUID array in multiple mode.
|
|
23
|
+
// Server schema: z.uuid() or z.array(z.uuid()).
|
|
24
|
+
// REFERENCE_COMBOBOX_LIMIT is centralized in hooks/reference-limits.ts
|
|
25
|
+
// (see there for the rationale behind the default values).
|
|
26
|
+
export function ReferenceInput({ field, id, hasError, onChange, Input, featureName, }) {
|
|
27
|
+
const { Banner } = usePrimitives();
|
|
28
|
+
const t = useTranslation();
|
|
29
|
+
const state = useReferenceInputState(field, featureName, onChange);
|
|
30
|
+
const { options, createTarget } = state;
|
|
31
|
+
// Single mode uses a String/null value; multiple mode uses an array. Coerce
|
|
32
|
+
// to the shape expected by the combobox so storage drift (for example, an
|
|
33
|
+
// old String from the server where an array is now expected) cannot crash it.
|
|
34
|
+
const baseInputProps = {
|
|
35
|
+
id,
|
|
36
|
+
name: field.field,
|
|
37
|
+
// Disable the input during the initial load; subsequent loads show a
|
|
38
|
+
// loading indicator in the popover.
|
|
39
|
+
disabled: field.readOnly || (state.loading && options.length === 0),
|
|
40
|
+
required: field.required,
|
|
41
|
+
hasError,
|
|
42
|
+
options,
|
|
43
|
+
onSearchChange: state.handleSearchChange,
|
|
44
|
+
loading: state.loading,
|
|
45
|
+
...(createTarget !== undefined && {
|
|
46
|
+
onCreate: () => state.setCreateOpen(true),
|
|
47
|
+
createLabel: t("kumiko.actions.create"),
|
|
48
|
+
}),
|
|
49
|
+
};
|
|
50
|
+
const createDialog = (_jsxs(_Fragment, { children: [createTarget !== undefined && (_jsx(ReferenceCreateDialog, { open: state.createOpen, onClose: () => state.setCreateOpen(false), onCreated: state.handleCreated, featureName: state.refFeature, screen: createTarget.screen, entity: createTarget.entity })), state.createdWithoutIdWarning && (_jsx(Banner, { variant: "error", children: t("kumiko.field.reference-created-no-id") }))] }));
|
|
51
|
+
if (state.isMultiple) {
|
|
52
|
+
const arrayValue = stringArrayOf(field.value);
|
|
53
|
+
return (_jsxs(_Fragment, { children: [_jsx(Input, { kind: "combobox", ...baseInputProps, multiple: true, value: arrayValue, onChange: (v) => onChange(v) }), createDialog] }));
|
|
54
|
+
}
|
|
55
|
+
const stringValue = field.value === undefined || field.value === null ? "" : String(field.value);
|
|
56
|
+
return (_jsxs(_Fragment, { children: [_jsx(Input, { kind: "combobox", ...baseInputProps, value: stringValue, onChange: (v) => onChange(v === "" ? null : v) }), createDialog] }));
|
|
57
|
+
}
|
|
58
|
+
// Read-only text for `type: "reference"` — resolves the referenced row's
|
|
59
|
+
// label via the same lookup query as ReferenceInput's combobox, but shows
|
|
60
|
+
// plain text instead of mounting an (unusable, disabled) combobox.
|
|
61
|
+
export function ReadOnlyReferenceValue({ field, featureName, }) {
|
|
62
|
+
const { Text, Link } = usePrimitives();
|
|
63
|
+
const t = useTranslation();
|
|
64
|
+
const resolveReferenceLink = useReferenceLinkResolver();
|
|
65
|
+
const refEntity = field.refEntity ?? "";
|
|
66
|
+
const refFeature = field.refFeature ?? featureName;
|
|
67
|
+
const isMultiple = field.refMultiple === true;
|
|
68
|
+
const { queryQn, labelKey } = referenceOptionSource(field, refFeature, refEntity);
|
|
69
|
+
const queryResult = useQuery(queryQn, {
|
|
70
|
+
limit: REFERENCE_COMBOBOX_LIMIT,
|
|
71
|
+
});
|
|
72
|
+
const ids = isMultiple
|
|
73
|
+
? stringArrayOf(field.value)
|
|
74
|
+
: typeof field.value === "string" && field.value !== ""
|
|
75
|
+
? [field.value]
|
|
76
|
+
: [];
|
|
77
|
+
if (ids.length === 0)
|
|
78
|
+
return _jsx(Text, { testId: `field-value-${field.field}`, children: EMPTY_VALUE_PLACEHOLDER });
|
|
79
|
+
const rows = queryResult.data?.rows ?? [];
|
|
80
|
+
// System-scope ids (e.g. SYSTEM_TENANT_ID) never have a backing row —
|
|
81
|
+
// same central fallback as useReferenceLookup (render-list.tsx), so a
|
|
82
|
+
// reference field showing one on the detail path also gets a label
|
|
83
|
+
// instead of the raw id.
|
|
84
|
+
const systemLabel = SYSTEM_REFERENCE_LABELS[`${refFeature}:${refEntity}`];
|
|
85
|
+
const parts = ids.map((id) => {
|
|
86
|
+
if (systemLabel !== undefined && (id === systemLabel.id || systemLabel.aliases?.includes(id))) {
|
|
87
|
+
return { id, label: t(systemLabel.labelKey), link: undefined };
|
|
88
|
+
}
|
|
89
|
+
const row = rows.find((r) => String(r["id"] ?? "") === id);
|
|
90
|
+
if (row === undefined) {
|
|
91
|
+
const unknownKey = UNRESOLVED_REFERENCE_LABEL_KEYS[`${refFeature}:${refEntity}`];
|
|
92
|
+
const label = unknownKey !== undefined &&
|
|
93
|
+
!queryResult.loading &&
|
|
94
|
+
queryResult.error === null &&
|
|
95
|
+
isCompleteRowPage(queryResult.data)
|
|
96
|
+
? t(unknownKey)
|
|
97
|
+
: id;
|
|
98
|
+
return { id, label, link: undefined };
|
|
99
|
+
}
|
|
100
|
+
return { id, label: String(row[labelKey] ?? id), link: resolveReferenceLink(refEntity, id) };
|
|
101
|
+
});
|
|
102
|
+
return (_jsx(Text, { testId: `field-value-${field.field}`, children: parts.map(({ id, label, link }, index) => (_jsxs(Fragment, { children: [index > 0 && ", ", link === undefined ? (label) : (_jsx(Link, { href: link.href, onPress: link.onPress, testId: `field-link-${field.field}-${id}`, children: label }))] }, id))) }));
|
|
103
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { RenderInputArgs, SharedInputProps } from "./render-field-input-shared.ts";
|
|
3
|
+
export declare function renderNumberInput({ field, Input, onChange, appLocale, row }: RenderInputArgs, { common, placeholderProp, affixProps }: SharedInputProps): ReactNode;
|
|
4
|
+
export declare function renderDecimalInput({ field, Input, onChange, appLocale }: RenderInputArgs, { common, placeholderProp, affixProps }: SharedInputProps): ReactNode;
|
|
5
|
+
export declare function renderTzInput({ field, Input, onChange }: RenderInputArgs, { common }: SharedInputProps): ReactNode;
|
|
6
|
+
export declare function renderMoneyInput({ field, Input, onChange, appLocale }: RenderInputArgs, { common }: SharedInputProps): ReactNode;
|
|
7
|
+
export declare function renderBooleanInput({ field, Input, onChange }: RenderInputArgs, { common }: SharedInputProps): ReactNode;
|
|
8
|
+
export declare function renderDateInput({ field, Input, onChange, appLocale }: RenderInputArgs, { common }: SharedInputProps): ReactNode;
|
|
9
|
+
export declare function renderTimestampInput({ field, Input, onChange, appLocale }: RenderInputArgs, { common }: SharedInputProps): ReactNode;
|
|
10
|
+
export declare function renderLocatedTimestampInput({ field, Input, onChange, appLocale }: RenderInputArgs, { common }: SharedInputProps): ReactNode;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { resolveFieldIcon } from "./render-field-meta.js";
|
|
3
|
+
import { locatedValue, moneyMinorValue, moneyPayload, numberValue, resolveMoneyCurrency, resolveNumberUnit, stringValue, } from "./render-field-values.js";
|
|
4
|
+
// decimal/bigInt are both plain numbers on the wire (fieldToZod:
|
|
5
|
+
// z.number() / z.number().int().safe()) — the number input's onChange
|
|
6
|
+
// already emits `number | undefined`, so no extra coercion is needed
|
|
7
|
+
// beyond what "number" already does.
|
|
8
|
+
export function renderNumberInput({ field, Input, onChange, appLocale, row }, { common, placeholderProp, affixProps }) {
|
|
9
|
+
const icon = resolveFieldIcon(field);
|
|
10
|
+
const unit = resolveNumberUnit(field.unit, row);
|
|
11
|
+
return (_jsx(Input, { kind: "number", ...common, value: numberValue(field.value), onChange: (v) => onChange(v), locale: appLocale, ...(field.grouping !== undefined && { grouping: field.grouping }), ...(field.type === "bigInt" && { integer: true }), ...placeholderProp, ...affixProps, ...(icon !== undefined && { icon }), ...(unit !== undefined && { unit }) }));
|
|
12
|
+
}
|
|
13
|
+
export function renderDecimalInput({ field, Input, onChange, appLocale }, { common, placeholderProp, affixProps }) {
|
|
14
|
+
const icon = resolveFieldIcon(field);
|
|
15
|
+
return (_jsx(Input, { kind: "number", ...common, value: numberValue(field.value), onChange: (v) => onChange(v), locale: appLocale, ...(field.grouping !== undefined && { grouping: field.grouping }), ...placeholderProp, ...affixProps, ...(icon !== undefined && { icon }) }));
|
|
16
|
+
}
|
|
17
|
+
export function renderTzInput({ field, Input, onChange }, { common }) {
|
|
18
|
+
return (_jsx(Input, { kind: "tz", ...common, value: stringValue(field.value), onChange: (v) => onChange(v) }));
|
|
19
|
+
}
|
|
20
|
+
export function renderMoneyInput({ field, Input, onChange, appLocale }, { common }) {
|
|
21
|
+
const currency = resolveMoneyCurrency(field.value, field.currency);
|
|
22
|
+
return (_jsx(Input, { kind: "money", ...common, value: moneyMinorValue(field.value, currency), onChange: (v) => onChange(moneyPayload(v, currency)), currency: currency, locale: appLocale }));
|
|
23
|
+
}
|
|
24
|
+
export function renderBooleanInput({ field, Input, onChange }, { common }) {
|
|
25
|
+
return (_jsx(Input, { kind: "boolean", ...common, value: field.value === true, onChange: (v) => onChange(v) }));
|
|
26
|
+
}
|
|
27
|
+
export function renderDateInput({ field, Input, onChange, appLocale }, { common }) {
|
|
28
|
+
return (_jsx(Input, { kind: "date", ...common, value: stringValue(field.value), onChange: (v) => onChange(v), locale: field.dateLocale ?? appLocale, ...(field.min !== undefined && { min: field.min }), ...(field.max !== undefined && { max: field.max }) }));
|
|
29
|
+
}
|
|
30
|
+
export function renderTimestampInput({ field, Input, onChange, appLocale }, { common }) {
|
|
31
|
+
return (_jsx(Input, { kind: "timestamp", ...common, value: stringValue(field.value), onChange: (v) => onChange(v), locale: field.dateLocale ?? appLocale, ...(field.wallClock !== undefined && { wallClock: field.wallClock }), ...(field.min !== undefined && { min: field.min }), ...(field.max !== undefined && { max: field.max }) }));
|
|
32
|
+
}
|
|
33
|
+
export function renderLocatedTimestampInput({ field, Input, onChange, appLocale }, { common }) {
|
|
34
|
+
return (_jsx(Input, { kind: "locatedTimestamp", ...common, value: locatedValue(field.value), onChange: (v) => onChange(v), locale: field.dateLocale ?? appLocale, ...(field.min !== undefined && { min: field.min }), ...(field.max !== undefined && { max: field.max }) }));
|
|
35
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { resolveFieldIcon } from "./render-field-meta.js";
|
|
3
|
+
import { stringValue } from "./render-field-values.js";
|
|
4
|
+
function renderPasswordInput({ field, Input, onChange }, { common }) {
|
|
5
|
+
return (_jsx(Input, { kind: "password", ...common, value: stringValue(field.value), onChange: (v) => onChange(v) }));
|
|
6
|
+
}
|
|
7
|
+
function renderMonthInput({ field, Input, onChange }, { common }) {
|
|
8
|
+
return (_jsx(Input, { kind: "month", ...common, value: stringValue(field.value), onChange: (v) => onChange(v) }));
|
|
9
|
+
}
|
|
10
|
+
function renderTextareaInput({ field, Input, onChange }, { common, placeholderProp }) {
|
|
11
|
+
const rows = typeof field.multiline === "object" ? field.multiline.rows : undefined;
|
|
12
|
+
return (_jsx(Input, { kind: "textarea", ...common, value: stringValue(field.value), onChange: (v) => onChange(v), ...(rows !== undefined && { rows }), ...placeholderProp }));
|
|
13
|
+
}
|
|
14
|
+
function renderTextInput({ field, Input, onChange }, { common, placeholderProp, affixProps }) {
|
|
15
|
+
const icon = resolveFieldIcon(field);
|
|
16
|
+
return (_jsx(Input, { kind: "text", ...common, value: stringValue(field.value), onChange: (v) => onChange(v), ...placeholderProp, ...affixProps, ...(icon !== undefined && { icon }), ...(field.autoComplete !== undefined && { autoComplete: field.autoComplete }) }));
|
|
17
|
+
}
|
|
18
|
+
// text + unknown scalar type → text input. If TextFieldDef.multiline
|
|
19
|
+
// is set (the view-model carries it), the renderer switches to
|
|
20
|
+
// textarea. longText always renders a textarea — that's the point of
|
|
21
|
+
// the type — regardless of whether `multiline` is set; `multiline`
|
|
22
|
+
// only supplies an optional `{ rows }` override for it.
|
|
23
|
+
export function renderFallbackInput(args, shared) {
|
|
24
|
+
const { field } = args;
|
|
25
|
+
if (field.type === "text" && field.format === "password") {
|
|
26
|
+
return renderPasswordInput(args, shared);
|
|
27
|
+
}
|
|
28
|
+
if (field.type === "text" && field.format === "month")
|
|
29
|
+
return renderMonthInput(args, shared);
|
|
30
|
+
if (field.type === "longText" || (field.type === "text" && field.multiline)) {
|
|
31
|
+
return renderTextareaInput(args, shared);
|
|
32
|
+
}
|
|
33
|
+
return renderTextInput(args, shared);
|
|
34
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { FieldAffix } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
2
|
+
import type { SelectOptionEntry } from "./availability-options-select.tsx";
|
|
3
|
+
export declare function stringValue(v: unknown): string;
|
|
4
|
+
export declare function numberValue(v: unknown): number | "";
|
|
5
|
+
export declare function resolveMoneyCurrency(value: unknown, fieldCurrency: string | undefined): string;
|
|
6
|
+
export declare function resolveNumberUnit(unit: string | {
|
|
7
|
+
readonly field: string;
|
|
8
|
+
} | undefined, row: Readonly<Record<string, unknown>> | undefined): string | undefined;
|
|
9
|
+
export declare function resolveAffix(affix: FieldAffix | undefined, row: Readonly<Record<string, unknown>> | undefined): string | undefined;
|
|
10
|
+
export declare function stringArrayOf(value: unknown): readonly string[];
|
|
11
|
+
export declare function withPinnedValues(next: readonly string[], options: readonly SelectOptionEntry[], current: readonly string[]): readonly string[];
|
|
12
|
+
export declare function moneyMinorValue(v: unknown, currency: string): number | "";
|
|
13
|
+
export declare function moneyPayload(minorUnits: number | undefined, currency: string): unknown;
|
|
14
|
+
export declare function locatedValue(v: unknown): {
|
|
15
|
+
at: string;
|
|
16
|
+
tz: string;
|
|
17
|
+
utc?: string;
|
|
18
|
+
} | "";
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { propertyOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
2
|
+
import { currencyDecimals } from "../headless/index.js";
|
|
3
|
+
export function stringValue(v) {
|
|
4
|
+
if (v === undefined || v === null)
|
|
5
|
+
return "";
|
|
6
|
+
return typeof v === "string" ? v : String(v);
|
|
7
|
+
}
|
|
8
|
+
export function numberValue(v) {
|
|
9
|
+
if (v === undefined || v === null || v === "")
|
|
10
|
+
return "";
|
|
11
|
+
return typeof v === "number" ? v : Number(v);
|
|
12
|
+
}
|
|
13
|
+
// Read/initial value → MoneyInput's minor-units contract. The server's
|
|
14
|
+
// read shape is `{amount, currency, amountMinor}` in MAJOR units — deliberately
|
|
15
|
+
// NOT reading `amountMinor` here: the server derives it via a flat
|
|
16
|
+
// MINOR_UNIT_SCALE=100 (money.ts), which disagrees with currencyDecimals for
|
|
17
|
+
// zero-decimal currencies like JPY, so it would double-scale JPY amounts.
|
|
18
|
+
// Deriving from `amount * 10**currencyDecimals(currency)` keeps this the
|
|
19
|
+
// exact inverse of moneyPayload below. A bare number (e.g. ConfigEditBody's
|
|
20
|
+
// stored-config coercion) is MAJOR units too — every producer in this repo
|
|
21
|
+
// hands rehydrateMoney's `{amount,…}` shape or a raw major-unit number, never
|
|
22
|
+
// pre-scaled minor units.
|
|
23
|
+
// A stored row can carry a legacy/imported currency that is not an ISO-4217
|
|
24
|
+
// shape; Intl.NumberFormat throws a RangeError on it and would crash the screen.
|
|
25
|
+
const ISO_CURRENCY_CODE = /^[A-Z]{3}$/;
|
|
26
|
+
export function resolveMoneyCurrency(value, fieldCurrency) {
|
|
27
|
+
if (typeof value === "object" && value !== null && "currency" in value) {
|
|
28
|
+
const { currency } = value;
|
|
29
|
+
if (typeof currency === "string") {
|
|
30
|
+
const normalized = currency.toUpperCase();
|
|
31
|
+
if (ISO_CURRENCY_CODE.test(normalized))
|
|
32
|
+
return normalized;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return fieldCurrency ?? "EUR";
|
|
36
|
+
}
|
|
37
|
+
// The suffix span is absolutely positioned inside a fixed right padding sized
|
|
38
|
+
// for short symbols; a free-text sibling value longer than any real unit
|
|
39
|
+
// symbol would overflow the number.
|
|
40
|
+
const MAX_SIBLING_UNIT_LENGTH = 8;
|
|
41
|
+
// Static unit → used as-is. Sibling-field reference → read the live row
|
|
42
|
+
// value; missing/empty/non-string/overlong sibling means no suffix, never a guess.
|
|
43
|
+
export function resolveNumberUnit(unit, row) {
|
|
44
|
+
if (unit === undefined)
|
|
45
|
+
return undefined;
|
|
46
|
+
if (typeof unit === "string")
|
|
47
|
+
return unit;
|
|
48
|
+
const sibling = row?.[unit.field];
|
|
49
|
+
if (typeof sibling !== "string")
|
|
50
|
+
return undefined;
|
|
51
|
+
const trimmed = sibling.trim();
|
|
52
|
+
return trimmed.length > 0 && trimmed.length <= MAX_SIBLING_UNIT_LENGTH ? trimmed : undefined;
|
|
53
|
+
}
|
|
54
|
+
// String affix → used as-is (the view model already translated it). Field
|
|
55
|
+
// affix → the live form value; missing, empty or non-scalar means no affix.
|
|
56
|
+
export function resolveAffix(affix, row) {
|
|
57
|
+
if (affix === undefined)
|
|
58
|
+
return undefined;
|
|
59
|
+
if (typeof affix === "string")
|
|
60
|
+
return affix;
|
|
61
|
+
const value = row?.[affix.field];
|
|
62
|
+
const text = typeof value === "number" ? String(value) : value;
|
|
63
|
+
return typeof text === "string" && text.trim().length > 0 ? text.trim() : undefined;
|
|
64
|
+
}
|
|
65
|
+
export function stringArrayOf(value) {
|
|
66
|
+
return Array.isArray(value)
|
|
67
|
+
? value.filter((item) => typeof item === "string")
|
|
68
|
+
: [];
|
|
69
|
+
}
|
|
70
|
+
// The combobox cannot disable a single option, so removals of disabled options
|
|
71
|
+
// the form already holds are merged back.
|
|
72
|
+
export function withPinnedValues(next, options, current) {
|
|
73
|
+
const missingPinned = options
|
|
74
|
+
.filter((opt) => opt.disabled === true && current.includes(opt.value) && !next.includes(opt.value))
|
|
75
|
+
.map((opt) => opt.value);
|
|
76
|
+
return missingPinned.length === 0 ? next : [...next, ...missingPinned];
|
|
77
|
+
}
|
|
78
|
+
export function moneyMinorValue(v, currency) {
|
|
79
|
+
if (v === undefined || v === null || v === "")
|
|
80
|
+
return "";
|
|
81
|
+
if (typeof v === "number")
|
|
82
|
+
return Math.round(v * 10 ** currencyDecimals(currency));
|
|
83
|
+
if (typeof v === "object") {
|
|
84
|
+
const amount = propertyOf(v, "amount");
|
|
85
|
+
if (typeof amount === "number")
|
|
86
|
+
return Math.round(amount * 10 ** currencyDecimals(currency));
|
|
87
|
+
}
|
|
88
|
+
return "";
|
|
89
|
+
}
|
|
90
|
+
// MoneyInput's minor-units onChange → the server payload shape
|
|
91
|
+
// (`z.object({amount, currency})`, schema-builder.ts) — MAJOR units, no
|
|
92
|
+
// `amountMinor` (the server derives that itself on write).
|
|
93
|
+
export function moneyPayload(minorUnits, currency) {
|
|
94
|
+
if (minorUnits === undefined)
|
|
95
|
+
return undefined;
|
|
96
|
+
return { amount: minorUnits / 10 ** currencyDecimals(currency), currency };
|
|
97
|
+
}
|
|
98
|
+
// A locatedTimestamp field value is supplied by the read wrapper as
|
|
99
|
+
// `{ at, tz, utc }`; an unset value becomes "" as the empty sentinel,
|
|
100
|
+
// consistent with money and timestamp fields.
|
|
101
|
+
export function locatedValue(v) {
|
|
102
|
+
if (v !== null && typeof v === "object" && "at" in v && "tz" in v) {
|
|
103
|
+
const utc = propertyOf(v, "utc");
|
|
104
|
+
return {
|
|
105
|
+
at: typeof v.at === "string" ? v.at : "",
|
|
106
|
+
tz: typeof v.tz === "string" ? v.tz : "",
|
|
107
|
+
...(typeof utc === "string" && { utc }),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return "";
|
|
111
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { EditFieldViewModel } from "../headless/index.ts";
|
|
3
|
+
export declare function WriteOnlyTextInput({ field, id, hasError, onChange, changed, }: {
|
|
4
|
+
readonly field: EditFieldViewModel;
|
|
5
|
+
readonly id: string;
|
|
6
|
+
readonly hasError: boolean;
|
|
7
|
+
readonly onChange: (value: unknown) => void;
|
|
8
|
+
readonly changed: boolean;
|
|
9
|
+
}): ReactNode;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useTranslation } from "../i18n.js";
|
|
3
|
+
import { usePrimitives } from "../primitives.js";
|
|
4
|
+
// Wire value of a writeOnly field is `true` (set) / `null` (empty), never the
|
|
5
|
+
// secret. Typing sets a new value; an untouched or emptied input submits
|
|
6
|
+
// nothing / "" which the server reads as "keep". `null` after a loaded `true`
|
|
7
|
+
// (changed) means "remove on save".
|
|
8
|
+
export function WriteOnlyTextInput({ field, id, hasError, onChange, changed, }) {
|
|
9
|
+
const { Input } = usePrimitives();
|
|
10
|
+
const t = useTranslation();
|
|
11
|
+
const isSet = field.value === true;
|
|
12
|
+
const isMarkedForRemoval = field.value === null && changed;
|
|
13
|
+
if (isMarkedForRemoval) {
|
|
14
|
+
return (_jsx(Input, { kind: "password", id: id, name: field.field, disabled: true, hasError: hasError, value: "", onChange: () => { }, autoComplete: "new-password", placeholder: t("kumiko.field.writeOnly.willRemove"), testId: `field-write-only-removal-${field.field}`, trailingAction: {
|
|
15
|
+
icon: "undo",
|
|
16
|
+
label: t("kumiko.field.writeOnly.undo"),
|
|
17
|
+
onPress: () => onChange(true),
|
|
18
|
+
} }));
|
|
19
|
+
}
|
|
20
|
+
return (_jsx(Input, { kind: "password", id: id, name: field.field, disabled: field.readOnly, required: field.required && !isSet, hasError: hasError, value: typeof field.value === "string" ? field.value : "", onChange: (v) => onChange(v), autoComplete: "new-password", ...(isSet && { placeholder: t("kumiko.field.writeOnly.setPlaceholder") }), ...(isSet &&
|
|
21
|
+
!field.required &&
|
|
22
|
+
!field.readOnly && {
|
|
23
|
+
trailingAction: {
|
|
24
|
+
icon: "clear",
|
|
25
|
+
label: t("kumiko.field.writeOnly.remove"),
|
|
26
|
+
onPress: () => onChange(null),
|
|
27
|
+
},
|
|
28
|
+
}) }));
|
|
29
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { EditFieldViewModel, FieldIssue } from "../headless/index.ts";
|
|
3
3
|
import { type FieldProps } from "../primitives.tsx";
|
|
4
4
|
export type RenderFieldProps = {
|
|
5
5
|
readonly field: EditFieldViewModel;
|