@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
@@ -0,0 +1,80 @@
1
+ import { default as React } from 'react';
2
+ import { CellRendererProps } from '../index.js';
3
+ /**
4
+ * Renders `markdown` values as formatted GFM markdown (lazy-loaded, sanitized)
5
+ * instead of the raw markup string.
6
+ *
7
+ * Markdown ONLY. `MarkdownContent` runs react-markdown with no `rehype-raw`, so
8
+ * raw HTML in the string is not parsed and never reaches the DOM — that is this
9
+ * renderer's trust boundary, not an oversight, and it must stay that way
10
+ * (objectui#5452). `richtext` used to be routed here too and rendered as a
11
+ * COMPLETELY EMPTY cell, because a richtext value is entirely HTML and this
12
+ * pipeline drops all of it; see {@link HtmlCellRenderer}, which is where that
13
+ * type belongs. Loosening this pipeline to pass raw HTML through would have
14
+ * "fixed" one type by moving every `markdown` cell's trust boundary.
15
+ */
16
+ export declare function MarkdownCellRenderer({ value }: CellRendererProps): React.ReactElement;
17
+ /**
18
+ * Renders an `html` value as sanitized, formatted HTML instead of raw markup.
19
+ *
20
+ * Also the renderer for `richtext` (objectui#5452). Both types store an HTML
21
+ * string: the spec's `Field.richtext` is documented as "Formatted content with
22
+ * HTML/WYSIWYG", the showcase seed's own specimen is
23
+ * `<p>Rich <strong>text</strong></p>`, and this repo's designer bridge already
24
+ * maps `richtext` onto its `html` type. {@link sanitizeHtml} above removes
25
+ * script/style/iframe/object/embed blocks, inline event handlers and
26
+ * `javascript:` URLs, and touches nothing a rich-text editor legitimately
27
+ * emits — headings, paragraphs, emphasis, lists, links, quotes all survive, so
28
+ * routing `richtext` here restores the content rather than trading a blank cell
29
+ * for a mangled one.
30
+ */
31
+ export declare function HtmlCellRenderer({ value }: CellRendererProps): React.ReactElement;
32
+ /**
33
+ * Field type → display pipeline, for the three types `RichTextField` serves.
34
+ *
35
+ * THE table, singular. `getCellRenderer`'s standard map spreads it (so the
36
+ * grid, kanban, gallery, related list, dashboard panel and detail read mode
37
+ * resolve through these entries) and `RichTextField`'s readonly branch indexes
38
+ * it (so a readonly form field renders through the very same component). A
39
+ * change here moves every read surface at once; there is no second copy left
40
+ * to forget.
41
+ *
42
+ * `richtext` reads through the HTML renderer — NOT the markdown one, which
43
+ * drops raw HTML and therefore rendered every populated richtext value as a
44
+ * blank cell (objectui#5452).
45
+ */
46
+ export declare const RICH_TEXT_CELL_RENDERERS: {
47
+ readonly markdown: typeof MarkdownCellRenderer;
48
+ readonly html: typeof HtmlCellRenderer;
49
+ readonly richtext: typeof HtmlCellRenderer;
50
+ };
51
+ /** The rich-content field types, i.e. the keys of {@link RICH_TEXT_CELL_RENDERERS}. */
52
+ export type RichTextFieldType = keyof typeof RICH_TEXT_CELL_RENDERERS;
53
+ /**
54
+ * The SYNTAX a rich-content type stores, derived from the renderer that type
55
+ * resolves to rather than declared a second time.
56
+ *
57
+ * This is what `RichTextField`'s editor header names ("Format: markdown"), and
58
+ * deriving it from {@link RICH_TEXT_CELL_RENDERERS} is the point: the header
59
+ * used to be computed as `field.format || 'markdown'`, and since `format` is
60
+ * declared on no rich-content field type (only on `date`/`datetime`/`time`/
61
+ * `phone`/`auto_number`), it read `undefined` for every real field and labelled
62
+ * an `html` field "Format: markdown" — a label pointing at a pipeline the value
63
+ * does not go through. Derived, the label cannot disagree with the renderer.
64
+ *
65
+ * `undefined` for a type with no pipeline, rather than a guessed default: the
66
+ * caller has to say what it does with "not a rich-content type" instead of
67
+ * silently getting the markdown answer, which is the bug this replaces.
68
+ */
69
+ export declare function richTextSyntax(fieldType: string): 'markdown' | 'html' | undefined;
70
+ /**
71
+ * The display pipeline a field type reads through, or `undefined` when the
72
+ * type is not one of the three rich-content types.
73
+ *
74
+ * The lookup is a function rather than a raw index so the "not a rich-content
75
+ * type" answer is in the RETURN TYPE. Indexing {@link RICH_TEXT_CELL_RENDERERS}
76
+ * directly hands back a non-optional component for any key the caller casts,
77
+ * so the miss becomes a runtime `undefined` the compiler has been told cannot
78
+ * happen — and a caller that checks for it reads as dead code.
79
+ */
80
+ export declare function richTextCellRenderer(fieldType: string): React.FC<CellRendererProps> | undefined;
@@ -1,10 +1,4 @@
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
- */
1
+ import { DomProps as CoreDomProps } from '@object-ui/core';
8
2
  /**
9
3
  * The RUNTIME EXECUTOR of the "DOM pass-through" section of
10
4
  * {@link FieldWidgetComponentProps} (objectui#3291).
@@ -59,13 +53,39 @@
59
53
  * open spread. If a field node should be able to author one, DECLARE it on
60
54
  * `FieldWidgetComponentProps` first and add it here — do not reopen the spread
61
55
  * (AGENTS.md #0.1: fix the contract, never widen the consumer).
56
+ *
57
+ * ## Where the mechanism lives now, and why the key list stayed here
58
+ *
59
+ * objectui#4425 phase 2 promoted this whitelist to the SDUI widget contract
60
+ * generally, so the MECHANISM — filter by a declared key list plus the `aria-*`
61
+ * / `data-*` open families — moved to `@object-ui/core`
62
+ * (`utils/dom-props.ts`, `pickDomProps`), where every plugin package can reach
63
+ * it without depending on `@object-ui/fields` (the objectui#4409
64
+ * dependency-direction method: `plugin-chatbot` declares `@object-ui/core` and
65
+ * does not declare this package).
66
+ *
67
+ * The KEY LIST did not move, because it is not the same list. Measured on
68
+ * objectui#4431:
69
+ *
70
+ * - `name` and `disabled` are here and NOT in the SDUI set: both are legal
71
+ * only on form controls, which is what every widget in this package renders
72
+ * and what `FieldWidgetComponentProps` declares. `name` on the `<div>` an
73
+ * SDUI container widget renders is a leak — one of the 14 attributes #4431
74
+ * closed — and two widgets here already hand-strip it when they spread onto
75
+ * something that is not a control (`ObjectRefField`'s trigger,
76
+ * `FileField`'s dropzone).
77
+ * - `role` is in the SDUI set and NOT here: `SchemaRenderer` resolves it for
78
+ * every node from `@objectstack/spec`'s `AriaPropsSchema`, while this
79
+ * contract deliberately does not declare it (see "Deliberately NOT
80
+ * forwarded" above).
81
+ *
82
+ * So: one mechanism, two declarations, each bound by the assertions below to
83
+ * the contract it executes. Not two judges.
62
84
  */
63
85
  declare const DOM_PASS_THROUGH_KEYS: readonly ["id", "name", "autoFocus", "tabIndex", "onBlur", "onFocus", "onClick", "className", "disabled"];
64
86
  type DomPassThroughKey = (typeof DOM_PASS_THROUGH_KEYS)[number];
65
87
  /** The subset of `P` this helper forwards, with each key's declared type. */
66
- export type DomProps<P> = Pick<P, Extract<keyof P, DomPassThroughKey>> & {
67
- [K in Extract<keyof P, `data-${string}` | `aria-${string}`>]: P[K];
68
- };
88
+ export type DomProps<P> = CoreDomProps<P, DomPassThroughKey>;
69
89
  /**
70
90
  * Keep only what may legitimately become a DOM attribute, and drop everything
71
91
  * else — renderer plumbing, and any extra key an author put on the field
@@ -0,0 +1,189 @@
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 props that make a group-labelled widget's rendered surface the thing its
10
+ * host label actually NAMES — and, where nothing else can carry it, DESCRIBES
11
+ * (objectui#3990, objectui#4005).
12
+ *
13
+ * ## What it is for
14
+ *
15
+ * A widget declared `labelling: 'group'` (objectui#3961) is told by the form
16
+ * renderer: your label publishes an `id` and drops its `for`, so name yourself
17
+ * by IDREF. The renderer hands down facts that address the field as a WHOLE:
18
+ *
19
+ * - `id` — the host's control id (`…-form-item`), the id the label's `for`
20
+ * used to point at;
21
+ * - `aria-labelledby` — the IDREF of that visible label;
22
+ * - `aria-describedby` — the IDREF of `<FormDescription>`, the field's visible
23
+ * help text. Which surface may consume it is the one thing that is NOT
24
+ * uniform, so it is decided per call site — see {@link HostGroupSurface}.
25
+ *
26
+ * Every editable branch already consumes them inside its wider `toDomProps`
27
+ * spread. The branches that returned EARLY did not — a field-level
28
+ * `readonly: true`, or an option list with zero offered options — so the label
29
+ * published an id that no element in the document referenced. Measured on
30
+ * `origin/main`, one field per row, reading the host label against the DOM:
31
+ *
32
+ * ```
33
+ * multiselect readonly+value consumers=0 byLabelText=0 namedByRole=[]
34
+ * multiselect readonly+empty consumers=0 byLabelText=0 namedByRole=[]
35
+ * multiselect zeroOptions consumers=0 byLabelText=0 namedByRole=[]
36
+ * multiselect editable consumers=1 byLabelText=1 namedByRole=[group:1]
37
+ * ```
38
+ *
39
+ * All seven group-labelled types measured the same, in every readonly state.
40
+ * This is quieter than the dangling `for` #3961 replaced: a `for` that resolves
41
+ * to nothing can at least be swept for, while "a label published an id and
42
+ * nobody consumed it" looks like healthy markup.
43
+ *
44
+ * ## The description was the other half of the same defect (objectui#4005)
45
+ *
46
+ * #3990 landed the `id` + `aria-labelledby` pair above and deliberately stopped
47
+ * there. The reasoning it recorded — a readonly display has no focusable
48
+ * control, and `aria-describedby` is announced when focus lands on something
49
+ * focusable — was the right call for the surface as it stood THEN: before this
50
+ * helper existed, a readonly branch had no role at all, and on a role-less
51
+ * `div` / `span` an `aria-describedby` is as inert as the `aria-labelledby`
52
+ * was. That premise expired the moment #3990 gave those surfaces
53
+ * `role="group"`. A group IS a description carrier under ARIA 1.2: assistive
54
+ * technology announces a group's name and description on entering it, with no
55
+ * focusable control required. So the help text stayed measurably unreachable
56
+ * for exactly as long as it took to have somewhere to put it. Measured on
57
+ * `origin/main` at `c25222758`, a real form, one field per row, counting the
58
+ * elements whose `aria-describedby` names the rendered `<FormDescription>`:
59
+ *
60
+ * ```
61
+ * descEl consumers consumerTags
62
+ * address editable desc=YES SET 1 [input]
63
+ * address readonly desc=YES SET 0 []
64
+ * geolocation editable desc=YES SET 1 [input]
65
+ * geolocation readonly desc=YES SET 0 []
66
+ * checkboxes editable desc=YES SET 1 [div[group]]
67
+ * checkboxes readonly desc=YES SET 0 []
68
+ * radio editable desc=YES SET 1 [div[radiogroup]]
69
+ * radio readonly desc=YES SET 0 []
70
+ * rating editable desc=YES SET 1 [div[group]]
71
+ * rating readonly desc=YES SET 0 []
72
+ * file editable desc=YES SET 1 [div[button]]
73
+ * file readonly desc=YES SET 0 []
74
+ * multiselect editable desc=YES SET 1 [div[group]]
75
+ * multiselect readonly desc=YES SET 0 []
76
+ * ```
77
+ *
78
+ * All seven render the `<FormDescription>` in the readonly state — none is
79
+ * exempt for want of one — and all seven left it referenced by nothing.
80
+ *
81
+ * ## What still does NOT come along, and why that is a boundary and not an oversight
82
+ *
83
+ * `aria-invalid` and `aria-required` are CONTROL-channel state: they report
84
+ * what a user's own editing may do wrong, to the element they would edit. A
85
+ * readonly display cannot be edited and cannot be made invalid by the person
86
+ * reading it, so putting either on it announces a state that has no action
87
+ * behind it. Spreading the whole `toDomProps` result would drag both back —
88
+ * along with `disabled` / `tabIndex` / the focus handlers, which only mean
89
+ * something on an interactive element, and `name`, which is DOM-legal on form
90
+ * controls only and on a `div` is exactly the leak objectui#3291 sweeps for.
91
+ * That is what objectui#3291 and objectui#3318 drew the line against, and
92
+ * adding the description does not move it: the description is what the FIELD
93
+ * is, the other two are what the CONTROL is doing. The line is pinned from both
94
+ * sides in `composite-group-label-readonly-e2e.test.tsx` — the description must
95
+ * be there, `aria-invalid` / `aria-required` must not — so neither a later
96
+ * "spread it all" nor a later "take it all back out" can pass quietly.
97
+ *
98
+ * Spreading the full result would also put the host's `className` on top of the
99
+ * widget's own, because the widgets whose readonly surface is a placeholder or
100
+ * a plain container (`EmptyValue`, `FileField`, `AddressField`) keep
101
+ * `className` inside `props`.
102
+ *
103
+ * ## Why `role` travels with the keys
104
+ *
105
+ * `aria-labelledby` on a role-less `div` / `span` names NOTHING — `generic`
106
+ * prohibits an author name — so the pair is inert without a role that supports
107
+ * naming. `group` is the one that fits a readonly surface: it names a set of
108
+ * values (chips, checked labels, stars, file names, a formatted address)
109
+ * without promising the interactivity `textbox` or `radiogroup` would. It is
110
+ * deliberately NOT the role the same widget answers with while editable:
111
+ * `RadioField`'s readonly branch renders the chosen label as text with no radios
112
+ * left in it, and `FileField`'s renders file names with no dropzone button.
113
+ *
114
+ * The role is emitted ONLY when a host actually named the field, mirroring
115
+ * every editable branch's `isLabelledGroup` test: standalone rendering (the
116
+ * grid's inline cell editor, a bare SDUI node) hands down neither key, so every
117
+ * value here is `undefined`, React emits no attribute, and that markup stays
118
+ * byte-identical to what it was.
119
+ */
120
+ export interface HostGroupProps {
121
+ /** The host's control id — the id its label would have pointed `for` at. */
122
+ id?: string;
123
+ /** IDREF of the host's visible label. */
124
+ 'aria-labelledby'?: string;
125
+ /**
126
+ * IDREF of the host's visible help text — present only on a surface that
127
+ * renders no input of the field's own. See {@link HostGroupSurface}.
128
+ */
129
+ 'aria-describedby'?: string;
130
+ /** `'group'` when — and only when — a host named this surface. */
131
+ role?: 'group';
132
+ }
133
+ /**
134
+ * WHERE the surface being named sits relative to the field's own inputs. The
135
+ * name is uniform; the DESCRIPTION is not, so every call site has to say which
136
+ * of the two shapes it is, and the compiler makes the next widget author say it
137
+ * too rather than inherit whichever default happened to be there.
138
+ *
139
+ * - `'above-the-inputs'` — a composite's EDITABLE container (`address`,
140
+ * `geolocation`): the field's real inputs are inside it and each already
141
+ * receives `aria-describedby` in its own `toDomProps` spread, straight from
142
+ * `<FormControl>`'s Slot. This surface therefore takes the NAME only.
143
+ * objectui#3318 chose that split deliberately and
144
+ * `composite-group-label-e2e.test.tsx` pins it: the help text is announced
145
+ * when focus reaches the box the user types into, and announcing it a second
146
+ * time on the container that merely wraps those boxes is a double channel of
147
+ * exactly the kind objectui#3290 removed from the required marker.
148
+ *
149
+ * - `'instead-of-the-inputs'` — every readonly branch, and the zero-option box
150
+ * (`OptionsEmptyState`). The field renders no input at all here, so no
151
+ * element inside can carry the description and this surface is the only
152
+ * candidate there is. It takes the name AND the description.
153
+ *
154
+ * Note what the discriminator is NOT about: whether the surface contains
155
+ * anything focusable. `geolocation`'s readonly row holds a "View on map" link
156
+ * — focusable, and correctly announced as itself. It is not an input OF THE
157
+ * FIELD and never receives the field's `aria-describedby`, so that row is still
158
+ * `'instead-of-the-inputs'`.
159
+ */
160
+ export type HostGroupSurface = 'above-the-inputs' | 'instead-of-the-inputs';
161
+ /**
162
+ * Read the whole-field keys out of a widget's leftover props, plus the role
163
+ * that makes them mean something. See {@link HostGroupProps} and
164
+ * {@link HostGroupSurface}.
165
+ *
166
+ * Routed through {@link toDomProps} rather than reading `props` directly so the
167
+ * keys keep ONE gate: they reach an element only if the DOM pass-through
168
+ * whitelist still forwards them, and its compile-time assertions bind that
169
+ * whitelist to the props contract in both directions.
170
+ *
171
+ * `aria-describedby` is forwarded exactly as the host handed it down, with no
172
+ * check that it resolves — a widget cannot see whether `<FormDescription>`
173
+ * rendered, and a DOM probe for it would be a layout-effect hack around the
174
+ * renderer's own knowledge. That is not a new gap: `<FormControl>`'s Slot emits
175
+ * the IDREF unconditionally, so a field with NO description already hands every
176
+ * editable control an `aria-describedby` pointing at an element that does not
177
+ * exist. Measured on `origin/main` at `c25222758`, `desc=NO`: all seven
178
+ * group-labelled widgets plus the builtin `input` and the single-control
179
+ * `email` carry exactly one such reference, on the same element that would
180
+ * carry a resolving one. A readonly surface answering identically is parity
181
+ * with the editable branch, not a class of its own — and an IDREF that resolves
182
+ * to nothing is inert for assistive technology, where an unconsumed
183
+ * description is simply lost.
184
+ */
185
+ export declare function toHostGroupProps(props: {
186
+ id?: string;
187
+ 'aria-labelledby'?: string;
188
+ 'aria-describedby'?: string;
189
+ }, surface: HostGroupSurface): HostGroupProps;
@@ -157,9 +157,14 @@ export type FieldWidgetComponentProps<T = any> = {
157
157
  onUploadingChange?: (uploading: boolean) => void;
158
158
  /**
159
159
  * DataSource for widgets that query records (lookup / user / object-ref /
160
- * recipient-picker / grid). Injected by the form renderer for the field
161
- * types that need it, and passed directly by inline-edit hosts. Option
162
- * widgets destructure it purely to keep it off their DOM spread.
160
+ * recipient-picker). Injected by the form renderer for the field types that
161
+ * need it, and passed directly by inline-edit hosts. Option widgets
162
+ * destructure it purely to keep it off their DOM spread.
163
+ *
164
+ * `grid` was listed here with zero consumers (objectui#4814): `GridField.tsx`
165
+ * never reads `dataSource`, and no data-source table has ever contained the
166
+ * key, so the claim described a wiring that did not exist. `owner` is absent
167
+ * for a different reason — the spelling itself is retired (same card).
163
168
  *
164
169
  * Left structural (`unknown`): `@object-ui/fields` must not depend on a
165
170
  * concrete adapter — every consumer narrows it itself.
@@ -46,8 +46,14 @@ export interface UseRecordQueryOptions {
46
46
  * `$filter` — already merged by the caller (base `lookup_filters`, dependent
47
47
  * lookup chain, candidate hygiene like `banned != true`, …). Compared by
48
48
  * value, so a referentially-new-but-equal object each render will not loop.
49
+ *
50
+ * Either the `QueryParams.$filter` record form or a lowered ObjectQL AST node
51
+ * (an array), since the picker's merge now produces both (#3831). Typed
52
+ * `unknown` rather than `Record< string, any >` so an array cannot slip
53
+ * through a type that silently accepts it; emptiness is decided by
54
+ * {@link hasFilter}, not by `Object.keys`.
49
55
  */
50
- filter?: Record<string, any>;
56
+ filter?: unknown;
51
57
  /** `$expand` — related entities to include (e.g. `['primary_business_unit_id']`). */
52
58
  expand?: string[];
53
59
  /** `$searchFields` — narrow the server searchable set (ADR-0061). */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@object-ui/fields",
3
- "version": "17.3.0",
3
+ "version": "17.6.0",
4
4
  "description": "Field renderers and registry for Object UI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -18,37 +18,40 @@
18
18
  },
