diff --git a/.changeset/6138-fields-schema-block-parity-pr2.md b/.changeset/6138-fields-schema-block-parity-pr2.md new file mode 100644 index 0000000000..2bcd93ad29 --- /dev/null +++ b/.changeset/6138-fields-schema-block-parity-pr2.md @@ -0,0 +1,49 @@ +--- +--- + +Docs only, publishes nothing: batch 2 of 2 for objectui#6138 — the remaining +**22** `content/docs/fields` pages have their `Field Schema` block converted from +a self-declared interface to a literal **annotated** against that field type's +exported `*FieldMetadata`, each linking the shared +`content/docs/fields/widget-props.mdx` page batch 1 added. + +A self-declared interface with no imports type-checks no matter what it says, so +`check-doc-snippet-types` reported these pages green while being structurally +unable to see whether the documented shape matched the shipped one. An annotated +literal is judged by the compiler instead: the page becomes incapable of teaching +a key the type does not have. + +That property depends on the annotated types being **sealed**, so it was measured +rather than assumed. All 26 types this batch annotates against — the 22 field +metadata types plus `SelectOptionMetadata`, `GridColumnDefinition`, +`UploadedFileMetadata` and `LookupColumnDef` — carry no index signature, and a +nonsense key on each produces `TS2353`. The same control run against the +components lane's `BaseSchema` / `ButtonSchema` produces **zero** diagnostics, +which is what an open type does and why that lane cannot use this mechanism +(objectui#6143). + +Documentation defects the conversion forced out, each a page teaching something +no shipped type declares: + +- `grid.mdx` documented a per-column `editable` flag and a string `width`. + `GridColumnDefinition` declares neither — `width` is a number of pixels — and + no widget reads a column-level `editable`. Both are corrected. +- `formula.mdx` documented `return_type` as taking `currency`; the shipped union + is `'text' | 'number' | 'boolean' | 'date' | 'datetime'`. +- `lookup.mdx` documented option keys `_id` and `name`; the widget matches + options by `value` and labels them by `label`, and those two keys are read off + **records**, not options. +- `user.mdx` documented the value as a user object; the field stores the user's + id and the picker resolves the rest from `sys_user`. + +Two undeclared-but-consumed keys were found by checking each divergence against +its renderer, and are filed rather than deleted or documented as metadata: +`dependsOn` on select and `description` on a lookup's static options +(objectui#6153, the same class as objectui#6140). The location field's stored +`{ latitude, longitude }` value shape is declared by no exported type at all +(objectui#6154), so that page describes it in prose and points at the card. + +The gate's blocks-to-compile count rises from 248 to 249 — 21 conversions are +one-block-for-one-block and `lookup.mdx` becomes two blocks (data-source-backed +and static-option) — with diagnostics at 0, no new `FRAGMENT_MARKER` +declarations, and the declared-fragment count unmoved at 111. diff --git a/content/docs/fields/auto-number.mdx b/content/docs/fields/auto-number.mdx index b592abe782..59bd4bc207 100644 --- a/content/docs/fields/auto-number.mdx +++ b/content/docs/fields/auto-number.mdx @@ -19,21 +19,27 @@ The AutoNumber Field component displays auto-generated sequence numbers. This is ## Field Schema +An auto-number field is authored as `AutoNumberFieldMetadata` (`@object-ui/types`), +which is the source of truth for the key set: it extends `BaseFieldMetadata` with the +sequence format and its starting point. + ```ts -interface AutoNumberFieldSchema { - type: 'auto_number'; - name: string; // Field name/ID - label?: string; // Field label - value?: string | number; // Generated value (read-only) - readonly: true; // Always read-only - className?: string; // Additional CSS classes - - // AutoNumber Configuration - format?: string; // Number format template - starting_number?: number; // Starting sequence number -} +import type { AutoNumberFieldMetadata } from '@object-ui/types'; + +const invoiceNumber: AutoNumberFieldMetadata = { + type: 'auto_number', + name: 'invoice_number', + label: 'Invoice Number', + help: 'Assigned by the platform when the record is created.', + readonly: true, + format: 'INV-{0000}', + starting_number: 1000, +}; ``` +The generated value, and the `className` a host supplies, are **not** metadata keys — +they are runtime widget props. See [Field Widget Props](/docs/fields/widget-props). + ## Format Templates Common format patterns: diff --git a/content/docs/fields/boolean.mdx b/content/docs/fields/boolean.mdx index 3f19887950..6d94ea095e 100644 --- a/content/docs/fields/boolean.mdx +++ b/content/docs/fields/boolean.mdx @@ -19,20 +19,26 @@ The Boolean Field component provides a switch or checkbox input for collecting t ## Field Schema +A boolean field is authored as `BooleanFieldMetadata` (`@object-ui/types`), which is +the source of truth for the key set. It adds nothing of its own to +`BaseFieldMetadata` beyond the discriminant — a checkbox needs no extra configuration. + ```ts -interface BooleanFieldSchema { - type: 'boolean'; - name: string; // Field name/ID - label?: string; // Field label - description?: string; // Helper text - value?: boolean; // Default value - required?: boolean; // Is field required - readonly?: boolean; // Read-only mode - disabled?: boolean; // Disabled state - className?: string; // Additional CSS classes -} +import type { BooleanFieldMetadata } from '@object-ui/types'; + +const isActive: BooleanFieldMetadata = { + type: 'boolean', + name: 'is_active', + label: 'Active', + description: 'Inactive records stay searchable but are excluded from lists.', + required: false, + defaultValue: true, +}; ``` +The value being edited, and the `className` / `disabled` a host supplies, are **not** +metadata keys — they are runtime widget props. See [Field Widget Props](/docs/fields/widget-props). + ## Use Cases - **Feature Toggles**: Enable/disable features diff --git a/content/docs/fields/currency.mdx b/content/docs/fields/currency.mdx index 4d81ba471a..d004d7ecbb 100644 --- a/content/docs/fields/currency.mdx +++ b/content/docs/fields/currency.mdx @@ -15,25 +15,29 @@ The Currency Field component provides a formatted currency input with proper loc ## Field Schema +A currency field is authored as `CurrencyFieldMetadata` (`@object-ui/types`), which is +the source of truth for the key set: it extends `BaseFieldMetadata` with the currency +code, the decimal precision and the two numeric bounds. + ```ts -interface CurrencyFieldSchema { - type: 'currency'; - name: string; // Field name/ID - label?: string; // Field label - placeholder?: string; // Placeholder text - value?: number; // Default value - currency?: string; // Currency code (default: 'USD') - required?: boolean; // Is field required - readonly?: boolean; // Read-only mode - disabled?: boolean; // Disabled state - className?: string; // Additional CSS classes - - // Validation - min?: number; // Minimum value - max?: number; // Maximum value -} +import type { CurrencyFieldMetadata } from '@object-ui/types'; + +const amount: CurrencyFieldMetadata = { + type: 'currency', + name: 'amount', + label: 'Amount', + placeholder: '0.00', + required: true, + currency: 'USD', + precision: 2, + min: 0, + max: 1000000, +}; ``` +The value being edited, and the `className` / `disabled` a host supplies, are **not** +metadata keys — they are runtime widget props. See [Field Widget Props](/docs/fields/widget-props). + ## Supported Currencies - **USD**: US Dollar ($) diff --git a/content/docs/fields/datetime.mdx b/content/docs/fields/datetime.mdx index 3feba05e89..3819cb2a7f 100644 --- a/content/docs/fields/datetime.mdx +++ b/content/docs/fields/datetime.mdx @@ -23,25 +23,28 @@ The DateTime Field component provides a combined date and time input for collect ## Field Schema +A datetime field is authored as `DateTimeFieldMetadata` (`@object-ui/types`), which is +the source of truth for the key set: it extends `BaseFieldMetadata` with a display +format and the two range bounds. + ```ts -interface DateTimeFieldSchema { - type: 'datetime'; - name: string; // Field name/ID - label?: string; // Field label - placeholder?: string; // Placeholder text - value?: string; // Default value (ISO 8601 format) - required?: boolean; // Is field required - readonly?: boolean; // Read-only mode - disabled?: boolean; // Disabled state - className?: string; // Additional CSS classes - - // Validation - format?: string; // Display format - min_date?: string | Date; // Minimum date/time - max_date?: string | Date; // Maximum date/time -} +import type { DateTimeFieldMetadata } from '@object-ui/types'; + +const startsAt: DateTimeFieldMetadata = { + type: 'datetime', + name: 'starts_at', + label: 'Starts At', + placeholder: 'Pick a date and time', + required: true, + format: 'yyyy-MM-dd HH:mm', + min_date: '2024-01-01T00:00:00Z', + max_date: '2030-12-31T23:59:59Z', +}; ``` +The value being edited, and the `className` / `disabled` a host supplies, are **not** +metadata keys — they are runtime widget props. See [Field Widget Props](/docs/fields/widget-props). + ## Date Format The datetime field stores values in ISO 8601 format: `YYYY-MM-DDTHH:mm` diff --git a/content/docs/fields/email.mdx b/content/docs/fields/email.mdx index 39a9fef743..43b4696458 100644 --- a/content/docs/fields/email.mdx +++ b/content/docs/fields/email.mdx @@ -15,20 +15,26 @@ The Email Field component provides a text input with built-in email validation a ## Field Schema +An email field is authored as `EmailFieldMetadata` (`@object-ui/types`), which is the +source of truth for the key set: it extends `BaseFieldMetadata` with a single length +bound. Address-shape validation is the widget's, not a metadata key. + ```ts -interface EmailFieldSchema { - type: 'email'; - name: string; // Field name/ID - label?: string; // Field label - placeholder?: string; // Placeholder text - value?: string; // Default value - required?: boolean; // Is field required - readonly?: boolean; // Read-only mode - disabled?: boolean; // Disabled state - className?: string; // Additional CSS classes -} +import type { EmailFieldMetadata } from '@object-ui/types'; + +const contactEmail: EmailFieldMetadata = { + type: 'email', + name: 'contact_email', + label: 'Email Address', + placeholder: 'name@example.com', + required: true, + max_length: 254, +}; ``` +The value being edited, and the `className` / `disabled` a host supplies, are **not** +metadata keys — they are runtime widget props. See [Field Widget Props](/docs/fields/widget-props). + ## Validation The email field automatically validates: diff --git a/content/docs/fields/file.mdx b/content/docs/fields/file.mdx index cedd6d72fb..39330d5bb0 100644 --- a/content/docs/fields/file.mdx +++ b/content/docs/fields/file.mdx @@ -19,33 +19,37 @@ The File Field component provides a file upload interface with support for multi ## Field Schema +A file field is authored as `FileFieldMetadata` (`@object-ui/types`), which is the +source of truth for the key set: it extends `BaseFieldMetadata` with the upload limits. +A stored file is `UploadedFileMetadata`, exported from the same package. + ```ts -interface FileFieldSchema { - type: 'file'; - name: string; // Field name/ID - label?: string; // Field label - value?: FileMetadata | FileMetadata[]; // Current file(s) - required?: boolean; // Is field required - readonly?: boolean; // Read-only mode - disabled?: boolean; // Disabled state - className?: string; // Additional CSS classes - - // File Options - multiple?: boolean; // Allow multiple files - accept?: string[]; // Accepted MIME types - max_size?: number; // Max file size in bytes - max_files?: number; // Max number of files -} - -interface FileMetadata { - name: string; // File name - original_name?: string; // Original file name - size?: number; // File size in bytes - mime_type?: string; // MIME type - url?: string; // File URL -} +import type { FileFieldMetadata, UploadedFileMetadata } from '@object-ui/types'; + +const attachments: FileFieldMetadata = { + type: 'file', + name: 'attachments', + label: 'Attachments', + help: 'PDF or Word, up to 10 MB each.', + multiple: true, + accept: ['application/pdf', 'application/msword'], + max_size: 10 * 1024 * 1024, + max_files: 5, +}; + +// The shape of one stored file — the field's VALUE, not its metadata. +const storedFile: UploadedFileMetadata = { + name: 'contract.pdf', + original_name: 'Contract (signed).pdf', + size: 248_310, + mime_type: 'application/pdf', + url: 'https://files.example.com/contract.pdf', +}; ``` +The value being edited, and the `className` / `disabled` a host supplies, are **not** +metadata keys — they are runtime widget props. See [Field Widget Props](/docs/fields/widget-props). + ## Accepted File Types Common MIME type examples: diff --git a/content/docs/fields/formula.mdx b/content/docs/fields/formula.mdx index 793fb7d90b..2fc8d558fb 100644 --- a/content/docs/fields/formula.mdx +++ b/content/docs/fields/formula.mdx @@ -19,21 +19,28 @@ The Formula Field component displays computed values calculated from other field ## Field Schema +A formula field is authored as `FormulaFieldMetadata` (`@object-ui/types`), which is +the source of truth for the key set: it extends `BaseFieldMetadata` with the +expression, its declared return type and the recompute switch. `return_type` is a +closed union — `'text' | 'number' | 'boolean' | 'date' | 'datetime'`. + ```ts -interface FormulaFieldSchema { - type: 'formula'; - name: string; // Field name/ID - label?: string; // Field label - value?: any; // Computed value (read-only) - readonly: true; // Always read-only - className?: string; // Additional CSS classes - - // Formula Configuration - formula?: string; // Formula expression - return_type?: string; // Return type (text, number, boolean, date, currency) -} +import type { FormulaFieldMetadata } from '@object-ui/types'; + +const totalPrice: FormulaFieldMetadata = { + type: 'formula', + name: 'total_price', + label: 'Total Price', + readonly: true, + formula: 'quantity * unit_price', + return_type: 'number', + auto_compute: true, +}; ``` +The computed value, and the `className` a host supplies, are **not** metadata keys — +they are runtime widget props. See [Field Widget Props](/docs/fields/widget-props). + ## Return Types The formula field formats values based on return type: diff --git a/content/docs/fields/grid.mdx b/content/docs/fields/grid.mdx index f5b407e39c..74824a2c46 100644 --- a/content/docs/fields/grid.mdx +++ b/content/docs/fields/grid.mdx @@ -19,32 +19,37 @@ The Grid Field component provides an inline table for managing related records o ## Field Schema +A grid field is authored as `GridFieldMetadata` (`@object-ui/types`), which is the +source of truth for the key set: it extends `BaseFieldMetadata` with the column list +and the row-count and row-action limits. Each column is a `GridColumnDefinition`, so +the columns are checked by the same compiler that checks the field. + ```ts -interface GridFieldSchema { - type: 'grid'; - name: string; // Field name/ID - label?: string; // Field label - value?: any[]; // Array of row objects - required?: boolean; // Is field required - readonly?: boolean; // Read-only mode - disabled?: boolean; // Disabled state - className?: string; // Additional CSS classes - - // Grid Options - columns?: ColumnDefinition[]; // Column definitions -} +import type { GridFieldMetadata } from '@object-ui/types'; -interface ColumnDefinition { - name: string; // Column field name - label: string; // Column header label - type: string; // Field type (text, number, etc.) - width?: string; // Column width - editable?: boolean; // Is column editable - required?: boolean; // Is column required - [key: string]: any; // Additional field-specific props -} +const lineItems: GridFieldMetadata = { + type: 'grid', + name: 'line_items', + label: 'Line Items', + columns: [ + { name: 'product', label: 'Product', type: 'lookup', required: true, width: 240 }, + { name: 'quantity', label: 'Qty', type: 'number', defaultValue: 1, width: 80 }, + { name: 'unit_price', label: 'Unit Price', type: 'currency', width: 120 }, + ], + min_rows: 1, + max_rows: 50, + allow_add: true, + allow_delete: true, + allow_reorder: false, +}; ``` +A column's `width` is a **number** of pixels, and there is no per-column `editable` +key: whether cells can be edited follows the field's own read-only state. + +The value being edited, and the `className` / `disabled` a host supplies, are **not** +metadata keys — they are runtime widget props. See [Field Widget Props](/docs/fields/widget-props). + ## Column Types Columns can use any field type: diff --git a/content/docs/fields/image.mdx b/content/docs/fields/image.mdx index 78263f8be6..bc91d42da4 100644 --- a/content/docs/fields/image.mdx +++ b/content/docs/fields/image.mdx @@ -15,29 +15,30 @@ The Image Field component provides an image upload interface with thumbnail prev ## Field Schema +An image field is authored as `ImageFieldMetadata` (`@object-ui/types`), which is the +source of truth for the key set: it extends `BaseFieldMetadata` with the upload limits +and the two pixel bounds. A stored image is `UploadedFileMetadata`, the same value +shape the file field stores. + ```ts -import type { UploadedFileMetadata } from '@object-ui/types'; - -interface ImageFieldSchema { - type: 'image'; - name: string; // Field name/ID - label?: string; // Field label - value?: UploadedFileMetadata | UploadedFileMetadata[]; // Current image(s) - required?: boolean; // Is field required - readonly?: boolean; // Read-only mode - disabled?: boolean; // Disabled state - className?: string; // Additional CSS classes - - // Image Options - multiple?: boolean; // Allow multiple images - accept?: string[]; // Accepted image types - max_size?: number; // Max file size in bytes - max_files?: number; // Max number of images - max_width?: number; // Max image width - max_height?: number; // Max image height -} +import type { ImageFieldMetadata } from '@object-ui/types'; + +const productPhotos: ImageFieldMetadata = { + type: 'image', + name: 'product_photos', + label: 'Product Photos', + multiple: true, + accept: ['image/png', 'image/jpeg', 'image/webp'], + max_size: 5 * 1024 * 1024, + max_files: 8, + max_width: 4096, + max_height: 4096, +}; ``` +The value being edited, and the `className` / `disabled` a host supplies, are **not** +metadata keys — they are runtime widget props. See [Field Widget Props](/docs/fields/widget-props). + ## Accepted Image Types By default, accepts all common image formats: diff --git a/content/docs/fields/location.mdx b/content/docs/fields/location.mdx index 50652d9a66..85210c637e 100644 --- a/content/docs/fields/location.mdx +++ b/content/docs/fields/location.mdx @@ -19,28 +19,31 @@ The Location Field component provides an input for geographic coordinates, stori ## Field Schema +A location field is authored as `LocationFieldMetadata` (`@object-ui/types`), which is +the source of truth for the key set: it extends `BaseFieldMetadata` with the map's +default zoom level. + ```ts -interface LocationFieldSchema { - type: 'location'; - name: string; // Field name/ID - label?: string; // Field label - placeholder?: string; // Placeholder text - value?: LocationValue; // Default coordinates - required?: boolean; // Is field required - readonly?: boolean; // Read-only mode - disabled?: boolean; // Disabled state - className?: string; // Additional CSS classes - - // Map Options - default_zoom?: number; // Default map zoom level -} +import type { LocationFieldMetadata } from '@object-ui/types'; -interface LocationValue { - latitude: number; // -90 to 90 - longitude: number; // -180 to 180 -} +const officeLocation: LocationFieldMetadata = { + type: 'location', + name: 'office_location', + label: 'Office Location', + placeholder: 'latitude, longitude', + required: false, + default_zoom: 12, +}; ``` +The coordinates themselves are the field's **value**, not metadata: the widget stores +an object carrying a `latitude` and a `longitude` and displays it as a comma-separated +pair. No exported type declares that value shape today, which is tracked as +[objectui#6154](https://github.com/objectstack-ai/objectui/issues/6154). + +The value being edited, and the `className` / `disabled` a host supplies, are **not** +metadata keys — they are runtime widget props. See [Field Widget Props](/docs/fields/widget-props). + ## Data Format The location field stores coordinates as an object: diff --git a/content/docs/fields/lookup.mdx b/content/docs/fields/lookup.mdx index 144044bbbc..8fdfdce07d 100644 --- a/content/docs/fields/lookup.mdx +++ b/content/docs/fields/lookup.mdx @@ -15,56 +15,61 @@ The Lookup Field component provides a reference field for creating relationships ## Field Schema +A lookup field is authored as `LookupFieldMetadata` (`@object-ui/types`), which is the +source of truth for the key set: it extends `BaseFieldMetadata` with the reference +target, the display and id fields, an optional static option list, and the Record +Picker's column and paging configuration. Static options are `SelectOptionMetadata`, +and picker columns are `LookupColumnDef` — both exported, both checked here. + ```ts -import type { DataSource } from '@object-ui/types'; - -interface LookupFieldSchema { - type: 'lookup' | 'master_detail'; - name: string; // Field name/ID - label?: string; // Field label - placeholder?: string; // Placeholder text - value?: string | string[]; // Default value(s) - multiple?: boolean; // Allow multiple selections - required?: boolean; // Is field required - readonly?: boolean; // Read-only mode - disabled?: boolean; // Disabled state - className?: string; // Additional CSS classes - - // Reference Configuration - reference_to: string; // Referenced object/table name - reference_field?: string; // Field to display (default: 'name') - description_field?: string; // Secondary field shown below label - id_field?: string; // ID field on records (default: '_id') - options?: LookupOption[]; // Available options (if static) - - // Data Source (automatic via SchemaRendererContext, or explicit) - // When a DataSource is available, the popup dynamically loads - // records from the referenced object on open, with debounced search. - dataSource?: DataSource; - - // Quick-create callback (shown when no results found) - onCreateNew?: (searchQuery: string) => void; - - // === Record Picker Configuration (Enterprise) === - - // Columns to show in the Record Picker dialog table. - // Accepts field names or { field, label, width } objects. - // When omitted, auto-infers from reference_field. - lookup_columns?: Array; - - // Custom page size for the Record Picker dialog (default: 10) - lookup_page_size?: number; -} +import type { LookupFieldMetadata } from '@object-ui/types'; -interface LookupOption { - label: string; // Display label - value: string; // Record ID - description?: string; // Secondary text below label - _id?: string; // Alternative ID field - name?: string; // Alternative label field -} +const accountId: LookupFieldMetadata = { + type: 'lookup', + name: 'account_id', + label: 'Account', + placeholder: 'Search accounts…', + required: true, + reference_to: 'accounts', + reference_field: 'name', + description_field: 'industry', + id_field: '_id', + multiple: false, + searchable: true, + allow_create: true, + // Record Picker dialog (Enterprise): columns accept a field name or a descriptor. + lookup_columns: ['name', { field: 'industry', label: 'Industry', width: '160px' }], + lookup_page_size: 10, + lookup_filters: [{ field: 'active', operator: 'eq', value: true }], +}; ``` +When no data source is available the field falls back to a static option list: + +```ts +import type { LookupFieldMetadata } from '@object-ui/types'; + +const priority: LookupFieldMetadata = { + type: 'lookup', + name: 'priority', + label: 'Priority', + reference_to: 'priorities', + options: [ + { label: 'High', value: 'high' }, + { label: 'Normal', value: 'normal' }, + ], +}; +``` + +The picker also searches a `description` on a static option, but +`SelectOptionMetadata` does not declare that key; the gap is tracked as +[objectui#6153](https://github.com/objectstack-ai/objectui/issues/6153). The +`dataSource` a host injects and the `onCreateNew` callback it passes are widget props, +not metadata. + +The value being edited, and the `className` / `disabled` a host supplies, are **not** +metadata keys — they are runtime widget props. See [Field Widget Props](/docs/fields/widget-props). + ## Dynamic Data Source When a `DataSource` is available (via `SchemaRendererContext`, explicit prop, or field config), the Lookup popup **automatically** fetches records from the referenced object: diff --git a/content/docs/fields/number.mdx b/content/docs/fields/number.mdx index 3178e80b0e..3f3208ddc6 100644 --- a/content/docs/fields/number.mdx +++ b/content/docs/fields/number.mdx @@ -19,26 +19,30 @@ The Number Field component provides a numeric input for collecting integer or de ## Field Schema +A number field is authored as `NumberFieldMetadata` (`@object-ui/types`), which is the +source of truth for the key set: it extends `BaseFieldMetadata` with the numeric +bounds, the stored precision and scale, and the stepper increment. + ```ts -interface NumberFieldSchema { - type: 'number'; - name: string; // Field name/ID - label?: string; // Field label - placeholder?: string; // Placeholder text - value?: number; // Default value - required?: boolean; // Is field required - readonly?: boolean; // Read-only mode - disabled?: boolean; // Disabled state - className?: string; // Additional CSS classes - - // Validation - min?: number; // Minimum value - max?: number; // Maximum value - precision?: number; // Decimal places (default: 0) - step?: number; // Increment/decrement step -} +import type { NumberFieldMetadata } from '@object-ui/types'; + +const quantity: NumberFieldMetadata = { + type: 'number', + name: 'quantity', + label: 'Quantity', + placeholder: '0', + required: true, + min: 0, + max: 9999, + precision: 10, + scale: 0, + step: 1, +}; ``` +The value being edited, and the `className` / `disabled` a host supplies, are **not** +metadata keys — they are runtime widget props. See [Field Widget Props](/docs/fields/widget-props). + ## Use Cases - **Quantities**: Order quantities, stock levels diff --git a/content/docs/fields/object.mdx b/content/docs/fields/object.mdx index df6ade2ff1..ba5121142f 100644 --- a/content/docs/fields/object.mdx +++ b/content/docs/fields/object.mdx @@ -23,23 +23,29 @@ The Object Field component provides a JSON editor for storing and editing struct ## Field Schema +An object field is authored as `ObjectFieldMetadata` (`@object-ui/types`), which is the +source of truth for the key set: it extends `BaseFieldMetadata` with one key, an +optional `schema` describing the JSON the field accepts. + ```ts -interface ObjectFieldSchema { - type: 'object'; - name: string; // Field name/ID - label?: string; // Field label - placeholder?: string; // Placeholder text - value?: Record; // JSON object value - required?: boolean; // Is field required - readonly?: boolean; // Read-only mode - disabled?: boolean; // Disabled state - className?: string; // Additional CSS classes - - // Object Options - schema?: Record; // Optional schema definition -} +import type { ObjectFieldMetadata } from '@object-ui/types'; + +const settings: ObjectFieldMetadata = { + type: 'object', + name: 'settings', + label: 'Settings', + placeholder: '{ }', + help: 'Stored as JSON.', + schema: { + theme: { type: 'string' }, + notifications: { type: 'boolean' }, + }, +}; ``` +The value being edited, and the `className` / `disabled` a host supplies, are **not** +metadata keys — they are runtime widget props. See [Field Widget Props](/docs/fields/widget-props). + ## JSON Validation The field validates JSON syntax in real-time: diff --git a/content/docs/fields/password.mdx b/content/docs/fields/password.mdx index 2b7af5485c..6aa2ff3ebe 100644 --- a/content/docs/fields/password.mdx +++ b/content/docs/fields/password.mdx @@ -19,24 +19,27 @@ The Password Field component provides a secure text input for passwords with a t ## Field Schema +A password field is authored as `PasswordFieldMetadata` (`@object-ui/types`), which is +the source of truth for the key set: it extends `BaseFieldMetadata` with the two length +bounds. The reveal toggle and the masked read-only rendering are the widget's. + ```ts -interface PasswordFieldSchema { - type: 'password'; - name: string; // Field name/ID - label?: string; // Field label - placeholder?: string; // Placeholder text - value?: string; // Default value - required?: boolean; // Is field required - readonly?: boolean; // Read-only mode (shows ••••••) - disabled?: boolean; // Disabled state - className?: string; // Additional CSS classes - - // Validation - min_length?: number; // Minimum character length - max_length?: number; // Maximum character length -} +import type { PasswordFieldMetadata } from '@object-ui/types'; + +const password: PasswordFieldMetadata = { + type: 'password', + name: 'password', + label: 'Password', + placeholder: 'Enter a password', + required: true, + min_length: 12, + max_length: 128, +}; ``` +The value being edited, and the `className` / `disabled` a host supplies, are **not** +metadata keys — they are runtime widget props. See [Field Widget Props](/docs/fields/widget-props). + ## Features - **Masked Input**: Password is hidden by default with bullet points (•) diff --git a/content/docs/fields/percent.mdx b/content/docs/fields/percent.mdx index 979ad8b3e9..229a2be82a 100644 --- a/content/docs/fields/percent.mdx +++ b/content/docs/fields/percent.mdx @@ -23,25 +23,27 @@ The Percent Field component provides a percentage input that automatically conve ## Field Schema +A percent field is authored as `PercentFieldMetadata` (`@object-ui/types`), which is +the source of truth for the key set: it extends `BaseFieldMetadata` with the decimal +precision and the two bounds. + ```ts -interface PercentFieldSchema { - type: 'percent'; - name: string; // Field name/ID - label?: string; // Field label - placeholder?: string; // Placeholder text - value?: number; // Default value (0-1 decimal) - required?: boolean; // Is field required - readonly?: boolean; // Read-only mode - disabled?: boolean; // Disabled state - className?: string; // Additional CSS classes - - // Validation - precision?: number; // Decimal places (default: 2) - min?: number; // Minimum value (0-1) - max?: number; // Maximum value (0-1) -} +import type { PercentFieldMetadata } from '@object-ui/types'; + +const discountRate: PercentFieldMetadata = { + type: 'percent', + name: 'discount_rate', + label: 'Discount Rate', + placeholder: '0%', + precision: 2, + min: 0, + max: 1, +}; ``` +The value being edited, and the `className` / `disabled` a host supplies, are **not** +metadata keys — they are runtime widget props. See [Field Widget Props](/docs/fields/widget-props). + ## Value Conversion The percent field handles automatic conversion: diff --git a/content/docs/fields/phone.mdx b/content/docs/fields/phone.mdx index e15b23f4db..69062700c4 100644 --- a/content/docs/fields/phone.mdx +++ b/content/docs/fields/phone.mdx @@ -15,20 +15,25 @@ The Phone Field component provides a text input optimized for phone number entry ## Field Schema +A phone field is authored as `PhoneFieldMetadata` (`@object-ui/types`), which is the +source of truth for the key set: it extends `BaseFieldMetadata` with a display format. + ```ts -interface PhoneFieldSchema { - type: 'phone'; - name: string; // Field name/ID - label?: string; // Field label - placeholder?: string; // Placeholder text - value?: string; // Default value - required?: boolean; // Is field required - readonly?: boolean; // Read-only mode - disabled?: boolean; // Disabled state - className?: string; // Additional CSS classes -} +import type { PhoneFieldMetadata } from '@object-ui/types'; + +const mobile: PhoneFieldMetadata = { + type: 'phone', + name: 'mobile', + label: 'Mobile', + placeholder: '(555) 000-0000', + required: false, + format: '(###) ###-####', +}; ``` +The value being edited, and the `className` / `disabled` a host supplies, are **not** +metadata keys — they are runtime widget props. See [Field Widget Props](/docs/fields/widget-props). + ## Cell Renderer In tables/grids, phone numbers are clickable tel: links: diff --git a/content/docs/fields/select.mdx b/content/docs/fields/select.mdx index 25fb192fdb..a2d194d86c 100644 --- a/content/docs/fields/select.mdx +++ b/content/docs/fields/select.mdx @@ -90,40 +90,40 @@ candidate query is filtered server-side and paginated. See ## Field Schema +A select field is authored as `SelectFieldMetadata` (`@object-ui/types`), which is the +source of truth for the key set: it extends `BaseFieldMetadata` with the option list +and the multiple/searchable switches. Each option is a `SelectOptionMetadata`, so the +options are checked by the same compiler that checks the field. + ```ts -interface SelectFieldSchema { - type: 'select'; - name: string; // Field name/ID - label?: string; // Field label - placeholder?: string; // Placeholder text - value?: string | string[]; // Default value(s) - multiple?: boolean; // Allow multiple selections - required?: boolean; // Is field required - readonly?: boolean; // Read-only mode - disabled?: boolean; // Disabled state - className?: string; // Additional CSS classes - - // Options - options: SelectOption[]; // Available options - - // Cascading: sibling field(s) whose value drives this option list. While any - // is empty the field is gated ("Select first"); it re-evaluates as - // they change. Same knob dependent lookups use. - dependsOn?: string | string[]; -} +import type { SelectFieldMetadata } from '@object-ui/types'; + +const status: SelectFieldMetadata = { + type: 'select', + name: 'status', + label: 'Status', + placeholder: 'Select a status', + required: true, + multiple: false, + searchable: true, + options: [ + { label: 'Draft', value: 'draft', color: 'gray' }, + { label: 'Active', value: 'active', color: 'blue' }, + // Offered only when the predicate is true, evaluated against the live record. + { label: 'Archived', value: 'archived', color: 'red', visibleWhen: "current_user.is_admin" }, + { label: 'Locked', value: 'locked', disabled: true }, + ], +}; +``` -interface SelectOption { - label: string; // Display label - value: string; // Option value - color?: string; // Badge color (gray, red, blue, etc.) - disabled?: boolean; // Disable specific option +Cascading option lists are driven by a sibling field's value. The widget reads a +camelCase `dependsOn` off the metadata, but no exported metadata type declares it — +`BaseFieldMetadata` declares the snake_case `depends_on` instead — so the two +spellings disagree and the gap is tracked as +[objectui#6153](https://github.com/objectstack-ai/objectui/issues/6153). - // Per-option visibility predicate (CEL). The option is offered only when TRUE, - // evaluated against the live record + current_user (same engine/env as a - // field-level visibleWhen). Omit = always available. - visibleWhen?: string; -} -``` +The value being edited, and the `className` / `disabled` a host supplies, are **not** +metadata keys — they are runtime widget props. See [Field Widget Props](/docs/fields/widget-props). ## Available Colors diff --git a/content/docs/fields/summary.mdx b/content/docs/fields/summary.mdx index 9bf0f5abd3..b3dd0f9430 100644 --- a/content/docs/fields/summary.mdx +++ b/content/docs/fields/summary.mdx @@ -19,22 +19,30 @@ The Summary Field component displays aggregated values from related records. Thi ## Field Schema +A summary field is authored as `SummaryFieldMetadata` (`@object-ui/types`), which is +the source of truth for the key set: it extends `BaseFieldMetadata` with the related +object, the aggregated field, the aggregation and its filter. `summary_type` is a +closed union — `'count' | 'sum' | 'avg' | 'min' | 'max' | 'first' | 'last'`. + ```ts -interface SummaryFieldSchema { - type: 'summary'; - name: string; // Field name/ID - label?: string; // Field label - value?: number; // Computed value (read-only) - readonly: true; // Always read-only - className?: string; // Additional CSS classes - - // Summary Configuration - summary_object?: string; // Related object name - summary_field?: string; // Field to aggregate - summary_type?: 'count' | 'sum' | 'avg' | 'min' | 'max'; -} +import type { SummaryFieldMetadata } from '@object-ui/types'; + +const totalRevenue: SummaryFieldMetadata = { + type: 'summary', + name: 'total_revenue', + label: 'Total Revenue', + readonly: true, + summary_object: 'opportunities', + summary_field: 'amount', + summary_type: 'sum', + summary_filter: { stage: 'closed_won' }, + auto_update: true, +}; ``` +The computed value, and the `className` a host supplies, are **not** metadata keys — +they are runtime widget props. See [Field Widget Props](/docs/fields/widget-props). + ## Summary Types - **count**: Count of related records diff --git a/content/docs/fields/time.mdx b/content/docs/fields/time.mdx index 2dda605c77..1bed6a2847 100644 --- a/content/docs/fields/time.mdx +++ b/content/docs/fields/time.mdx @@ -23,23 +23,25 @@ The Time Field component provides a time-only input for collecting hour and minu ## Field Schema +A time field is authored as `TimeFieldMetadata` (`@object-ui/types`), which is the +source of truth for the key set: it extends `BaseFieldMetadata` with a display format. + ```ts -interface TimeFieldSchema { - type: 'time'; - name: string; // Field name/ID - label?: string; // Field label - placeholder?: string; // Placeholder text - value?: string; // Default value (HH:mm format) - required?: boolean; // Is field required - readonly?: boolean; // Read-only mode - disabled?: boolean; // Disabled state - className?: string; // Additional CSS classes - - // Validation - format?: string; // Display format -} +import type { TimeFieldMetadata } from '@object-ui/types'; + +const startTime: TimeFieldMetadata = { + type: 'time', + name: 'start_time', + label: 'Start Time', + placeholder: 'HH:mm', + required: true, + format: 'HH:mm', +}; ``` +The value being edited, and the `className` / `disabled` a host supplies, are **not** +metadata keys — they are runtime widget props. See [Field Widget Props](/docs/fields/widget-props). + ## Time Format The time field uses 24-hour format: `HH:mm` diff --git a/content/docs/fields/url.mdx b/content/docs/fields/url.mdx index 9ff8b517f7..801955a9f6 100644 --- a/content/docs/fields/url.mdx +++ b/content/docs/fields/url.mdx @@ -15,20 +15,25 @@ The URL Field component provides a text input with URL validation and clickable ## Field Schema +A URL field is authored as `UrlFieldMetadata` (`@object-ui/types`), which is the source +of truth for the key set: it extends `BaseFieldMetadata` with a single length bound. + ```ts -interface UrlFieldSchema { - type: 'url'; - name: string; // Field name/ID - label?: string; // Field label - placeholder?: string; // Placeholder text - value?: string; // Default value - required?: boolean; // Is field required - readonly?: boolean; // Read-only mode - disabled?: boolean; // Disabled state - className?: string; // Additional CSS classes -} +import type { UrlFieldMetadata } from '@object-ui/types'; + +const website: UrlFieldMetadata = { + type: 'url', + name: 'website', + label: 'Website', + placeholder: 'https://example.com', + required: false, + max_length: 2048, +}; ``` +The value being edited, and the `className` / `disabled` a host supplies, are **not** +metadata keys — they are runtime widget props. See [Field Widget Props](/docs/fields/widget-props). + ## Validation The URL field automatically validates: diff --git a/content/docs/fields/user.mdx b/content/docs/fields/user.mdx index 7a665a50bc..3f07925833 100644 --- a/content/docs/fields/user.mdx +++ b/content/docs/fields/user.mdx @@ -23,30 +23,33 @@ it display-only. ## Field Schema +A user field is authored as `UserFieldMetadata` (`@object-ui/types`), which is the +source of truth for the key set: it extends `BaseFieldMetadata` with the picker style, +the subtitle fields, the avatar field and the candidate filters. `user` is a lookup +specialised to the `sys_user` system object, so its filters are `LookupFilterDef`. + ```ts -interface UserFieldSchema { - type: 'user'; - name: string; // Field name/ID - label?: string; // Field label - value?: User | User[]; // Selected user(s) - required?: boolean; // Is field required - readonly?: boolean; // Read-only mode - disabled?: boolean; // Disabled state - className?: string; // Additional CSS classes - - // User Options - multiple?: boolean; // Allow multiple users -} +import type { UserFieldMetadata } from '@object-ui/types'; -interface User { - id: string; // User ID - name?: string; // Display name - username?: string; // Username - email?: string; // Email address - avatar?: string; // Avatar URL -} +const owner: UserFieldMetadata = { + type: 'user', + name: 'owner_id', + label: 'Owner', + required: true, + multiple: false, + picker: 'search', + subtitle: ['primary_business_unit_id.name', 'email'], + avatar_field: 'image', + lookup_filters: [{ field: 'banned', operator: 'ne', value: true }], +}; ``` +The field stores the selected user's id (or an array of ids when `multiple` is set), +not a user object; the picker resolves names and avatars from `sys_user` itself. + +The value being edited, and the `className` / `disabled` a host supplies, are **not** +metadata keys — they are runtime widget props. See [Field Widget Props](/docs/fields/widget-props). + ## User vs Owner Both are the same field **type**. What differs is the field's name and whether diff --git a/content/docs/fields/vector.mdx b/content/docs/fields/vector.mdx index 2b50ddaddf..e45b6ae97e 100644 --- a/content/docs/fields/vector.mdx +++ b/content/docs/fields/vector.mdx @@ -15,20 +15,26 @@ The Vector Field component displays vector embeddings used in AI/ML applications ## Field Schema +A vector field is authored as `VectorFieldMetadata` (`@object-ui/types`), which is the +source of truth for the key set: it extends `BaseFieldMetadata` with the embedding +dimensionality and whether the stored vector is normalised. + ```ts -interface VectorFieldSchema { - type: 'vector'; - name: string; // Field name/ID - label?: string; // Field label - value?: number[]; // Vector array - readonly: true; // Always read-only - className?: string; // Additional CSS classes - - // Vector Options - dimensions?: number; // Vector dimensionality -} +import type { VectorFieldMetadata } from '@object-ui/types'; + +const embedding: VectorFieldMetadata = { + type: 'vector', + name: 'embedding', + label: 'Embedding', + readonly: true, + dimensions: 1536, + normalize: true, +}; ``` +The computed value, and the `className` a host supplies, are **not** metadata keys — +they are runtime widget props. See [Field Widget Props](/docs/fields/widget-props). + ## Display Format Vectors are displayed with a preview: