@object-ui/fields 17.3.0 → 17.6.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.
Files changed (265) hide show
  1. package/CHANGELOG.md +1655 -0
  2. package/README.md +1 -12
  3. package/dist/FieldEditWidget.d.ts +23 -3
  4. package/dist/currency.d.ts +46 -0
  5. package/dist/field-type-alias.d.ts +35 -1
  6. package/dist/index.cjs +4 -4
  7. package/dist/index.css +651 -0
  8. package/dist/index.d.ts +474 -85
  9. package/dist/index.js +5418 -5023
  10. package/dist/widgets/AddressField.d.ts +4 -12
  11. package/dist/widgets/AutoNumberField.d.ts +1 -1
  12. package/dist/widgets/AvatarField.d.ts +1 -1
  13. package/dist/widgets/BooleanField.d.ts +1 -1
  14. package/dist/widgets/CapabilityMultiSelectField.d.ts +1 -1
  15. package/dist/widgets/CheckboxesField.d.ts +1 -1
  16. package/dist/widgets/CodeField.d.ts +1 -1
  17. package/dist/widgets/ColorField.d.ts +1 -1
  18. package/dist/widgets/CurrencyField.d.ts +1 -1
  19. package/dist/widgets/DateField.d.ts +1 -1
  20. package/dist/widgets/DateTimeField.d.ts +1 -1
  21. package/dist/widgets/EmailField.d.ts +1 -1
  22. package/dist/widgets/FileField.d.ts +18 -2
  23. package/dist/widgets/FilterConditionField.d.ts +28 -1
  24. package/dist/widgets/FormulaField.d.ts +1 -1
  25. package/dist/widgets/FullscreenFieldEditor.d.ts +49 -152
  26. package/dist/widgets/GeolocationField.d.ts +1 -1
  27. package/dist/widgets/GridField.d.ts +68 -6
  28. package/dist/widgets/ImageField.d.ts +1 -1
  29. package/dist/widgets/LocationField.d.ts +1 -1
  30. package/dist/widgets/LookupField.d.ts +1 -1
  31. package/dist/widgets/MasterDetailField.d.ts +1 -1
  32. package/dist/widgets/MultiSelectField.d.ts +1 -1
  33. package/dist/widgets/NumberField.d.ts +1 -1
  34. package/dist/widgets/ObjectField.d.ts +1 -1
  35. package/dist/widgets/ObjectRefField.d.ts +1 -1
  36. package/dist/widgets/OptionsEmptyState.d.ts +21 -1
  37. package/dist/widgets/PasswordField.d.ts +1 -1
  38. package/dist/widgets/PercentField.d.ts +1 -1
  39. package/dist/widgets/PhoneField.d.ts +1 -1
  40. package/dist/widgets/QRCodeField.d.ts +1 -1
  41. package/dist/widgets/RadioField.d.ts +1 -1
  42. package/dist/widgets/RatingField.d.ts +1 -1
  43. package/dist/widgets/RecipientPickerField.d.ts +1 -1
  44. package/dist/widgets/RecordPickerDialog.d.ts +34 -10
  45. package/dist/widgets/RichTextField.d.ts +37 -1
  46. package/dist/widgets/SelectField.d.ts +26 -6
  47. package/dist/widgets/SignatureField.d.ts +32 -2
  48. package/dist/widgets/SliderField.d.ts +37 -2
  49. package/dist/widgets/SummaryField.d.ts +1 -1
  50. package/dist/widgets/TagsField.d.ts +1 -1
  51. package/dist/widgets/TextAreaField.d.ts +6 -2
  52. package/dist/widgets/TextField.d.ts +1 -1
  53. package/dist/widgets/TimeField.d.ts +1 -1
  54. package/dist/widgets/UrlField.d.ts +1 -1
  55. package/dist/widgets/UserField.d.ts +1 -1
  56. package/dist/widgets/VectorField.d.ts +1 -1
  57. package/dist/widgets/_cell-renderer-bridge.d.ts +1 -1
  58. package/dist/widgets/address-format.d.ts +88 -0
  59. package/dist/widgets/file-size-guard.d.ts +41 -0
  60. package/dist/widgets/lookupColumnDisplay.d.ts +93 -0
  61. package/dist/widgets/nativeDateValue.d.ts +37 -0
  62. package/dist/widgets/richTextDisplay.d.ts +80 -0
  63. package/dist/widgets/toDomProps.d.ts +30 -10
  64. package/dist/widgets/toHostGroupProps.d.ts +189 -0
  65. package/dist/widgets/types.d.ts +8 -3
  66. package/dist/widgets/useRecordQuery.d.ts +7 -1
  67. package/package.json +17 -14
  68. package/dist/FieldEditWidget.test.d.ts +0 -8
  69. package/dist/__tests__/DateCellRenderer.test.d.ts +0 -0
  70. package/dist/__tests__/NumberCellRenderer.test.d.ts +0 -0
  71. package/dist/__tests__/cell-truncation.test.d.ts +0 -0
  72. package/dist/__tests__/field-carrier-sdui.test.d.ts +0 -0
  73. package/dist/__tests__/lookupCellDisplayField.test.d.ts +0 -0
  74. package/dist/__tests__/lookupCellDisplayName.test.d.ts +0 -0
  75. package/dist/__tests__/lookupCellReferenceKey.test.d.ts +0 -0
  76. package/dist/__tests__/registry-collision.test.d.ts +0 -0
  77. package/dist/__tests__/resolveFieldCurrency.test.d.ts +0 -1
  78. package/dist/__tests__/spec-symbol-batch7.test.d.ts +0 -8
  79. package/dist/__tests__/validation-feedback.test.d.ts +0 -0
  80. package/dist/__tests__/widget-aria-invalid-e2e.test.d.ts +0 -0
  81. package/dist/__tests__/widget-aria-invalid-registry-e2e.test.d.ts +0 -0
  82. package/dist/__tests__/widget-dom-leak-e2e.test.d.ts +0 -0
  83. package/dist/__tests__/widget-props-contract.test.d.ts +0 -8
  84. package/dist/coerce-safe-value.test.d.ts +0 -1
  85. package/dist/complex-widgets.test.d.ts +0 -1
  86. package/dist/datetime-widgets.test.d.ts +0 -1
  87. package/dist/field-type-coverage.test.d.ts +0 -1
  88. package/dist/standard-widgets.test.d.ts +0 -0
  89. package/dist/widgets/AddressField.uniqueIds.test.d.ts +0 -0
  90. package/dist/widgets/CapabilityMultiSelectField.test.d.ts +0 -0
  91. package/dist/widgets/CharacterCount.d.ts +0 -56
  92. package/dist/widgets/CheckboxesField.cascade.test.d.ts +0 -0
  93. package/dist/widgets/GeolocationField.uniqueIds.test.d.ts +0 -0
  94. package/dist/widgets/GridField.test.d.ts +0 -1
  95. package/dist/widgets/ImageLightbox.test.d.ts +0 -1
  96. package/dist/widgets/LookupField.dependsOn.test.d.ts +0 -8
  97. package/dist/widgets/LookupField.gateHintLabel.test.d.ts +0 -8
  98. package/dist/widgets/LookupField.hydration.test.d.ts +0 -1
  99. package/dist/widgets/LookupField.idField.test.d.ts +0 -1
  100. package/dist/widgets/MultiSelectField.cascade.test.d.ts +0 -0
  101. package/dist/widgets/OptionsEmptyState.no-provider.test.d.ts +0 -0
  102. package/dist/widgets/OptionsEmptyState.test.d.ts +0 -0
  103. package/dist/widgets/PeoplePicker.test.d.ts +0 -8
  104. package/dist/widgets/PeoplePickerComponents.test.d.ts +0 -8
  105. package/dist/widgets/RadioField.cascade.test.d.ts +0 -0
  106. package/dist/widgets/RecipientPickerField.test.d.ts +0 -0
  107. package/dist/widgets/RecordPickerDialog.filterOptions.test.d.ts +0 -8
  108. package/dist/widgets/RecordPickerDialog.selectLabel.test.d.ts +0 -8
  109. package/dist/widgets/SelectField.cascade.test.d.ts +0 -0
  110. package/dist/widgets/TagsField.placeholder.no-provider.test.d.ts +0 -0
  111. package/dist/widgets/TagsField.placeholder.test.d.ts +0 -0
  112. package/dist/widgets/UserFieldPicker.test.d.ts +0 -8
  113. package/dist/widgets/__tests__/FilterConditionField.matchAll.test.d.ts +0 -8
  114. package/dist/widgets/__tests__/FilterConditionField.operators.test.d.ts +0 -8
  115. package/dist/widgets/__tests__/FullscreenFieldEditor.i18n.test.d.ts +0 -0
  116. package/dist/widgets/__tests__/FullscreenFieldEditor.no-provider.test.d.ts +0 -0
  117. package/dist/widgets/__tests__/RichTextField.fullscreenDisabled.test.d.ts +0 -0
  118. package/dist/widgets/__tests__/RichTextField.mobileFullscreen.test.d.ts +0 -0
  119. package/dist/widgets/__tests__/TextAreaField.characterCount.announcements.test.d.ts +0 -0
  120. package/dist/widgets/__tests__/TextAreaField.characterCount.i18n.test.d.ts +0 -0
  121. package/dist/widgets/__tests__/TextAreaField.characterCount.no-provider.test.d.ts +0 -0
  122. package/dist/widgets/__tests__/TextAreaField.fullscreenCharacterCount.i18n.test.d.ts +0 -0
  123. package/dist/widgets/__tests__/TextAreaField.fullscreenCharacterCount.test.d.ts +0 -0
  124. package/dist/widgets/__tests__/TextAreaField.fullscreenDisabled.test.d.ts +0 -0
  125. package/dist/widgets/__tests__/TextAreaField.mobileFullscreen.test.d.ts +0 -0
  126. package/dist/widgets/deriveLookupColumns.test.d.ts +0 -1
  127. package/dist/widgets/file-value.test.d.ts +0 -1
  128. package/dist/widgets/personDisplay.test.d.ts +0 -8
  129. package/dist/widgets/recentLookups.test.d.ts +0 -1
  130. package/dist/widgets/useRecordQuery.test.d.ts +0 -8
  131. package/dist/widgets/useUploadingSignal.test.d.ts +0 -1
  132. package/src/FieldEditWidget.test.ts +0 -133
  133. package/src/FieldEditWidget.tsx +0 -191
  134. package/src/__tests__/DateCellRenderer.test.tsx +0 -73
  135. package/src/__tests__/NumberCellRenderer.test.tsx +0 -60
  136. package/src/__tests__/cell-truncation.test.tsx +0 -96
  137. package/src/__tests__/field-carrier-sdui.test.tsx +0 -169
  138. package/src/__tests__/lookupCellDisplayField.test.tsx +0 -107
  139. package/src/__tests__/lookupCellDisplayName.test.tsx +0 -119
  140. package/src/__tests__/lookupCellReferenceKey.test.tsx +0 -57
  141. package/src/__tests__/registry-collision.test.ts +0 -91
  142. package/src/__tests__/resolveFieldCurrency.test.ts +0 -20
  143. package/src/__tests__/spec-symbol-batch7.test.ts +0 -126
  144. package/src/__tests__/validation-feedback.test.tsx +0 -361
  145. package/src/__tests__/widget-aria-invalid-e2e.test.tsx +0 -265
  146. package/src/__tests__/widget-aria-invalid-registry-e2e.test.tsx +0 -338
  147. package/src/__tests__/widget-dom-leak-e2e.test.tsx +0 -598
  148. package/src/__tests__/widget-props-contract.test.tsx +0 -142
  149. package/src/coerce-safe-value.test.ts +0 -34
  150. package/src/complex-widgets.test.tsx +0 -908
  151. package/src/currency.ts +0 -14
  152. package/src/datetime-widgets.test.tsx +0 -93
  153. package/src/field-type-alias.ts +0 -100
  154. package/src/field-type-coverage.test.ts +0 -67
  155. package/src/index.tsx +0 -2503
  156. package/src/standard-widgets.test.tsx +0 -357
  157. package/src/widgets/AddressField.tsx +0 -132
  158. package/src/widgets/AddressField.uniqueIds.test.tsx +0 -92
  159. package/src/widgets/AutoNumberField.tsx +0 -18
  160. package/src/widgets/AvatarField.tsx +0 -117
  161. package/src/widgets/BooleanField.tsx +0 -52
  162. package/src/widgets/CapabilityMultiSelectField.test.tsx +0 -168
  163. package/src/widgets/CapabilityMultiSelectField.tsx +0 -229
  164. package/src/widgets/CharacterCount.tsx +0 -263
  165. package/src/widgets/CheckboxesField.cascade.test.tsx +0 -143
  166. package/src/widgets/CheckboxesField.tsx +0 -136
  167. package/src/widgets/CodeField.tsx +0 -41
  168. package/src/widgets/ColorField.tsx +0 -53
  169. package/src/widgets/CurrencyField.tsx +0 -95
  170. package/src/widgets/DateField.tsx +0 -31
  171. package/src/widgets/DateTimeField.tsx +0 -37
  172. package/src/widgets/EmailField.tsx +0 -37
  173. package/src/widgets/FileField.tsx +0 -494
  174. package/src/widgets/FilterConditionField.tsx +0 -454
  175. package/src/widgets/FormulaField.tsx +0 -33
  176. package/src/widgets/FullscreenFieldEditor.tsx +0 -271
  177. package/src/widgets/GeolocationField.tsx +0 -167
  178. package/src/widgets/GeolocationField.uniqueIds.test.tsx +0 -88
  179. package/src/widgets/GridField.test.tsx +0 -406
  180. package/src/widgets/GridField.tsx +0 -966
  181. package/src/widgets/ImageCropperDialog.tsx +0 -245
  182. package/src/widgets/ImageField.tsx +0 -255
  183. package/src/widgets/ImageLightbox.test.tsx +0 -52
  184. package/src/widgets/ImageLightbox.tsx +0 -108
  185. package/src/widgets/LocationField.tsx +0 -57
  186. package/src/widgets/LookupField.dependsOn.test.tsx +0 -233
  187. package/src/widgets/LookupField.gateHintLabel.test.tsx +0 -135
  188. package/src/widgets/LookupField.hydration.test.tsx +0 -136
  189. package/src/widgets/LookupField.idField.test.tsx +0 -61
  190. package/src/widgets/LookupField.tsx +0 -1291
  191. package/src/widgets/MarkdownContent.tsx +0 -19
  192. package/src/widgets/MasterDetailField.tsx +0 -131
  193. package/src/widgets/MultiSelectField.cascade.test.tsx +0 -143
  194. package/src/widgets/MultiSelectField.tsx +0 -142
  195. package/src/widgets/NumberField.tsx +0 -49
  196. package/src/widgets/ObjectField.tsx +0 -94
  197. package/src/widgets/ObjectRefField.tsx +0 -121
  198. package/src/widgets/OptionsEmptyState.no-provider.test.tsx +0 -88
  199. package/src/widgets/OptionsEmptyState.test.tsx +0 -169
  200. package/src/widgets/OptionsEmptyState.tsx +0 -83
  201. package/src/widgets/PasswordField.tsx +0 -49
  202. package/src/widgets/PeoplePicker.test.tsx +0 -360
  203. package/src/widgets/PeoplePicker.tsx +0 -579
  204. package/src/widgets/PeoplePickerComponents.test.tsx +0 -104
  205. package/src/widgets/PercentField.tsx +0 -95
  206. package/src/widgets/PersonRow.tsx +0 -117
  207. package/src/widgets/PhoneField.tsx +0 -37
  208. package/src/widgets/QRCodeField.tsx +0 -103
  209. package/src/widgets/RadioField.cascade.test.tsx +0 -141
  210. package/src/widgets/RadioField.tsx +0 -111
  211. package/src/widgets/RatingField.tsx +0 -81
  212. package/src/widgets/RecipientPickerField.test.tsx +0 -157
  213. package/src/widgets/RecipientPickerField.tsx +0 -188
  214. package/src/widgets/RecordPickerDialog.filterOptions.test.tsx +0 -611
  215. package/src/widgets/RecordPickerDialog.selectLabel.test.tsx +0 -112
  216. package/src/widgets/RecordPickerDialog.tsx +0 -1316
  217. package/src/widgets/RichTextField.tsx +0 -176
  218. package/src/widgets/SelectField.cascade.test.tsx +0 -138
  219. package/src/widgets/SelectField.tsx +0 -162
  220. package/src/widgets/SelectionTray.tsx +0 -110
  221. package/src/widgets/SignatureField.tsx +0 -140
  222. package/src/widgets/SliderField.tsx +0 -39
  223. package/src/widgets/SummaryField.tsx +0 -31
  224. package/src/widgets/TagsField.placeholder.no-provider.test.tsx +0 -68
  225. package/src/widgets/TagsField.placeholder.test.tsx +0 -95
  226. package/src/widgets/TagsField.tsx +0 -88
  227. package/src/widgets/TextAreaField.tsx +0 -213
  228. package/src/widgets/TextField.tsx +0 -45
  229. package/src/widgets/TimeField.tsx +0 -31
  230. package/src/widgets/UrlField.tsx +0 -46
  231. package/src/widgets/UserField.tsx +0 -58
  232. package/src/widgets/UserFieldPicker.test.tsx +0 -78
  233. package/src/widgets/VectorField.tsx +0 -29
  234. package/src/widgets/__tests__/FilterConditionField.matchAll.test.ts +0 -66
  235. package/src/widgets/__tests__/FilterConditionField.operators.test.ts +0 -142
  236. package/src/widgets/__tests__/FullscreenFieldEditor.i18n.test.tsx +0 -248
  237. package/src/widgets/__tests__/FullscreenFieldEditor.no-provider.test.tsx +0 -116
  238. package/src/widgets/__tests__/RichTextField.fullscreenDisabled.test.tsx +0 -244
  239. package/src/widgets/__tests__/RichTextField.mobileFullscreen.test.tsx +0 -195
  240. package/src/widgets/__tests__/TextAreaField.characterCount.announcements.test.tsx +0 -417
  241. package/src/widgets/__tests__/TextAreaField.characterCount.i18n.test.tsx +0 -287
  242. package/src/widgets/__tests__/TextAreaField.characterCount.no-provider.test.tsx +0 -160
  243. package/src/widgets/__tests__/TextAreaField.fullscreenCharacterCount.i18n.test.tsx +0 -161
  244. package/src/widgets/__tests__/TextAreaField.fullscreenCharacterCount.test.tsx +0 -346
  245. package/src/widgets/__tests__/TextAreaField.fullscreenDisabled.test.tsx +0 -246
  246. package/src/widgets/__tests__/TextAreaField.mobileFullscreen.test.tsx +0 -175
  247. package/src/widgets/_cell-renderer-bridge.ts +0 -19
  248. package/src/widgets/deriveLookupColumns.test.ts +0 -112
  249. package/src/widgets/deriveLookupColumns.ts +0 -108
  250. package/src/widgets/file-value.test.ts +0 -230
  251. package/src/widgets/file-value.ts +0 -246
  252. package/src/widgets/openNativePicker.ts +0 -18
  253. package/src/widgets/personDisplay.test.ts +0 -113
  254. package/src/widgets/personDisplay.ts +0 -92
  255. package/src/widgets/recentLookups.test.ts +0 -45
  256. package/src/widgets/recentLookups.ts +0 -47
  257. package/src/widgets/toDomProps.ts +0 -175
  258. package/src/widgets/types.ts +0 -242
  259. package/src/widgets/useCascadingOptions.ts +0 -46
  260. package/src/widgets/useFieldTranslation.ts +0 -150
  261. package/src/widgets/useRecordQuery.test.ts +0 -174
  262. package/src/widgets/useRecordQuery.ts +0 -264
  263. package/src/widgets/useUploadingSignal.test.tsx +0 -62
  264. package/src/widgets/useUploadingSignal.ts +0 -20
  265. package/src/withFieldCarrier.tsx +0 -75
