@cosmicdrift/kumiko-renderer 0.386.0 → 0.388.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/LICENSE +21 -57
- package/dist/app/action-form-body.d.ts +2 -2
- package/dist/app/action-form-body.js +12 -4
- package/dist/app/action-form-shim.d.ts +8 -8
- package/dist/app/action-form-shim.js +20 -22
- package/dist/app/action-form-support.d.ts +1 -0
- package/dist/app/action-form-support.js +1 -1
- package/dist/app/app-features-context.js +4 -4
- package/dist/app/column-renderers.d.ts +7 -9
- package/dist/app/column-renderers.js +15 -18
- package/dist/app/config-edit-body.js +7 -3
- package/dist/app/config-edit-shim.d.ts +7 -8
- package/dist/app/config-edit-shim.js +18 -20
- package/dist/app/custom-screens.d.ts +3 -3
- package/dist/app/custom-screens.js +8 -8
- package/dist/app/dashboard-body.d.ts +2 -2
- package/dist/app/dashboard-body.js +2 -2
- package/dist/app/entity-edit-prefill.d.ts +11 -0
- package/dist/app/entity-edit-prefill.js +18 -13
- package/dist/app/entity-edit-screen.js +4 -4
- package/dist/app/entity-list-screen.js +3 -3
- package/dist/app/extension-form-submit.d.ts +1 -1
- package/dist/app/extension-form-submit.js +14 -15
- package/dist/app/extension-sections.d.ts +29 -28
- package/dist/app/extension-sections.js +10 -10
- package/dist/app/feature-schema.js +11 -11
- package/dist/app/form-schema.js +2 -2
- package/dist/app/header-actions-bar.js +2 -2
- package/dist/app/kumiko-screen.js +8 -10
- package/dist/app/layout-fields.js +1 -1
- package/dist/app/list-facets.js +3 -3
- package/dist/app/nav.d.ts +10 -10
- package/dist/app/nav.js +13 -13
- package/dist/app/projection-detail-body.js +4 -4
- package/dist/app/projection-detail-shim.d.ts +6 -5
- package/dist/app/projection-detail-shim.js +23 -22
- package/dist/app/projection-list-body.js +10 -10
- package/dist/app/projection-list-shim.d.ts +5 -5
- package/dist/app/projection-list-shim.js +17 -18
- package/dist/app/qn.js +10 -12
- package/dist/app/qualify-screen-id.js +1 -1
- package/dist/app/screen-access.js +2 -2
- package/dist/app/screen-shared.js +1 -1
- package/dist/app/secret-mint-body.js +4 -4
- package/dist/app/write-failed-error.js +8 -7
- package/dist/components/edit-mounts.d.ts +2 -2
- package/dist/components/embedded-list-field.js +5 -5
- package/dist/components/grid-cell-for-field.d.ts +1 -1
- package/dist/components/reference-create-dialog.js +3 -3
- package/dist/components/related-list-section.d.ts +1 -1
- package/dist/components/related-list-section.js +15 -2
- package/dist/components/render-edit-action-button.d.ts +1 -3
- package/dist/components/render-edit-action-button.js +2 -2
- package/dist/components/render-edit-logic.js +1 -1
- package/dist/components/render-edit-types.d.ts +6 -6
- package/dist/components/render-edit.js +74 -73
- package/dist/components/render-field.d.ts +7 -7
- package/dist/components/render-field.js +62 -61
- package/dist/components/render-list.d.ts +42 -43
- package/dist/components/render-list.js +93 -84
- package/dist/components/use-wizard-form-draft.js +10 -10
- package/dist/context/dispatcher-context.js +5 -5
- package/dist/context/user-roles-context.js +2 -2
- package/dist/flow-diagram/render.js +9 -1
- package/dist/headless/apex/index.d.ts +1 -2
- package/dist/headless/apex/index.js +5 -6
- package/dist/headless/form/form-controller.js +14 -6
- package/dist/headless/form/types.d.ts +1 -0
- package/dist/headless/form/zod-bridge.js +2 -2
- package/dist/headless/format/index.js +89 -64
- package/dist/headless/format/money.js +1 -1
- package/dist/headless/index.js +2 -2
- package/dist/headless/view-model/edit.js +4 -2
- package/dist/headless/view-model/list.js +184 -150
- package/dist/headless/view-model/types.d.ts +6 -3
- package/dist/hooks/live-refetch-scheduler.js +4 -0
- package/dist/hooks/reference-limits.js +11 -12
- package/dist/hooks/use-list-url-state.d.ts +13 -13
- package/dist/hooks/use-list-url-state.js +21 -22
- package/dist/hooks/use-query.js +3 -3
- package/dist/hooks/use-reference-lookup.d.ts +9 -1
- package/dist/hooks/use-reference-lookup.js +32 -19
- package/dist/hooks/use-store.js +18 -22
- package/dist/hooks/use-stream-handler.js +4 -2
- package/dist/i18n-defaults.js +6 -8
- package/dist/i18n.d.ts +10 -10
- package/dist/i18n.js +25 -25
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6 -4
- package/dist/live/dispatcher-live.js +2 -2
- package/dist/primitives.d.ts +244 -221
- package/dist/primitives.js +1 -1
- package/dist/sse/live-events.d.ts +5 -5
- package/dist/sse/live-events.js +17 -17
- package/dist/tokens.d.ts +8 -8
- package/dist/tokens.js +16 -17
- package/dist/web/app/browser-locale.d.ts +9 -9
- package/dist/web/app/browser-locale.js +19 -19
- package/dist/web/app/client-plugin.d.ts +44 -52
- package/dist/web/app/client-plugin.js +4 -5
- package/dist/web/app/create-app.d.ts +28 -31
- package/dist/web/app/create-app.js +29 -25
- package/dist/web/app/dashboard-body.js +17 -1
- package/dist/web/app/dashboard-panels.js +12 -2
- package/dist/web/app/document-lang-sync.js +1 -1
- package/dist/web/app/draft-storage.js +1 -1
- package/dist/web/app/nav-providers-context.d.ts +3 -3
- package/dist/web/app/nav-providers-context.js +3 -3
- package/dist/web/app/nav.d.ts +18 -17
- package/dist/web/app/nav.js +66 -64
- package/dist/web/app/rich-content-editor.js +1 -1
- package/dist/web/components/config-cascade.js +17 -18
- package/dist/web/components/config-source-badge.js +4 -4
- package/dist/web/index.d.ts +1 -1
- package/dist/web/index.js +1 -1
- package/dist/web/layout/app-layout.d.ts +9 -9
- package/dist/web/layout/app-layout.js +2 -2
- package/dist/web/layout/avatar.d.ts +4 -3
- package/dist/web/layout/avatar.js +15 -17
- package/dist/web/layout/default-app-shell.d.ts +22 -23
- package/dist/web/layout/editor-panel.js +3 -3
- package/dist/web/layout/nav-tree.js +86 -84
- package/dist/web/layout/profile-menu.d.ts +1 -1
- package/dist/web/layout/shell-header.js +2 -1
- package/dist/web/layout/sidebar-brand.d.ts +7 -7
- package/dist/web/layout/sidebar-user.d.ts +1 -1
- package/dist/web/layout/sidebar-user.js +4 -4
- package/dist/web/layout/sidebar.d.ts +8 -8
- package/dist/web/layout/sidebar.js +7 -7
- package/dist/web/layout/target-resolver-stub.d.ts +5 -5
- package/dist/web/layout/target-resolver-stub.js +20 -20
- package/dist/web/layout/target-url.d.ts +7 -7
- package/dist/web/layout/target-url.js +22 -22
- package/dist/web/layout/theme-toggle.js +4 -5
- package/dist/web/layout/uniform-bar-controls.d.ts +1 -0
- package/dist/web/layout/uniform-bar-controls.js +4 -0
- package/dist/web/layout/workspace-shell.d.ts +21 -21
- package/dist/web/layout/workspace-shell.js +29 -30
- package/dist/web/lib/cn.d.ts +4 -4
- package/dist/web/lib/cn.js +4 -4
- package/dist/web/lib/file-ref-download.js +4 -1
- package/dist/web/primitives/action-menu.d.ts +8 -8
- package/dist/web/primitives/action-menu.js +14 -13
- package/dist/web/primitives/calendar-popover.d.ts +1 -1
- package/dist/web/primitives/calendar-popover.js +10 -10
- package/dist/web/primitives/card-styles.js +1 -1
- package/dist/web/primitives/card.js +2 -2
- package/dist/web/primitives/code-input.d.ts +6 -0
- package/dist/web/primitives/code-input.js +31 -0
- package/dist/web/primitives/combobox.d.ts +12 -12
- package/dist/web/primitives/combobox.js +34 -35
- package/dist/web/primitives/data-table-cell.js +2 -2
- package/dist/web/primitives/data-table.js +6 -6
- package/dist/web/primitives/date-field.js +2 -2
- package/dist/web/primitives/date-input.js +4 -4
- package/dist/web/primitives/default-form.js +2 -2
- package/dist/web/primitives/dropdown-menu.js +14 -15
- package/dist/web/primitives/embedded-list-input.js +3 -3
- package/dist/web/primitives/facet-filter.js +2 -2
- package/dist/web/primitives/field.js +9 -8
- package/dist/web/primitives/file-thumbnail.d.ts +3 -0
- package/dist/web/primitives/file-thumbnail.js +5 -0
- package/dist/web/primitives/form.d.ts +1 -1
- package/dist/web/primitives/form.js +6 -6
- package/dist/web/primitives/input.js +8 -3
- package/dist/web/primitives/layout-elements.js +7 -5
- package/dist/web/primitives/layout.js +2 -2
- package/dist/web/primitives/located-timestamp-input.js +4 -4
- package/dist/web/primitives/money-input.js +13 -17
- package/dist/web/primitives/page-header.js +3 -1
- package/dist/web/primitives/pager.js +1 -1
- package/dist/web/primitives/row-actions.js +6 -7
- package/dist/web/primitives/secret-reveal.js +1 -1
- package/dist/web/primitives/timestamp-input.d.ts +9 -9
- package/dist/web/primitives/timestamp-input.js +21 -21
- package/dist/web/primitives/toast.js +12 -13
- package/dist/web/primitives/use-focus-on-mount.d.ts +2 -0
- package/dist/web/primitives/use-focus-on-mount.js +9 -0
- package/dist/web/sse/live-events.d.ts +6 -6
- package/dist/web/sse/live-events.js +3 -3
- package/dist/web/tokens.d.ts +11 -12
- package/dist/web/tokens.js +35 -37
- package/dist/web/version/update-checker.js +14 -13
- package/dist/web/widgets/ai-text-field.js +1 -1
- package/dist/web/widgets/bounded-load-cache.d.ts +1 -1
- package/dist/web/widgets/bounded-load-cache.js +10 -4
- package/dist/web/widgets/charts.d.ts +10 -10
- package/dist/web/widgets/charts.js +8 -8
- package/dist/web/widgets/choice-cards.d.ts +17 -0
- package/dist/web/widgets/choice-cards.js +12 -0
- package/dist/web/widgets/collapsible-section.d.ts +3 -3
- package/dist/web/widgets/collapsible-section.js +7 -7
- package/dist/web/widgets/detail-list.d.ts +5 -3
- package/dist/web/widgets/detail-list.js +5 -3
- package/dist/web/widgets/feed-list.d.ts +2 -2
- package/dist/web/widgets/feed-list.js +2 -2
- package/dist/web/widgets/form-fields.d.ts +14 -14
- package/dist/web/widgets/form-fields.js +14 -13
- package/dist/web/widgets/index.d.ts +1 -0
- package/dist/web/widgets/index.js +5 -4
- package/dist/web/widgets/infinity-list.js +3 -3
- package/dist/web/widgets/mode-switch.d.ts +2 -2
- package/dist/web/widgets/mode-switch.js +2 -2
- package/dist/web/widgets/pdf-preview.js +1 -1
- package/dist/web/widgets/plan-card.d.ts +2 -0
- package/dist/web/widgets/plan-card.js +1 -1
- package/dist/web/widgets/progress-list.d.ts +1 -2
- package/dist/web/widgets/progress-list.js +1 -2
- package/dist/web/widgets/query-table.d.ts +6 -6
- package/dist/web/widgets/result-panel.d.ts +16 -16
- package/dist/web/widgets/result-panel.js +10 -10
- package/dist/web/widgets/section-card.d.ts +3 -3
- package/dist/web/widgets/section-card.js +3 -3
- package/dist/web/widgets/stat.d.ts +4 -4
- package/dist/web/widgets/stat.js +6 -6
- package/dist/web/widgets/states.d.ts +4 -4
- package/dist/web/widgets/states.js +4 -4
- package/dist/web/widgets/upload-zone.js +13 -5
- package/dist/web/widgets/use-draft.d.ts +7 -7
- package/dist/web/widgets/use-draft.js +7 -7
- package/package.json +6 -6
- package/src/changes.json +49 -0
|
@@ -16,7 +16,7 @@ import { fileIdsOf } from "../file-ids-of.js";
|
|
|
16
16
|
import { applyFormatSpec, currencyDecimals, EMPTY_VALUE_PLACEHOLDER, } from "../headless/index.js";
|
|
17
17
|
import { REFERENCE_COMBOBOX_LIMIT } from "../hooks/reference-limits.js";
|
|
18
18
|
import { useQuery } from "../hooks/use-query.js";
|
|
19
|
-
import { referenceOptionSource } from "../hooks/use-reference-lookup.js";
|
|
19
|
+
import { isCompleteRowPage, referenceOptionSource } from "../hooks/use-reference-lookup.js";
|
|
20
20
|
import { useLocale, useTranslation } from "../i18n.js";
|
|
21
21
|
import { usePrimitives } from "../primitives.js";
|
|
22
22
|
import { AvailabilityOptionsMultiSelect, AvailabilityOptionsSelect, staticSelectOptions, } from "./availability-options-select.js";
|
|
@@ -29,11 +29,10 @@ const AllFieldsRequiredContext = createContext(false);
|
|
|
29
29
|
export const AllFieldsRequiredProvider = AllFieldsRequiredContext.Provider;
|
|
30
30
|
export function RenderField({ field, issues, onChange, featureName, labelAppendix, fieldAppendix, appendixPlacement, description, status, allIssues, valueDisplay = "form", row, changed, layout, accent, fillDisabled, hideLabel, }) {
|
|
31
31
|
const { Field, Input, Banner, Text, JsonView } = usePrimitives();
|
|
32
|
-
//
|
|
33
|
-
// navigator.language
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
// Standalone-Consumer/Tests müssen wrappen (createKumikoApp tut es).
|
|
32
|
+
// The app locale is required for money/date inputs; otherwise they fall back
|
|
33
|
+
// to navigator.language instead of the selected app language. RenderField is
|
|
34
|
+
// publicly exported and therefore requires a LocaleProvider; standalone
|
|
35
|
+
// consumers and tests must wrap it (createKumikoApp does this).
|
|
37
36
|
const appLocale = useLocale().locale();
|
|
38
37
|
const t = useTranslation();
|
|
39
38
|
const allFieldsRequired = useContext(AllFieldsRequiredContext);
|
|
@@ -45,13 +44,13 @@ export function RenderField({ field, issues, onChange, featureName, labelAppendi
|
|
|
45
44
|
// the list-column path (render-list.tsx). Only takes effect once the field
|
|
46
45
|
// is actually readOnly — a FormatSpec/PlatformComponent renderer has no
|
|
47
46
|
// editable widget of its own, so applying it to an editable field would
|
|
48
|
-
// silently make that field un-editable
|
|
47
|
+
// silently make that field un-editable.
|
|
49
48
|
const readOnlyText = valueDisplay === "text" && field.readOnly && field.renderer === undefined;
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
// The list input prints its own list-level issues (empty state
|
|
54
|
-
// the Field wrapper must not print them a second time.
|
|
49
|
+
// The reference field renders its own component because it uses useQuery()
|
|
50
|
+
// for the live lookup and must therefore be mounted as a React component,
|
|
51
|
+
// not called as a pure render function.
|
|
52
|
+
// The list input prints its own list-level issues (including the empty state),
|
|
53
|
+
// so the Field wrapper must not print them a second time.
|
|
55
54
|
const listPrintsOwnIssues = field.type === "embedded" &&
|
|
56
55
|
field.embeddedListCells !== undefined &&
|
|
57
56
|
!(field.readOnly && field.renderer !== undefined);
|
|
@@ -168,38 +167,35 @@ function WriteOnlyTextInput({ field, id, hasError, onChange, changed, }) {
|
|
|
168
167
|
},
|
|
169
168
|
}) }));
|
|
170
169
|
}
|
|
171
|
-
//
|
|
172
|
-
//
|
|
173
|
-
//
|
|
174
|
-
//
|
|
175
|
-
// -
|
|
176
|
-
// -
|
|
177
|
-
//
|
|
178
|
-
// - Multi-Mode (Tier 2.7e-Multi via field.refMultiple): value ist
|
|
179
|
-
// ein UUID-Array, Combobox rendert Selected-Tags.
|
|
170
|
+
// ReferenceInput renders a searchable combobox populated by a live query for
|
|
171
|
+
// the referenced entity. With the default limit of 200, larger datasets miss
|
|
172
|
+
// the tail in the dropdown; a server-side debounced search query comes later.
|
|
173
|
+
// - Display = row[refLabelField], default labelField "id".
|
|
174
|
+
// - During loading, the dropdown is empty and the field is disabled.
|
|
175
|
+
// - In multiple mode (field.refMultiple), value is a UUID array and the
|
|
176
|
+
// combobox renders tags.
|
|
180
177
|
//
|
|
181
|
-
// Storage: UI
|
|
182
|
-
// Server
|
|
183
|
-
// REFERENCE_COMBOBOX_LIMIT
|
|
184
|
-
// (
|
|
178
|
+
// Storage: the UI value is a UUID (row.id), or a UUID array in multiple mode.
|
|
179
|
+
// Server schema: z.uuid() or z.array(z.uuid()).
|
|
180
|
+
// REFERENCE_COMBOBOX_LIMIT is centralized in hooks/reference-limits.ts
|
|
181
|
+
// (see there for the rationale behind the default values).
|
|
185
182
|
function ReferenceInput({ field, id, hasError, onChange, Input, featureName, }) {
|
|
186
183
|
const { Banner } = usePrimitives();
|
|
187
184
|
const refEntity = field.refEntity ?? "";
|
|
188
185
|
const refFeature = field.refFeature ?? featureName;
|
|
189
186
|
const isMultiple = field.refMultiple === true;
|
|
190
|
-
//
|
|
191
|
-
//
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
//
|
|
187
|
+
// refFeature may differ from featureName (for example,
|
|
188
|
+
// items.assignee → users:query:user:list). The default is the same feature,
|
|
189
|
+
// as provided by the ViewModel (parseRefTarget).
|
|
190
|
+
// refOptionsQuery replaces that source with an author-supplied handler whose
|
|
191
|
+
// rows already carry a composed `label`.
|
|
195
192
|
const { queryQn, labelKey } = referenceOptionSource(field, refFeature, refEntity);
|
|
196
|
-
//
|
|
197
|
-
//
|
|
198
|
-
// refFeature
|
|
199
|
-
//
|
|
200
|
-
//
|
|
201
|
-
//
|
|
202
|
-
// Combobox rendert dann ohne den Footer.
|
|
193
|
+
// "+ Neu" in the combobox opens the referenced entity's create screen in a
|
|
194
|
+
// dialog instead of leaving the current form.
|
|
195
|
+
// refFeature may differ from the currently rendered feature; appFeatures
|
|
196
|
+
// (createKumikoApp) knows all feature schemas, not only the active screen.
|
|
197
|
+
// If the feature, screen, or entity is not registered, or allowCreate:false,
|
|
198
|
+
// onCreate remains undefined and the combobox renders without the footer.
|
|
203
199
|
const appFeatures = useAppFeatures();
|
|
204
200
|
const userRoles = useUserRoles();
|
|
205
201
|
const t = useTranslation();
|
|
@@ -210,10 +206,10 @@ function ReferenceInput({ field, id, hasError, onChange, Input, featureName, })
|
|
|
210
206
|
s.allowCreate !== false &&
|
|
211
207
|
screenAccessAllows(s.access, userRoles));
|
|
212
208
|
const refEntityDef = refTargetSchema?.entities[refEntity];
|
|
213
|
-
//
|
|
214
|
-
//
|
|
215
|
-
//
|
|
216
|
-
//
|
|
209
|
+
// When the user types in the combobox, the server filters through the
|
|
210
|
+
// existing list-payload `search` parameter. The combobox debounces the
|
|
211
|
+
// keystroke itself (300ms) and calls onSearchChange.
|
|
212
|
+
// An empty initial state requests the first 50 items in default order.
|
|
217
213
|
const [searchTerm, setSearchTerm] = useState("");
|
|
218
214
|
const queryPayload = useMemo(() => searchTerm === ""
|
|
219
215
|
? { limit: REFERENCE_COMBOBOX_LIMIT }
|
|
@@ -254,13 +250,14 @@ function ReferenceInput({ field, id, hasError, onChange, Input, featureName, })
|
|
|
254
250
|
return { value: idVal, label };
|
|
255
251
|
});
|
|
256
252
|
}, [queryResult.data, labelKey]);
|
|
257
|
-
// Single
|
|
258
|
-
//
|
|
259
|
-
//
|
|
253
|
+
// Single mode uses a String/null value; multiple mode uses an array. Coerce
|
|
254
|
+
// to the shape expected by the combobox so storage drift (for example, an
|
|
255
|
+
// old String from the server where an array is now expected) cannot crash it.
|
|
260
256
|
const baseInputProps = {
|
|
261
257
|
id,
|
|
262
258
|
name: field.field,
|
|
263
|
-
//
|
|
259
|
+
// Disable the input during the initial load; subsequent loads show a
|
|
260
|
+
// loading indicator in the popover.
|
|
264
261
|
disabled: field.readOnly || (queryResult.loading && options.length === 0),
|
|
265
262
|
required: field.required,
|
|
266
263
|
hasError,
|
|
@@ -295,7 +292,7 @@ function FieldRendererOutput({ field, renderer, row, appLocale, }) {
|
|
|
295
292
|
if (isFormatSpec(renderer)) {
|
|
296
293
|
// format:"json" wants the raw value (JsonView stringifies + highlights
|
|
297
294
|
// itself) — applyFormatSpec's already-indented string is only the
|
|
298
|
-
// fallback for primitives-providers without JsonView
|
|
295
|
+
// fallback for primitives-providers without JsonView.
|
|
299
296
|
if (renderer.format === "json" && JsonView !== undefined) {
|
|
300
297
|
return (_jsx(JsonView, { value: field.value, indent: renderer.indent, testId: `field-value-${field.field}` }));
|
|
301
298
|
}
|
|
@@ -308,8 +305,8 @@ function FieldRendererOutput({ field, renderer, row, appLocale, }) {
|
|
|
308
305
|
// App locale as default when the FormatSpec declares none of its own —
|
|
309
306
|
// otherwise locale-sensitive formats (timestamp/date/number/decimal/
|
|
310
307
|
// bigInt/unit) fell back to Intl's runtime default instead of the app
|
|
311
|
-
// language chosen via LocaleProvider
|
|
312
|
-
// when set; coalesce undefined (spread override) back to appLocale
|
|
308
|
+
// language chosen via LocaleProvider. Prefer renderer.locale
|
|
309
|
+
// when set; coalesce undefined (spread override) back to appLocale.
|
|
313
310
|
return (_jsx(Text, { testId: `field-value-${field.field}`, children: applyFormatSpec({
|
|
314
311
|
...renderer,
|
|
315
312
|
locale: stringPropertyOf(renderer, "locale") ?? appLocale,
|
|
@@ -357,7 +354,10 @@ function ReadOnlyReferenceValue({ field, featureName, }) {
|
|
|
357
354
|
const row = rows.find((r) => String(r["id"] ?? "") === id);
|
|
358
355
|
if (row === undefined) {
|
|
359
356
|
const unknownKey = UNRESOLVED_REFERENCE_LABEL_KEYS[`${refFeature}:${refEntity}`];
|
|
360
|
-
const label = unknownKey !== undefined &&
|
|
357
|
+
const label = unknownKey !== undefined &&
|
|
358
|
+
!queryResult.loading &&
|
|
359
|
+
queryResult.error === null &&
|
|
360
|
+
isCompleteRowPage(queryResult.data)
|
|
361
361
|
? t(unknownKey)
|
|
362
362
|
: id;
|
|
363
363
|
return { id, label, link: undefined };
|
|
@@ -417,7 +417,7 @@ function resolveFieldIcon(field) {
|
|
|
417
417
|
// from the EditFieldViewModel (computeEditViewModel pulls them from
|
|
418
418
|
// SelectFieldDef.options). Structural types without a widget (embedded,
|
|
419
419
|
// jsonb, multiSelect) render read-only instead of a data-destroying text
|
|
420
|
-
// fallback
|
|
420
|
+
// fallback; truly unknown scalar types still fall back to text.
|
|
421
421
|
function renderInput({ field, id, hasError, onChange, Input, appLocale, Banner, Text, JsonView, t, row, fill, }) {
|
|
422
422
|
const common = {
|
|
423
423
|
id,
|
|
@@ -438,7 +438,7 @@ function renderInput({ field, id, hasError, onChange, Input, appLocale, Banner,
|
|
|
438
438
|
// decimal/bigInt are both plain numbers on the wire (fieldToZod:
|
|
439
439
|
// z.number() / z.number().int().safe()) — the number input's onChange
|
|
440
440
|
// already emits `number | undefined`, so no extra coercion is needed
|
|
441
|
-
// beyond what "number" already does
|
|
441
|
+
// beyond what "number" already does.
|
|
442
442
|
case "number":
|
|
443
443
|
case "bigInt": {
|
|
444
444
|
const icon = resolveFieldIcon(field);
|
|
@@ -472,11 +472,11 @@ function renderInput({ field, id, hasError, onChange, Input, appLocale, Banner,
|
|
|
472
472
|
case "locatedTimestamp":
|
|
473
473
|
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 }) }));
|
|
474
474
|
case "select": {
|
|
475
|
-
// Translated
|
|
476
|
-
// (computeEditViewModel,
|
|
475
|
+
// Translated option labels come from the ViewModel builder
|
|
476
|
+
// (computeEditViewModel, convention key
|
|
477
477
|
// `<feature>:entity:<entity>:field:<field>:option:<value>`).
|
|
478
|
-
//
|
|
479
|
-
//
|
|
478
|
+
// If no translations are registered, fall back to the raw value as the
|
|
479
|
+
// label, which ComboboxInput then shows unchanged.
|
|
480
480
|
const selectOptions = field.optionLabels !== undefined ? staticSelectOptions(field) : (field.options ?? []);
|
|
481
481
|
// `display` is shared with multiSelect's "checkboxes", which is not a
|
|
482
482
|
// select presentation — narrow to the two a select field can request.
|
|
@@ -495,11 +495,11 @@ function renderInput({ field, id, hasError, onChange, Input, appLocale, Banner,
|
|
|
495
495
|
case "images":
|
|
496
496
|
return (_jsx(FileListField, { field: field, id: id, kind: field.type === "images" ? "image" : "file", hasError: hasError, onChange: (ids) => onChange(ids) }));
|
|
497
497
|
// embedded (without embeddedListCells — that's embeddedList, which has
|
|
498
|
-
// had its own EmbeddedListField widget
|
|
498
|
+
// had its own EmbeddedListField widget) and jsonb carry
|
|
499
499
|
// arbitrary objects. Without a dedicated widget these must NOT fall
|
|
500
500
|
// through to a text input: stringValue() turns them into
|
|
501
501
|
// "[object Object]" and saving that overwrites the real data with the
|
|
502
|
-
// mangled string
|
|
502
|
+
// mangled string. A `required: true` on either is caught loudly
|
|
503
503
|
// at boot (validateNoWidgetRequiredField in the framework package)
|
|
504
504
|
// instead of silently failing here.
|
|
505
505
|
case "embedded":
|
|
@@ -519,7 +519,7 @@ function renderInput({ field, id, hasError, onChange, Input, appLocale, Banner,
|
|
|
519
519
|
// is set (the view-model carries it), the renderer switches to
|
|
520
520
|
// textarea. longText always renders a textarea — that's the point of
|
|
521
521
|
// the type — regardless of whether `multiline` is set; `multiline`
|
|
522
|
-
// only supplies an optional `{ rows }` override for it
|
|
522
|
+
// only supplies an optional `{ rows }` override for it.
|
|
523
523
|
if (field.type === "longText" || (field.type === "text" && field.multiline)) {
|
|
524
524
|
const rows = typeof field.multiline === "object" ? field.multiline.rows : undefined;
|
|
525
525
|
return (_jsx(Input, { kind: "textarea", ...common, value: stringValue(field.value), onChange: (v) => onChange(v), ...(rows !== undefined && { rows }), ...placeholderProp }));
|
|
@@ -628,8 +628,9 @@ function moneyPayload(minorUnits, currency) {
|
|
|
628
628
|
return undefined;
|
|
629
629
|
return { amount: minorUnits / 10 ** currencyDecimals(currency), currency };
|
|
630
630
|
}
|
|
631
|
-
// locatedTimestamp
|
|
632
|
-
//
|
|
631
|
+
// A locatedTimestamp field value is supplied by the read wrapper as
|
|
632
|
+
// `{ at, tz, utc }`; an unset value becomes "" as the empty sentinel,
|
|
633
|
+
// consistent with money and timestamp fields.
|
|
633
634
|
function locatedValue(v) {
|
|
634
635
|
if (v !== null && typeof v === "object" && "at" in v && "tz" in v) {
|
|
635
636
|
const utc = propertyOf(v, "utc");
|
|
@@ -644,7 +645,7 @@ function locatedValue(v) {
|
|
|
644
645
|
// Read-only text for a `field.readOnly` field without its own `renderer` —
|
|
645
646
|
// per-type formatting that reuses the same value-shaping helpers as the
|
|
646
647
|
// editable widgets above, so a text value and its would-be Input widget stay
|
|
647
|
-
// derived from the identical parse
|
|
648
|
+
// derived from the identical parse. `type: "reference"` isn't
|
|
648
649
|
// covered here — it needs a live label lookup, see ReadOnlyReferenceValue.
|
|
649
650
|
// `isComplexFieldType` types aren't covered either — callers keep those on
|
|
650
651
|
// `renderInput`'s existing Banner fallback.
|
|
@@ -11,22 +11,21 @@ export type RenderListProps = {
|
|
|
11
11
|
readonly featureName: string;
|
|
12
12
|
readonly translate?: Translate;
|
|
13
13
|
readonly onRowClick?: (row: ListRowViewModel) => void;
|
|
14
|
-
/**
|
|
15
|
-
*
|
|
14
|
+
/** Overrides the default empty box. When set, the automatic CTA via
|
|
15
|
+
* `onCreate` is ignored; the caller's content takes precedence. */
|
|
16
16
|
readonly emptyState?: ReactNode;
|
|
17
|
-
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
/** Adds a "+ New" button to the toolbar and uses it as the CTA in the
|
|
18
|
+
* default empty box. The caller provides the action (for example,
|
|
19
|
+
* navigation to the edit screen). */
|
|
20
20
|
readonly onCreate?: () => void;
|
|
21
|
-
/** Label
|
|
22
|
-
* (`kumiko.actions.create`).
|
|
23
|
-
* überschreiben. */
|
|
21
|
+
/** Label for the + New button. Defaults to the i18n bundle key
|
|
22
|
+
* (`kumiko.actions.create`). The caller can override it. */
|
|
24
23
|
readonly createLabel?: string;
|
|
25
|
-
/**
|
|
26
|
-
*
|
|
24
|
+
/** Enables a search input in the toolbar. Filtering is server-side via
|
|
25
|
+
* the caller's onSearchChange; RenderList does not filter locally. */
|
|
27
26
|
readonly searchable?: boolean;
|
|
28
|
-
/** Placeholder
|
|
29
|
-
*
|
|
27
|
+
/** Placeholder for the search input. Defaults to the i18n bundle key
|
|
28
|
+
* (`kumiko.list.search-placeholder`). */
|
|
30
29
|
readonly searchPlaceholder?: string;
|
|
31
30
|
/** Hint at the left of the toolbar (already translated). */
|
|
32
31
|
readonly description?: string;
|
|
@@ -38,35 +37,35 @@ export type RenderListProps = {
|
|
|
38
37
|
readonly defaultFilterActive?: boolean;
|
|
39
38
|
/** Plural-forms key for the footer count noun; default is the entity's `:noun` convention key. */
|
|
40
39
|
readonly itemNounKey?: string;
|
|
41
|
-
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
40
|
+
/** Current search term (from URL state / the parent). RenderList buffers
|
|
41
|
+
* typing locally with a 300ms debounce before calling onSearchChange;
|
|
42
|
+
* otherwise every keystroke would trigger a server round trip. */
|
|
44
43
|
readonly searchValue?: string;
|
|
45
|
-
/**
|
|
46
|
-
*
|
|
44
|
+
/** Called when the debounced search term changes. The caller uses it to
|
|
45
|
+
* set URL state (?<id>.q=…) and trigger a refetch. */
|
|
47
46
|
readonly onSearchChange?: (next: string) => void;
|
|
48
|
-
/**
|
|
47
|
+
/** Current sorting, or null for the server's default order. */
|
|
49
48
|
readonly sort?: ListSort | null;
|
|
50
|
-
/**
|
|
49
|
+
/** Called with the next sort state after a header click. */
|
|
51
50
|
readonly onSortChange?: (next: ListSort | null) => void;
|
|
52
|
-
/** Pager
|
|
53
|
-
*
|
|
54
|
-
* screen.pagination. */
|
|
51
|
+
/** Pager props for pagination="pages". If undefined, no pager UI is
|
|
52
|
+
* rendered; the default or infinite mode is used. KumikoScreen supplies
|
|
53
|
+
* this according to screen.pagination. */
|
|
55
54
|
readonly pager?: {
|
|
56
55
|
readonly page: number;
|
|
57
56
|
readonly limit: number;
|
|
58
57
|
readonly total: number;
|
|
59
58
|
readonly onPageChange: (next: number) => void;
|
|
60
59
|
};
|
|
61
|
-
/** Infinite-
|
|
62
|
-
*
|
|
63
|
-
*
|
|
60
|
+
/** Infinite-scroll wiring for pagination="infinite". KumikoScreen keeps
|
|
61
|
+
* the accumulated rows and cursor; RenderList passes the callbacks
|
|
62
|
+
* through to DataTable. */
|
|
64
63
|
readonly onReachEnd?: () => void;
|
|
65
64
|
readonly loadingMore?: boolean;
|
|
66
65
|
readonly hasMore?: boolean;
|
|
67
|
-
/**
|
|
68
|
-
* EntityListScreenDefinition.rowActions: handler-QN
|
|
69
|
-
* i18n
|
|
66
|
+
/** Per-row actions in resolved form. KumikoScreen builds them from
|
|
67
|
+
* EntityListScreenDefinition.rowActions: handler-QN becomes a dispatcher
|
|
68
|
+
* call, and i18n keys become translated strings. */
|
|
70
69
|
readonly rowActions?: readonly DataTableRowAction[];
|
|
71
70
|
/** How the row-action column renders (see `DataTableProps.rowActionMode`).
|
|
72
71
|
* KumikoScreen derives it from the actions themselves; without it the
|
|
@@ -75,38 +74,38 @@ export type RenderListProps = {
|
|
|
75
74
|
/** Forwarded to `DataTableProps.rowGrouping` / `rowTone`. */
|
|
76
75
|
readonly rowGrouping?: DataTableRowGrouping;
|
|
77
76
|
readonly rowTone?: DataTableProps["rowTone"];
|
|
78
|
-
/** Toolbar
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
77
|
+
/** Toolbar actions in the list header in resolved form. KumikoScreen
|
|
78
|
+
* builds them from EntityListScreenDefinition.toolbarActions:
|
|
79
|
+
* navigate targets use useNav and handler-QN uses a dispatcher call.
|
|
80
|
+
* RenderList renders the buttons on the right side of the toolbar,
|
|
81
|
+
* before "+ New". */
|
|
82
82
|
readonly toolbarActions?: readonly ToolbarActionButton[];
|
|
83
|
-
/** Faceted
|
|
84
|
-
* select/boolean
|
|
83
|
+
/** Faceted filter dropdowns. KumikoScreen builds them from filterable
|
|
84
|
+
* select/boolean fields; RenderList passes them through to DataTable. */
|
|
85
85
|
readonly filterFacets?: readonly DataTableFacet[];
|
|
86
|
-
/**
|
|
86
|
+
/** Currently selected facet values by field (from URL state). */
|
|
87
87
|
readonly filterValues?: Readonly<Record<string, readonly string[]>>;
|
|
88
|
-
/**
|
|
88
|
+
/** Toggles a facet value (sets URL state and refetches). */
|
|
89
89
|
readonly onFilterChange?: (field: string, values: readonly string[]) => void;
|
|
90
90
|
/** Reset aller aktiven Facets. */
|
|
91
91
|
readonly onFilterReset?: () => void;
|
|
92
|
-
/** Time-range filters
|
|
92
|
+
/** Time-range filters — forwarded to the DataTable. */
|
|
93
93
|
readonly dateRangeFacets?: readonly DataTableDateRangeFacet[];
|
|
94
94
|
/** One bound changed (set URL state + refetch). */
|
|
95
95
|
readonly onDateRangeChange?: (field: string, bound: "from" | "to", value: string) => void;
|
|
96
96
|
/** Forwarded to `DataTableProps.chromeless` — drops the table's own card
|
|
97
97
|
* frame for a host that already provides one (relatedList in a tabs-mode
|
|
98
|
-
* section
|
|
98
|
+
* section). Default false. */
|
|
99
99
|
readonly chromeless?: boolean;
|
|
100
100
|
/** Forwarded to `DataTableProps.scrollBody` — bounds the table height and
|
|
101
101
|
* scrolls rows internally instead of growing the page (relatedList in a
|
|
102
|
-
* tabs-mode section
|
|
102
|
+
* tabs-mode section). Default false. */
|
|
103
103
|
readonly scrollBody?: boolean;
|
|
104
104
|
/** Forwarded to `DataTableProps.screenPadding` — `true`: the table carries
|
|
105
105
|
* the shared screen padding because it is the screen body (entityList/
|
|
106
|
-
* projectionList
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
* padded host. */
|
|
106
|
+
* projectionList). Explicit `false`: no padding at all — the host already
|
|
107
|
+
* pads its own children (relatedList in a tabs-mode Card). Omitted: legacy
|
|
108
|
+
* `p-6` fallback for a standalone table with no padded host. */
|
|
110
109
|
readonly screenPadding?: boolean;
|
|
111
110
|
/** Forwarded to `DataTableProps.expandedRowIds` / `onToggleRowExpanded` /
|
|
112
111
|
* `renderExpandedRow` (entityList `expandableRow`). */
|