Skip to content

Latest commit

 

History

History
157 lines (122 loc) · 5.7 KB

File metadata and controls

157 lines (122 loc) · 5.7 KB
title Select Field
description Dropdown selection field with single or multiple options

The Select Field component provides a dropdown for selecting one or more options from a predefined list.

Basic Usage

With Colors

Multiple Selection

Cascading & Role-Gated Options

An option can carry a visibleWhen CEL predicate — it is offered only when the predicate is TRUE. The predicate is evaluated against the live record plus current_user, the same engine and binding environment as a field-level visibleWhen. This single mechanism covers two needs:

  • Cascading / dependent options — narrow a child list by a parent field (country → province → city).
  • Role / context gating — offer an option only to certain users.

Declare the sibling field(s) a select reacts to with dependsOn. While any is empty the control is gated (a "Select country first" hint) instead of showing an unfiltered list; once the parent changes, the list re-evaluates and any now-invalid selection is cleared automatically (no stale "China + California" pair).

Cascading (country → province)

{
  "type": "form",
  "fields": [
    { "name": "country", "label": "Country", "type": "select", "options": [
      { "label": "China", "value": "cn" },
      { "label": "United States", "value": "us" }
    ]},
    { "name": "province", "label": "Province", "type": "select", "dependsOn": "country", "options": [
      { "label": "Zhejiang",   "value": "zj", "visibleWhen": "record.country == 'cn'" },
      { "label": "Guangdong",  "value": "gd", "visibleWhen": "record.country == 'cn'" },
      { "label": "California",  "value": "ca", "visibleWhen": "record.country == 'us'" },
      { "label": "Texas",       "value": "tx", "visibleWhen": "record.country == 'us'" }
    ]}
  ]
}

Chain a third level (city, dependsOn: "province") the same way — the gate and cascade-clear propagate down the chain.

Role-gated option

{ "name": "visibility", "type": "select", "options": [
  { "label": "Private (only me)", "value": "private" },
  { "label": "My team", "value": "team" },
  { "label": "Whole organization", "value": "org" },
  { "label": "Public — external", "value": "public", "visibleWhen": "'admin' in current_user.positions" }
]}

Security — hiding is UX, not authorization. A visibleWhen on an option only removes it from the dropdown on the client; a determined caller can still submit the value. When an option is gated for access-control reasons the server must also reject writes of that value (the rule-validator evaluates the picked value's visibleWhen). Use option visibleWhen for convenience and cascades freely; for real authorization, pair it with server-side enforcement.

When to use options vs. a lookup

visibleWhen options are for small, static dictionaries (category → subcategory, a handful of provinces). When the data is large, changes over time, or is shared across forms (real country/province/city tables, org units, product catalogs), model each level as a lookup with depends_on instead — the candidate query is filtered server-side and paginated. See Lookup Field.

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.

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 },
  ],
};

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.

The value being edited, and the className / disabled a host supplies, are not metadata keys — they are runtime widget props. See Field Widget Props.

Available Colors

  • gray - Default neutral color
  • red - For errors, urgent items
  • orange - For warnings, high priority
  • yellow - For pending, attention needed
  • green - For success, completed
  • blue - For info, in progress
  • indigo - For special items
  • purple - For creative, design
  • pink - For featured items

Cell Renderer

In tables/grids, select values are displayed as colored badges:

import { SelectCellRenderer } from '@object-ui/fields';

// Single value: Colored badge
// Multiple values: Multiple badges in a row

Use Cases

  • Status Fields: Order status, task status
  • Categories: Product categories, content types
  • Tags: Multi-tag selection
  • Priorities: Task or ticket priorities
  • Roles: User roles or permissions