|
6 | 6 |
|
7 | 7 | --- |
8 | 8 |
|
9 | | -## 1. The Widget Protocol |
| 9 | +## 1. Component Categories |
| 10 | + |
| 11 | +You will be asked to build components in these 3 standard slots. Refer to `packages/spec` for the complete Zod definitions. |
| 12 | + |
| 13 | +### A. Field Widgets (`field.*`) |
| 14 | +Responsible for **Input** (Edit Mode) and **Display** (Read Mode) of a specific data type. |
| 15 | +* **Contract:** Must implement `FieldWidgetProps` (Ref: `src/ui/widget.zod.ts`). |
| 16 | + ```typescript |
| 17 | + type FieldWidgetProps<T = any> = { |
| 18 | + value: T; |
| 19 | + onChange: (val: T) => void; |
| 20 | + field: FieldSchema; // Config |
| 21 | + readonly?: boolean; |
| 22 | + } |
| 23 | + ``` |
| 24 | +* **Required Types (Ref: `src/data/field.zod.ts`):** |
| 25 | + * **Core:** `text`, `textarea`, `email`, `url`, `phone`, `password` |
| 26 | + * **Rich Content:** `markdown`, `html`, `richtext` |
| 27 | + * **Numbers:** `number`, `currency`, `percent` |
| 28 | + * **Date & Time:** `date`, `datetime`, `time` |
| 29 | + * **Logic:** `boolean` (Checkbox/Switch) |
| 30 | + * **Selection:** `select` (Dropdown/Radio) |
| 31 | + * **Relational:** `lookup` (Searchable Ref), `master_detail` |
| 32 | + * **Media:** `image` (Upload/Preview), `file`, `avatar` |
| 33 | + * **Calculated:** `formula` (Readonly), `summary`, `autonumber` |
| 34 | + * **Enhanced:** `location` (Lat/Long Map), `address`, `code` (Monaco), `color` (Picker), `rating` (Stars), `slider` (Range), `signature` (Pad), `qrcode` (Generator). |
| 35 | +
|
| 36 | +### B. List View Layouts (`view.list.*`) |
| 37 | +Responsible for rendering a collection of records. |
| 38 | +* **Contract:** Must implement `ListViewComponentProps`. |
| 39 | + ```typescript |
| 40 | + type ListViewComponentProps = { |
| 41 | + config: ListView; // Config (Ref:src/ui/view.zod.ts) |
| 42 | + data: any[]; // Runtime Collection |
| 43 | + isLoading?: boolean; |
| 44 | + onAction?: (actionId: string, record: any) => void; |
| 45 | + onSelectionChange?: (selectedIds: string[]) => void; |
| 46 | + } |
| 47 | + ``` |
| 48 | +* **Required Types:** `grid` (DataGrid), `kanban` (Drag & Drop), `calendar` (Events), `gantt` (Timeline), `map` (Markers). |
| 49 | +
|
| 50 | +### C. Form View Layouts (`view.form.*`) |
| 51 | +Responsible for rendering a single record detail. |
| 52 | +* **Contract:** Must implement `FormViewComponentProps`. |
| 53 | + ```typescript |
| 54 | + type FormViewComponentProps = { |
| 55 | + config: FormView; // Config (Ref:src/ui/view.zod.ts) |
| 56 | + data: any; // Single Runtime Record |
| 57 | + isLoading?: boolean; |
| 58 | + onAction?: (actionId: string, record: any) => void; |
| 59 | + onChange?: (field: string, value: any) => void; |
| 60 | + } |
| 61 | + ``` |
| 62 | +* **Required Types:** `simple` (Sections), `tabbed` (Tabs), `wizard` (Steps). |
| 63 | +
|
| 64 | +### D. Dashboard Widgets (`widget.*`) |
| 65 | +Standalone cards placed on a dashboard grid. |
| 66 | +* **Contract:** Must implement `DashboardWidgetProps` (Ref: `src/ui/dashboard.zod.ts`). |
| 67 | + ```typescript |
| 68 | + type DashboardWidgetProps = { |
| 69 | + config: DashboardWidgetSchema; // Config |
| 70 | + data?: any; // Runtime Data (optional) |
| 71 | + width: number; |
| 72 | + height: number; |
| 73 | + } |
| 74 | + ``` |
| 75 | +* **Required Types (Ref: `src/ui/dashboard.zod.ts`):** |
| 76 | + * **Charts:** `metric` (KPI), `bar`, `line`, `pie`, `donut`, `funnel`. |
| 77 | + * **Content:** `table` (Data List), `text` (Rich Text/Markdown). |
10 | 78 |
|
11 | | -Widgets are the atomic building blocks of ObjectUI. They render specific `FieldTypes` or `ViewComponents`. |
12 | | - |
13 | | -**Reference:** `@objectstack/spec` -> `dist/ui/widget.zod.d.ts` |
14 | | - |
15 | | -## 2. Widget Registration |
16 | | - |
17 | | -You must register the component map so the Server-Driven UI engine knows what to render. |
18 | | - |
19 | | -```typescript |
20 | | -// src/components/registry.ts |
21 | | -import { RatingField } from './RatingField'; |
22 | | -import { KanbanBoard } from './KanbanBoard'; |
23 | | - |
24 | | -export const widgetRegistry = { |
25 | | - // Field Widgets (Input/Display) |
26 | | - 'field.rating': RatingField, |
27 | | - |
28 | | - // View Widgets (Layouts) |
29 | | - 'view.kanban': KanbanBoard |
30 | | -}; |
31 | | -``` |
| 79 | +--- |
32 | 80 |
|
33 | | -## 3. The Component Contract |
| 81 | +## 2. API Reference & Contracts |
34 | 82 |
|
35 | | -All field widgets receive a standard set of props. |
| 83 | +### A. Field Widget Implementation |
| 84 | +**Reference:** `@objectstack/spec` -> `dist/ui/widget.zod.d.ts` |
36 | 85 |
|
37 | 86 | ```typescript |
38 | | -import { FieldSchema } from '@objectstack/spec/data'; |
39 | | - |
40 | | -interface FieldWidgetProps<T = any> { |
41 | | - // Data |
42 | | - value: T; |
43 | | - onChange: (newValue: T) => void; |
44 | | - |
45 | | - // Metadata |
46 | | - field: FieldSchema; // The Zod definition |
47 | | - mode: 'read' | 'edit'; |
48 | | - errorMessage?: string; |
49 | | - |
50 | | - // Context |
51 | | - objectName: string; |
52 | | - recordId?: string; |
53 | | -} |
54 | | -``` |
55 | | - |
56 | | -### Example: Rating Star Widget |
57 | | -```tsx |
58 | | -export function RatingField({ value, onChange, mode, field }: FieldWidgetProps<number>) { |
59 | | - const max = field.scale || 5; |
| 87 | +import { FieldWidgetProps } from '@objectstack/spec/ui'; |
| 88 | + |
| 89 | +export function RatingField({ |
| 90 | + value, |
| 91 | + onChange, |
| 92 | + field, |
| 93 | + mode |
| 94 | +}: FieldWidgetProps<number>) { |
60 | 95 |
|
61 | 96 | if (mode === 'read') { |
62 | | - return <div>{'⭐'.repeat(value)}</div>; |
| 97 | + return <span>{'★'.repeat(value || 0)}</span>; |
63 | 98 | } |
64 | 99 |
|
65 | 100 | return ( |
66 | 101 | <div className="flex gap-1"> |
67 | | - {Array.from({ length: max }).map((_, i) => ( |
68 | | - <button key={i} onClick={() => onChange(i + 1)}> |
69 | | - {i < value ? '★' : '☆'} |
| 102 | + {[1, 2, 3, 4, 5].map((star) => ( |
| 103 | + <button |
| 104 | + key={star} |
| 105 | + onClick={() => onChange(star)} |
| 106 | + className={star <= value ? 'text-yellow-500' : 'text-gray-300'} |
| 107 | + > |
| 108 | + ★ |
70 | 109 | </button> |
71 | 110 | ))} |
72 | 111 | </div> |
73 | 112 | ); |
74 | 113 | } |
75 | 114 | ``` |
76 | 115 |
|
| 116 | +### B. Dashboard Widget Implementation |
| 117 | +**Reference:** `@objectstack/spec` -> `dist/ui/dashboard.zod.d.ts` |
| 118 | + |
| 119 | +```typescript |
| 120 | +import { DashboardWidgetProps } from '@objectstack/spec/ui'; |
| 121 | + |
| 122 | +export function WelcomeCard({ config, user }: DashboardWidgetProps) { |
| 123 | + return ( |
| 124 | + <div className="card p-4 bg-blue-50"> |
| 125 | + <h3>Hello, {user.name}!</h3> |
| 126 | + <p>{config.welcomeMessage || 'Have a great day.'}</p> |
| 127 | + </div> |
| 128 | + ); |
| 129 | +} |
| 130 | +``` |
| 131 | + |
| 132 | +--- |
| 133 | + |
| 134 | +## 3. Widget Registration |
| 135 | + |
| 136 | +You must register the component map so the Server-Driven UI engine knows what to render. |
| 137 | + |
| 138 | +```typescript |
| 139 | +// src/components/registry.ts |
| 140 | +import { RatingField } from './RatingField'; |
| 141 | +import { WelcomeCard } from './WelcomeCard'; |
| 142 | + |
| 143 | +export const widgetRegistry = { |
| 144 | + // Field Widgets (Maps to FieldType or Custom 'widget' property) |
| 145 | + 'field.rating': RatingField, |
| 146 | + |
| 147 | + // Dashboard Widgets (Maps to widget 'type') |
| 148 | + 'widget.welcome_card': WelcomeCard |
| 149 | +}; |
| 150 | +``` |
| 151 | + |
77 | 152 | ## 4. Key Directives for AI |
78 | 153 |
|
79 | 154 | * **Statelessness:** Widgets should rely on `props.value` and `props.onChange`. Avoid internal state unless necessary for transient UI interactions (like hover). |
|
0 commit comments