19
19
  "files": [
20
20
  "dist",
21
- "src",
22
21
  "README.md",
23
22
  "CHANGELOG.md",
24
23
  "LICENSE"
25
24
  ],
26
25
  "dependencies": {
27
- "@objectstack/spec": "^17.0.0-rc.2",
26
+ "@objectstack/spec": "^17.0.0",
28
27
  "clsx": "^2.1.1",
29
- "lucide-react": "^1.28.0",
28
+ "lucide-react": "^1.31.0",
30
29
  "react-markdown": "^10.1.0",
31
30
  "rehype-sanitize": "^6.0.0",
32
31
  "remark-gfm": "^4.0.1",
33
32
  "tailwind-merge": "^3.6.0",
34
- "@object-ui/components": "17.3.0",
35
- "@object-ui/core": "17.3.0",
36
- "@object-ui/i18n": "17.3.0",
37
- "@object-ui/providers": "17.3.0",
38
- "@object-ui/react": "17.3.0",
39
- "@object-ui/types": "17.3.0"
33
+ "@object-ui/components": "17.6.0",
34
+ "@object-ui/core": "17.6.0",
35
+ "@object-ui/i18n": "17.6.0",
36
+ "@object-ui/providers": "17.6.0",
37
+ "@object-ui/react": "17.6.0",
38
+ "@object-ui/types": "17.6.0"
40
39
  },
41
40
  "peerDependencies": {
42
41
  "react": "^18.0.0 || ^19.0.0",
43
42
  "react-dom": "^18.0.0 || ^19.0.0"
44
43
  },
45
44
  "devDependencies": {
45
+ "@tailwindcss/postcss": "^4.3.3",
46
46
  "@types/react": "19.2.18",
47
47
  "@types/react-dom": "19.2.4",
48
48
  "@vitejs/plugin-react": "^6.0.5",
49
+ "postcss": "^8.5.26",
50
+ "tailwindcss": "^4.3.3",
49
51
  "typescript": "^6.0.3",
50
- "vite": "^8.2.0",
51
- "vite-plugin-dts": "^5.0.3"
52
+ "vite": "^8.2.1",
53
+ "vite-plugin-dts": "^5.0.3",
54
+ "@object-ui/test-support": "17.4.0"
52
55
  },
53
56
  "publishConfig": {
54
57
  "access": "public"
@@ -77,9 +80,9 @@
77
80
  },
78
81
  "homepage": "https://www.objectui.org/docs/fields",
79
82
  "scripts": {
80
- "build": "tsc && vite build",
83
+ "build": "tsc && vite build && node scripts/build-css.mjs",
81
84
  "clean": "rm -rf dist",
82
- "type-check": "tsc --noEmit",
85
+ "type-check": "tsc --noEmit && tsc -p tsconfig.test.json",
83
86
  "test": "vitest run",
84
87
  "lint": "eslint ."
85
88
  }
