@object-ui/fields 16.1.0 → 17.2.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 +626 -0
- package/dist/FieldEditWidget.d.ts +9 -2
- package/dist/__tests__/spec-symbol-batch7.test.d.ts +8 -0
- package/dist/__tests__/widget-props-contract.test.d.ts +8 -0
- package/dist/field-type-alias.d.ts +16 -0
- package/dist/index.cjs +5 -5
- package/dist/index.d.ts +14 -11
- package/dist/index.js +2426 -2100
- package/dist/widgets/AddressField.d.ts +2 -2
- 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/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 +34 -2
- package/dist/widgets/FormulaField.d.ts +2 -2
- package/dist/widgets/GeolocationField.d.ts +2 -2
- package/dist/widgets/GridField.d.ts +2 -2
- package/dist/widgets/ImageField.d.ts +2 -2
- package/dist/widgets/ImageLightbox.d.ts +30 -0
- package/dist/widgets/ImageLightbox.test.d.ts +1 -0
- package/dist/widgets/LocationField.d.ts +2 -2
- package/dist/widgets/LookupField.d.ts +2 -2
- package/dist/widgets/LookupField.hydration.test.d.ts +1 -0
- package/dist/widgets/LookupField.idField.test.d.ts +1 -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/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/RecipientPickerField.test.d.ts +0 -0
- package/dist/widgets/RichTextField.d.ts +2 -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/TextAreaField.d.ts +2 -2
- 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__/FilterConditionField.matchAll.test.d.ts +8 -0
- package/dist/widgets/__tests__/FilterConditionField.operators.test.d.ts +8 -0
- package/dist/widgets/file-value.d.ts +99 -0
- package/dist/widgets/file-value.test.d.ts +1 -0
- package/dist/widgets/types.d.ts +119 -3
- package/dist/widgets/useFieldTranslation.d.ts +2 -0
- package/package.json +9 -8
- package/src/FieldEditWidget.test.ts +74 -2
- package/src/FieldEditWidget.tsx +50 -8
- package/src/__tests__/spec-symbol-batch7.test.ts +112 -0
- package/src/__tests__/widget-props-contract.test.tsx +129 -0
- package/src/complex-widgets.test.tsx +3 -3
- package/src/datetime-widgets.test.tsx +2 -2
- package/src/field-type-alias.ts +100 -0
- package/src/index.tsx +97 -134
- package/src/widgets/AddressField.tsx +2 -2
- package/src/widgets/AutoNumberField.tsx +2 -2
- package/src/widgets/AvatarField.tsx +2 -2
- package/src/widgets/BooleanField.tsx +2 -2
- package/src/widgets/CapabilityMultiSelectField.tsx +4 -4
- package/src/widgets/CheckboxesField.tsx +12 -9
- package/src/widgets/CodeField.tsx +2 -2
- package/src/widgets/ColorField.tsx +2 -2
- package/src/widgets/CurrencyField.tsx +2 -2
- package/src/widgets/DateField.tsx +2 -2
- package/src/widgets/DateTimeField.tsx +2 -2
- package/src/widgets/EmailField.tsx +2 -2
- package/src/widgets/FileField.tsx +90 -59
- package/src/widgets/FilterConditionField.tsx +112 -12
- package/src/widgets/FormulaField.tsx +2 -2
- package/src/widgets/GeolocationField.tsx +2 -2
- package/src/widgets/GridField.tsx +3 -3
- package/src/widgets/ImageField.tsx +73 -35
- package/src/widgets/ImageLightbox.test.tsx +52 -0
- package/src/widgets/ImageLightbox.tsx +108 -0
- package/src/widgets/LocationField.tsx +2 -2
- package/src/widgets/LookupField.hydration.test.tsx +136 -0
- package/src/widgets/LookupField.idField.test.tsx +61 -0
- package/src/widgets/LookupField.tsx +116 -19
- package/src/widgets/MasterDetailField.tsx +2 -2
- package/src/widgets/MultiSelectField.tsx +10 -7
- package/src/widgets/NumberField.tsx +2 -2
- package/src/widgets/ObjectField.tsx +2 -2
- package/src/widgets/ObjectRefField.tsx +13 -8
- package/src/widgets/PasswordField.tsx +2 -2
- package/src/widgets/PeoplePicker.test.tsx +20 -0
- package/src/widgets/PeoplePicker.tsx +15 -3
- package/src/widgets/PercentField.tsx +2 -2
- package/src/widgets/PhoneField.tsx +2 -2
- package/src/widgets/QRCodeField.tsx +2 -2
- package/src/widgets/RadioField.tsx +10 -7
- package/src/widgets/RatingField.tsx +2 -2
- package/src/widgets/RecipientPickerField.test.tsx +157 -0
- package/src/widgets/RecipientPickerField.tsx +40 -20
- package/src/widgets/RichTextField.tsx +2 -2
- package/src/widgets/SelectField.tsx +5 -5
- package/src/widgets/SignatureField.tsx +2 -2
- package/src/widgets/SliderField.tsx +2 -2
- package/src/widgets/SummaryField.tsx +2 -2
- package/src/widgets/TagsField.tsx +3 -3
- package/src/widgets/TextAreaField.tsx +6 -3
- package/src/widgets/TextField.tsx +2 -2
- package/src/widgets/TimeField.tsx +2 -2
- package/src/widgets/UrlField.tsx +2 -2
- package/src/widgets/UserField.tsx +2 -2
- package/src/widgets/VectorField.tsx +2 -2
- package/src/widgets/__tests__/FilterConditionField.matchAll.test.ts +66 -0
- package/src/widgets/__tests__/FilterConditionField.operators.test.ts +142 -0
- package/src/widgets/file-value.test.ts +230 -0
- package/src/widgets/file-value.ts +246 -0
- package/src/widgets/types.ts +129 -6
- package/src/widgets/useFieldTranslation.ts +30 -0
package/src/widgets/types.ts
CHANGED
|
@@ -1,10 +1,57 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { AriaAttributes, FocusEventHandler, MouseEventHandler } from 'react';
|
|
2
|
+
import type { DependsOnInput, FieldMetadata } from '@object-ui/types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Props every field widget in this package receives at RUNTIME.
|
|
6
|
+
*
|
|
7
|
+
* Named `FieldWidgetComponentProps`, not `FieldWidgetProps` (objectui#3161,
|
|
8
|
+
* objectstack#4115 ledger batch 7), adopting the name `@object-ui/app-shell`
|
|
9
|
+
* settled on for the same split in objectui#3169: `@objectstack/spec/ui` owns
|
|
10
|
+
* `FieldWidgetProps` for the DECLARED widget-plugin contract — a zod object
|
|
11
|
+
* (`FieldWidgetPropsSchema`) a plugin manifest is validated against, with
|
|
12
|
+
* `field` narrowed to `{ name?, label?, type: <the FieldType enum> }` and
|
|
13
|
+
* `readonly` / `required` carrying `.default()`s. This is the React interface
|
|
14
|
+
* the widgets in this directory actually implement, against
|
|
15
|
+
* `@object-ui/types`'s much richer `FieldMetadata`.
|
|
16
|
+
*
|
|
17
|
+
* The two are NOT interchangeable, and the divergence is not cosmetic: the
|
|
18
|
+
* spec's contract names the error slot `error`, this one names it
|
|
19
|
+
* `errorMessage`, so a widget written to the spec's declared props renders no
|
|
20
|
+
* validation message here. That divergence is tracked in objectui#3222 and is
|
|
21
|
+
* deliberately NOT resolved here — but it is now *visible*: reading
|
|
22
|
+
* `props.error` or `props.required` off this type is a compile error rather
|
|
23
|
+
* than a silent `any`.
|
|
24
|
+
*
|
|
25
|
+
* ## Why there is no `[key: string]: any` (objectui#3221)
|
|
26
|
+
*
|
|
27
|
+
* This type used to end in an index signature, "load-bearing" for the widgets'
|
|
28
|
+
* `...props` spreads. It was also why every drift above was invisible: **a type
|
|
29
|
+
* that claims to have every key can never be reported as missing one** (the
|
|
30
|
+
* objectstack#4075 mechanism). `props.required` and `props.error` were legal
|
|
31
|
+
* reads typed `any` and always `undefined` at runtime; a misspelled prop
|
|
32
|
+
* (`readOnly` for `readonly`, `onchange` for `onChange`) compiled; and any
|
|
33
|
+
* structural/parity comparison against the type was useless *in principle*,
|
|
34
|
+
* which is why the batch-7 symbol guard was the only detector that could see
|
|
35
|
+
* the collision at all.
|
|
36
|
+
*
|
|
37
|
+
* It is replaced by the CLOSED set below: the controlled-input contract, the
|
|
38
|
+
* host plumbing the form renderer genuinely forwards, and a DOM pass-through
|
|
39
|
+
* allowance. `data-*` is an open family by design (it is open in HTML too) and
|
|
40
|
+
* is expressed as a template-literal index signature, which — unlike
|
|
41
|
+
* `[key: string]` — leaves `keyof` finite, so an undeclared prop still fails.
|
|
42
|
+
*
|
|
43
|
+
* Adding a key here is a contract change: say who produces it and who reads it.
|
|
44
|
+
*/
|
|
45
|
+
export type FieldWidgetComponentProps<T = any> = {
|
|
46
|
+
/* ── The controlled-input contract every widget implements ─────────────── */
|
|
2
47
|
|
|
3
|
-
export type FieldWidgetProps<T = any> = {
|
|
4
48
|
value: T;
|
|
5
49
|
onChange: (val: T) => void;
|
|
6
|
-
|
|
7
|
-
|
|
50
|
+
/**
|
|
51
|
+
* The field's metadata. Deliberately the looser `@object-ui/types` shape
|
|
52
|
+
* rather than the spec's, to avoid a circular dependency for now.
|
|
53
|
+
*/
|
|
54
|
+
field: FieldMetadata;
|
|
8
55
|
readonly?: boolean;
|
|
9
56
|
disabled?: boolean;
|
|
10
57
|
className?: string;
|
|
@@ -15,5 +62,81 @@ export type FieldWidgetProps<T = any> = {
|
|
|
15
62
|
* widgets ignore it.
|
|
16
63
|
*/
|
|
17
64
|
onUploadingChange?: (uploading: boolean) => void;
|
|
18
|
-
|
|
19
|
-
|
|
65
|
+
|
|
66
|
+
/* ── Host plumbing: what a rendering host actually forwards ─────────────── */
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The raw authored node the widget was rendered from. Two hosts supply it:
|
|
70
|
+
* `SchemaRenderer` (`<Component schema={schema} {...schema} />`) and the
|
|
71
|
+
* form renderer's `renderFieldComponent` (`schema={props.field || ...}`),
|
|
72
|
+
* which is why ~25 widgets read `field || schema` for their config.
|
|
73
|
+
*
|
|
74
|
+
* This is a second carrier for what `field` already means, i.e. a de-facto
|
|
75
|
+
* second contract (AGENTS.md #0.1) — declared here so it is at least
|
|
76
|
+
* visible; converging the two is tracked separately.
|
|
77
|
+
*/
|
|
78
|
+
schema?: FieldMetadata | Record<string, unknown>;
|
|
79
|
+
/**
|
|
80
|
+
* DataSource for widgets that query records (lookup / user / object-ref /
|
|
81
|
+
* recipient-picker / grid). Injected by the form renderer for the field
|
|
82
|
+
* types that need it, and passed directly by inline-edit hosts. Option
|
|
83
|
+
* widgets destructure it purely to keep it off their DOM spread.
|
|
84
|
+
*
|
|
85
|
+
* Left structural (`unknown`): `@object-ui/fields` must not depend on a
|
|
86
|
+
* concrete adapter — every consumer narrows it itself.
|
|
87
|
+
*/
|
|
88
|
+
dataSource?: unknown;
|
|
89
|
+
/**
|
|
90
|
+
* Live sibling-field values driving cascading / role-gated options and
|
|
91
|
+
* dependent lookups (ADR-0058, #2215/#2284). The form renderer passes the
|
|
92
|
+
* in-progress record; widgets fall back to `SchemaRendererContext`.
|
|
93
|
+
*/
|
|
94
|
+
dependentValues?: Record<string, unknown>;
|
|
95
|
+
/**
|
|
96
|
+
* Controlling field(s) that gate this field's option list. Normally declared
|
|
97
|
+
* on the field metadata; accepted as a prop so a host can drive the gate for
|
|
98
|
+
* a field it synthesised. Field metadata wins when both are present.
|
|
99
|
+
*/
|
|
100
|
+
dependsOn?: DependsOnInput;
|
|
101
|
+
/**
|
|
102
|
+
* Hint shown when a dependency-gated option list is still waiting on its
|
|
103
|
+
* controlling field. Forwarded by the form renderer; the option widgets in
|
|
104
|
+
* this package currently discard it and render their own message instead.
|
|
105
|
+
*/
|
|
106
|
+
emptyHint?: string;
|
|
107
|
+
/**
|
|
108
|
+
* Render as a single-line, borderless control for a grid cell (the inline
|
|
109
|
+
* editor and the line-item grid set it) instead of the full form layout.
|
|
110
|
+
*/
|
|
111
|
+
compact?: boolean;
|
|
112
|
+
/**
|
|
113
|
+
* Receive the FULL selected record rather than just its id, so a host can
|
|
114
|
+
* auto-fill sibling fields from it (a line-item grid copying a product's
|
|
115
|
+
* price). When provided, the host owns the resulting value change.
|
|
116
|
+
*/
|
|
117
|
+
onSelectRecord?: (record: Record<string, any>) => void;
|
|
118
|
+
/**
|
|
119
|
+
* Offer a "create new" affordance in a record picker, carrying whatever the
|
|
120
|
+
* user had typed. Also declarable on the field metadata.
|
|
121
|
+
*/
|
|
122
|
+
onCreateNew?: (searchQuery: string) => void;
|
|
123
|
+
|
|
124
|
+
/* ── DOM pass-through: what `...props` may legitimately reach an input ──── */
|
|
125
|
+
|
|
126
|
+
id?: string;
|
|
127
|
+
/** react-hook-form's field name, spread in by the form renderer. */
|
|
128
|
+
name?: string;
|
|
129
|
+
autoFocus?: boolean;
|
|
130
|
+
tabIndex?: number;
|
|
131
|
+
onBlur?: FocusEventHandler<HTMLElement>;
|
|
132
|
+
onFocus?: FocusEventHandler<HTMLElement>;
|
|
133
|
+
onClick?: MouseEventHandler<HTMLElement>;
|
|
134
|
+
} & AriaAttributes & {
|
|
135
|
+
/**
|
|
136
|
+
* Arbitrary `data-*` attributes (test ids, analytics hooks). Open by design,
|
|
137
|
+
* but a template-literal key — `keyof` stays finite, so this does NOT
|
|
138
|
+
* reintroduce the index signature objectui#3221 removed: `props.required`
|
|
139
|
+
* is still an error, `props['data-testid']` is still fine.
|
|
140
|
+
*/
|
|
141
|
+
[dataAttribute: `data-${string}`]: string | number | boolean | undefined;
|
|
142
|
+
};
|
|
@@ -39,6 +39,36 @@ const FIELD_DEFAULTS: Record<string, string> = {
|
|
|
39
39
|
'lookup.nextPage': 'Next page',
|
|
40
40
|
'lookup.jumpToPage': 'Jump to page',
|
|
41
41
|
'lookup.retry': 'Retry',
|
|
42
|
+
// objectstack#3821 — sharing-rule authoring widgets (object-ref /
|
|
43
|
+
// recipient-picker / filter-condition). The recipient placeholder is keyed
|
|
44
|
+
// PER TYPE rather than interpolating the enum value into an English
|
|
45
|
+
// sentence, which no locale could translate.
|
|
46
|
+
'fields.objectRef.loading': 'Loading objects…',
|
|
47
|
+
'fields.objectRef.placeholder': 'Select an object',
|
|
48
|
+
'fields.objectRef.search': 'Search objects…',
|
|
49
|
+
'fields.objectRef.empty': 'No objects found',
|
|
50
|
+
'fields.recipient.selectTypeFirst': 'Select a recipient type first.',
|
|
51
|
+
'fields.recipient.loading': 'Loading…',
|
|
52
|
+
'fields.recipient.search': 'Search…',
|
|
53
|
+
'fields.recipient.empty': 'No matches',
|
|
54
|
+
'fields.recipient.select': 'Select a recipient',
|
|
55
|
+
'fields.recipient.selectUser': 'Select a user',
|
|
56
|
+
'fields.recipient.selectTeam': 'Select a team',
|
|
57
|
+
'fields.recipient.selectBusinessUnit': 'Select a business unit',
|
|
58
|
+
'fields.recipient.selectPosition': 'Select a position',
|
|
59
|
+
'fields.recipient.selectUnitAndSubordinates': 'Select a business unit',
|
|
60
|
+
'fields.filterCondition.selectObjectFirst': 'Select an object first.',
|
|
61
|
+
// objectstack#3896 — this used to be 'All records'. An empty criteria never
|
|
62
|
+
// meant "share everything"; it meant the predicate was missing, and the
|
|
63
|
+
// sharing evaluator failed open on it. Such a rule is now refused on save
|
|
64
|
+
// and shares nothing, so say that rather than advertise the old bug.
|
|
65
|
+
'fields.filterCondition.noCriteria': 'No criteria — this rule shares nothing',
|
|
66
|
+
'fields.filterCondition.criteriaRequired':
|
|
67
|
+
'Add at least one condition. A rule with no criteria would share every record, so it cannot be saved.',
|
|
68
|
+
'fields.filterCondition.invalidJson': 'Invalid JSON — the rule will match no records until fixed.',
|
|
69
|
+
'fields.filterCondition.jsonOnly': 'This criteria can only be edited as JSON',
|
|
70
|
+
'fields.filterCondition.editAsJson': 'Edit as JSON',
|
|
71
|
+
'fields.filterCondition.useVisualBuilder': 'Use visual builder',
|
|
42
72
|
// objectui#2600 B5 — capability picker scope group headers.
|
|
43
73
|
'capability.group.platform': 'Platform',
|
|
44
74
|
'capability.group.org': 'Organization',
|