@@ -1,5 +1,5 @@
1
1
  import { default as React } from 'react';
2
- import { FieldWidgetComponentProps } from './types';
2
+ import { FieldWidgetComponentProps } from './types.js';
3
3
  /**
4
4
  * PercentField - Percentage input with configurable decimal precision
5
5
  * Stores values as decimals (0-1) and displays as percentages (0-100%)
@@ -1,5 +1,5 @@
1
1
  import { default as React } from 'react';
2
- import { FieldWidgetComponentProps } from './types';
2
+ import { FieldWidgetComponentProps } from './types.js';
3
3
  /**
4
4
  * PhoneField - Telephone number input with tel link in readonly mode
5
5
  * Renders as a clickable tel link when readonly, standard phone input otherwise
@@ -1,5 +1,5 @@
1
1
  import { default as React } from 'react';
2
- import { FieldWidgetComponentProps } from './types';
2
+ import { FieldWidgetComponentProps } from './types.js';
3
3
  /**
4
4
  * QR Code field widget - generates QR codes from text
5
5
  * Uses a simple SVG-based QR code generator
@@ -1,5 +1,5 @@
1
1
  import { default as React } from 'react';
2
- import { FieldWidgetComponentProps } from './types';
2
+ import { FieldWidgetComponentProps } from './types.js';
3
3
  /**
4
4
  * RadioField - choose exactly one value from a fixed option set, rendered as a
5
5
  * radio group. The stored value is the selected option value (string). Used for
@@ -1,5 +1,5 @@
1
1
  import { default as React } from 'react';
2
- import { FieldWidgetComponentProps } from './types';
2
+ import { FieldWidgetComponentProps } from './types.js';
3
3
  /**
4
4
  * Rating field widget - provides a star rating input
5
5
  * Supports numeric values from 0 to max (default 5)
@@ -1,4 +1,4 @@
1
1
  import { default as React } from 'react';
2
- import { FieldWidgetComponentProps } from './types';
2
+ import { FieldWidgetComponentProps } from './types.js';
3
3
  export declare function RecipientPickerField({ value, onChange, readonly, className, error, ...props }: FieldWidgetComponentProps<string>): React.JSX.Element;
4
4
  export default RecipientPickerField;
@@ -1,13 +1,11 @@
1
1
  import { default as React } from 'react';
2
2
  import { DataSource, LookupColumnDef, LookupFilterDef } from '@object-ui/types';
3
+ import { LookupCellRendererResolver } from './lookupColumnDisplay.js';
3
4
  /**
4
5
  * Cell renderer function signature — matches getCellRenderer from @object-ui/fields.
5
6
  * Accepts a field type and returns a React component that renders a formatted cell.
6
7
  */
