@object-ui/fields 17.7.0 → 18.0.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.
@@ -0,0 +1,259 @@
1
+ import { ActionParamDef, ActionParamOption } from '@object-ui/core';
2
+ import { I18nLabel } from '@objectstack/spec/ui';
3
+ /**
4
+ * Resolve a param `type` to the form widget key that renders it — the fold from
5
+ * a PARAM type spelling to its widget, the one alias table, read rather than
6
+ * restated (see `isLookupResolvedType` below, and app-shell's `paramToField`,
7
+ * which asks the same question one step later). Any type in `FORM_FIELD_TYPES`
8
+ * resolves to itself (identity — asserted by app-shell's drift test); aliases
9
+ * and unknown types resolve through the same fallback chain the form applies
10
+ * (unknown → `text`).
11
+ */
12
+ export declare function resolveParamWidgetType(paramType: string): string;
13
+ /**
14
+ * Resolved params keep raw `FieldType` values (`text` / `email` / `select` /
15
+ * `file` / …), matching the `FieldType` enum in `@objectstack/spec`. **Do
16
+ * not** translate into the FormField widget vocabulary (`field:select`, …)
17
+ * here — `ActionParamDialog` owns that translation via its `paramToField()`
18
+ * adapter (ADR-0059).
19
+ */
20
+ /**
21
+ * One option as AUTHORED on an action param — {@link ActionParamOption} with
22
+ * the label on the authoring side of rc.6's `I18nLabel` widening.
23
+ *
24
+ * The distinction is the whole point of the authored/resolved split: an author
25
+ * may write `{ value: 'high', label: { en: 'High', 'zh-CN': '高' } }`, and
26
+ * `ActionParamDialog` reads `ActionParamOption.label` as a plain `string`. Left
27
+ * un-narrowed, the map would ride an INLINE option list straight through to the
28
+ * select widget (inline options pass through verbatim — see `resolvedOptions`),
29
+ * and render as `[object Object]` with no diagnostic anywhere. So the two types
30
+ * are kept apart and {@link resolveOptionLabels} is the one crossing point.
31
+ *
32
+ * Spelled out rather than `Omit<ActionParamOption, 'label'> & { … }`: `Omit`
33
+ * over a type carrying `[key: string]: unknown` resolves `Exclude<keyof T,
34
+ * 'label'>` back to `string | number`, so it drops `value`'s type AND keeps
35
+ * `label` as `unknown` — the derivation silently erases exactly the two keys it
36
+ * is supposed to be about.
37
+ */
38
+ export type RawActionParamOption = {
39
+ /** Authored label — a plain string, or rc.6's inline per-locale map. */
40
+ label?: I18nLabel;
41
+ value: string;
42
+ /**
43
+ * Everything else an option declares (`visibleWhen` / `color` / `icon` /
44
+ * `disabled`), preserved verbatim — `fbc23e094`.
45
+ */
46
+ [key: string]: unknown;
47
+ };
48
+ /** Raw param as authored on a schema action (post-zod). */
49
+ export interface RawActionParam {
50
+ name?: string;
51
+ field?: string;
52
+ objectOverride?: string;
53
+ /**
54
+ * Display label, as authored. `@objectstack/spec` 17.0.0-rc.6 widened
55
+ * `I18nLabel` from `string` to `string | Record<string, string>`, so an
56
+ * author may inline a per-locale map here
57
+ * (`label: { en: 'Reason', 'zh-CN': '原因' }`) — the same widening the spec's
58
+ * `ActionParamSchema.label` carries.
59
+ *
60
+ * This restatement is the load-bearing half (objectui#3174): when it drifts
61
+ * from what `@object-ui/types`' `ActionParam` says an author may write, this
62
+ * resolver stops accepting the public authoring type. That drift is exactly
63
+ * what a widened `I18nLabel` produced, and it is why the fix belongs on this
64
+ * DECLARATION rather than on the test that surfaced it.
65
+ *
66
+ * `resolveActionParams` emits `ActionParamDef.label`, which is a plain
67
+ * `string` — so the map is resolved on the way out (see {@link
68
+ * ResolveActionParamsContext.locale}), never forwarded.
69
+ */
70
+ label?: I18nLabel;
71
+ type?: string;
72
+ required?: boolean;
73
+ /**
74
+ * Option list authored inline on the param. Speaks the same vocabulary as the
75
+ * resolved side ({@link ActionParamOption}): the two keys this resolver reads
76
+ * plus a catch-all for whatever else an option declares (`visibleWhen`,
77
+ * `color`, `icon`, `disabled`) — `fbc23e094`.
78
+ *
79
+ * One key differs, and it is the same rc.6 widening as {@link
80
+ * RawActionParam.label} one level down: an option's `label` is authored as
81
+ * `I18nLabel` and RESOLVED to a `string` before it reaches
82
+ * `ActionParamOption`. See {@link RawActionParamOption}.
83
+ */
84
+ options?: RawActionParamOption[];
85
+ placeholder?: string;
86
+ helpText?: string;
87
+ defaultValue?: unknown;
88
+ /** When true, seed defaultValue from the row record using the field name. */
89
+ defaultFromRow?: boolean;
90
+ /**
91
+ * Carry-over declaration — the spec's `ActionParamSchema.carryOver`
92
+ * (objectui#6246): seeded from the row, rendered read-only, submitted
93
+ * verbatim. Copied onto `ActionParamDef.carryOver` unchanged on every branch
94
+ * below; `ActionParamDialog` is what honours it.
95
+ */
96
+ carryOver?: boolean;
97
+ /** Allow multiple values (file/image/lookup/user params → array value). */
98
+ multiple?: boolean;
99
+ /** Accepted upload types (MIME types / extensions) for `file`/`image` params. */
100
+ accept?: string[];
101
+ /** Max upload size in bytes for `file`/`image` params. */
102
+ maxSize?: number;
103
+ /**
104
+ * Reference target for an INLINE `lookup`/`master_detail` param (objectstack-ai/objectstack#3405) —
105
+ * the object whose records the picker searches. Field-backed params inherit
106
+ * it from the referenced field instead (see `lookupExtras` below). Spelled
107
+ * `reference` to match `FieldSchema.reference` / `ActionParamSchema.reference`.
108
+ */
109
+ reference?: string;
110
+ /**
111
+ * Visibility predicate (CEL) — mirrors the spec `ActionParamSchema.visible`.
112
+ * The server serialises it through `ExpressionInputSchema` as an
113
+ * `{ dialect, source }` envelope, so accept both the raw string and the
114
+ * envelope. Propagated to `ActionParamDef.visible` so `ActionParamDialog`
115
+ * can gate the param (e.g. hide `phoneNumber` unless `features.phoneNumber`).
116
+ * Absent = always visible.
117
+ */
118
+ visible?: string | {
119
+ dialect?: string;
120
+ source?: string;
121
+ };
122
+ }
123
+ /**
124
+ * Keys of the RESOLVED `ActionParamDef` that are not authorable on a raw param,
125
+ * mapped to the prescription an author needs (objectui#3174).
126
+ *
127
+ * They are deliberately ABSENT from `RawActionParam` above rather than declared
128
+ * and ignored: the whole defect this table exists for is that
129
+ * `@object-ui/types`' public `ActionParam` declared them "for parity with the
130
+ * resolved shape", so `{ name: 'account_id', type: 'lookup', referenceTo:
131
+ * 'account' }` type-checked, lost its picker target here, and rendered as a
132
+ * plain record-id text box. `ActionParamSchema` in `@objectstack/spec` is
133
+ * `.strict()` and rejects every one of them at parse time — `referenceTo` by
134
+ * name, with `reference` as the suggestion — so resolving them here would make
135
+ * objectui accept metadata the platform itself refuses to store. That is a
136
+ * second de-facto contract, not a kindness (AGENTS.md "contract-first"): such a
137
+ * param would work in a locally-authored TS action and fail at publish.
138
+ *
139
+ * So the disposition is: recognise, name, and refuse — never silently drop, and
140
+ * never quietly honour.
141
+ */
142
+ export declare const RESOLVED_ONLY_PARAM_KEYS: Readonly<Record<string, string>>;
143
+ /** Field metadata as exposed by `useMetadata().objects[].fields`. */
144
+ interface RuntimeField {
145
+ type?: string;
146
+ label?: string;
147
+ required?: boolean;
148
+ placeholder?: string;
149
+ /**
150
+ * `@objectstack/spec`'s `FieldSchema.inlineHelpText`, the help line drawn
151
+ * under the field's control (objectui#12213). Read first for a field-backed
152
+ * param's `helpText`, through `fieldHelpText`, the read the record form
153
+ * shares.
154
+ */
155
+ inlineHelpText?: string;
156
+ /**
157
+ * The help line's fallback, read second through `fieldHelpText`. An
158
+ * objectstack server on `@objectstack/spec` 17.7.0 or later serves a field's
159
+ * translated help text here.
160
+ */
161
+ description?: string;
162
+ /**
163
+ * The field's own option list — `select` / `radio` / `checkboxes` metadata as
164
+ * `@objectstack/spec`'s `SelectOptionSchema` declares it, so entries may carry
165
+ * `visibleWhen` / `color` / `icon` / `disabled` alongside label and value. A
166
+ * bare string is shorthand for `{ label: s, value: s }`.
167
+ */
168
+ options?: Array<ActionParamOption | string>;
169
+ multiple?: boolean;
170
+ defaultValue?: unknown;
171
+ accept?: string[];
172
+ maxSize?: number;
173
+ reference?: string;
174
+ displayField?: string;
175
+ reference_field?: string;
176
+ id_field?: string;
177
+ descriptionField?: string;
178
+ description_field?: string;
179
+ title_format?: string;
180
+ lookupColumns?: unknown[];
181
+ lookupFilters?: unknown[];
182
+ lookup_filters?: unknown[];
183
+ lookupPageSize?: number;
184
+ dependsOn?: unknown[];
185
+ }
186
+ interface RuntimeObject {
187
+ name?: string;
188
+ fields?: Record<string, RuntimeField>;
189
+ }
190
+ export interface ResolveActionParamsContext {
191
+ /** Default object name when a param's `objectOverride` is absent. */
192
+ objectName: string;
193
+ /** All known runtime objects (`useMetadata().objects`). */
194
+ objects: RuntimeObject[];
195
+ /** i18n resolver — `useObjectLabel().fieldLabel`. */
196
+ fieldLabel: (objectName: string, fieldName: string, fallback: string) => string;
197
+ /** Optional option-label translator — `useObjectLabel().fieldOptionLabel`. */
198
+ fieldOptionLabel?: (objectName: string, fieldName: string, optionValue: string, fallback: string) => string;
199
+ /**
200
+ * Row record providing default values for params with `defaultFromRow` set.
201
+ * Used by list_item actions (edit/delete dialogs) so the dialog opens with
202
+ * the row's current values pre-filled.
203
+ */
204
+ row?: Record<string, unknown>;
205
+ /**
206
+ * Active UI language (BCP-47) used to resolve an inline per-locale
207
+ * {@link RawActionParam.label} down to the one string `ActionParamDef.label`
208
+ * can carry. Both callers thread `useObjectTranslation().language`.
209
+ *
210
+ * Optional and nullish-tolerant: omitted means "no locale known", which the
211
+ * spec's resolver documents as resolving to `en`. It is a context field
212
+ * rather than a hook read because this is a pure function — the caller is
213
+ * the component that already knows the language.
214
+ */
215
+ locale?: string;
216
+ }
217
+ /**
218
+ * Resolve a single raw param against object metadata. Inline params pass
219
+ * through (with safe defaults); field-backed params inherit from the
220
+ * referenced field and accept inline overrides on top.
221
+ */
222
+ export declare function resolveActionParam(param: RawActionParam, ctx: ResolveActionParamsContext): ActionParamDef;
223
+ /**
224
+ * Union the objects a CALLER holds with the ones the console's metadata store
225
+ * holds, caller first — the object list `resolveActionParams()` should be given.
226
+ *
227
+ * ## Why a union and not simply the store (`6cc910b6d`)
228
+ *
229
+ * The two lists answer different questions and neither contains the other.
230
+ * The caller's list is the world that caller is rendering: `ObjectView` passes
231
+ * the full published set, `DeclaredActionsBar` passes the ONE object it is
232
+ * drawing a toolbar for, `ConsoleShell`'s root runtime passes none, and a
233
+ * preview/draft host passes an overlaid world whose defs deliberately differ
234
+ * from the published ones. The store's list is every object the session has
235
+ * loaded. A caller that carries a def must keep it — replacing it would make a
236
+ * draft overlay render its published twin — so the caller is ranked FIRST and
237
+ * `resolveActionParam`'s `find()` takes the first match by name.
238
+ *
239
+ * What the store adds is the objects the caller never had: the owner of a
240
+ * `{ field }` param dispatched from a surface that is not scoped to that object
241
+ * at all, and the `objectOverride` target of a cross-object param. Before this,
242
+ * such a param resolved against a list that could not contain its owner and
243
+ * degraded — for a field-backed param, which carries no inline `type`, into a
244
+ * bare `text` param: an empty box, no dropdown, no request for the referenced
245
+ * object on the wire, while the identical field rendered a real picker on a
246
+ * record form off this very store.
247
+ *
248
+ * ⛔ Not a lenient fallback in the AGENTS.md #0.1 sense: no dialect is being
249
+ * tolerated and no shape coerced. The metadata is spec-compliant and already in
250
+ * the client; this is the seam that was not handing it over.
251
+ *
252
+ * Pure, and deduplicated by object `name` so a def cannot be resolved twice.
253
+ * Entries with no usable `name` are carried through from the caller's list
254
+ * (they are that caller's business) and skipped from the store's.
255
+ */
256
+ export declare function withKnownObjects<T>(supplied: readonly T[] | undefined, stored: readonly T[] | undefined): T[];
257
+ /** Resolve an array of raw action params. */
258
+ export declare function resolveActionParams(params: RawActionParam[] | undefined, ctx: ResolveActionParamsContext): ActionParamDef[];
259
+ export {};
@@ -3,5 +3,46 @@ import { FieldWidgetComponentProps } from './types.js';
3
3
  /**
4
4
  * FormulaField - Read-only computed field
5
5
  * Values are computed on the backend and cannot be edited
6
+ *
7
+ * ## One value, one reading (objectui#11748)
8
+ *
9
+ * This is the read-only FORM face of a formula; `FormulaCellRenderer` in this
10
+ * package's barrel is its TABLE face. The two used to format the value each by
11
+ * itself, so one stored value read two ways: a number here was `200000` (no
12
+ * `returnType`) or `200000.00` (a `toFixed(2)`), in monospace, beside the
13
+ * cell's `200,000`; a declared boolean or date read `Yes` / `Jul 4` here and
14
+ * `true` / `2026-07-04` in the cell.
15
+ *
16
+ * Both faces now read the SAME rule and draw each type through the calls the
17
+ * matching field type's faces make, so they agree on every value:
18
+ *
19
+ * - which type: the declared `returnType`; with none, a JS number is a
20
+ * number and anything else is text. ⛔ Nothing is inferred from the
21
+ * expression: the spec's own field form says consumers read `returnType`
22
+ * "instead of re-parsing the expression".
23
+ * - number: `formatNumberFieldValue`, the one call every number face makes
24
+ * (the locale's grouping, the width `resolveFieldScale` answers for a
25
+ * declared `scale`). The value is read the way the formula cell reads it
26
+ * (objectui#11683): blank is empty, and a declared number held as a
27
+ * numeric string is a number.
28
+ * - currency: money, through the currency field's own faces (`CurrencyField`
29
+ * read-only here, `CurrencyCellRenderer` in the cell), reading the
30
+ * formula's own `currencyConfig` as they read a currency field's
31
+ * (objectui#12111).
32
+ * - boolean: the locale's word, as `BooleanField`'s read-only branch draws
33
+ * it; only a JS boolean is a boolean, as there (objectui#8593).
34
+ * - date: `formatDate`'s DEFAULT face, as `DateField`'s read-only branch
35
+ * draws it (objectui#8194).
36
+ * - text: the value as the cell prints it, in the font `TextField`'s
37
+ * read-only branch draws (objectui#12357), declared `text` or not.
38
+ *
39
+ * Both faces also share the emptiness floor (`isEmptyValue` on the coerced
40
+ * value), so an empty value reads the shared affordance in both.
41
+ *
42
+ * The rule is spelled here and in the cell, because the barrel and this lazily
43
+ * loaded widget may not import each other (the reason `number-format.ts`
44
+ * gives). `__tests__/formulaFaces.returnType-11748.test.tsx` draws every
45
+ * declared type in both faces and compares the two texts, so neither can move
46
+ * alone.
6
47
  */
7
48
  export declare function FormulaField({ value, field, ...props }: FieldWidgetComponentProps<any>): React.JSX.Element;
@@ -52,8 +52,10 @@ import { InlineGridColumn } from '@objectstack/spec/data';
52
52
  *
53
53
  * Field-level config: the keys `GridFieldMetadata` (`@object-ui/types`)
54
54
  * declares, each read under that one spelling (objectui#11070 round 8), and
55
- * no other key: `sort_field`, the last key read before any face declared it,
56
- * is declared there too (objectui#11070 round 9).
55
+ * no other key: `sortField`, the last key read before any face declared it,
56
+ * is declared there too (objectui#11070 round 9). objectui#11610 renamed all
57
+ * eight from snake_case to camelCase; a field still carrying a snake_case
58
+ * spelling is refused by name instead of drawn (`RetiredGridFieldKeys`).
57
59
  */
58
60
  /**
59
61
  * One grid column — `@objectstack/spec`'s inline grid column
@@ -130,7 +132,7 @@ export declare function lookupAutofillPatch(columns: GridColumn[], col: GridColu
130
132
  export declare function computeRow(columns: GridColumn[], row: Row, tenantCurrency?: string): Row;
131
133
  /** Sum a numeric column across rows (ignoring blanks/NaN). */
132
134
  export declare function sumColumn(rows: Row[], field: string): number;
133
- export declare function GridField({ value, onChange, field, readonly, disabled, className, error, onRowExpand, displayMode, onAdd, ...props }: FieldWidgetComponentProps<Row[]> & {
135
+ type GridFieldProps = FieldWidgetComponentProps<Row[]> & {
134
136
  /** When provided, each row shows an "expand" button that opens the row in a
135
137
  * full form (the host — e.g. MasterDetailForm — renders the drawer/modal and
136
138
  * writes the edited values back). Lets a "fat" child be edited in a real form
@@ -147,7 +149,13 @@ export declare function GridField({ value, onChange, field, readonly, disabled,
147
149
  * `readonlyWhen` / `requiredWhen` CEL predicate — so a line cell can react to
148
150
  * the header (`parent.status == 'paid'`). Supplied by MasterDetailForm. */
149
151
  contextRecord?: Record<string, unknown>;
150
- }): React.JSX.Element;
152
+ };
153
+ /**
154
+ * The `grid` field widget. A field whose metadata carries one of the retired
155
+ * snake_case keys is refused by name ({@link RetiredGridFieldKeys}); any other
156
+ * field draws the grid.
157
+ */
158
+ export declare function GridField(props: GridFieldProps): React.JSX.Element;
151
159
  /** Semantic alias — the master-detail subform's child grid. */
152
160
  export declare const LineItemsField: typeof GridField;
153
161
  export {};
@@ -1,5 +1,37 @@
1
1
  import { default as React } from 'react';
2
2
  import { FieldWidgetComponentProps } from './types.js';
3
+ /**
4
+ * RecipientPickerField — dependent record picker for a polymorphic recipient
5
+ * reference (e.g. `sys_sharing_rule.recipient_id`), whose target object is
6
+ * decided by a sibling `recipient_type` select.
7
+ *
8
+ * Reached via the field `widget: 'recipient-picker'` hint (resolves as
9
+ * `field:recipient-picker`). Reads the live `recipient_type` from
10
+ * `dependentValues` (the form record), loads candidate records of the mapped
11
+ * object via `dataSource.find(...)`, and stores the value the sharing evaluator
12
+ * expects for that type:
13
+ *
14
+ * user → sys_user, store `id`
15
+ * team → sys_team, store `id`
16
+ * business_unit → sys_business_unit, store `id`
17
+ * unit_and_subordinates → sys_business_unit, store `id`
18
+ *
19
+ * One kind lists CATALOG ITEMS instead of records (objectui#7611, ADR-0131
20
+ * D3/D7): its candidates are the `position` items of the environment registry,
21
+ * read through the console's metadata store, and the picker stores the item's
22
+ * `name` (matched against `sys_user_position.position` at evaluation time):
23
+ *
24
+ * position → the `position` registry, store `name`
25
+ *
26
+ * One kind is NOT a record picker and therefore has no row above:
27
+ *
28
+ * field → a user-valued COLUMN of the shared object, store
29
+ * the column's `name` (see FIELD_RECIPIENT_TYPE)
30
+ *
31
+ * When `recipient_type` changes after mount the stored id is reset (an id valid
32
+ * for one type is meaningless for another). Unknown types degrade to a plain
33
+ * text input so nothing breaks.
34
+ */
3
35
  /** A user-valued column offered by the `field` recipient mode. */
4
36
  interface UserFieldDef {
5
37
  /** The machine name — this is what gets stored in `recipient_id`. */
@@ -0,0 +1,77 @@
1
+ import { default as React } from 'react';
2
+ import { FieldWidgetComponentProps } from './types.js';
3
+ /**
4
+ * RegistryRefField — a record-form field that names ONE item of ONE metadata
5
+ * registry type by its machine name (objectui#7611, ruling B2 on
6
+ * objectstack#22865).
7
+ *
8
+ * The record-form face of the mechanism the metadata forms already have
9
+ * (`ref-multi:permission`, objectui#12126): the registry's items are listed by
10
+ * name, with the author's label beside the name, and the value written is the
11
+ * NAME — one string, never an id, never a row. A grant names its position or
12
+ * its permission set that way (ADR-0131 D4), and the server refuses a name the
13
+ * registry does not hold, so this widget only has to make the right name easy
14
+ * to pick.
15
+ *
16
+ * Reached through a field's `widget:` hint, which the form renderer looks up as
17
+ * `field:<widget>` (the spec's `FieldSchema.widget`). One registration per
18
+ * registry type, with the type in the key, as `object-ref` carries `object`:
19
+ *
20
+ * position-ref → `GET /api/v1/meta/position`
21
+ * permission-ref → `GET /api/v1/meta/permission`
22
+ *
23
+ * The catalog comes from `DataSource.listMetadataItems`, which rejects on a
24
+ * failure rather than answering `[]`, so its four arms are told apart the way
25
+ * the metadata forms' `ref:dataset` / `ref-multi:<type>` pickers tell them
26
+ * apart:
27
+ *
28
+ * - IDLE — no data source, or one that cannot enumerate a registry: a text
29
+ * box that writes the typed name;
30
+ * - LOADING — the stored name in a disabled box;
31
+ * - FAILED — a notice naming the failure and its cause, beside a text box that
32
+ * stays enabled, so a failed catalog does not also block the edit;
33
+ * - LOADED — nothing a grant can name: the same text box as IDLE; otherwise
34
+ * a searchable picker. A stored name the catalog does not list stays
35
+ * selectable as its own option, flagged "not found", so it is neither
36
+ * dropped nor read as a listed item. The flag is a claim about the
37
+ * registry's answer, so it is made only once the catalog has answered.
38
+ *
39
+ * The picker OFFERS only what a grant can name (objectui#12200). A position
40
+ * grant cannot name an audience anchor — `everyone` or `guest` (ADR-0090
41
+ * D5/D9), held implicitly by every authenticated or anonymous principal — and
42
+ * the server refuses a `sys_user_position` row that does. So `position-ref`
43
+ * leaves the anchors out of its options ({@link nonGrantableNames}). A stored
44
+ * anchor is still the row's value: it stays on screen as its own option, by its
45
+ * label beside its name, and it is not flagged "not found", because the
46
+ * registry does list it.
47
+ *
48
+ * ⛔ No arm writes anything but a name the person picked or typed: no label,
49
+ * no id, no first-item default.
50
+ */
51
+ /** One listed item of a registry: the name stored, the label shown beside it. */
52
+ export interface RegistryItemHeader {
53
+ name: string;
54
+ label?: string;
55
+ }
56
+ /** The catalog, in whichever of its four states the load is actually in. */
57
+ export type RegistryCatalogState = {
58
+ status: 'idle';
59
+ } | {
60
+ status: 'loading';
61
+ } | {
62
+ status: 'error';
63
+ message: string;
64
+ } | {
65
+ status: 'loaded';
66
+ items: RegistryItemHeader[];
67
+ };
68
+ export interface RegistryRefFieldProps extends FieldWidgetComponentProps<string> {
69
+ /** The registry type whose items this field names (`position`, `permission`). */
70
+ registryType: string;
71
+ }
72
+ export declare function RegistryRefField({ registryType, value, onChange, readonly, className, error, ...props }: RegistryRefFieldProps): React.JSX.Element;
73
+ /** `position-ref`: names one position of the position registry. */
74
+ export declare function PositionRefField(props: FieldWidgetComponentProps<string>): React.JSX.Element;
75
+ /** `permission-ref`: names one permission set of the `permission` registry. */
76
+ export declare function PermissionRefField(props: FieldWidgetComponentProps<string>): React.JSX.Element;
77
+ export default RegistryRefField;
@@ -3,5 +3,35 @@ import { FieldWidgetComponentProps } from './types.js';
3
3
  /**
4
4
  * SummaryField - Read-only aggregation field
5
5
  * Values are aggregated from related records and cannot be edited
6
+ *
7
+ * ## One value, one reading (objectui#11752)
8
+ *
9
+ * This is the read-only FORM face of a roll-up; its TABLE face is the formula
10
+ * cell, `FormulaCellRenderer` in this package's barrel, which `summary` is
11
+ * registered to. The two used to format the value each by itself, so one
12
+ * stored value read two ways: this face picked a format by the roll-up's
13
+ * aggregation `function` (`count` as it arrived, the other four at two fixed
14
+ * decimals), so a `sum` over `15750.5` read `15750.50`, ungrouped and in every
15
+ * locale, beside the cell's `15,750.5`.
16
+ *
17
+ * This face now reads the value the way the cell reads a summary's value,
18
+ * whatever the function:
19
+ *
20
+ * - the emptiness FLOOR (`isEmptyValue`) on the coerced value, as the cell
21
+ * takes it, so `''` and `[]` read the shared affordance in both faces;
22
+ * - a JS number: `formatNumberFieldValue`, the one call every number face
23
+ * makes (the display locale's grouping, the width `resolveFieldScale`
24
+ * answers for a declared `scale`, the value's natural precision when none
25
+ * is declared). A `count` is a whole number, so it reads whole; an author
26
+ * who wants a fixed width on a `sum` or an `avg` declares `scale`, and both
27
+ * faces honour it.
28
+ * - anything else: the coerced value as text, as the cell prints it. A
29
+ * summary declares no `returnType`, so a string of digits is not a number.
30
+ *
31
+ * The rule is spelled here and in the cell, because the barrel and this lazily
32
+ * loaded widget may not import each other (the reason `number-format.ts`
33
+ * gives). `__tests__/summaryFaces.numberFace-11752.test.tsx` draws every
34
+ * aggregation function in both faces and compares the two texts, so neither
35
+ * can move alone.
6
36
  */
7
37
  export declare function SummaryField({ value, field, ...props }: FieldWidgetComponentProps<any>): React.JSX.Element;
@@ -0,0 +1,18 @@
1
+ import { default as React } from 'react';
2
+ /** Draws a boolean value as the current locale's word for it. */
3
+ export type BooleanValueLabel = (value: boolean) => string;
4
+ /**
5
+ * The current locale's word for a boolean value: `common.yes` for `true`,
6
+ * `common.no` for `false`.
7
+ *
8
+ * The caller decides WHICH values are booleans; this only names them. Call it
9
+ * once at the top of a component and use the returned function in the render.
10
+ */
11
+ export declare function useBooleanValueLabel(): BooleanValueLabel;
12
+ /**
13
+ * The same word as a node, for a caller that is a plain function and cannot
14
+ * call a hook itself (`renderLookupColumnValue`).
15
+ */
16
+ export declare function BooleanValueText({ value }: {
17
+ value: boolean;
18
+ }): React.ReactElement;
@@ -0,0 +1,40 @@
1
+ import { default as React } from 'react';
2
+ import { FieldWidgetComponentProps } from '../types.js';
3
+ /**
4
+ * The `date` and date-time controls of the form field widgets
5
+ * (objectui#12622, objectui#12642).
6
+ *
7
+ * `fieldWidgetMap`'s `date` / `datetime` widgets — what a record form resolves
8
+ * `field:date` / `field:datetime` to, and what `getLazyFieldWidget` hands the
9
+ * action-param and bulk-action dialogs — draw the platform picker instead of
10
+ * the browser's native input: a text box in the display locale's format (keyboard
11
+ * entry), a calendar popover (the same `Calendar` the calendar view's date
12
+ * popover opens, localized by the same resolver), a clear button, and
13
+ * today / tomorrow shortcuts.
14
+ *
15
+ * ## The value written is unchanged
16
+ *
17
+ * Both faces go through the native adapters `DateField` / `DateTimeField` use
18
+ * (`toDateInputValue`, `toDateTimeInputValue`, `fromDateTimeInputValue`), so a
19
+ * `date` field writes `YYYY-MM-DD` and a `datetime` field writes an ISO
20
+ * instant, exactly as the native inputs did; clearing writes `''` as the
21
+ * native inputs did.
22
+ *
23
+ * ## Where the native input stays
24
+ *
25
+ * - The read-only face is `DateField` / `DateTimeField`'s own, rendered as is.
26
+ * - `FieldEditWidget` (the grid's inline cell editor and the kanban
27
+ * required-fields dialog) draws THIS picker since objectui#12664, loaded
28
+ * lazily so it stays in its own chunk; the grid's cell seam blurs the
29
+ * control before Enter or a click outside commits, and that blur is where
30
+ * the typed text is read. plugin-detail's `InlineFieldInput` (the record
31
+ * page's details and highlights) delegates `date` and `datetime` to
32
+ * `FieldEditWidget` too since objectui#12759. The list filter rows are
33
+ * objectui#12743.
34
+ * - A form whose `field:date` widget is not registered (no
35
+ * `@object-ui/fields` loaded) falls back to the form renderer's own
36
+ * `<input type={inputType}>`, with the `inputType` `ObjectForm` maps.
37
+ */
38
+ export declare function DatePickerField(props: FieldWidgetComponentProps<string>): React.JSX.Element;
39
+ /** The `datetime` face of {@link DatePickerField}. */
40
+ export declare function DateTimePickerField(props: FieldWidgetComponentProps<string>): React.JSX.Element;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ /**
9
+ * The display text for a native-shaped value (`YYYY-MM-DD`, or
10
+ * `YYYY-MM-DDTHH:mm` when `withTime`). `''` for an empty or unreadable value.
11
+ */
12
+ export declare function formatLocaleDateText(value: string, locale: string, withTime: boolean): string;
13
+ /**
14
+ * The pattern the text box expects, for its placeholder: the locale's own
15
+ * order and separators with `YYYY` / `MM` / `DD` (and `HH` / `mm`) in place of
16
+ * the digits.
17
+ */
18
+ export declare function localeDatePattern(locale: string, withTime: boolean): string;
19
+ /**
20
+ * Reads what was typed into the text box.
21
+ *
22
+ * Returns `''` for blank text, the native-shaped value for a real day (and a
23
+ * real clock time when `withTime`), and `null` for text that names no real
24
+ * date. Accepted:
25
+ *
26
+ * - the locale's own digit order, with any separators (`10/16/2026` in `en`,
27
+ * `2026/10/16` in `zh`, `16.10.2026` in `de`);
28
+ * - an ISO date `2026-10-16`, in every locale (a four-digit first group is
29
+ * read as the year);
30
+ * - a two-digit year, read in this century;
31
+ * - with `withTime`: an hour and minute after the date, 24-hour, or 12-hour
32
+ * with `AM` / `PM` or the locale's own afternoon marker. With no time, the
33
+ * time is midnight.
34
+ */
35
+ export declare function parseLocaleDateText(text: string, locale: string, withTime: boolean): string | null;
36
+ /** The local calendar day of `d` as `YYYY-MM-DD`. */
37
+ export declare function toLocalDay(d: Date): string;
38
+ /** Local midnight of a `YYYY-MM-DD` day, for the calendar to select. */
39
+ export declare function fromLocalDay(day: string): Date | undefined;
@@ -24,7 +24,7 @@ interface ObjectSchemaLike {
24
24
  }
25
25
  /**
26
26
  * Derive sensible Record Picker columns from a referenced object's schema when
27
- * a lookup field declares no explicit `lookup_columns`.
27
+ * a lookup field declares no explicit `lookupColumns`.
28
28
  *
29
29
  * Priority:
30
30
  * 1. The referenced object's ADR-0085 `highlightFields` — the canonical
@@ -1,5 +1,35 @@
1
1
  import { default as React } from 'react';
2
2
  import { FileValueView } from './file-value.js';
3
+ /**
4
+ * The translated label and hint for a file the reader may not open
5
+ * (objectui#12339): a value that arrived as the refused marker
6
+ * `{ id, metadataRefused: true }` carries no name of its own, and drawing the
7
+ * generic `fields.file.fileFallback` for it would read as an ordinary file.
8
+ */
9
+ export declare function useRestrictedFileText(): {
10
+ label: string;
11
+ hint: string;
12
+ };
13
+ /**
14
+ * The restricted state of one file value (`view.metadataRefused`): a lock
15
+ * and the translated "Restricted file" label instead of a name, and ⛔ no
16
+ * thumbnail — no MIME type was served, so there is nothing to decide one on.
17
+ *
18
+ * It stays an anchor to the view's id-derived URL: whether this reader may
19
+ * download the file is the download endpoint's own decision, and it may
20
+ * answer `403 FILE_DOWNLOAD_DENIED`. The accessible name is the visible
21
+ * label; the hint rides on `title`.
22
+ *
23
+ * `compact` draws the lock alone, for a cell or tile that has no room for
24
+ * the label — the label then becomes the anchor's `aria-label`.
25
+ */
26
+ export declare function RestrictedFileAffordance({ view, className, compact, icon, }: {
27
+ view: FileValueView;
28
+ className?: string;
29
+ compact?: boolean;
30
+ /** Suppressed where the row already draws its own icon to the left. */
31
+ icon?: boolean;
32
+ }): React.ReactElement;
3
33
  /**
4
34
  * One file: a keyboard-reachable view/download link when it resolves, and the
5
35
  * caller's own `fallback` text when it does not.
@@ -0,0 +1,17 @@
1
+ import { FileRefusedValue } from '@objectstack/spec/data';
2
+ /**
3
+ * Is this value the platform's refused marker `{ id, metadataRefused: true }`?
4
+ *
5
+ * Judged by the spec's own closed `FileRefusedValueSchema`, not by a second
6
+ * hand-written shape here: an object with any other key is not the marker,
7
+ * and reads through `readFileValue`'s ordinary object arm.
8
+ */
9
+ export declare function isFileRefusedValue(value: unknown): value is FileRefusedValue;
10
+ /**
11
+ * One element of a field value as an editable widget re-submits it beside or
12
+ * after an edit (append, remove, crop): the refused marker is reduced to its
13
+ * bare `sys_file` id — the stored form — and ⛔ never sent back as the marker
14
+ * object, which is a read form no record write accepts. Every other element
15
+ * passes through untouched, as before.
16
+ */
17
+ export declare function fileValueForResubmit(value: unknown): unknown;