@object-ui/fields 17.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.
Files changed (105) hide show
  1. package/CHANGELOG.md +194 -0
  2. package/dist/FieldEditWidget.d.ts +2 -2
  3. package/dist/__tests__/spec-symbol-batch7.test.d.ts +8 -0
  4. package/dist/__tests__/widget-props-contract.test.d.ts +8 -0
  5. package/dist/index.cjs +3 -3
  6. package/dist/index.js +290 -292
  7. package/dist/widgets/AddressField.d.ts +2 -2
  8. package/dist/widgets/AutoNumberField.d.ts +2 -2
  9. package/dist/widgets/AvatarField.d.ts +2 -2
  10. package/dist/widgets/BooleanField.d.ts +2 -2
  11. package/dist/widgets/CapabilityMultiSelectField.d.ts +2 -2
  12. package/dist/widgets/CheckboxesField.d.ts +2 -2
  13. package/dist/widgets/CodeField.d.ts +2 -2
  14. package/dist/widgets/ColorField.d.ts +2 -2
  15. package/dist/widgets/CurrencyField.d.ts +2 -2
  16. package/dist/widgets/DateField.d.ts +2 -2
  17. package/dist/widgets/DateTimeField.d.ts +2 -2
  18. package/dist/widgets/EmailField.d.ts +2 -2
  19. package/dist/widgets/FileField.d.ts +2 -2
  20. package/dist/widgets/FilterConditionField.d.ts +2 -2
  21. package/dist/widgets/FormulaField.d.ts +2 -2
  22. package/dist/widgets/GeolocationField.d.ts +2 -2
  23. package/dist/widgets/GridField.d.ts +2 -2
  24. package/dist/widgets/ImageField.d.ts +2 -2
  25. package/dist/widgets/LocationField.d.ts +2 -2
  26. package/dist/widgets/LookupField.d.ts +2 -2
  27. package/dist/widgets/MasterDetailField.d.ts +2 -2
  28. package/dist/widgets/MultiSelectField.d.ts +2 -2
  29. package/dist/widgets/NumberField.d.ts +2 -2
  30. package/dist/widgets/ObjectField.d.ts +2 -2
  31. package/dist/widgets/ObjectRefField.d.ts +2 -2
  32. package/dist/widgets/PasswordField.d.ts +2 -2
  33. package/dist/widgets/PercentField.d.ts +2 -2
  34. package/dist/widgets/PhoneField.d.ts +2 -2
  35. package/dist/widgets/QRCodeField.d.ts +2 -2
  36. package/dist/widgets/RadioField.d.ts +2 -2
  37. package/dist/widgets/RatingField.d.ts +2 -2
  38. package/dist/widgets/RecipientPickerField.d.ts +2 -2
  39. package/dist/widgets/RichTextField.d.ts +2 -2
  40. package/dist/widgets/SelectField.d.ts +2 -2
  41. package/dist/widgets/SignatureField.d.ts +2 -2
  42. package/dist/widgets/SliderField.d.ts +2 -2
  43. package/dist/widgets/SummaryField.d.ts +2 -2
  44. package/dist/widgets/TagsField.d.ts +2 -2
  45. package/dist/widgets/TextAreaField.d.ts +2 -2
  46. package/dist/widgets/TextField.d.ts +2 -2
  47. package/dist/widgets/TimeField.d.ts +2 -2
  48. package/dist/widgets/UrlField.d.ts +2 -2
  49. package/dist/widgets/UserField.d.ts +2 -2
  50. package/dist/widgets/VectorField.d.ts +2 -2
  51. package/dist/widgets/file-value.d.ts +20 -33
  52. package/dist/widgets/types.d.ts +119 -3
  53. package/package.json +8 -8
  54. package/src/FieldEditWidget.tsx +6 -4
  55. package/src/__tests__/spec-symbol-batch7.test.ts +112 -0
  56. package/src/__tests__/widget-props-contract.test.tsx +129 -0
  57. package/src/complex-widgets.test.tsx +3 -3
  58. package/src/datetime-widgets.test.tsx +2 -2
  59. package/src/index.tsx +3 -1
  60. package/src/widgets/AddressField.tsx +2 -2
  61. package/src/widgets/AutoNumberField.tsx +2 -2
  62. package/src/widgets/AvatarField.tsx +2 -2
  63. package/src/widgets/BooleanField.tsx +2 -2
  64. package/src/widgets/CapabilityMultiSelectField.tsx +4 -4
  65. package/src/widgets/CheckboxesField.tsx +4 -4
  66. package/src/widgets/CodeField.tsx +2 -2
  67. package/src/widgets/ColorField.tsx +2 -2
  68. package/src/widgets/CurrencyField.tsx +2 -2
  69. package/src/widgets/DateField.tsx +2 -2
  70. package/src/widgets/DateTimeField.tsx +2 -2
  71. package/src/widgets/EmailField.tsx +2 -2
  72. package/src/widgets/FileField.tsx +2 -2
  73. package/src/widgets/FilterConditionField.tsx +3 -3
  74. package/src/widgets/FormulaField.tsx +2 -2
  75. package/src/widgets/GeolocationField.tsx +2 -2
  76. package/src/widgets/GridField.tsx +3 -3
  77. package/src/widgets/ImageField.tsx +2 -2
  78. package/src/widgets/LocationField.tsx +2 -2
  79. package/src/widgets/LookupField.tsx +10 -10
  80. package/src/widgets/MasterDetailField.tsx +2 -2
  81. package/src/widgets/MultiSelectField.tsx +4 -4
  82. package/src/widgets/NumberField.tsx +2 -2
  83. package/src/widgets/ObjectField.tsx +2 -2
  84. package/src/widgets/ObjectRefField.tsx +4 -4
  85. package/src/widgets/PasswordField.tsx +2 -2
  86. package/src/widgets/PercentField.tsx +2 -2
  87. package/src/widgets/PhoneField.tsx +2 -2
  88. package/src/widgets/QRCodeField.tsx +2 -2
  89. package/src/widgets/RadioField.tsx +4 -4
  90. package/src/widgets/RatingField.tsx +2 -2
  91. package/src/widgets/RecipientPickerField.tsx +4 -4
  92. package/src/widgets/RichTextField.tsx +2 -2
  93. package/src/widgets/SelectField.tsx +5 -5
  94. package/src/widgets/SignatureField.tsx +2 -2
  95. package/src/widgets/SliderField.tsx +2 -2
  96. package/src/widgets/SummaryField.tsx +2 -2
  97. package/src/widgets/TagsField.tsx +3 -3
  98. package/src/widgets/TextAreaField.tsx +2 -2
  99. package/src/widgets/TextField.tsx +2 -2
  100. package/src/widgets/TimeField.tsx +2 -2
  101. package/src/widgets/UrlField.tsx +2 -2
  102. package/src/widgets/UserField.tsx +2 -2
  103. package/src/widgets/VectorField.tsx +2 -2
  104. package/src/widgets/file-value.ts +21 -5
  105. package/src/widgets/types.ts +129 -6