7
- export type CellRendererResolver = (fieldType: string) => React.FC<{
8
- value: any;
9
- field: any;
10
- }>;
8
+ export type CellRendererResolver = LookupCellRendererResolver;
11
9
  /**
12
10
  * Filter column definition used by the inline filter bar.
13
11
  * A subset of LookupColumnDef enriched with filter-specific metadata.
@@ -126,13 +124,39 @@ export interface RecordPickerDialogProps {
126
124
  */
127
125
  lookupFilters?: LookupFilterDef[];
128
126
  /**
129
- * Hard filter constraints applied to every query, in QueryParams.$filter
130
- * record form. Unlike `lookupFilters`, entries here never surface in the
131
- * filter bar and cannot be overridden by user filter input — used for the
132
- * dependent (cascading) lookup chain, where the parent field's value MUST
133
- * scope the candidate set (#2215).
127
+ * Hard filter constraint applied to every query. Unlike `lookupFilters`,
128
+ * entries here never surface in the filter bar and cannot be overridden by
129
+ * user filter input.
130
+ *
131
+ * Two shapes, discriminated STRUCTURALLY (`Array.isArray`), because the two
132
+ * callers speak two legitimate vocabularies and neither should be bent into
133
+ * the other:
134
+ *
135
+ * - **`QueryParams.$filter` record form** (`{ account: 'a1' }`) — the
136
+ * dependent (cascading) lookup chain, where the parent field's value MUST
137
+ * scope the candidate set (#2215). Merged by KEY OVERWRITE, so a cascaded
138
+ * value REPLACES a stale `lookupFilters` entry on the same field instead of
139
+ * intersecting with it. That precedence is load-bearing: an `and` of both
140
+ * would ask for `account = 'stale' AND account = 'a1'` and return nothing.
141
+ * - **A spec `ViewFilterRule[]`** (`[{ field, operator, value? }]`) — an
142
+ * author's `record:related_list.add.picker.filter`, handed over VERBATIM
143
+ * (#3831). Lowered by `mergeFilterNodes`, the repo's single filter sink, so
144
+ * all 19 `VIEW_FILTER_OPERATORS` reach the wire — including the four
145
+ * (`before`, `after`, `is_empty`, `is_not_empty`) the record form has no
146
+ * `$op` for. No second operator vocabulary is introduced here: two already
147
+ * exist (the spec's `AST_OPERATOR_MAP`, data-objectstack's
148
+ * `FILTER_OPERATOR_ALIASES`) and #3948 is what a third costs.
149
+ *
150
+ * The discriminator is exact rather than heuristic — every AST node is an
151
+ * ARRAY and a rule is a plain OBJECT, the same predicate `toFilterNode` uses.
152
+ *
153
+ * Typed `unknown` rather than `Record< string, any >` on purpose: that type
154
+ * ACCEPTED a rule array (TypeScript lets an array satisfy a string index of
155
+ * `any`), the old object-spread merge then flattened it to
156
+ * `{"0": {...}, "1": {...}}`, and the query filtered on columns literally
157
+ * named `0`/`1` — type-check green, wrong query, no diagnostic anywhere.
134
158
  */