@@ -1,8 +0,0 @@
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
- export {};
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1 +0,0 @@
1
- export {};
@@ -1,8 +0,0 @@
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
- export {};
File without changes
File without changes
File without changes
@@ -1,8 +0,0 @@
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
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
File without changes
File without changes
File without changes
@@ -1,56 +0,0 @@
1
- import { default as React } from 'react';
2
- export interface CharacterCountProps {
3
- /**
4
- * Length of the text this surface is counting. The inline surface passes the
5
- * committed value's length; the fullscreen dialog passes the DRAFT's, which
6
- * is the number that is true of what the user is looking at.
7
- */
8
- length: number;
9
- /**
10
- * The declared cap. Callers render this component only when the field
11
- * declares one, so it is non-optional here — a counter with no cap to count
12
- * against has nothing to say and must not be mounted at all (an unconditional
13
- * live region in every long-text field is the objectui#3408 defect back
14
- * again).
15
- */
16
- maxLength: number;
17
- /**
18
- * The description node's `id`, minted by the host from `useId()`.
19
- *
20
- * The host MUST also name it in the edited control's `aria-describedby` —
21
- * this component renders the sentence, only the host knows which element is
22
- * being described, and a description nothing references is a node no screen
23
- * reader will ever reach. That association is asserted from the OUTSIDE (the
24
- * tests resolve `aria-describedby` and concatenate the referenced nodes' text,
25
- * exactly as an assistive technology does) rather than trusted here.
26
- *
27
- * Two surfaces of one widget therefore need two ids: the dialog's draft and
28
- * the inline value diverge the moment the user types in the dialog, so one
29
- * shared id would point both controls at whichever sentence rendered last.
30
- */
31
- descriptionId: string;
32
- /**
33
- * Render node 3, the threshold-gated debounced `aria-live` status region.
34
- *
35
- * Required on purpose — see the component doc above. `false` removes the
36
- * region from the DOM entirely (not merely empties it), which is the only
37
- * form of "no live region" a test can tell apart from "a live region that
38
- * happens to be silent right now".
39
- */
40
- announceNearLimit: boolean;
41
- /**
42
- * Classes for the visible digits. Required because placement is entirely the
43
- * surface's business — inline pins them to the textarea's bottom-right
44
- * corner, the dialog lays them out as a footer item — and a default borrowed
45
- * from either one would position the other wrongly while looking deliberate.
46
- */
47
- className: string;
48
- /**
49
- * `data-testid` for the visible digits, namespaced per surface
50
- * (`textarea-character-count`, `textarea-fullscreen-character-count`) so a
51
- * test that finds digits can say WHICH surface it found them on. Same
52
- * convention, same reason, as `FullscreenFieldEditor`'s `testIdPrefix`.
53
- */
54
- testId: string;
55
- }
56
- export declare function CharacterCount({ length, maxLength, descriptionId, announceNearLimit, className, testId, }: CharacterCountProps): React.JSX.Element;
File without changes
File without changes
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1,8 +0,0 @@
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
- export {};
@@ -1,8 +0,0 @@
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
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
File without changes
File without changes
@@ -1,8 +0,0 @@
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
- export {};
@@ -1,8 +0,0 @@
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
- export {};
File without changes
File without changes