@object-ui/fields 17.1.0 → 17.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +786 -0
- package/dist/FieldEditWidget.d.ts +2 -2
- package/dist/__tests__/cell-truncation.test.d.ts +0 -0
- package/dist/__tests__/field-carrier-sdui.test.d.ts +0 -0
- package/dist/__tests__/spec-symbol-batch7.test.d.ts +8 -0
- package/dist/__tests__/widget-aria-invalid-e2e.test.d.ts +0 -0
- package/dist/__tests__/widget-aria-invalid-registry-e2e.test.d.ts +0 -0
- package/dist/__tests__/widget-dom-leak-e2e.test.d.ts +0 -0
- package/dist/__tests__/widget-props-contract.test.d.ts +8 -0
- package/dist/index.cjs +5 -5
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2882 -2569
- package/dist/widgets/AddressField.d.ts +2 -2
- package/dist/widgets/AddressField.uniqueIds.test.d.ts +0 -0
- package/dist/widgets/AutoNumberField.d.ts +2 -2
- package/dist/widgets/AvatarField.d.ts +2 -2
- package/dist/widgets/BooleanField.d.ts +2 -2
- package/dist/widgets/CapabilityMultiSelectField.d.ts +2 -2
- package/dist/widgets/CharacterCount.d.ts +56 -0
- package/dist/widgets/CheckboxesField.d.ts +2 -2
- package/dist/widgets/CodeField.d.ts +2 -2
- package/dist/widgets/ColorField.d.ts +2 -2
- package/dist/widgets/CurrencyField.d.ts +2 -2
- package/dist/widgets/DateField.d.ts +2 -2
- package/dist/widgets/DateTimeField.d.ts +2 -2
- package/dist/widgets/EmailField.d.ts +2 -2
- package/dist/widgets/FileField.d.ts +2 -2
- package/dist/widgets/FilterConditionField.d.ts +2 -2
- package/dist/widgets/FormulaField.d.ts +2 -2
- package/dist/widgets/FullscreenFieldEditor.d.ts +155 -0
- package/dist/widgets/GeolocationField.d.ts +2 -2
- package/dist/widgets/GeolocationField.uniqueIds.test.d.ts +0 -0
- package/dist/widgets/GridField.d.ts +2 -2
- package/dist/widgets/ImageField.d.ts +2 -2
- package/dist/widgets/LocationField.d.ts +2 -2
- package/dist/widgets/LookupField.d.ts +2 -2
- package/dist/widgets/LookupField.gateHintLabel.test.d.ts +8 -0
- package/dist/widgets/MasterDetailField.d.ts +2 -2
- package/dist/widgets/MultiSelectField.d.ts +2 -2
- package/dist/widgets/NumberField.d.ts +2 -2
- package/dist/widgets/ObjectField.d.ts +2 -2
- package/dist/widgets/ObjectRefField.d.ts +2 -2
- package/dist/widgets/OptionsEmptyState.d.ts +45 -0
- package/dist/widgets/OptionsEmptyState.no-provider.test.d.ts +0 -0
- package/dist/widgets/OptionsEmptyState.test.d.ts +0 -0
- package/dist/widgets/PasswordField.d.ts +2 -2
- package/dist/widgets/PercentField.d.ts +2 -2
- package/dist/widgets/PhoneField.d.ts +2 -2
- package/dist/widgets/QRCodeField.d.ts +2 -2
- package/dist/widgets/RadioField.d.ts +2 -2
- package/dist/widgets/RatingField.d.ts +2 -2
- package/dist/widgets/RecipientPickerField.d.ts +2 -2
- package/dist/widgets/RecordPickerDialog.d.ts +17 -1
- package/dist/widgets/RecordPickerDialog.filterOptions.test.d.ts +8 -0
- package/dist/widgets/RecordPickerDialog.selectLabel.test.d.ts +8 -0
- package/dist/widgets/RichTextField.d.ts +20 -2
- package/dist/widgets/SelectField.d.ts +2 -2
- package/dist/widgets/SignatureField.d.ts +2 -2
- package/dist/widgets/SliderField.d.ts +2 -2
- package/dist/widgets/SummaryField.d.ts +2 -2
- package/dist/widgets/TagsField.d.ts +2 -2
- package/dist/widgets/TagsField.placeholder.no-provider.test.d.ts +0 -0
- package/dist/widgets/TagsField.placeholder.test.d.ts +0 -0
- package/dist/widgets/TextAreaField.d.ts +45 -6
- package/dist/widgets/TextField.d.ts +2 -2
- package/dist/widgets/TimeField.d.ts +2 -2
- package/dist/widgets/UrlField.d.ts +2 -2
- package/dist/widgets/UserField.d.ts +2 -2
- package/dist/widgets/VectorField.d.ts +2 -2
- package/dist/widgets/__tests__/FullscreenFieldEditor.i18n.test.d.ts +0 -0
- package/dist/widgets/__tests__/FullscreenFieldEditor.no-provider.test.d.ts +0 -0
- package/dist/widgets/__tests__/RichTextField.fullscreenDisabled.test.d.ts +0 -0
- package/dist/widgets/__tests__/RichTextField.mobileFullscreen.test.d.ts +0 -0
- package/dist/widgets/__tests__/TextAreaField.characterCount.announcements.test.d.ts +0 -0
- package/dist/widgets/__tests__/TextAreaField.characterCount.i18n.test.d.ts +0 -0
- package/dist/widgets/__tests__/TextAreaField.characterCount.no-provider.test.d.ts +0 -0
- package/dist/widgets/__tests__/TextAreaField.fullscreenCharacterCount.i18n.test.d.ts +0 -0
- package/dist/widgets/__tests__/TextAreaField.fullscreenCharacterCount.test.d.ts +0 -0
- package/dist/widgets/__tests__/TextAreaField.fullscreenDisabled.test.d.ts +0 -0
- package/dist/widgets/__tests__/TextAreaField.mobileFullscreen.test.d.ts +0 -0
- package/dist/widgets/file-value.d.ts +20 -33
- package/dist/widgets/toDomProps.d.ts +91 -0
- package/dist/widgets/types.d.ts +215 -4
- package/dist/withFieldCarrier.d.ts +56 -0
- package/package.json +13 -13
- package/src/FieldEditWidget.tsx +6 -4
- package/src/__tests__/cell-truncation.test.tsx +96 -0
- package/src/__tests__/field-carrier-sdui.test.tsx +169 -0
- package/src/__tests__/spec-symbol-batch7.test.ts +126 -0
- package/src/__tests__/validation-feedback.test.tsx +22 -13
- package/src/__tests__/widget-aria-invalid-e2e.test.tsx +265 -0
- package/src/__tests__/widget-aria-invalid-registry-e2e.test.tsx +338 -0
- package/src/__tests__/widget-dom-leak-e2e.test.tsx +598 -0
- package/src/__tests__/widget-props-contract.test.tsx +142 -0
- package/src/complex-widgets.test.tsx +3 -3
- package/src/datetime-widgets.test.tsx +2 -2
- package/src/index.tsx +77 -27
- package/src/widgets/AddressField.tsx +32 -13
- package/src/widgets/AddressField.uniqueIds.test.tsx +92 -0
- package/src/widgets/AutoNumberField.tsx +2 -2
- package/src/widgets/AvatarField.tsx +10 -3
- package/src/widgets/BooleanField.tsx +5 -5
- package/src/widgets/CapabilityMultiSelectField.tsx +4 -4
- package/src/widgets/CharacterCount.tsx +263 -0
- package/src/widgets/CheckboxesField.tsx +35 -18
- package/src/widgets/CodeField.tsx +10 -3
- package/src/widgets/ColorField.tsx +12 -3
- package/src/widgets/CurrencyField.tsx +6 -5
- package/src/widgets/DateField.tsx +4 -4
- package/src/widgets/DateTimeField.tsx +4 -4
- package/src/widgets/EmailField.tsx +6 -6
- package/src/widgets/FileField.tsx +14 -4
- package/src/widgets/FilterConditionField.tsx +14 -3
- package/src/widgets/FormulaField.tsx +3 -3
- package/src/widgets/FullscreenFieldEditor.tsx +271 -0
- package/src/widgets/GeolocationField.tsx +22 -7
- package/src/widgets/GeolocationField.uniqueIds.test.tsx +88 -0
- package/src/widgets/GridField.tsx +4 -4
- package/src/widgets/ImageField.tsx +10 -3
- package/src/widgets/LocationField.tsx +10 -3
- package/src/widgets/LookupField.gateHintLabel.test.tsx +135 -0
- package/src/widgets/LookupField.tsx +50 -15
- package/src/widgets/MasterDetailField.tsx +3 -3
- package/src/widgets/MultiSelectField.tsx +34 -18
- package/src/widgets/NumberField.tsx +5 -5
- package/src/widgets/ObjectField.tsx +18 -9
- package/src/widgets/ObjectRefField.tsx +14 -4
- package/src/widgets/OptionsEmptyState.no-provider.test.tsx +88 -0
- package/src/widgets/OptionsEmptyState.test.tsx +169 -0
- package/src/widgets/OptionsEmptyState.tsx +83 -0
- package/src/widgets/PasswordField.tsx +5 -5
- package/src/widgets/PeoplePicker.tsx +7 -1
- package/src/widgets/PercentField.tsx +6 -5
- package/src/widgets/PhoneField.tsx +6 -6
- package/src/widgets/QRCodeField.tsx +10 -3
- package/src/widgets/RadioField.tsx +28 -18
- package/src/widgets/RatingField.tsx +19 -4
- package/src/widgets/RecipientPickerField.tsx +22 -4
- package/src/widgets/RecordPickerDialog.filterOptions.test.tsx +611 -0
- package/src/widgets/RecordPickerDialog.selectLabel.test.tsx +112 -0
- package/src/widgets/RecordPickerDialog.tsx +173 -8
- package/src/widgets/RichTextField.tsx +152 -25
- package/src/widgets/SelectField.tsx +53 -22
- package/src/widgets/SignatureField.tsx +2 -2
- package/src/widgets/SliderField.tsx +3 -3
- package/src/widgets/SummaryField.tsx +3 -3
- package/src/widgets/TagsField.placeholder.no-provider.test.tsx +68 -0
- package/src/widgets/TagsField.placeholder.test.tsx +95 -0
- package/src/widgets/TagsField.tsx +25 -4
- package/src/widgets/TextAreaField.tsx +172 -94
- package/src/widgets/TextField.tsx +5 -5
- package/src/widgets/TimeField.tsx +4 -4
- package/src/widgets/UrlField.tsx +6 -6
- package/src/widgets/UserField.tsx +3 -3
- package/src/widgets/VectorField.tsx +3 -3
- package/src/widgets/__tests__/FullscreenFieldEditor.i18n.test.tsx +248 -0
- package/src/widgets/__tests__/FullscreenFieldEditor.no-provider.test.tsx +116 -0
- package/src/widgets/__tests__/RichTextField.fullscreenDisabled.test.tsx +244 -0
- package/src/widgets/__tests__/RichTextField.mobileFullscreen.test.tsx +195 -0
- package/src/widgets/__tests__/TextAreaField.characterCount.announcements.test.tsx +417 -0
- package/src/widgets/__tests__/TextAreaField.characterCount.i18n.test.tsx +287 -0
- package/src/widgets/__tests__/TextAreaField.characterCount.no-provider.test.tsx +160 -0
- package/src/widgets/__tests__/TextAreaField.fullscreenCharacterCount.i18n.test.tsx +161 -0
- package/src/widgets/__tests__/TextAreaField.fullscreenCharacterCount.test.tsx +346 -0
- package/src/widgets/__tests__/TextAreaField.fullscreenDisabled.test.tsx +246 -0
- package/src/widgets/__tests__/TextAreaField.mobileFullscreen.test.tsx +175 -0
- package/src/widgets/file-value.ts +21 -5
- package/src/widgets/toDomProps.ts +175 -0
- package/src/widgets/types.ts +230 -7
- package/src/widgets/useFieldTranslation.ts +60 -0
- package/src/withFieldCarrier.tsx +75 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { FieldWidgetComponentProps } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* QR Code field widget - generates QR codes from text
|
|
5
5
|
* Uses a simple SVG-based QR code generator
|
|
6
6
|
*/
|
|
7
|
-
export declare function QRCodeField({ value, onChange, field, readonly, ...props }:
|
|
7
|
+
export declare function QRCodeField({ value, onChange, field, readonly, error, ...props }: FieldWidgetComponentProps<string>): React.JSX.Element;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { FieldWidgetComponentProps } from './types';
|
|
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
|
|
@@ -12,4 +12,4 @@ import { FieldWidgetProps } from './types';
|
|
|
12
12
|
* first" hint while a dependency is empty, and a value no longer offered (parent
|
|
13
13
|
* changed / predicate flipped) is cleared.
|
|
14
14
|
*/
|
|
15
|
-
export declare function RadioField({ value, onChange, field, readonly, className,
|
|
15
|
+
export declare function RadioField({ value, onChange, field, readonly, className, dependentValues, dependsOn: dependsOnProp, emptyHint, dataSource: _dataSource, error, ...props }: FieldWidgetComponentProps<string>): React.JSX.Element;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { FieldWidgetComponentProps } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* Rating field widget - provides a star rating input
|
|
5
5
|
* Supports numeric values from 0 to max (default 5)
|
|
6
6
|
*/
|
|
7
|
-
export declare function RatingField({ value, onChange, field, readonly, className, ...props }:
|
|
7
|
+
export declare function RatingField({ value, onChange, field, readonly, className, error, ...props }: FieldWidgetComponentProps<number>): React.JSX.Element;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
export declare function RecipientPickerField({ value, onChange, readonly, className, ...props }:
|
|
2
|
+
import { FieldWidgetComponentProps } from './types';
|
|
3
|
+
export declare function RecipientPickerField({ value, onChange, readonly, className, error, ...props }: FieldWidgetComponentProps<string>): React.JSX.Element;
|
|
4
4
|
export default RecipientPickerField;
|
|
@@ -140,6 +140,22 @@ export interface RecordPickerDialogProps {
|
|
|
140
140
|
* Typically pass `getCellRenderer` from @object-ui/fields.
|
|
141
141
|
*/
|
|
142
142
|
cellRenderer?: CellRendererResolver;
|
|
143
|
+
/**
|
|
144
|
+
* The referenced object's schema `fields` map (field name → field
|
|
145
|
+
* definition). When provided, cell renderers receive the FULL field
|
|
146
|
+
* metadata — `options`, `currency`, `scale`, `precision`, `format`,
|
|
147
|
+
* `reference_to`, … — exactly like the list view enriches its columns from
|
|
148
|
+
* the object schema. Without it a `select` column falls back to
|
|
149
|
+
* title-casing the raw stored value instead of resolving the option label
|
|
150
|
+
* (#3333: `manufacturing` rendered as "Manufacturing" instead of the
|
|
151
|
+
* authored option label).
|
|
152
|
+
*
|
|
153
|
+
* The filter bar reads the same map: a `select` filter column with no
|
|
154
|
+
* authored `options` takes them from the schema field here, so the filter
|
|
155
|
+
* panel's dropdown offers exactly the options the table cells render
|
|
156
|
+
* (#3336 — it used to open empty, leaving the field unfilterable).
|
|
157
|
+
*/
|
|
158
|
+
fieldsMeta?: Record<string, any>;
|
|
143
159
|
/**
|
|
144
160
|
* Filter bar column definitions.
|
|
145
161
|
* When provided, shows an inline filter bar below the search input.
|
|
@@ -185,4 +201,4 @@ export interface RecordPickerDialogProps {
|
|
|
185
201
|
* states, and single/multi-select. Responsive: mobile-friendly width
|
|
186
202
|
* via Tailwind breakpoints.
|
|
187
203
|
*/
|
|
188
|
-
export declare function RecordPickerDialog({ open, onOpenChange, title, multiple, dataSource, objectName, columns: columnsProp, displayField, titleFormat, idField, pageSize, value, onSelect, onSelectRecords, lookupFilters, baseFilter, cellRenderer, filterColumns, renderFilterBar, renderGrid, }: RecordPickerDialogProps): React.JSX.Element;
|
|
204
|
+
export declare function RecordPickerDialog({ open, onOpenChange, title, multiple, dataSource, objectName, columns: columnsProp, displayField, titleFormat, idField, pageSize, value, onSelect, onSelectRecords, lookupFilters, baseFilter, cellRenderer, fieldsMeta, filterColumns, renderFilterBar, renderGrid, }: RecordPickerDialogProps): React.JSX.Element;
|
|
@@ -1,8 +1,26 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { FieldWidgetComponentProps } from './types';
|
|
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
|
|
6
6
|
* a rich text editor like TipTap, Lexical, or Slate.
|
|
7
|
+
*
|
|
8
|
+
* Reached by forms as `field:markdown` and `field:html`; both resolve here.
|
|
9
|
+
*
|
|
10
|
+
* ## Fullscreen editing (objectui#3301)
|
|
11
|
+
*
|
|
12
|
+
* `ObjectForm` stamps `mobile_fullscreen` onto the metadata of every long-text
|
|
13
|
+
* field — textarea AND rich-text — when `ObjectFormSchema.mobile
|
|
14
|
+
* .fullscreenLongText` is set, and `ObjectFormSchema.mobile`'s own JSDoc has
|
|
15
|
+
* always promised "textarea/rich-text get an expand button". This widget never
|
|
16
|
+
* read the flag, so for `field:markdown` / `field:html` that promise did
|
|
17
|
+
* nothing at all: the producer stamped, and no consumer existed.
|
|
18
|
+
*
|
|
19
|
+
* The flag is read off `field` and nowhere else — the single metadata carrier
|
|
20
|
+
* since objectui#3233 — matching `TextAreaField` read for read, so a
|
|
21
|
+
* misspelled flag stays inert in both widgets rather than being caught by a
|
|
22
|
+
* tolerant fallback in one of them. The affordance and dialog themselves come
|
|
23
|
+
* from the shared `FullscreenFieldEditor`, so one form-level setting keeps
|
|
24
|
+
* producing one behaviour across both widgets.
|
|
7
25
|
*/
|
|
8
|
-
export declare function RichTextField({ value, onChange, field, readonly,
|
|
26
|
+
export declare function RichTextField({ value, onChange, field, readonly, error, ...props }: FieldWidgetComponentProps<string>): React.JSX.Element;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { FieldWidgetComponentProps } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* SelectField - dropdown selection widget.
|
|
5
5
|
*
|
|
@@ -15,4 +15,4 @@ import { FieldWidgetProps } from './types';
|
|
|
15
15
|
* the shared {@link useCascadingOptions} hook (#2715), so single and multi stay
|
|
16
16
|
* in lockstep.
|
|
17
17
|
*/
|
|
18
|
-
export declare function SelectField(props:
|
|
18
|
+
export declare function SelectField(props: FieldWidgetComponentProps<any>): React.JSX.Element;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { FieldWidgetComponentProps } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* Signature field widget - provides a signature pad for capturing signatures
|
|
5
5
|
* Outputs signature as a base64 PNG image
|
|
6
6
|
*/
|
|
7
|
-
export declare function SignatureField({ value, onChange, readonly }:
|
|
7
|
+
export declare function SignatureField({ value, onChange, readonly }: FieldWidgetComponentProps<string>): React.JSX.Element;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { FieldWidgetComponentProps } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* Slider field widget - provides a range slider input
|
|
5
5
|
* Supports numeric values with configurable min, max, and step
|
|
6
6
|
*/
|
|
7
|
-
export declare function SliderField({ value, onChange, field, readonly, ...props }:
|
|
7
|
+
export declare function SliderField({ value, onChange, field, readonly, ...props }: FieldWidgetComponentProps<number>): React.JSX.Element;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { FieldWidgetComponentProps } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* SummaryField - Read-only aggregation field
|
|
5
5
|
* Values are aggregated from related records and cannot be edited
|
|
6
6
|
*/
|
|
7
|
-
export declare function SummaryField({ value, field, ...props }:
|
|
7
|
+
export declare function SummaryField({ value, field, ...props }: FieldWidgetComponentProps<any>): React.JSX.Element;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { FieldWidgetComponentProps } from './types';
|
|
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
|
|
6
6
|
* string[]. Used for the `tags` field type.
|
|
7
7
|
*/
|
|
8
|
-
export declare function TagsField({ value, onChange, field, readonly, className, ...props }:
|
|
8
|
+
export declare function TagsField({ value, onChange, field, readonly, className, error, ...props }: FieldWidgetComponentProps<string[]>): React.JSX.Element;
|
|
File without changes
|
|
File without changes
|
|
@@ -1,12 +1,51 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { FieldWidgetComponentProps } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* TextAreaField - Multi-line text input widget
|
|
5
5
|
* Supports configurable row count and preserves whitespace in readonly mode.
|
|
6
6
|
*
|
|
7
|
-
* Mobile UX (round 3): when the
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* Mobile UX (round 3): when the FIELD METADATA carries `mobile_fullscreen:
|
|
8
|
+
* true`, an "expand" affordance opens a fullscreen edit dialog — much easier
|
|
9
|
+
* on phones than tapping a 4-row textarea trapped between other fields.
|
|
10
|
+
*
|
|
11
|
+
* That flag has exactly one producer: `ObjectForm` stamps it onto every
|
|
12
|
+
* long-text field when `ObjectFormSchema.mobile.fullscreenLongText` is set
|
|
13
|
+
* (`plugin-form/src/ObjectForm.tsx`). It reaches this widget on `field` — the
|
|
14
|
+
* single metadata carrier since objectui#3233. A `SchemaRenderer`-hosted node
|
|
15
|
+
* arrives there too: the registry adapter (`withFieldCarrier`) maps the SDUI
|
|
16
|
+
* `schema` node onto `field` before the widget sees it.
|
|
17
|
+
*
|
|
18
|
+
* The affordance, the dialog and the draft/commit semantics live in the shared
|
|
19
|
+
* `FullscreenFieldEditor` — the same producer stamps the same flag on
|
|
20
|
+
* rich-text fields, and `RichTextField` renders it from there too
|
|
21
|
+
* (objectui#3301). Only the EDITOR differs per widget; here it is a
|
|
22
|
+
* full-height `Textarea`.
|
|
23
|
+
*
|
|
24
|
+
* There is deliberately NO widget-prop override. A `mobileFullscreen`
|
|
25
|
+
* (camelCase) prop was read here and written by nobody in the repo, and the
|
|
26
|
+
* snake_case `mobile_fullscreen` prop cannot arrive either: the form renderer
|
|
27
|
+
* strips both `mobile_fullscreen` and `fullscreen` from the props it forwards
|
|
28
|
+
* to registered widgets (`stripRegisteredFieldProps` in
|
|
29
|
+
* `components/src/renderers/form/form.tsx`). Reading keys nobody produces
|
|
30
|
+
* documented a contract that never held and invited the next author to pass a
|
|
31
|
+
* silently-ignored prop, so the reads are gone (objectui#3232). If a host
|
|
32
|
+
* override is ever genuinely needed, declare ONE key on
|
|
33
|
+
* `FieldWidgetComponentProps`, stop stripping it, and have a host pass it.
|
|
34
|
+
*
|
|
35
|
+
* ## The character counter (objectui#3408, shared across surfaces in #3417)
|
|
36
|
+
*
|
|
37
|
+
* Both editing surfaces below — the inline `<Textarea>` and the fullscreen
|
|
38
|
+
* dialog's — render the SAME `CharacterCount`, which owns the three-node
|
|
39
|
+
* GOV.UK shape (decorative digits / accessible description / gated status
|
|
40
|
+
* region) and the debounce and threshold behind it. Until #3417 the fullscreen
|
|
41
|
+
* footer had its own hand-written copy that was digits and nothing else, which
|
|
42
|
+
* is what a second copy of a UI always costs: it drifts, and nothing reports
|
|
43
|
+
* the drift.
|
|
44
|
+
*
|
|
45
|
+
* This widget's remaining job is the part only it can do — mint the two
|
|
46
|
+
* description ids and name each one in the `aria-describedby` of the control
|
|
47
|
+
* it describes. The surfaces differ in exactly one behaviour, and it is
|
|
48
|
+
* declared rather than implied: `announceNearLimit`. Inline announces
|
|
49
|
+
* (unchanged since #3408); the fullscreen dialog does not.
|
|
11
50
|
*/
|
|
12
|
-
export declare function TextAreaField({ value, onChange, field, readonly,
|
|
51
|
+
export declare function TextAreaField({ value, onChange, field, readonly, error, ...props }: FieldWidgetComponentProps<string>): React.JSX.Element;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { FieldWidgetComponentProps } from './types';
|
|
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
|
|
6
6
|
*/
|
|
7
|
-
export declare function TextField({ value, onChange, field, readonly, ...props }:
|
|
7
|
+
export declare function TextField({ value, onChange, field, readonly, ...props }: FieldWidgetComponentProps<string>): React.JSX.Element;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { FieldWidgetComponentProps } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* TimeField - Time picker input widget
|
|
5
5
|
* Uses native time input for hour and minute selection
|
|
6
6
|
*/
|
|
7
|
-
export declare function TimeField({ value, onChange, field, readonly, ...props }:
|
|
7
|
+
export declare function TimeField({ value, onChange, field, readonly, ...props }: FieldWidgetComponentProps<string>): React.JSX.Element;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { FieldWidgetComponentProps } from './types';
|
|
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
|
|
6
6
|
*/
|
|
7
|
-
export declare function UrlField({ value, onChange, field, readonly,
|
|
7
|
+
export declare function UrlField({ value, onChange, field, readonly, error, ...props }: FieldWidgetComponentProps<string>): React.JSX.Element;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
export declare function UserField(props:
|
|
2
|
+
import { FieldWidgetComponentProps } from './types';
|
|
3
|
+
export declare function UserField(props: FieldWidgetComponentProps<any>): React.JSX.Element;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { FieldWidgetComponentProps } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* VectorField - Vector/embedding data display
|
|
5
5
|
* Shows vector embeddings in a read-only format
|
|
6
6
|
*/
|
|
7
|
-
export declare function VectorField({ value, field, ...props }:
|
|
7
|
+
export declare function VectorField({ value, field, ...props }: FieldWidgetComponentProps<number[]>): React.JSX.Element;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
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,33 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* File field value shapes (ObjectStack ADR-0104 D3 wave 2).
|
|
3
|
-
*
|
|
4
|
-
* A `file`/`image`/`avatar`/`video`/`audio` field value reaches the UI in one
|
|
5
|
-
* of three forms, and every widget that renders one has to cope with all
|
|
6
|
-
* three — so the rules live here once rather than being re-derived per widget.
|
|
7
|
-
*
|
|
8
|
-
* 1. **Reference** — a bare `sys_file` id string. The form the backend stores
|
|
9
|
-
* once file-as-reference is adopted.
|
|
10
|
-
* 2. **Expanded** — `{ id, name, size, mimeType, url }`, what the read path
|
|
11
|
-
* returns after resolving a stored reference. Note `mimeType`: this comes
|
|
12
|
-
* from the platform's spec-owned `FileValueSchema` and is **camelCase**.
|
|
13
|
-
* 3. **Legacy inline blob** — `{ file_id?, name, original_name, size,
|
|
14
|
-
* mime_type, url }`, the pre-reference shape this package used to build
|
|
15
|
-
* itself, with **snake_case** `mime_type`.
|
|
16
|
-
*
|
|
17
|
-
* That casing split is the whole reason this module exists: a widget reading
|
|
18
|
-
* only `mime_type` silently stops recognising images the moment the backend
|
|
19
|
-
* starts returning the expanded form, and the failure looks like "thumbnails
|
|
20
|
-
* disappeared" rather than anything pointing at a value shape.
|
|
21
|
-
*
|
|
22
|
-
* ## Submitting
|
|
23
|
-
*
|
|
24
|
-
* When the upload adapter surfaced a `fileId`, the widget submits the **bare
|
|
25
|
-
* id** — the reference form. When it did not (the object-URL fallback adapter,
|
|
26
|
-
* an older backend), it submits the legacy blob unchanged. So the same build
|
|
27
|
-
* works against a backend that has adopted references and one that has not.
|
|
28
|
-
* Action params already POST a bare fileId; this brings record field values
|
|
29
|
-
* onto the same contract.
|
|
30
|
-
*/
|
|
1
|
+
import { isFileIdToken } from '@objectstack/spec/data';
|
|
31
2
|
/** A file value normalised for rendering, whatever form it arrived in. */
|
|
32
3
|
export interface FileValueView {
|
|
33
4
|
/** `sys_file` id, when the value carries one. */
|
|
@@ -43,10 +14,26 @@ export interface FileValueView {
|
|
|
43
14
|
}
|
|
44
15
|
/**
|
|
45
16
|
* A minted file id: uuid/nanoid-shaped, and crucially not a URL — a URL always
|
|
46
|
-
* carries `:`, `/` or `.`, so it can never match.
|
|
47
|
-
*
|
|
17
|
+
* carries `:`, `/` or `.`, so it can never match.
|
|
18
|
+
*
|
|
19
|
+
* This IS the platform's arbiter now, not a mirror of it (objectui#3161,
|
|
20
|
+
* objectstack#4115 ledger batch 7). The declaration that stood here said it
|
|
21
|
+
* "mirrors the platform's `isFileIdToken`" while being a character-for-character
|
|
22
|
+
* copy of that function's body under that function's own name — the shape
|
|
23
|
+
* objectui#3003 argued about and objectui#3169 caught again in
|
|
24
|
+
* `isAggregatedViewContainer`: every value test and every behaviour test passes
|
|
25
|
+
* against a faithful copy, so reference identity is the only check that can tell
|
|
26
|
+
* a re-export from a fork. It is asserted in
|
|
27
|
+
* `__tests__/spec-symbol-batch7.test.ts`.
|
|
28
|
+
*
|
|
29
|
+
* Why it matters that this one is shared rather than duplicated: the regex is a
|
|
30
|
+
* WIRE decision. Widening it server-side (say, ids grow past 64 chars) while a
|
|
31
|
+
* copy here keeps the old bound turns every new id into "not a reference", and
|
|
32
|
+
* the widget then submits the legacy inline blob against a backend that expects
|
|
33
|
+
* a reference. That failure surfaces as a broken thumbnail, nowhere near a
|
|
34
|
+
* regex.
|
|
48
35
|
*/
|
|
49
|
-
export
|
|
36
|
+
export { isFileIdToken };
|
|
50
37
|
/**
|
|
51
38
|
* The `sys_file` id a value refers to, or `undefined`.
|
|
52
39
|
*
|
|
@@ -0,0 +1,91 @@
|
|
|
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 RUNTIME EXECUTOR of the "DOM pass-through" section of
|
|
10
|
+
* {@link FieldWidgetComponentProps} (objectui#3291).
|
|
11
|
+
*
|
|
12
|
+
* ## The defect
|
|
13
|
+
*
|
|
14
|
+
* Field widgets forwarded their leftover props to a control with a bare
|
|
15
|
+
* spread — `const { inputType, ...domProps } = props as any; <Input
|
|
16
|
+
* {...domProps} />`. Whatever a host handed the widget therefore became a DOM
|
|
17
|
+
* attribute. Measured on `origin/main`, a real form + a real widget:
|
|
18
|
+
*
|
|
19
|
+
* ```
|
|
20
|
+
* <input placeholder="PH-f" zzcanary="CANARY-STR" zzcanaryobj="[object Object]"
|
|
21
|
+
* zzcanarynum="42" id="…" type="text" value="" name="f">
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* The `[object Object]` this issue is named for is an authored field-config
|
|
25
|
+
* key being `String()`-ed onto an attribute. React 19 does not warn: an
|
|
26
|
+
* all-lowercase unknown attribute is passed through in complete silence, which
|
|
27
|
+
* is why this survived so long.
|
|
28
|
+
*
|
|
29
|
+
* ## Why a whitelist, and not a list of keys to drop
|
|
30
|
+
*
|
|
31
|
+
* The biggest leak source is not any NAMED renderer prop — it is the open tail
|
|
32
|
+
* of author-supplied keys. The form renderer destructures a fixed set of known
|
|
33
|
+
* keys and forwards `...fieldProps` verbatim
|
|
34
|
+
* (`components/src/renderers/form/form.tsx`), and `SchemaRenderer` is wider
|
|
35
|
+
* still: it spreads the whole authored node as props and has no strip layer at
|
|
36
|
+
* all, so on the SDUI path a widget's own spread is the ONLY line of defence.
|
|
37
|
+
*
|
|
38
|
+
* A blacklist enumerating today's renderer-only keys (`error`, `emptyHint`,
|
|
39
|
+
* `dataSource`, `dependentValues`, `dependsOn`, `options`, `inputType`, …)
|
|
40
|
+
* would pass every one of the canaries above and would not stop the next
|
|
41
|
+
* authored key either. Only "keep the known-safe set, drop the rest" closes
|
|
42
|
+
* it, which is what this function does.
|
|
43
|
+
*
|
|
44
|
+
* ## Declared = enforced
|
|
45
|
+
*
|
|
46
|
+
* {@link FieldWidgetComponentProps} already DECLARES the closed set of keys a
|
|
47
|
+
* widget may receive, and {@link FieldWidgetDomProps} names the subset that may
|
|
48
|
+
* legitimately reach a DOM element (objectui#3221). Until now that was a
|
|
49
|
+
* type-level claim a widget could violate at runtime simply by spreading. This
|
|
50
|
+
* function is that declaration's executable form, and TWO compile-time
|
|
51
|
+
* assertions below bind them in both directions — forwarding an undeclared key
|
|
52
|
+
* and declaring an unforwarded DOM key are each a compile error. Neither
|
|
53
|
+
* direction can drift silently.
|
|
54
|
+
*
|
|
55
|
+
* ## Deliberately NOT forwarded
|
|
56
|
+
*
|
|
57
|
+
* `role` and other HTML global attributes are absent because the contract does
|
|
58
|
+
* not declare them. They reached the DOM before this change only through the
|
|
59
|
+
* open spread. If a field node should be able to author one, DECLARE it on
|
|
60
|
+
* `FieldWidgetComponentProps` first and add it here — do not reopen the spread
|
|
61
|
+
* (AGENTS.md #0.1: fix the contract, never widen the consumer).
|
|
62
|
+
*/
|
|
63
|
+
declare const DOM_PASS_THROUGH_KEYS: readonly ["id", "name", "autoFocus", "tabIndex", "onBlur", "onFocus", "onClick", "className", "disabled"];
|
|
64
|
+
type DomPassThroughKey = (typeof DOM_PASS_THROUGH_KEYS)[number];
|
|
65
|
+
/** 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
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Keep only what may legitimately become a DOM attribute, and drop everything
|
|
71
|
+
* else — renderer plumbing, and any extra key an author put on the field
|
|
72
|
+
* config or SDUI node.
|
|
73
|
+
*
|
|
74
|
+
* Replaces the bare `{...props}` spread in every field widget that renders a
|
|
75
|
+
* host element:
|
|
76
|
+
*
|
|
77
|
+
* ```tsx
|
|
78
|
+
* // before — forwards whatever it was handed
|
|
79
|
+
* const { inputType, ...domProps } = props as any;
|
|
80
|
+
* return <Input {...domProps} … />;
|
|
81
|
+
*
|
|
82
|
+
* // after
|
|
83
|
+
* return <Input {...toDomProps(props)} … />;
|
|
84
|
+
* ```
|
|
85
|
+
*
|
|
86
|
+
* Semantic props a widget INTERPRETS (`value`, `onChange`, `field`,
|
|
87
|
+
* `readonly`, `error`, …) are read from `props` as before; this function only
|
|
88
|
+
* governs what gets spread.
|
|
89
|
+
*/
|
|
90
|
+
export declare function toDomProps<P extends object>(props: P): DomProps<P>;
|
|
91
|
+
export {};
|