135
- baseFilter?: Record<string, any>;
159
+ baseFilter?: unknown;
136
160
  /**
137
161
  * Cell renderer resolver function.
138
162
  * When provided, columns with a `type` property will be rendered using the
@@ -1,5 +1,5 @@
1
1
  import { default as React } from 'react';
2
- import { FieldWidgetComponentProps } from './types';
2
+ import { FieldWidgetComponentProps } from './types.js';
3
3
  /**
4
4
  * Rich text field with markdown/HTML support
5
5
  * For now, this is a simple textarea. A full implementation would use
@@ -22,5 +22,41 @@ import { FieldWidgetComponentProps } from './types';
22
22
  * tolerant fallback in one of them. The affordance and dialog themselves come
23
23
  * from the shared `FullscreenFieldEditor`, so one form-level setting keeps
24
24
  * producing one behaviour across both widgets.
25
+ *
26
+ * ## Readonly display (objectui#5498)
27
+ *
28
+ * The readonly early return used to render `{value}` as a React TEXT CHILD,
29
+ * so a readonly field of any of the three types showed the user the markup
30
+ * SOURCE of their own content — a `markdown` field's asterisks and hashes, a
31
+ * `richtext` field's tags. The `prose` classes on that wrapper were the tell:
32
+ * they exist to style RENDERED rich content and there was none to style. Every
33
+ * other read surface — grid, kanban card, gallery, related list, dashboard
34
+ * record panel, record detail read mode — dispatches through `getCellRenderer`
35
+ * and renders the same stored bytes FORMATTED, so one field disagreed with
36
+ * itself depending on which surface it was read on, and this branch was the
37
+ * one disagreeing with the platform.
38
+ *
39
+ * It now renders through {@link richTextCellRenderer} — literally the same
40
+ * components `getCellRenderer` resolves, one table shared by both (see
41
+ * `./richTextDisplay.tsx`), so the html/richtext path carries that renderer's
42
+ * `sanitizeHtml` trust boundary rather than a second hand-rolled escape, and a
43
+ * future change to either pipeline moves both surfaces at once.
44
+ *
45
+ * ## Host plumbing (objectui#4810)
46
+ *
47
+ * The editable branch had no `toDomProps` at all, so everything
48
+ * `<FormControl>`'s Radix `Slot` hands down — the field's `id`, the
49
+ * `aria-describedby` naming its `<FormDescription>` and `<FormMessage>` —
50
+ * arrived as props and landed on no element. Measured on a real form: the
51
+ * visible label's `for` pointed at an id nothing carried (`for=DANGLING`) and
52
+ * the rendered description had zero consumers, for all three registry keys
53
+ * (`markdown` / `html` / `richtext`) since they are this one widget.
54
+ *
55
+ * The fix is objectui#3318's standing recipe — `toDomProps(props)` spread onto
56
+ * the REAL focusable control, which here is the `<Textarea>` inside
57
+ * `RichTextEditorSurface`, never the wrapper `<div>`s (a non-focusable wrapper
58
+ * carrying the id would satisfy `document.getElementById` and still leave the
59
+ * label inert). Only the inline surface receives it; see `domProps` there for
60
+ * why the dialog's copy must not.
25
61
  */