@@ -1,12 +1,12 @@
1
1
  import React from 'react';
2
2
  import { Input, EmptyValue } from '@object-ui/components';
3
- import { FieldWidgetProps } from './types';
3
+ import { FieldWidgetComponentProps } from './types';
4
4
 
5
5
  /**
6
6
  * Color field widget - provides a color picker input
7
7
  * Supports hex color values (e.g., #ff0000)
8
8
  */
9
- export function ColorField({ value, onChange, field, readonly, ...props }: FieldWidgetProps<string>) {
9
+ export function ColorField({ value, onChange, field, readonly, ...props }: FieldWidgetComponentProps<string>) {
10
10
  const colorField = (field || (props as any).schema) as any;
11
11
 
12
12
  if (readonly) {
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { Input, EmptyValue } from '@object-ui/components';
3
- import { FieldWidgetProps } from './types';
3
+ import { FieldWidgetComponentProps } from './types';
4
4
  import { useLocalization } from '@object-ui/i18n';
5
5
  import { resolveFieldCurrency } from '../currency';
6
6
 
@@ -36,7 +36,7 @@ const CURRENCY_SYMBOLS: Record<string, string> = {
36
36
  USD: '$',
37
37
  };
38
38
 
39
- export function CurrencyField({ value, onChange, field, readonly, errorMessage, className, ...props }: FieldWidgetProps<number>) {
39
+ export function CurrencyField({ value, onChange, field, readonly, errorMessage, className, ...props }: FieldWidgetComponentProps<number>) {
40
40
  const currencyField = (field || (props as any).schema) as any;
41
41
  // Shared precedence: field currency → currencyConfig → tenant default (ADR-0053).
42
42
  const { currency: tenantCurrency } = useLocalization();
@@ -1,13 +1,13 @@
1
1
  import React from 'react';
2
2
  import { Input, EmptyValue } from '@object-ui/components';
3
- import { FieldWidgetProps } from './types';
3
+ import { FieldWidgetComponentProps } from './types';
4
4
  import { openNativePicker } from './openNativePicker';
5
5
 
6
6
  /**
7
7
  * DateField - Date picker input widget
8
8
  * Uses native date input and displays locale-formatted date in readonly mode
9
9
  */
10
- export function DateField({ value, onChange, field, readonly, ...props }: FieldWidgetProps<string>) {
10
+ export function DateField({ value, onChange, field, readonly, ...props }: FieldWidgetComponentProps<string>) {
11
11
  if (readonly) {
12
12
  return value ? <span className="text-sm">{new Date(value).toLocaleDateString()}</span> : <EmptyValue />;
13
13
  }
@@ -1,13 +1,13 @@
1
1
  import React from 'react';
2
2
  import { Input, EmptyValue } from '@object-ui/components';
3
- import { FieldWidgetProps } from './types';
3
+ import { FieldWidgetComponentProps } from './types';
4
4
  import { openNativePicker } from './openNativePicker';
5
5
 
6
6
  /**
7
7
  * DateTimeField - Combined date and time picker widget
8
8
  * Displays both date and time in locale format when readonly
9
9
  */
10
- export function DateTimeField({ value, onChange, field, readonly, ...props }: FieldWidgetProps<string>) {
10
+ export function DateTimeField({ value, onChange, field, readonly, ...props }: FieldWidgetComponentProps<string>) {
11
11
  if (readonly) {
12
12
  if (!value) return <EmptyValue />;
13
13
  const date = new Date(value);
@@ -1,12 +1,12 @@
1
1
  import React from 'react';
2
2
  import { Input, EmptyValue } from '@object-ui/components';
3
- import { FieldWidgetProps } from './types';
3
+ import { FieldWidgetComponentProps } from './types';
4
4
 
5
5
  /**
6
6
  * EmailField - Email address input with mailto link in readonly mode
7
7
  * Renders as a clickable mailto link when readonly, standard email input otherwise
8
8
  */
9
- export function EmailField({ value, onChange, field, readonly, errorMessage, ...props }: FieldWidgetProps<string>) {
9
+ export function EmailField({ value, onChange, field, readonly, errorMessage, ...props }: FieldWidgetComponentProps<string>) {
10
10
  const config = field || (props as any).schema;
11
11
  if (readonly) {
12
12
  if (!value) return <EmptyValue />;
@@ -3,7 +3,7 @@ import { Button, EmptyValue } from '@object-ui/components';
3
3
  import { useUpload } from '@object-ui/providers';
4
4
  import { useObjectTranslation } from '@object-ui/i18n';
5
5
  import { Upload, X, File as FileIcon, ImageIcon, Camera, Loader2 } from 'lucide-react';
6
- import { FieldWidgetProps } from './types';
6
+ import { FieldWidgetComponentProps } from './types';
7
7
  import { useUploadingSignal } from './useUploadingSignal';
8
8
  import {
9
9
  fileValueForSubmit,
@@ -115,7 +115,7 @@ function useFileUploads(opts: {
115
115
  * Supports single and multiple file uploads with configurable accepted file types.
116
116
  * L2: File size validation, per-file progress indicators, error messages.
117
117
  */
118
- export function FileField({ value, onChange, field, readonly, onUploadingChange, ...props }: FieldWidgetProps<any>) {
118
+ export function FileField({ value, onChange, field, readonly, onUploadingChange, ...props }: FieldWidgetComponentProps<any>) {
119
119
  const { t } = useObjectTranslation();
120
120
  const inputRef = useRef<HTMLInputElement>(null);
121
121
  const cameraRef = useRef<HTMLInputElement>(null);
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { FilterBuilder, cn } from '@object-ui/components';
3
3
  import { SchemaRendererContext } from '@object-ui/react';
4
- import type { FieldWidgetProps } from './types';
4
+ import type { FieldWidgetComponentProps } from './types';
5
5
  import { useFieldTranslation } from './useFieldTranslation';
6
6
 
7
7
  /**
@@ -298,10 +298,10 @@ export function FilterConditionField({
298
298
  readonly,
299
299
  className,
300
300
  ...props
301
- }: FieldWidgetProps<string | object>) {
301
+ }: FieldWidgetComponentProps<string | object>) {
302
302
  const ctx = React.useContext(SchemaRendererContext);
303
303
  const { t } = useFieldTranslation();
304
- const dataSource: any = (props as any).dataSource ?? (ctx as any)?.dataSource ?? null;
304
+ const dataSource: any = props.dataSource ?? (ctx as any)?.dataSource ?? null;
305
305
  const dependentValues: Record<string, any> = (props as any).dependentValues ?? {};
306
306
  const objectName = String(dependentValues.object_name ?? '');
307
307
 
@@ -1,12 +1,12 @@
1
1
  import React from 'react';
2
2
  import { EmptyValue } from '@object-ui/components';
3
- import { FieldWidgetProps } from './types';
3
+ import { FieldWidgetComponentProps } from './types';
4
4
 
5
5
  /**
6
6
  * FormulaField - Read-only computed field
7
7
  * Values are computed on the backend and cannot be edited
8
8
  */
9
- export function FormulaField({ value, field, ...props }: FieldWidgetProps<any>) {
9
+ export function FormulaField({ value, field, ...props }: FieldWidgetComponentProps<any>) {
10
10
  const formulaField = (field || (props as any).schema) as any;
11
11
  const returnType = formulaField?.return_type || 'text';
12
12
 
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { Input, Button, Label, EmptyValue } from '@object-ui/components';
3
3
  import { MapPin, Crosshair } from 'lucide-react';
4
- import { FieldWidgetProps } from './types';
4
+ import { FieldWidgetComponentProps } from './types';
5
5
 
6
6
  /**
7
7
  * Geolocation data structure
@@ -16,7 +16,7 @@ export interface GeolocationValue {
16
16
  * Geolocation field widget - provides a location picker with coordinates
17
17
  * Supports manual entry and browser geolocation API
18
18
  */
19
- export function GeolocationField({ value, onChange, field, readonly, ...props }: FieldWidgetProps<GeolocationValue>) {
19
+ export function GeolocationField({ value, onChange, field, readonly, ...props }: FieldWidgetComponentProps<GeolocationValue>) {
20
20
  const [isLoading, setIsLoading] = React.useState(false);
21
21
  const location = value || {};
22
22
 
@@ -1,5 +1,5 @@
1
1
  import React, { useCallback, useRef } from 'react';
2
- import { FieldWidgetProps } from './types';
2
+ import { FieldWidgetComponentProps } from './types';
3
3
  import {
4
4
  cn,
5
5
  Button,
@@ -306,7 +306,7 @@ export function GridField({
306
306
  displayMode,
307
307
  onAdd,
308
308
  ...props
309
- }: FieldWidgetProps<Row[]> & {
309
+ }: FieldWidgetComponentProps<Row[]> & {
310
310
  /** When provided, each row shows an "expand" button that opens the row in a
311
311
  * full form (the host — e.g. MasterDetailForm — renders the drawer/modal and
312
312
  * writes the edited values back). Lets a "fat" child be edited in a real form
@@ -327,7 +327,7 @@ export function GridField({
327
327
  const cfg = (field || (props as any).schema || {}) as any;
328
328
  const allColumns: GridColumn[] = cfg.columns || [];
329
329
  const rows: Row[] = Array.isArray(value) ? value : [];
330
- const contextRecord = (props as any).contextRecord as Record<string, unknown> | undefined;
330
+ const contextRecord = props.contextRecord;
331
331
 
332
332
  // Per-cell CEL rule state (B2 in grids). A column with no readonlyWhen/
333
333
  // requiredWhen resolves to its static flags (cheap fast-path — no engine
@@ -3,7 +3,7 @@ import { Button, EmptyValue } from '@object-ui/components';
3
3
  import { useUpload } from '@object-ui/providers';
4
4
  import { useObjectTranslation } from '@object-ui/i18n';
5
5
  import { X, Image as ImageIcon, Crop as CropIcon, Loader2 } from 'lucide-react';
6
- import { FieldWidgetProps } from './types';
6
+ import { FieldWidgetComponentProps } from './types';
7
7
  import { ImageLightbox } from './ImageLightbox';
8
8
  import { useUploadingSignal } from './useUploadingSignal';
9
9
  import {
@@ -24,7 +24,7 @@ const ImageCropperDialog = lazy(() =>
24
24
  * ImageField - Image upload widget with preview thumbnails
25
25
  * Supports single and multiple image uploads with drag-and-drop and preview display
26
26
  */
27
- export function ImageField({ value, onChange, field, readonly, onUploadingChange, ...props }: FieldWidgetProps<any>) {
27
+ export function ImageField({ value, onChange, field, readonly, onUploadingChange, ...props }: FieldWidgetComponentProps<any>) {
28
28
  const inputRef = useRef<HTMLInputElement>(null);
29
29
  const imageField = (field || (props as any).schema) as any;
30
30
  const multiple = imageField?.multiple || false;
@@ -1,12 +1,12 @@
1
1
  import React from 'react';
2
2
  import { Input, EmptyValue } from '@object-ui/components';
3
- import { FieldWidgetProps } from './types';
3
+ import { FieldWidgetComponentProps } from './types';
4
4
 
5
5
  /**
6
6
  * LocationField - Geographic coordinate input for latitude and longitude
7
7
  * Stores location as { latitude, longitude } object and displays as comma-separated pair
8
8
  */
9
- export function LocationField({ value, onChange, field, readonly, ...props }: FieldWidgetProps<any>) {
9
+ export function LocationField({ value, onChange, field, readonly, ...props }: FieldWidgetComponentProps<any>) {
10
10
  const config = field || (props as any).schema;
11
11
  // Location is stored as { latitude, longitude } object
12
12
  // For display, convert to "latitude, longitude" string format
@@ -10,7 +10,7 @@ import { cn,
10
10
  PopoverTrigger,
11
11
  PopoverContent, EmptyValue } from '@object-ui/components';
12
12
  import { Search, X, Loader2, AlertCircle, Plus, TableProperties } from 'lucide-react';
13
- import { FieldWidgetProps } from './types';
13
+ import { FieldWidgetComponentProps } from './types';
14
14
  import type { DataSource, QueryParams, LookupColumnDef } from '@object-ui/types';
15
15
  import { RecordPickerDialog, lookupFiltersToRecord } from './RecordPickerDialog';
16
16
  import type { RecordPickerFilterColumn } from './RecordPickerDialog';
@@ -191,7 +191,7 @@ function mapFieldTypeToFilterType(
191
191
  * from the referenced object using `DataSource.find()`.
192
192
  * Falls back to static `options` when no DataSource is available.
193
193
  */
194
- export function LookupField({ value, onChange, field, readonly, ...props }: FieldWidgetProps<any>) {
194
+ export function LookupField({ value, onChange, field, readonly, ...props }: FieldWidgetComponentProps<any>) {
195
195
  const [isOpen, setIsOpen] = useState(false);
196
196
  const { t } = useFieldTranslation();
197
197
  const listboxId = React.useId();
@@ -280,13 +280,13 @@ export function LookupField({ value, onChange, field, readonly, ...props }: Fiel
280
280
  }, [fieldMeta?.depends_on, fieldMeta?.dependsOn]);
281
281
 
282
282
  // Resolve dependent field values from explicit prop or SchemaRendererContext.data
283
- const dependentValuesProp = (props as any).dependentValues as Record<string, any> | undefined;
283
+ const dependentValuesProp = props.dependentValues;
284
284
 
285
285
  // Resolve DataSource: explicit prop > field-level > wrapper field > SchemaRendererContext > none
286
286
  const ctx = useContext(SchemaRendererContext);
287
287
  const contextDataSource = ctx?.dataSource ?? null;
288
288
  const dataSource: DataSource | null =
289
- (props as any).dataSource ?? lookupField?.dataSource ?? fieldMeta?.dataSource ?? contextDataSource;
289
+ (props.dataSource as DataSource | null | undefined) ?? lookupField?.dataSource ?? fieldMeta?.dataSource ?? contextDataSource;
290
290
 
291
291
  /** Resolve dependent values from the explicit prop (preferred), the form-data
292
292
  * context provided by @object-ui/react, or finally `ctx.data` (record scope). */
@@ -369,7 +369,7 @@ export function LookupField({ value, onChange, field, readonly, ...props }: Fiel
369
369
 
370
370
  // Optional create-new callback
371
371
  const onCreateNew: ((searchQuery: string) => void) | undefined =
372
- (props as any).onCreateNew ?? lookupField?.onCreateNew;
372
+ props.onCreateNew ?? lookupField?.onCreateNew;
373
373
 
374
374
  // State for the full Record Picker dialog (Level 2)
375
375
  const [isPickerOpen, setIsPickerOpen] = useState(false);
@@ -657,7 +657,7 @@ export function LookupField({ value, onChange, field, readonly, ...props }: Fiel
657
657
  // auto-fill sibling fields from it — e.g. a line-item grid copying a product's
658
658
  // unit_price/description when the item is chosen. When provided (single
659
659
  // select), it drives the update and the host owns the resulting value change.
660
- const onSelectRecord = (props as any).onSelectRecord as ((record: LookupOption) => void) | undefined;
660
+ const onSelectRecord = props.onSelectRecord;
661
661
 
662
662
  const handleSelect = useCallback(
663
663
  (option: LookupOption) => {
@@ -906,7 +906,7 @@ export function LookupField({ value, onChange, field, readonly, ...props }: Fiel
906
906
  // Compact mode (e.g. inside a line-item grid cell): show the selected value
907
907
  // INSIDE a borderless trigger on a single line — no chip stacked above a
908
908
  // separate "Select…" button (which double-stacks and wastes the row height).
909
- const compact = !!(props as any).compact;
909
+ const compact = !!props.compact;
910
910
  const singleSelectedLabel = selectedOptions[0]?.label || selectedOptions[0]?.[displayField];
911
911
 
912
912
  // Shared field trigger — the anchor for either the inline PeoplePicker
@@ -920,8 +920,8 @@ export function LookupField({ value, onChange, field, readonly, ...props }: Fiel
920
920
  compact && 'h-8 rounded-none border-0 bg-transparent px-2 shadow-none focus-visible:ring-1 focus-visible:ring-ring/60',
921
921
  )}
922
922
  type="button"
923
- disabled={dependenciesMissing || (props as any).disabled}
924
- data-testid={dependenciesMissing ? 'lookup-trigger-gated' : (((props as any).name || lookupField?.name) ? `lookup-trigger-${(props as any).name || lookupField.name}` : 'lookup-trigger')}
923
+ disabled={dependenciesMissing || props.disabled}
924
+ data-testid={dependenciesMissing ? 'lookup-trigger-gated' : ((props.name || lookupField?.name) ? `lookup-trigger-${props.name || lookupField.name}` : 'lookup-trigger')}
925
925
  title={dependenciesMissing
926
926
  ? t('lookup.selectFirst', { fields: dependsOn.map(d => d.field).join(', ') })
927
927
  : undefined}
@@ -1214,7 +1214,7 @@ export function LookupField({ value, onChange, field, readonly, ...props }: Fiel
1214
1214
  type="button"
1215
1215
  // Gated exactly like the main trigger (#2215) — pre-fix this button
1216
1216
  // opened the full unscoped table while the dependency was missing.
1217
- disabled={dependenciesMissing || (props as any).disabled}
1217
+ disabled={dependenciesMissing || props.disabled}
1218
1218
  onClick={() => setIsPickerOpen(true)}
1219
1219
  aria-label={t('lookup.browseAll')}
1220
1220
  title={dependenciesMissing
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { Button, Badge, cn } from '@object-ui/components';
3
3
  import { Plus, X, ExternalLink } from 'lucide-react';
4
- import { FieldWidgetProps } from './types';
4
+ import { FieldWidgetComponentProps } from './types';
5
5
 
6
6
  /**
7
7
  * Master-Detail relationship data structure
@@ -23,7 +23,7 @@ export function MasterDetailField({
23
23
  readonly,
24
24
  className,
25
25
  ...props
26
- }: FieldWidgetProps<MasterDetailValue[]>) {
26
+ }: FieldWidgetComponentProps<MasterDetailValue[]>) {
27
27
  const items = value || [];
28
28
  const config = field || (props as any).schema;
29
29
 
@@ -1,7 +1,7 @@
1
1
  import React, { useEffect } from 'react';
2
2
  import { Badge, EmptyValue, cn } from '@object-ui/components';
3
3
  import type { OptionLike } from '@object-ui/core';
4
- import { FieldWidgetProps } from './types';
4
+ import { FieldWidgetComponentProps } from './types';
5
5
  import { useCascadingOptions } from './useCascadingOptions';
6
6
 
7
7
  interface Option extends OptionLike { color?: string }
@@ -32,11 +32,11 @@ export function MultiSelectField({
32
32
  emptyHint: _emptyHint,
33
33
  dataSource: _dataSource,
34
34
  ...props
35
- }: FieldWidgetProps<string[]>) {
35
+ }: FieldWidgetComponentProps<string[]>) {
36
36
  const config = (field || schema) as any;
37
37
  const rawOptions: Option[] = config?.options || [];
38
38
  const selected: string[] = Array.isArray(value) ? value : value == null ? [] : [value as unknown as string];
39
- const fieldName = (props as any).name || config?.name || (props as any).id || '';
39
+ const fieldName = props.name || config?.name || props.id || '';
40
40
 
41
41
  const dependsOn = config?.dependsOn ?? dependsOnProp;
42
42
  const { options, gated, dependsOnFields } = useCascadingOptions<Option>(
@@ -107,7 +107,7 @@ export function MultiSelectField({
107
107
  type="button"
108
108
  key={value}
109
109
  onClick={() => toggle(value)}
110
- disabled={(props as any).disabled}
110
+ disabled={props.disabled}
111
111
  aria-pressed={active}
112
112
  data-testid={`multiselect-option-${opt.value}`}
113
113
  className={cn(
@@ -1,13 +1,13 @@
1
1
  import React from 'react';
2
2
  import { Input, EmptyValue } from '@object-ui/components';
3
3
  import { NumberFieldMetadata } from '@object-ui/types';
4
- import { FieldWidgetProps } from './types';
4
+ import { FieldWidgetComponentProps } from './types';
5
5
 
6
6
  /**
7
7
  * NumberField - Numeric input with optional decimal precision
8
8
  * Supports min/max/step constraints and configurable decimal precision
9
9
  */
10
- export function NumberField({ value, onChange, field, readonly, ...props }: FieldWidgetProps<number>) {
10
+ export function NumberField({ value, onChange, field, readonly, ...props }: FieldWidgetComponentProps<number>) {
11
11
  if (readonly) {
12
12
  return value == null ? <EmptyValue /> : <span className="text-sm">{value}</span>;
13
13
  }
@@ -1,12 +1,12 @@
1
1
  import React, { useState, useEffect } from 'react';
2
2
  import { Textarea, cn, EmptyValue } from '@object-ui/components';
3
- import { FieldWidgetProps } from './types';
3
+ import { FieldWidgetComponentProps } from './types';
4
4
 
5
5
  /**
6
6
  * ObjectField - JSON object editor
7
7
  * Allows editing structured JSON data
8
8
  */
9
- export function ObjectField({ value, onChange, field, readonly, ...props }: FieldWidgetProps<any>) {
9
+ export function ObjectField({ value, onChange, field, readonly, ...props }: FieldWidgetComponentProps<any>) {
10
10
  const config = field || (props as any).schema;
11
11
 
12
12
  // Initialize string state based on value
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { Combobox, EmptyValue, cn } from '@object-ui/components';
3
3
  import { SchemaRendererContext } from '@object-ui/react';
4
- import type { FieldWidgetProps } from './types';
4
+ import type { FieldWidgetComponentProps } from './types';
5
5
  import { useFieldTranslation } from './useFieldTranslation';
6
6
 
7
7
  /**
@@ -32,11 +32,11 @@ export function ObjectRefField({
32
32
  readonly,
33
33
  className,
34
34
  ...props
35
- }: FieldWidgetProps<string>) {
35
+ }: FieldWidgetComponentProps<string>) {
36
36
  const ctx = React.useContext(SchemaRendererContext);
37
37
  const { t } = useFieldTranslation();
38
- const dataSource: any = (props as any).dataSource ?? (ctx as any)?.dataSource ?? null;
39
- const disabled = (props as any).disabled as boolean | undefined;
38
+ const dataSource: any = props.dataSource ?? (ctx as any)?.dataSource ?? null;
39
+ const disabled = props.disabled;
40
40
 
41
41
  const [objects, setObjects] = React.useState<ObjectHeader[] | null>(null);
42
42
 
@@ -2,13 +2,13 @@ import React, { useState } from 'react';
2
2
  import { Input } from '@object-ui/components';
3
3
  import { Button } from '@object-ui/components';
4
4
  import { Eye, EyeOff } from 'lucide-react';
5
- import { FieldWidgetProps } from './types';
5
+ import { FieldWidgetComponentProps } from './types';
6
6
 
7
7
  /**
8
8
  * PasswordField - Secure password input with visibility toggle
9
9
  * Includes show/hide toggle button and masks value in readonly mode
10
10
  */
11
- export function PasswordField({ value, onChange, field, readonly, className, ...props }: FieldWidgetProps<string>) {
11
+ export function PasswordField({ value, onChange, field, readonly, className, ...props }: FieldWidgetComponentProps<string>) {
12
12
  const [showPassword, setShowPassword] = useState(false);
13
13
  const config = field || (props as any).schema;
14
14
 
@@ -1,13 +1,13 @@
1
1
  import React from 'react';
2
2
  import { Input, Slider, EmptyValue } from '@object-ui/components';
3
- import { FieldWidgetProps } from './types';
3
+ import { FieldWidgetComponentProps } from './types';
4
4
 
5
5
  /**
6
6
  * PercentField - Percentage input with configurable decimal precision
7
7
  * Stores values as decimals (0-1) and displays as percentages (0-100%)
8
8
  * Includes a slider for interactive control.
9
9
  */
10
- export function PercentField({ value, onChange, field, readonly, errorMessage, className, ...props }: FieldWidgetProps<number>) {
10
+ export function PercentField({ value, onChange, field, readonly, errorMessage, className, ...props }: FieldWidgetComponentProps<number>) {
11
11
  const percentField = (field || (props as any).schema) as any;
12
12
  const precision = percentField?.precision ?? 2;
13
13
 
@@ -1,12 +1,12 @@
1
1
  import React from 'react';
2
2
  import { Input, EmptyValue } from '@object-ui/components';
3
- import { FieldWidgetProps } from './types';
3
+ import { FieldWidgetComponentProps } from './types';
4
4
 
5
5
  /**
6
6
  * PhoneField - Telephone number input with tel link in readonly mode
7
7
  * Renders as a clickable tel link when readonly, standard phone input otherwise
8
8
  */
9
- export function PhoneField({ value, onChange, field, readonly, errorMessage, ...props }: FieldWidgetProps<string>) {
9
+ export function PhoneField({ value, onChange, field, readonly, errorMessage, ...props }: FieldWidgetComponentProps<string>) {
10
10
  const config = field || (props as any).schema;
11
11
  if (readonly) {
12
12
  if (!value) return <EmptyValue />;
@@ -1,13 +1,13 @@
1
1
  import React from 'react';
2
2
  import { Input, Button, EmptyValue } from '@object-ui/components';
3
3
  import { QrCode, Copy } from 'lucide-react';
4
- import { FieldWidgetProps } from './types';
4
+ import { FieldWidgetComponentProps } from './types';
5
5
 
6
6
  /**
7
7
  * QR Code field widget - generates QR codes from text
8
8
  * Uses a simple SVG-based QR code generator
9
9
  */
10
- export function QRCodeField({ value, onChange, field, readonly, ...props }: FieldWidgetProps<string>) {
10
+ export function QRCodeField({ value, onChange, field, readonly, ...props }: FieldWidgetComponentProps<string>) {
11
11
  const [showQR, setShowQR] = React.useState(false);
12
12
  const config = field || (props as any).schema;
13
13
 
@@ -1,7 +1,7 @@
1
1
  import React, { useId, useEffect } from 'react';
2
2
  import { RadioGroup, RadioGroupItem, Label, EmptyValue } from '@object-ui/components';
3
3
  import { isValueStillOffered, type OptionLike } from '@object-ui/core';
4
- import { FieldWidgetProps } from './types';
4
+ import { FieldWidgetComponentProps } from './types';
5
5
  import { useCascadingOptions } from './useCascadingOptions';
6
6
 
7
7
  type Option = OptionLike;
@@ -30,11 +30,11 @@ export function RadioField({
30
30
  emptyHint: _emptyHint,
31
31
  dataSource: _dataSource,
32
32
  ...props
33
- }: FieldWidgetProps<string>) {
33
+ }: FieldWidgetComponentProps<string>) {
34
34
  const config = (field || schema) as any;
35
35
  const rawOptions: Option[] = config?.options || [];
36
36
  const groupId = useId();
37
- const fieldName = (props as any).name || config?.name || (props as any).id || '';
37
+ const fieldName = props.name || config?.name || props.id || '';
38
38
 
39
39
  const dependsOn = config?.dependsOn ?? dependsOnProp;
40
40
  const { options, gated, dependsOnFields } = useCascadingOptions<Option>(
@@ -81,7 +81,7 @@ export function RadioField({
81
81
  <RadioGroup
82
82
  value={value ?? ''}
83
83
  onValueChange={onChange}
84
- disabled={(props as any).disabled}
84
+ disabled={props.disabled}
85
85
  className={className}
86
86
  >
87
87
  {options.map((opt) => {
@@ -1,13 +1,13 @@
1
1
  import React from 'react';
2
2
  import { Star } from 'lucide-react';
3
3
  import { cn } from '@object-ui/components';
4
- import { FieldWidgetProps } from './types';
4
+ import { FieldWidgetComponentProps } from './types';
5
5
 
6
6
  /**
7
7
  * Rating field widget - provides a star rating input
8
8
  * Supports numeric values from 0 to max (default 5)
9
9
  */
10
- export function RatingField({ value, onChange, field, readonly, className, ...props }: FieldWidgetProps<number>) {
10
+ export function RatingField({ value, onChange, field, readonly, className, ...props }: FieldWidgetComponentProps<number>) {
11
11
  // Get rating-specific configuration from field metadata
12
12
  const ratingField = (field || (props as any).schema) as any;
13
13
  const max = ratingField?.max ?? 5;
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { Combobox, EmptyValue, cn } from '@object-ui/components';
3
3
  import { SchemaRendererContext } from '@object-ui/react';
4
- import type { FieldWidgetProps } from './types';
4
+ import type { FieldWidgetComponentProps } from './types';
5
5
  import { useFieldTranslation } from './useFieldTranslation';
6
6
 
7
7
  /**
@@ -54,11 +54,11 @@ export function RecipientPickerField({
54
54
  readonly,
55
55
  className,
56
56
  ...props
57
- }: FieldWidgetProps<string>) {
57
+ }: FieldWidgetComponentProps<string>) {
58
58
  const ctx = React.useContext(SchemaRendererContext);
59
59
  const { t } = useFieldTranslation();
60
- const dataSource: any = (props as any).dataSource ?? (ctx as any)?.dataSource ?? null;
61
- const disabled = (props as any).disabled as boolean | undefined;
60
+ const dataSource: any = props.dataSource ?? (ctx as any)?.dataSource ?? null;
61
+ const disabled = props.disabled;
62
62
  const dependentValues: Record<string, any> = (props as any).dependentValues ?? {};
63
63
  const recipientType = String(dependentValues.recipient_type ?? '');
64
64
  const mapping = TYPE_TO_OBJECT[recipientType];
@@ -1,14 +1,14 @@
1
1
  import React from 'react';
2
2
  import { Textarea, EmptyValue } from '@object-ui/components';
3
3
  import { useObjectTranslation } from '@object-ui/react';
4
- import { FieldWidgetProps } from './types';
4
+ import { FieldWidgetComponentProps } from './types';
5
5
 
6
6
  /**
7
7
  * Rich text field with markdown/HTML support
8
8
  * For now, this is a simple textarea. A full implementation would use
9
9
  * a rich text editor like TipTap, Lexical, or Slate.
10
10
  */
11
- export function RichTextField({ value, onChange, field, readonly, errorMessage, ...props }: FieldWidgetProps<string>) {
11
+ export function RichTextField({ value, onChange, field, readonly, errorMessage, ...props }: FieldWidgetComponentProps<string>) {
12
12
  const { t } = useObjectTranslation();
13
13
  if (readonly) {
14
14
  return (
@@ -10,7 +10,7 @@ import {
10
10
  import { isValueStillOffered } from '@object-ui/core';
11
11
  import { SelectFieldMetadata } from '@object-ui/types';
12
12
  import { useFieldTranslation } from './useFieldTranslation';
13
- import { FieldWidgetProps } from './types';
13
+ import { FieldWidgetComponentProps } from './types';
14
14
  import { MultiSelectField } from './MultiSelectField';
15
15
  import { useCascadingOptions } from './useCascadingOptions';
16
16
 
@@ -29,12 +29,12 @@ import { useCascadingOptions } from './useCascadingOptions';
29
29
  * the shared {@link useCascadingOptions} hook (#2715), so single and multi stay
30
30
  * in lockstep.
31
31
  */
32
- export function SelectField(props: FieldWidgetProps<any>) {
32
+ export function SelectField(props: FieldWidgetComponentProps<any>) {
33
33
  const config = (props.field || (props as any).schema) as SelectFieldMetadata | undefined;
34
34
  if ((config as any)?.multiple) {
35
35
  return <MultiSelectField {...props} />;
36
36
  }
37
- return <SingleSelectField {...(props as FieldWidgetProps<string>)} />;
37
+ return <SingleSelectField {...(props as FieldWidgetComponentProps<string>)} />;
38
38
  }
39
39
 
40
40
  /**
@@ -59,7 +59,7 @@ function SingleSelectField({
59
59
  emptyHint: _emptyHint,
60
60
  dataSource: _dataSource,
61
61
  ...props
62
- }: FieldWidgetProps<string>) {
62
+ }: FieldWidgetComponentProps<string>) {
63
63
  const config = (field || schema) as SelectFieldMetadata;
64
64
  const rawOptions = config?.options || [];
65
65
  const { t } = useFieldTranslation();
@@ -67,7 +67,7 @@ function SingleSelectField({
67
67
  // driven by synthetic DOM events, so e2e must target the trigger/options by a
68
68
  // deterministic testid keyed on the field name. `props.name` is the
69
69
  // react-hook-form field name spread in by the form renderer (FormField).
70
- const fieldName = (props as any).name || (config as any)?.name || props.id || '';
70
+ const fieldName = props.name || (config as any)?.name || props.id || '';
71
71
 
72
72
  const dependsOn = (config as any)?.dependsOn ?? dependsOnProp;
73
73
  const { options, gated, dependsOnFields } = useCascadingOptions(
@@ -1,13 +1,13 @@
1
1
  import React, { useRef, useEffect } from 'react';
2
2
  import { Button } from '@object-ui/components';
3
3
  import { Eraser } from 'lucide-react';
4
- import { FieldWidgetProps } from './types';
4
+ import { FieldWidgetComponentProps } from './types';
5
5
 
6
6
  /**
7
7
  * Signature field widget - provides a signature pad for capturing signatures
8
8
  * Outputs signature as a base64 PNG image
9
9
  */
10
- export function SignatureField({ value, onChange, readonly }: FieldWidgetProps<string>) {
10
+ export function SignatureField({ value, onChange, readonly }: FieldWidgetComponentProps<string>) {
11
11
  const canvasRef = useRef<HTMLCanvasElement>(null);
12
12
  const [isDrawing, setIsDrawing] = React.useState(false);
13
13
  const [isEmpty, setIsEmpty] = React.useState(!value);