26
62
  export declare function RichTextField({ value, onChange, field, readonly, error, ...props }: FieldWidgetComponentProps<string>): React.JSX.Element;
@@ -1,15 +1,35 @@
1
1
  import { default as React } from 'react';
2
- import { FieldWidgetComponentProps } from './types';
2
+ import { FieldWidgetComponentProps } from './types.js';
3
3
  /**
4
4
  * SelectField - dropdown selection widget.
5
5
  *
6
6
  * A field declared `multiple: true` selects zero-or-more values (spec:
7
7
  * `multiple` is valid on `select`), so it renders the multi-value chip picker
8
- * — the same widget the `multiselect` type uses. Delegating here (rather than
9
- * only at a type-resolution layer) means every surface that renders the
10
- * `select` widget — the object form, the inline grid editor, and
11
- * `ActionParamDialog` — inherits multi-select identically, with no drift
12
- * between them. Single-value selects keep the cascading dropdown below.
8
+ * — the same widget the `multiselect` type uses. Single-value selects keep the
9
+ * cascading dropdown below.
10
+ *
11
+ * The FORM no longer arrives here with `multiple` (objectui#3986):
12
+ * `mapFieldTypeToFormType` now resolves a `select` + `multiple: true` field to
13
+ * `field:multiselect`, so the object-form path renders `MultiSelectField`
14
+ * directly under its own registry id — which is what carries the
15
+ * `labelling: 'group'` declaration the host label needs. Deciding the widget at
16
+ * the type-resolution layer is what keeps the declaration and the render from
17
+ * disagreeing; a delegation invisible to the resolver could not.
18
+ *
19
+ * The branch below stays because it is NOT dead — measured entrances that reach
20
+ * it with `multiple` set, none of which consult that resolver:
21
+ *
22
+ * - **the inline grid editor** — `FieldEditWidget` looks `select` up in its own
23
+ * `EDIT_WIDGETS` table, which SHORT-CIRCUITS before the alias map is
24
+ * consulted, and forwards the whole metadata object as `field`;
25
+ * - **`ActionParamDialog`** — `paramToField` resolves through
26
+ * `resolveFormWidgetType`, which likewise returns `select` from
27
+ * `fieldWidgetMap` before reaching the alias map, and carries
28
+ * `multiple: param.multiple` on the field it builds;
29
+ * - **hand-written SDUI** — a `{ type: 'field:select' }` node whose metadata
30
+ * declares `multiple`, which addresses this widget by name.
31
+ *
32
+ * All three then inherit multi-select from here identically, with no drift.
13
33
  *
14
34
  * Both branches resolve per-option `visibleWhen` cascading / role-gating through
15
35
  * the shared {@link useCascadingOptions} hook (#2715), so single and multi stay
@@ -1,7 +1,37 @@
1
1
  import { default as React } from 'react';
2
- import { FieldWidgetComponentProps } from './types';
2
+ import { FieldWidgetComponentProps } from './types.js';
3
3
  /**
4
4
  * Signature field widget - provides a signature pad for capturing signatures
5
5
  * Outputs signature as a base64 PNG image
6
+ *
7
+ * ## Naming, and the half that is deliberately absent (objectui#3318)
8
+ *
9
+ * This widget forwarded nothing a host handed it — every branch below wrote its
10
+ * attributes by hand — so its visible label pointed `for` at an id no element
11
+ * in the row carried, and the rendered help text had zero consumers. Measured
12
+ * on `origin/main`, a real form, this field required and freshly failed:
13
+ *
14
+ * ```
15
+ * signature labelFor=…-form-item -> DANGLING descConsumers=0 ids=[]
16
+ * ```
17
+ *
18
+ * The name and the description arrive here, on the container, through
19
+ * {@link toHostGroupProps} — the objectui#3961 route for a widget whose surface
20
+ * no `<label for>` can reach (`<canvas>` is not a labelable element), and which
21
+ * therefore declares `labelling: 'group'` in `FIELD_WIDGET_LABELLING`.
22
+ * `'instead-of-the-inputs'` because this field renders no input of its own for
23
+ * the description to land on: the Clear button is auxiliary — it acts on the
24
+ * value, it is not the value's control — the same reading that put
25
+ * `geolocation`'s focusable "View on map" link on that side of the split.
26
+ *
27
+ * `aria-invalid` and `aria-required` are NOT here, and their absence is a
28
+ * verdict rather than an omission. The drawing surface has no keyboard path at
29
+ * all (measured: this row offers zero focusable elements while empty — the only
30
+ * candidate, Clear, is `disabled` until something has been drawn), so there is
31
+ * no element assistive technology would read a control state from. Marking the
32
+ * container instead would announce a state with no control behind it — the
33
+ * non-focusable-wrapper move objectui#3291 and #3318 both draw the line
34
+ * against. The type is recorded in that sweep's NOT_APPLICABLE ledger with a
35
+ * guard that turns red the day a real control appears here.
6
36
  */
7
- export declare function SignatureField({ value, onChange, readonly }: FieldWidgetComponentProps<string>): React.JSX.Element;
37
+ export declare function SignatureField({ value, onChange, readonly, ...props }: FieldWidgetComponentProps<string>): React.JSX.Element;
@@ -1,7 +1,42 @@
1
1
  import { default as React } from 'react';
2
- import { FieldWidgetComponentProps } from './types';
2
+ import { FieldWidgetComponentProps } from './types.js';
3
3
  /**
4
4
  * Slider field widget - provides a range slider input
5
5
  * Supports numeric values with configurable min, max, and step
6
+ *
7
+ * ## Where the host's facts go, and why (objectui#3318)
8
+ *
9
+ * This widget used to read exactly two keys off its props (`disabled`,
10
+ * `className`) and forward nothing else, so everything `<FormControl>`'s Slot
11
+ * delivered was dropped on the floor. Measured on `origin/main`, a real form
12
+ * with a required slider that had just failed validation:
13
+ *
14
+ * ```
15
+ * ariaInvalidTrue=[] labelFor=…-form-item -> DANGLING
16
+ * descConsumers=0 ids=[] <- no element of the row carried an id at all
17
+ * ```
18
+ *
19
+ * Three channels, one cause. The DOM pass-through now goes to the THUMB — the
20
+ * `span[role="slider"]` a keyboard user lands on, which is where ARIA state
21
+ * belongs — through the `thumbProps` the primitive grew for this
22
+ * (`lib/slider-thumb.ts`; the shadcn `ui/slider.tsx` renders its thumb
23
+ * internally and exports only `Slider`, so it cannot be addressed from here
24
+ * the way objectui#3306 addressed `SelectTrigger`).
25
+ *
26
+ * The split of which keys stay on Root is `SelectField`'s, key for key:
27
+ *
28
+ * - `name`: Root owns the hidden input that takes part in form submission; on
29
+ * a `span` it is not even DOM-legal, and is exactly the leak objectui#3291
30
+ * sweeps for.
31
+ * - `disabled`: Root is the single authority (it disables the thumb and the
32
+ * hidden input together); forwarding the raw prop to the thumb as well would
33
+ * give the state a second, OR-merged author.
34
+ *
35
+ * The visible label reaches the thumb by IDREF, not by `for`: a
36
+ * `span[role="slider"]` is not one of HTML's labelable elements, so this widget
37
+ * is declared `labelling: 'group'` for the same reason `file` is — see
38
+ * `FIELD_WIDGET_LABELLING` in `../index` and objectui#3961. No extra
39
+ * `role="group"` wrapper: there is one control here, and `slider` is a role
40
+ * that carries a name perfectly well on its own.
6
41
  */
7
- export declare function SliderField({ value, onChange, field, readonly, ...props }: FieldWidgetComponentProps<number>): React.JSX.Element;
42
+ export declare function SliderField({ value, onChange, field, readonly, error, ...props }: FieldWidgetComponentProps<number>): React.JSX.Element;
@@ -1,5 +1,5 @@
1
1
  import { default as React } from 'react';
2
- import { FieldWidgetComponentProps } from './types';
2
+ 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
@@ -1,5 +1,5 @@
1
1
  import { default as React } from 'react';
2
- import { FieldWidgetComponentProps } from './types';
2
+ import { FieldWidgetComponentProps } from './types.js';
3
3
  /**
4
4
  * TagsField - free-form list of string tags. Type a value and press Enter (or
5
5
  * comma) to add it; click a tag's × to remove it. The stored value is a
@@ -1,5 +1,5 @@
1
1
  import { default as React } from 'react';
2
- import { FieldWidgetComponentProps } from './types';
2
+ import { FieldWidgetComponentProps } from './types.js';
3
3
  /**
4
4
  * TextAreaField - Multi-line text input widget
5
5
  * Supports configurable row count and preserves whitespace in readonly mode.
@@ -35,7 +35,11 @@ import { FieldWidgetComponentProps } from './types';
35
35
  * ## The character counter (objectui#3408, shared across surfaces in #3417)
36
36
  *
37
37
  * Both editing surfaces below — the inline `<Textarea>` and the fullscreen
38
- * dialog's — render the SAME `CharacterCount`, which owns the three-node
38
+ * dialog's — render the SAME `CharacterCount`. It lives in
39
+ * `@object-ui/components` since objectui#3439, hoisted there so the form
40
+ * renderer's built-in `textarea` branch renders that very component too rather
41
+ * than growing a third copy; this widget's use of it is unchanged. It owns the
42
+ * three-node
39
43
  * GOV.UK shape (decorative digits / accessible description / gated status
40
44
  * region) and the debounce and threshold behind it. Until #3417 the fullscreen
41
45
  * footer had its own hand-written copy that was digits and nothing else, which
@@ -1,5 +1,5 @@
1
1
  import { default as React } from 'react';
2
- import { FieldWidgetComponentProps } from './types';
2
+ import { FieldWidgetComponentProps } from './types.js';
3
3
  /**
4
4
  * TextField - Standard single-line or multi-line text input
5
5
  * Automatically renders as a textarea when rows are configured in field metadata
@@ -1,5 +1,5 @@
1
1
  import { default as React } from 'react';
2
- import { FieldWidgetComponentProps } from './types';
2
+ import { FieldWidgetComponentProps } from './types.js';
3
3
  /**
4
4
  * TimeField - Time picker input widget
5
5
  * Uses native time input for hour and minute selection
@@ -1,5 +1,5 @@
1
1
  import { default as React } from 'react';
2
- import { FieldWidgetComponentProps } from './types';
2
+ import { FieldWidgetComponentProps } from './types.js';
3
3
  /**
4
4
  * UrlField - URL input with clickable link in readonly mode
5
5
  * Validates URLs to only render http/https links for security
@@ -1,3 +1,3 @@
1
1
  import { default as React } from 'react';
2
- import { FieldWidgetComponentProps } from './types';
2
+ import { FieldWidgetComponentProps } from './types.js';
3
3
  export declare function UserField(props: FieldWidgetComponentProps<any>): React.JSX.Element;
@@ -1,5 +1,5 @@
1
1
  import { default as React } from 'react';
2
- import { FieldWidgetComponentProps } from './types';
2
+ import { FieldWidgetComponentProps } from './types.js';
3
3
  /**
4
4
  * VectorField - Vector/embedding data display
5
5
  * Shows vector embeddings in a read-only format
@@ -1,3 +1,3 @@
1
- import { CellRendererResolver } from './RecordPickerDialog';
1
+ import { CellRendererResolver } from './RecordPickerDialog.js';
2
2
  export declare function setCellRendererResolver(resolver: CellRendererResolver): void;
3
3
  export declare function getCellRendererResolver(): CellRendererResolver | undefined;
@@ -0,0 +1,88 @@
1
+ import { AddressValue } from '@objectstack/spec/data';
2
+ /**
3
+ * Address data structure — `z.input` of `@objectstack/spec`'s
4
+ * `AddressValueSchema`, which is what the platform stores and what
5
+ * `/api/v1/data/**` serves back.
6
+ *
7
+ * The postal code is spelled `postalCode` (objectstack#5143). `AddressField`
8
+ * used to read and write `zipCode`, a key that appears nowhere in the spec, so
9
+ * the stored postal code never reached the input — and whatever the user then
10
+ * typed into that apparently-empty box was written under a key
11
+ * `AddressValueSchema` strips: lost outright on a new record, and on an
12
+ * existing one silently discarded while the stale stored value survived. (A
13
+ * postal code nobody touched did survive an unrelated edit, because the write
14
+ * spread the whole stored object through; the issue's account of that half is
15
+ * corrected in `AddressField.postalCode.test.tsx`.) One name, on both sides:
16
+ * the contract's.
17
+ *
18
+ * IMPORTED rather than declared since objectui#4167. rc.6 publishes
19
+ * `AddressValue` under this exact name, and the local copy — whose own comment
20
+ * already claimed to be "the part names of `AddressSchema`" — declared five of
21
+ * the seven parts. `countryCode` and `formatted` were missing, which is
22
+ * objectstack#4115's failure class precisely: a canonical CLAIM over a narrower
23
+ * shape, believed by the next reader. The claim is now the code.
24
+ *
25
+ * `AddressField` still renders five inputs and {@link formatAddress} still
26
+ * composes five parts, and that is a deliberate split rather than an omission:
27
+ * `formatted` is a derived one-line rendering of the parts (what this module
28
+ * computes) and `countryCode` is the ISO pair for `country`, neither of which a
29
+ * human types into a part box. Binding the type does not publish them as
30
+ * inputs — it stops the type from asserting the platform cannot store them, and
31
+ * it means a write from the widget no longer looks type-clean while dropping a
32
+ * key the contract carries: the `{ ...address }` spread in `handleFieldChange`
33
+ * preserves both, and now says so.
34
+ *
35
+ * The declaration moved here (objectui#4037) so the display cell renderer and
36
+ * the widget share one definition; the SOURCE of that definition is the spec.
37
+ */
38
+ export type { AddressValue };
39
+ /**
40
+ * The shape written by builds up to and including 17.0.0-rc.1, whose postal
41
+ * code landed under `zipCode` (objectstack#5143). Read-time compatibility ONLY,
42
+ * and deliberately not part of {@link AddressValue}: authoring code must not be
43
+ * able to spell `zipCode`, and nothing here ever writes it back — the first
44
+ * edit of any part normalizes the record onto `postalCode` (see
45
+ * `AddressField`'s `handleFieldChange`). This is not a producer alias to be
46
+ * honoured forever; it exists to carry data THIS widget mis-wrote, and can go
47
+ * once no such data remains.
48
+ */
49
+ export type LegacyAddressValue = AddressValue & {
50
+ zipCode?: string;
51
+ };
52
+ /** Postal code of a stored address, preferring the canonical key. */
53
+ export declare function readPostalCode(addr: AddressValue): string | undefined;
54
+ /**
55
+ * Formats a stored address as a single line, in the part order the reader's
56
+ * locale writes addresses in.
57
+ *
58
+ * Two orders, and the second is exactly the first reversed:
59
+ *
60
+ * - **small-to-large** (default; `en` and every Latin/Cyrillic/Arabic locale
61
+ * this repo ships) — `Street, City, State ZIP, Country`. Unchanged from
62
+ * what `AddressField`'s readonly branch has always produced, so English and
63
+ * provider-less rendering are byte-identical to before.
64
+ * - **large-to-small** ({@link LARGE_TO_SMALL_LANGUAGES}) — `Country, ZIP
65
+ * State, City, Street`. A `zh` console rendering a Chinese address
66
+ * largest-first is the whole point of objectui#4028: `中国, 310000 浙江,
67
+ * 杭州, 中策路 1 号` rather than the US-ordered line it produced before.
68
+ *
69
+ * Only the ORDER varies. The separator stays `', '` in both, because that is
70
+ * not what the issue reports and because these lines carry mixed-script data —
71
+ * a US address read on a `zh` console would look stranger under fullwidth
72
+ * punctuation than under the comma it already has.
73
+ *
74
+ * The state/postal-code pair keeps its one comma-delimited group in both
75
+ * orders, space-separated inside it ("CA 94102" / "310000 浙江"); either part
76
+ * alone occupies the group by itself. Missing parts are dropped rather than
77
+ * spaced over, so a street-only address is `"中策路 1 号"` and never `", , ,"`.
78
+ *
79
+ * Returns `''` when no part is usable — callers decide what an empty address
80
+ * looks like (an `EmptyValue` placeholder for the cell renderer), and a value
81
+ * carrying no recognized part at all is NOT silently blanked: see
82
+ * `AddressCellRenderer`, which falls back to JSON so unknown data stays visible.
83
+ *
84
+ * @param locale - BCP-47 tag from `useDisplayLocale()`. Omitted (tests, and any
85
+ * caller with no React context) means the small-to-large default, which is
86
+ * what this function did unconditionally before objectui#4028.
87
+ */
88
+ export declare function formatAddress(addr: AddressValue, locale?: string): string;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * The client-side `maxSize` guard shared by the file and image widgets
3
+ * (objectui#4141).
4
+ *
5
+ * Why this is shared rather than mirrored per widget: the guard is the only
6
+ * thing standing between a declared `maxSize` and a real `presigned → PUT →
7
+ * complete` round trip, and `paramToField` copies `maxSize` onto the field
8
+ * config for **every** param type regardless of which widget ends up rendering
9
+ * it. So a widget that re-derives the check drifts silently — the image param
10
+ * shipped receiving the constraint and never reading it, while the file param
11
+ * rejected the identical pick, and nothing failed until QA drove the two side by
12
+ * side. One exported function makes the difference visible: a widget either
13
+ * calls it or conspicuously does not.
14
+ *
15
+ * Both widgets deliberately share the `fields.file.exceedsMaxSize` key too. The
16
+ * sentence names a file and a limit and says nothing file-widget-specific, it is
17
+ * already translated in all 11 built-in locales, and a parallel `fields.image.*`
18
+ * twin would be the same sentence maintained twice — drifting the moment one
19
+ * translator rewords one of them.
20
+ */
21
+ /**
22
+ * Minimal shape of the i18next `t` this module needs, matching the established
23
+ * `TranslateFn` pattern in `app-shell/src/providers/writeWarningToast`.
24
+ */
25
+ export type TranslateFn = (key: string, options?: Record<string, unknown>) => string;
26
+ /**
27
+ * The rejection message for a pick that exceeds `maxSize`, or `null` when the
28
+ * pick is within the limit — so a caller reads it as `if (err) reject`.
29
+ *
30
+ * A falsy `maxSize` (undeclared, or an explicit `0`) means **unrestricted**,
31
+ * preserving the `if (maxSize && …)` semantics the file widget has always
32
+ * applied: an undeclared limit must never turn into a 0-byte one.
33
+ *
34
+ * Takes `name` and `size` separately instead of a `File` because the image
35
+ * widget's crop path validates a re-encoded `Blob`, which carries no name of its
36
+ * own — that name comes from the cropper's output.
37
+ */
38
+ export declare function maxSizeError(t: TranslateFn, pick: {
39
+ name: string;
40
+ size: number;
41
+ }, maxSize: number | undefined): string | null;
@@ -0,0 +1,93 @@
1
+ import { default as React } from 'react';
2
+ import { LookupColumnDef } from '@object-ui/types';
3
+ /**
4
+ * Cell renderer function signature — matches `getCellRenderer` from
5
+ * `@object-ui/fields`. Re-declared here (rather than imported from
6
+ * RecordPickerDialog) so this module sits BELOW both consumers in the import
7
+ * graph and neither surface can pull the other in.
8
+ */
9
+ export type LookupCellRendererResolver = (fieldType: string) => React.FC<{
10
+ value: any;
11
+ field: any;
12
+ }>;
13
+ /**
14
+ * A select option as the object schema declares it — `{ value, label }` plus
15
+ * whatever the renderer also reads (`color`, …), which the i18n translation
16
+ * carries through untouched.
17
+ */
18
+ export type SchemaOption = {
19
+ value: any;
20
+ label: string;
21
+ [key: string]: any;
22
+ };
23
+ /** Translate a schema field's options through the shared i18n option path. */
24
+ export type OptionTranslator = (objectName: string, fieldName: string, options: SchemaOption[]) => SchemaOption[];
25
+ /**
26
+ * Normalise a lookup_columns entry (string | LookupColumnDef) into a
27
+ * concrete LookupColumnDef object.
28
+ */
29
+ export declare function normalizeColumn(col: string | LookupColumnDef): LookupColumnDef;
30
+ /**
31
+ * Pretty-print a field name as a column header label.
32
+ * Converts snake_case / camelCase to Title Case.
33
+ */
34
+ export declare function fieldToLabel(field: string): string;
35
+ /**
36
+ * Resolve a field's select options from the referenced object's schema field
37
+ * definition (`fieldsMeta[field]`), translated through the shared i18n option
38
+ * path.
39
+ *
40
+ * This is the SINGLE source for every surface that needs option labels: the
41
+ * picker table cells (#3333), the picker's filter panel select inputs (#3336)
42
+ * and the inline dropdown's option preview (#5492) — so a picker column, the
43
+ * filter input for that same column and the dropdown row can never disagree
44
+ * about what an option is called.
45
+ *
46
+ * Returns `undefined` when the schema field declares no options: a select
47
+ * field with no authored options genuinely has nothing to offer, and
48
+ * synthesising entries from the loaded page's raw stored values would paper
49
+ * over the metadata gap with a list that changes per page.
50
+ */
51
+ export declare function resolveSchemaOptions(meta: {
52
+ options?: unknown;
53
+ } | undefined, objectName: string, fieldName: string, translateOptions: OptionTranslator): SchemaOption[] | undefined;
54
+ /**
55
+ * Field descriptors handed to the type-aware cell renderers, enriched from the
56
+ * referenced object's schema (`fieldsMeta`) the same way the list view enriches
57
+ * its columns. This is what lets a `select` column resolve its option label
58
+ * (options + i18n) instead of title-casing the raw value (#3333), and what
59
+ * carries `reference` / `reference_to` through to the lookup cell renderer so
60
+ * an unresolved foreign-key id resolves to a name (#5492).
61
+ *
62
+ * Columns whose def carries no `type` inherit the schema field's type, so
63
+ * string-authored `lookup_columns` format identically to object-authored ones.
64
+ */
65
+ export declare function buildLookupColumnDescriptors(columns: LookupColumnDef[], fieldsMeta: Record<string, any> | undefined, objectName: string, translateOptions: OptionTranslator): Record<string, any>;
66
+ export interface LookupColumnRenderContext {
67
+ /** Field descriptors from {@link buildLookupColumnDescriptors}. */
68
+ descriptors: Record<string, any>;
69
+ /** Type-aware renderer resolver; when absent every cell degrades to text. */
70
+ cellRenderer?: LookupCellRendererResolver;
71
+ /** The referenced object's `titleFormat`, applied to the display column. */
72
+ titleFormat?: string | null;
73
+ /** The lookup's display field — the column the title template stands in for. */
74
+ displayField?: string;
75
+ /** Locale for the plain-text `$date` fallback. */
76
+ displayLocale?: string;
77
+ }
78
+ /**
79
+ * Render one column of one candidate record.
80
+ *
81
+ * Order:
82
+ * 1. `titleFormat` template, for the display column only — so users see a
83
+ * human-readable name instead of a raw id when the display field
84
+ * (commonly defaulted to `name`) does not exist on the record.
85
+ * 2. the type-aware cell renderer for the column's field descriptor.
86
+ * 3. a plain-text fallback for columns with no descriptor / no resolver.
87
+ *
88
+ * Step 3 never blanks a populated value: an object with no recognised shape
89
+ * keeps its JSON and a primitive keeps its `String()` form. That floor is
90
+ * deliberate — an unresolved reference showing its bare id is strictly better
91
+ * than a silently empty column (objectui#5492).
92
+ */
93
+ export declare function renderLookupColumnValue(record: any, col: LookupColumnDef, ctx: LookupColumnRenderContext): React.ReactNode;
@@ -0,0 +1,37 @@
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
+ * Stored value → `<input type="date">` value (`YYYY-MM-DD`).
10
+ *
11
+ * A leading `YYYY-MM-DD` is passed through VERBATIM rather than re-parsed: a
12
+ * date-only string is parsed as UTC midnight by `new Date()`, so reading local
13
+ * calendar components back out of it shifts the day by one everywhere west of
14
+ * Greenwich. `2026-06-17` must stay June 17th in every zone.
15
+ */
16
+ export declare function toDateInputValue(value: unknown): string;
17
+ /**
18
+ * Stored value → `<input type="datetime-local">` value (`YYYY-MM-DDTHH:mm`),
19
+ * expressed in the viewer's local wall clock — the same instant the detail
20
+ * page shows, so opening the editor never appears to move the time.
21
+ *
22
+ * A value that is ALREADY a zone-less local datetime is truncated to minutes
23
+ * in place instead of being round-tripped through `Date`, which would
24
+ * reinterpret it and then re-render it, twice risking an off-by-a-zone shift.
25
+ */
26
+ export declare function toDateTimeInputValue(value: unknown): string;
27
+ /**
28
+ * `<input type="datetime-local">` value → stored value (ISO-8601, UTC).
29
+ *
30
+ * The control hands us a zone-less local wall clock; `new Date()` reads that
31
+ * form as local time, which is the inverse of what `toDateTimeInputValue`
32
+ * wrote — the pair is what keeps read and write on one basis.
33
+ *
34
+ * An unparseable string is returned untouched rather than blanked: dropping a
35
+ * value the user can see in the box would be a data loss dressed up as a fix.
36
+ */
37
+ export declare function fromDateTimeInputValue(value: string): string;