@process.co/ui 0.0.9 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +365 -282
- package/css/ui.css +176 -7
- package/dist/components/dev/index.cjs +486 -0
- package/dist/components/dev/index.cjs.map +1 -0
- package/dist/components/dev/index.d.cts +88 -0
- package/dist/components/dev/index.d.ts +88 -0
- package/dist/components/dev/index.js +474 -0
- package/dist/components/dev/index.js.map +1 -0
- package/dist/components/fields/index.cjs +452 -78
- package/dist/components/fields/index.cjs.map +1 -1
- package/dist/components/fields/index.d.cts +1 -1
- package/dist/components/fields/index.d.ts +1 -1
- package/dist/components/fields/index.js +435 -68
- package/dist/components/fields/index.js.map +1 -1
- package/dist/{index-nu_JyZnb.d.cts → index-B-kAG1RW.d.cts} +204 -18
- package/dist/{index-nu_JyZnb.d.ts → index-B-kAG1RW.d.ts} +204 -18
- package/dist/index.cjs +413 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +414 -26
- package/dist/index.js.map +1 -1
- package/package.json +11 -1
package/README.md
CHANGED
|
@@ -1,20 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @process.co/ui
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Table of Contents
|
|
6
|
-
|
|
7
|
-
- [Installation](#installation)
|
|
8
|
-
- [Usage](#usage)
|
|
9
|
-
- [Components](#components)
|
|
10
|
-
- [Basic Components](#basic-components)
|
|
11
|
-
- [Form Components](#form-components)
|
|
12
|
-
- [Layout Components](#layout-components)
|
|
13
|
-
- [Feedback Components](#feedback-components)
|
|
14
|
-
- [Navigation Components](#navigation-components)
|
|
15
|
-
- [Blocks](#blocks)
|
|
16
|
-
- [Providers](#providers)
|
|
17
|
-
- [Storybook](#storybook)
|
|
3
|
+
A React UI component library for Process.co applications with built-in collaborative editing, type inference, and expression support.
|
|
18
4
|
|
|
19
5
|
## Installation
|
|
20
6
|
|
|
@@ -22,393 +8,490 @@ This package contains a collection of reusable UI components for the proc-app pr
|
|
|
22
8
|
npm install @process.co/ui
|
|
23
9
|
```
|
|
24
10
|
|
|
25
|
-
Or with pnpm:
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
pnpm add @process.co/ui
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## Usage
|
|
32
|
-
|
|
33
|
-
### Importing Components
|
|
34
|
-
|
|
35
|
-
```tsx
|
|
36
|
-
import { Button, Card, Alert } from '@process.co/ui';
|
|
37
|
-
import { UIProvider } from '@process.co/ui';
|
|
38
|
-
```
|
|
39
|
-
|
|
40
11
|
### Importing Styles
|
|
41
12
|
|
|
42
|
-
The library includes pre-built CSS that must be imported in your application:
|
|
43
|
-
|
|
44
13
|
```tsx
|
|
45
|
-
// In your main app file or layout
|
|
46
14
|
import '@process.co/ui/styles';
|
|
47
15
|
```
|
|
48
16
|
|
|
49
|
-
|
|
17
|
+
---
|
|
50
18
|
|
|
51
|
-
|
|
52
|
-
@import '@process.co/ui/styles';
|
|
53
|
-
```
|
|
19
|
+
## Field Components
|
|
54
20
|
|
|
55
|
-
|
|
21
|
+
The `Input` and `Select` components are collaborative-aware form controls designed for building custom UIs that integrate with Process.co's flow editor.
|
|
22
|
+
|
|
23
|
+
### Quick Start
|
|
56
24
|
|
|
57
25
|
```tsx
|
|
58
|
-
import {
|
|
59
|
-
import { UIProvider } from '@process.co/ui';
|
|
26
|
+
import { Input, Select, useNodeProperty } from '@process.co/ui/fields';
|
|
60
27
|
import '@process.co/ui/styles';
|
|
61
28
|
|
|
62
|
-
|
|
63
|
-
|
|
29
|
+
function MyCustomControl({ fieldName }) {
|
|
30
|
+
const [value, setValue] = useNodeProperty(fieldName);
|
|
31
|
+
|
|
64
32
|
return (
|
|
65
|
-
<
|
|
66
|
-
|
|
67
|
-
|
|
33
|
+
<Input
|
|
34
|
+
fieldName="expression"
|
|
35
|
+
label="Expression"
|
|
36
|
+
expectedType="string"
|
|
37
|
+
value={value}
|
|
38
|
+
onChange={setValue}
|
|
39
|
+
/>
|
|
68
40
|
);
|
|
69
41
|
}
|
|
70
42
|
```
|
|
71
43
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
### Basic Components
|
|
44
|
+
---
|
|
75
45
|
|
|
76
|
-
|
|
77
|
-
A customizable button component with multiple variants and sizes.
|
|
46
|
+
## Input Component
|
|
78
47
|
|
|
79
|
-
|
|
80
|
-
import { Button } from '@repo/ui';
|
|
48
|
+
A text input with expression support, type inference, and real-time collaboration.
|
|
81
49
|
|
|
82
|
-
|
|
83
|
-
Click me
|
|
84
|
-
</Button>
|
|
85
|
-
```
|
|
50
|
+
### Props
|
|
86
51
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
52
|
+
| Prop | Type | Description |
|
|
53
|
+
|------|------|-------------|
|
|
54
|
+
| `fieldName` | `string` | Unique identifier for collaborative sync |
|
|
55
|
+
| `label` | `string` | Display label |
|
|
56
|
+
| `value` | `any` | Current value |
|
|
57
|
+
| `onChange` | `(value: any) => void` | Change handler |
|
|
58
|
+
| `expectedType` | `string` | Expected type for validation (see Type Inference) |
|
|
59
|
+
| `placeholder` | `string` | Placeholder text |
|
|
93
60
|
|
|
94
|
-
|
|
95
|
-
A clickable card component that links to external resources.
|
|
61
|
+
### Example
|
|
96
62
|
|
|
97
63
|
```tsx
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
</Card>
|
|
64
|
+
<Input
|
|
65
|
+
fieldName="userEmail"
|
|
66
|
+
label="Email Address"
|
|
67
|
+
expectedType="string"
|
|
68
|
+
value={email}
|
|
69
|
+
onChange={setEmail}
|
|
70
|
+
placeholder="user@example.com"
|
|
71
|
+
/>
|
|
107
72
|
```
|
|
108
73
|
|
|
109
|
-
|
|
110
|
-
- `title`: string (required)
|
|
111
|
-
- `href`: string (required)
|
|
112
|
-
- `className`: string (optional)
|
|
113
|
-
- `children`: React.ReactNode
|
|
74
|
+
---
|
|
114
75
|
|
|
115
|
-
|
|
116
|
-
A simple inline code component for displaying code snippets.
|
|
76
|
+
## Select Component
|
|
117
77
|
|
|
118
|
-
|
|
119
|
-
import { Code } from '@repo/ui';
|
|
78
|
+
A dropdown select with expression support and type-aware options.
|
|
120
79
|
|
|
121
|
-
|
|
122
|
-
```
|
|
80
|
+
### Props
|
|
123
81
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
82
|
+
| Prop | Type | Description |
|
|
83
|
+
|------|------|-------------|
|
|
84
|
+
| `fieldName` | `string` | Unique identifier for collaborative sync |
|
|
85
|
+
| `label` | `string` | Display label |
|
|
86
|
+
| `value` | `any` | Current selected value |
|
|
87
|
+
| `onChange` | `(value: any) => void` | Change handler |
|
|
88
|
+
| `options` | `SelectOption[]` | Available options |
|
|
89
|
+
| `expectedType` | `string` | Expected type for validation |
|
|
127
90
|
|
|
128
|
-
|
|
129
|
-
A header component with navigation and branding.
|
|
91
|
+
### SelectOption Type
|
|
130
92
|
|
|
131
93
|
```tsx
|
|
132
|
-
|
|
94
|
+
type SelectOption = {
|
|
95
|
+
value: string;
|
|
96
|
+
label: string;
|
|
97
|
+
node?: ReactNode; // Custom render
|
|
98
|
+
};
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Example
|
|
133
102
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
103
|
+
```tsx
|
|
104
|
+
<Select
|
|
105
|
+
fieldName="operator"
|
|
106
|
+
label="Operation"
|
|
107
|
+
value={operator}
|
|
108
|
+
onChange={setOperator}
|
|
109
|
+
options={[
|
|
110
|
+
{ value: 'equals', label: 'Equals' },
|
|
111
|
+
{ value: 'contains', label: 'Contains' },
|
|
112
|
+
{ value: 'startsWith', label: 'Starts With' },
|
|
113
|
+
]}
|
|
138
114
|
/>
|
|
139
115
|
```
|
|
140
116
|
|
|
141
|
-
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Type Inference System
|
|
120
|
+
|
|
121
|
+
The `$infer<...>` syntax enables automatic type inference and propagation between fields.
|
|
122
|
+
|
|
123
|
+
### How It Works
|
|
124
|
+
|
|
125
|
+
When a field uses `$infer` or `$infer<allowedTypes>` as its `expectedType`:
|
|
126
|
+
|
|
127
|
+
1. The field **infers** the type of the user's input
|
|
128
|
+
2. The field **publishes** that inferred type under its `fieldName`
|
|
129
|
+
3. Other fields can **subscribe** to that type using `$infer<[fieldName]>`
|
|
130
|
+
|
|
131
|
+
### Publishing Types (Automatic)
|
|
142
132
|
|
|
143
|
-
|
|
144
|
-
A text input component with built-in validation and error handling.
|
|
133
|
+
Any field with `$infer` syntax automatically publishes its inferred type:
|
|
145
134
|
|
|
146
135
|
```tsx
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
name="email"
|
|
152
|
-
type="email"
|
|
153
|
-
placeholder="Enter your email"
|
|
154
|
-
error={errors.email}
|
|
155
|
-
onChange={handleChange}
|
|
136
|
+
// This field publishes its inferred type as "switchExpression"
|
|
137
|
+
<Input
|
|
138
|
+
fieldName="switchExpression"
|
|
139
|
+
expectedType="$infer<string | number | boolean>"
|
|
156
140
|
/>
|
|
157
141
|
```
|
|
158
142
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
- `placeholder`: string
|
|
164
|
-
- `error`: string
|
|
165
|
-
- `required`: boolean
|
|
166
|
-
- Standard input HTML attributes
|
|
143
|
+
The field will:
|
|
144
|
+
1. Accept string, number, or boolean values
|
|
145
|
+
2. Infer the actual type from user input (e.g., if user types `"hello"`, infers `string`)
|
|
146
|
+
3. **Publish** the inferred type so other fields can access it via `fieldName`
|
|
167
147
|
|
|
168
|
-
|
|
169
|
-
A password input with strength meter visualization.
|
|
148
|
+
You can also use just `$infer` without constraints:
|
|
170
149
|
|
|
171
150
|
```tsx
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
onChange={setPassword}
|
|
177
|
-
requirements={{
|
|
178
|
-
minLength: 8,
|
|
179
|
-
requireUppercase: true,
|
|
180
|
-
requireNumbers: true,
|
|
181
|
-
requireSpecialChars: true
|
|
182
|
-
}}
|
|
151
|
+
// Publishes inferred type with no restrictions on allowed types
|
|
152
|
+
<Input
|
|
153
|
+
fieldName="myExpression"
|
|
154
|
+
expectedType="$infer"
|
|
183
155
|
/>
|
|
184
156
|
```
|
|
185
157
|
|
|
186
|
-
|
|
187
|
-
- `password`: string
|
|
188
|
-
- `onChange`: (value: string) => void
|
|
189
|
-
- `requirements`: PasswordRequirements object
|
|
190
|
-
|
|
191
|
-
### Layout Components
|
|
192
|
-
|
|
193
|
-
#### NotFound (404)
|
|
194
|
-
A pre-styled 404 error page layout.
|
|
158
|
+
Or with a single known type:
|
|
195
159
|
|
|
196
160
|
```tsx
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
161
|
+
// Publishes "string" as the inferred type for this field
|
|
162
|
+
<Input
|
|
163
|
+
fieldName="stringField"
|
|
164
|
+
expectedType="$infer<string>"
|
|
165
|
+
/>
|
|
200
166
|
```
|
|
201
167
|
|
|
202
|
-
|
|
168
|
+
### Subscribing to Types
|
|
203
169
|
|
|
204
|
-
|
|
205
|
-
A collapsible sidebar navigation component.
|
|
170
|
+
A field can **subscribe** to another field's published type:
|
|
206
171
|
|
|
207
172
|
```tsx
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
collapsed={isCollapsed}
|
|
213
|
-
onToggle={handleToggle}
|
|
173
|
+
// This field receives its expected type from "switchExpression"
|
|
174
|
+
<Input
|
|
175
|
+
fieldName="caseValue"
|
|
176
|
+
expectedType="$infer<[switchExpression]>"
|
|
214
177
|
/>
|
|
215
178
|
```
|
|
216
179
|
|
|
217
|
-
|
|
180
|
+
This field will:
|
|
181
|
+
1. Look up the inferred type published by `switchExpression`
|
|
182
|
+
2. Use that type for validation and autocomplete
|
|
183
|
+
3. Update automatically when the source field's type changes
|
|
218
184
|
|
|
219
|
-
|
|
220
|
-
A dismissible alert component for displaying messages.
|
|
185
|
+
### Multi-Field Subscription
|
|
221
186
|
|
|
222
|
-
|
|
223
|
-
import { Alert } from '@repo/ui';
|
|
187
|
+
Subscribe to multiple fields - types are intersected:
|
|
224
188
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
189
|
+
```tsx
|
|
190
|
+
<Input
|
|
191
|
+
fieldName="value"
|
|
192
|
+
expectedType='$infer<["statementField", "operatorField"]>'
|
|
229
193
|
/>
|
|
230
194
|
```
|
|
231
195
|
|
|
232
|
-
|
|
233
|
-
- `type`: 'success' | 'error' | 'warning' | 'info'
|
|
234
|
-
- `message`: string
|
|
235
|
-
- `onClose`: () => void (optional)
|
|
196
|
+
The expected type is computed by intersecting types from both fields.
|
|
236
197
|
|
|
237
|
-
|
|
238
|
-
An animated loading indicator.
|
|
198
|
+
### Example Flow
|
|
239
199
|
|
|
240
200
|
```tsx
|
|
241
|
-
|
|
201
|
+
// 1. Statement field publishes its inferred type
|
|
202
|
+
<Input
|
|
203
|
+
fieldName="statement"
|
|
204
|
+
expectedType="$infer<string | number | boolean>"
|
|
205
|
+
// User enters: this.user.age
|
|
206
|
+
// Infers and publishes: "number"
|
|
207
|
+
/>
|
|
242
208
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
209
|
+
// 2. Operator dropdown reads the published type to filter options
|
|
210
|
+
const ctx = useInferredTypes();
|
|
211
|
+
const statementType = ctx?.getInferredType('statement'); // "number"
|
|
212
|
+
const operators = statementType === 'number'
|
|
213
|
+
? ['=', '!=', '<', '>', '<=', '>=']
|
|
214
|
+
: ['=', '!='];
|
|
215
|
+
|
|
216
|
+
// 3. Value field subscribes to the statement's type
|
|
217
|
+
<Input
|
|
218
|
+
fieldName="value"
|
|
219
|
+
expectedType="$infer<[statement]>"
|
|
220
|
+
// Expects: "number" (from statement field)
|
|
247
221
|
/>
|
|
248
222
|
```
|
|
249
223
|
|
|
250
|
-
|
|
251
|
-
A full-screen loading component with animation, wrapped in React Query provider.
|
|
224
|
+
---
|
|
252
225
|
|
|
253
|
-
|
|
254
|
-
import { SuspenseLoader } from '@repo/ui';
|
|
226
|
+
## useInferredTypes Hook
|
|
255
227
|
|
|
256
|
-
|
|
228
|
+
Access inferred types programmatically for building type-aware UIs:
|
|
229
|
+
|
|
230
|
+
```tsx
|
|
231
|
+
import { useInferredTypes } from '@process.co/ui/fields';
|
|
232
|
+
|
|
233
|
+
function OperatorSelect() {
|
|
234
|
+
const ctx = useInferredTypes();
|
|
235
|
+
|
|
236
|
+
// Read the published type from another field
|
|
237
|
+
const expressionType = ctx?.getInferredType('switchExpression') || 'any';
|
|
238
|
+
|
|
239
|
+
// Manually publish a type (e.g., for operator narrowing)
|
|
240
|
+
ctx?.setInferredType('operatorNarrow', 'string');
|
|
241
|
+
|
|
242
|
+
// Filter operators based on type
|
|
243
|
+
const operators = expressionType === 'number'
|
|
244
|
+
? ['=', '!=', '<', '>', '<=', '>=']
|
|
245
|
+
: expressionType === 'string'
|
|
246
|
+
? ['=', '!=', 'contains', 'startsWith', 'endsWith']
|
|
247
|
+
: ['=', '!='];
|
|
248
|
+
|
|
249
|
+
return (
|
|
250
|
+
<Select options={operators.map(op => ({ value: op, label: op }))} />
|
|
251
|
+
);
|
|
252
|
+
}
|
|
257
253
|
```
|
|
258
254
|
|
|
259
|
-
|
|
260
|
-
A centered full-screen loading overlay.
|
|
255
|
+
### Context Methods
|
|
261
256
|
|
|
262
|
-
|
|
263
|
-
|
|
257
|
+
| Method | Description |
|
|
258
|
+
|--------|-------------|
|
|
259
|
+
| `getInferredType(fieldName)` | Get the published type for a field |
|
|
260
|
+
| `setInferredType(fieldName, type)` | Manually publish a type for a field |
|
|
261
|
+
| `clearInferredType(fieldName)` | Remove a published type for a field |
|
|
262
|
+
| `clearAllInferredTypes()` | Remove all published types |
|
|
263
|
+
| `inferredTypes` | Record of all fieldName → type mappings |
|
|
264
264
|
|
|
265
|
-
|
|
266
|
-
message="Loading..."
|
|
267
|
-
showSpinner={true}
|
|
268
|
-
/>
|
|
269
|
-
```
|
|
265
|
+
### Cleanup on Unmount
|
|
270
266
|
|
|
271
|
-
|
|
272
|
-
A simple tooltip component for hover information.
|
|
267
|
+
When building controls that publish inferred types, clean up on unmount to avoid stale types:
|
|
273
268
|
|
|
274
269
|
```tsx
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
270
|
+
useEffect(() => {
|
|
271
|
+
// Set the inferred type
|
|
272
|
+
ctx?.setInferredType(myFieldName, computedType);
|
|
273
|
+
|
|
274
|
+
// Cleanup on unmount
|
|
275
|
+
return () => {
|
|
276
|
+
ctx?.clearInferredType?.(myFieldName);
|
|
277
|
+
};
|
|
278
|
+
}, [myFieldName, computedType]);
|
|
280
279
|
```
|
|
281
280
|
|
|
282
|
-
|
|
281
|
+
---
|
|
283
282
|
|
|
284
|
-
|
|
285
|
-
A portal component for setting navigation breadcrumbs and content.
|
|
283
|
+
## useNodeProperty Hook
|
|
286
284
|
|
|
287
|
-
|
|
288
|
-
import NavigationPortal from '@repo/ui';
|
|
285
|
+
Subscribe to and update node properties with automatic collaboration sync:
|
|
289
286
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
287
|
+
```tsx
|
|
288
|
+
import { useNodeProperty } from '@process.co/ui/fields';
|
|
289
|
+
|
|
290
|
+
function MyControl({ fieldName }) {
|
|
291
|
+
const [value, setValue] = useNodeProperty<MyValueType>(fieldName);
|
|
292
|
+
|
|
293
|
+
// value: Current property value (undefined if not set)
|
|
294
|
+
// setValue: Update function with automatic sync
|
|
295
|
+
|
|
296
|
+
return (
|
|
297
|
+
<button onClick={() => setValue({ ...value, enabled: true })}>
|
|
298
|
+
Enable
|
|
299
|
+
</button>
|
|
300
|
+
);
|
|
301
|
+
}
|
|
300
302
|
```
|
|
301
303
|
|
|
302
|
-
|
|
303
|
-
- `breadcrumbs`: Breadcrumb[] (optional)
|
|
304
|
-
- `clearOnUnmount`: boolean (default: true)
|
|
305
|
-
- `keys`: unknown[] (for memoization)
|
|
306
|
-
- `children`: React.ReactNode (navigation content)
|
|
304
|
+
---
|
|
307
305
|
|
|
308
|
-
|
|
306
|
+
## Building Custom Controls
|
|
309
307
|
|
|
310
|
-
|
|
311
|
-
|
|
308
|
+
Custom controls integrate with Process.co's collaborative editing system through the `useNodeProperty` hook.
|
|
309
|
+
|
|
310
|
+
### Basic Pattern
|
|
312
311
|
|
|
313
312
|
```tsx
|
|
314
|
-
import {
|
|
313
|
+
import { useNodeProperty, useInferredTypes } from '@process.co/ui/fields';
|
|
314
|
+
|
|
315
|
+
interface MyControlProps {
|
|
316
|
+
fieldName: string;
|
|
317
|
+
readonly?: boolean;
|
|
318
|
+
}
|
|
315
319
|
|
|
316
|
-
|
|
320
|
+
export default function MyControl({ fieldName, readonly = false }: MyControlProps) {
|
|
321
|
+
// Subscribe to the property value
|
|
322
|
+
const [value, setValue] = useNodeProperty<MyValueType>(fieldName);
|
|
323
|
+
|
|
324
|
+
// Access type inference context (optional)
|
|
325
|
+
const ctx = useInferredTypes();
|
|
326
|
+
|
|
327
|
+
// Derive state from value
|
|
328
|
+
const items = value?.items ?? [];
|
|
329
|
+
|
|
330
|
+
// Update handler
|
|
331
|
+
const addItem = () => {
|
|
332
|
+
setValue({
|
|
333
|
+
...value,
|
|
334
|
+
items: [...items, { id: generateId(), name: '' }]
|
|
335
|
+
});
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
return (
|
|
339
|
+
<div>
|
|
340
|
+
{items.map(item => (
|
|
341
|
+
<ItemRow key={item.id} item={item} />
|
|
342
|
+
))}
|
|
343
|
+
{!readonly && <button onClick={addItem}>Add Item</button>}
|
|
344
|
+
</div>
|
|
345
|
+
);
|
|
346
|
+
}
|
|
317
347
|
```
|
|
318
348
|
|
|
319
|
-
|
|
320
|
-
A copyright notice component with customizable text.
|
|
349
|
+
### With Type Inference
|
|
321
350
|
|
|
322
351
|
```tsx
|
|
323
|
-
import {
|
|
324
|
-
|
|
325
|
-
|
|
352
|
+
import {
|
|
353
|
+
useNodeProperty,
|
|
354
|
+
useInferredTypes,
|
|
355
|
+
Input,
|
|
356
|
+
Select,
|
|
357
|
+
} from '@process.co/ui/fields';
|
|
358
|
+
|
|
359
|
+
export default function ConditionalEditor({ fieldName }) {
|
|
360
|
+
const [value, setValue] = useNodeProperty(fieldName);
|
|
361
|
+
const ctx = useInferredTypes();
|
|
362
|
+
|
|
363
|
+
// Get inferred type from statement field (it publishes automatically)
|
|
364
|
+
const statementType = ctx?.getInferredType(`${fieldName}_statement`) || 'any';
|
|
365
|
+
|
|
366
|
+
// Filter operators based on statement type
|
|
367
|
+
const operators = getOperatorsForType(statementType);
|
|
368
|
+
|
|
369
|
+
return (
|
|
370
|
+
<div>
|
|
371
|
+
{/* This field PUBLISHES its inferred type as "${fieldName}_statement" */}
|
|
372
|
+
<Input
|
|
373
|
+
fieldName={`${fieldName}_statement`}
|
|
374
|
+
label="Statement"
|
|
375
|
+
expectedType="$infer<string | number | boolean>"
|
|
376
|
+
value={value?.statement}
|
|
377
|
+
onChange={(v) => setValue({ ...value, statement: v })}
|
|
378
|
+
/>
|
|
379
|
+
|
|
380
|
+
<Select
|
|
381
|
+
fieldName={`${fieldName}_operator`}
|
|
382
|
+
label="Operator"
|
|
383
|
+
options={operators}
|
|
384
|
+
value={value?.operator}
|
|
385
|
+
onChange={(v) => setValue({ ...value, operator: v })}
|
|
386
|
+
/>
|
|
387
|
+
|
|
388
|
+
{/* This field SUBSCRIBES to the statement field's type */}
|
|
389
|
+
<Input
|
|
390
|
+
fieldName={`${fieldName}_value`}
|
|
391
|
+
label="Value"
|
|
392
|
+
expectedType={`$infer<[${fieldName}_statement]>`}
|
|
393
|
+
value={value?.value}
|
|
394
|
+
onChange={(v) => setValue({ ...value, value: v })}
|
|
395
|
+
/>
|
|
396
|
+
</div>
|
|
397
|
+
);
|
|
398
|
+
}
|
|
326
399
|
```
|
|
327
400
|
|
|
328
|
-
|
|
401
|
+
---
|
|
329
402
|
|
|
330
|
-
|
|
331
|
-
A component for social authentication providers.
|
|
332
|
-
|
|
333
|
-
```tsx
|
|
334
|
-
import { SocialLogin } from '@repo/ui';
|
|
335
|
-
|
|
336
|
-
<SocialLogin
|
|
337
|
-
providers={['google', 'github', 'microsoft']}
|
|
338
|
-
onLogin={handleSocialLogin}
|
|
339
|
-
/>
|
|
340
|
-
```
|
|
403
|
+
## Operator Utilities
|
|
341
404
|
|
|
342
|
-
|
|
405
|
+
Shared utilities for building query builders with type-aware operators.
|
|
343
406
|
|
|
344
|
-
|
|
345
|
-
The main provider component that wraps your application and provides context for translations, settings, and state management.
|
|
407
|
+
### Types
|
|
346
408
|
|
|
347
409
|
```tsx
|
|
348
|
-
import {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
410
|
+
import {
|
|
411
|
+
BaseOperatorType,
|
|
412
|
+
OperatorDef,
|
|
413
|
+
ParsedTypes,
|
|
414
|
+
} from '@process.co/ui/fields';
|
|
415
|
+
|
|
416
|
+
// BaseOperatorType includes: 'exists', 'not_exists', 'string_equals',
|
|
417
|
+
// 'string_contains', 'number_gt', 'boolean_equals', etc.
|
|
418
|
+
|
|
419
|
+
// OperatorDef<T> is generic - extend with custom operators
|
|
420
|
+
type MyOperator = 'expression' | 'custom_check';
|
|
421
|
+
const operators: OperatorDef<MyOperator>[] = [...];
|
|
357
422
|
```
|
|
358
423
|
|
|
359
|
-
|
|
360
|
-
- `useSettings`: Hook to access UI settings
|
|
361
|
-
- `useTranslation`: Hook for translations
|
|
362
|
-
- `KeyValueStore`: Type definition for storage
|
|
363
|
-
- `StorageType`: Enum for storage types
|
|
364
|
-
|
|
365
|
-
## Utility Functions
|
|
366
|
-
|
|
367
|
-
The package also exports utility functions from `lib/utils`:
|
|
424
|
+
### Functions
|
|
368
425
|
|
|
369
426
|
```tsx
|
|
370
|
-
import {
|
|
427
|
+
import {
|
|
428
|
+
parseInferredTypes,
|
|
429
|
+
computeExtendedType,
|
|
430
|
+
filterOperatorsByType,
|
|
431
|
+
getStringConstants,
|
|
432
|
+
getNumberConstants,
|
|
433
|
+
} from '@process.co/ui/fields';
|
|
434
|
+
|
|
435
|
+
// Parse type string into components
|
|
436
|
+
const parsed = parseInferredTypes('"adam" | "beth" | number');
|
|
437
|
+
// { baseTypes: ['string', 'number'], stringConstants: ['adam', 'beth'], ... }
|
|
438
|
+
|
|
439
|
+
// Filter operators by compatible type
|
|
440
|
+
const ops = filterOperatorsByType(OPERATORS, '"adam" | "beth"');
|
|
441
|
+
// Returns operators with types: ['any'] or ['string']
|
|
442
|
+
|
|
443
|
+
// Compute extended type for operators with extendsWithBase
|
|
444
|
+
const expectedType = computeExtendedType('"adam" | "beth"', operatorDef);
|
|
445
|
+
// If extendsWithBase: true, returns '"adam" | "beth" | string'
|
|
371
446
|
```
|
|
372
447
|
|
|
373
|
-
|
|
448
|
+
### Extended Type Narrowing
|
|
374
449
|
|
|
375
|
-
|
|
450
|
+
Some operators support `extendsWithBase` for flexible type matching:
|
|
376
451
|
|
|
377
|
-
```
|
|
378
|
-
|
|
452
|
+
```tsx
|
|
453
|
+
const OPERATORS: OperatorDef[] = [
|
|
454
|
+
// Exact match - only accepts the literal values
|
|
455
|
+
{ value: 'string_equals', narrowsTo: 'string', extendsWithBase: false },
|
|
456
|
+
|
|
457
|
+
// Extended match - accepts literals OR any string
|
|
458
|
+
{ value: 'string_starts_with', narrowsTo: 'string', extendsWithBase: true },
|
|
459
|
+
];
|
|
379
460
|
```
|
|
380
461
|
|
|
381
|
-
|
|
462
|
+
**Example:** If statement infers `"adam" | "beth"`:
|
|
463
|
+
- `string_equals` expects: `"adam" | "beth"` (must match exactly)
|
|
464
|
+
- `string_starts_with` expects: `"adam" | "beth" | string` (can provide partial match like `"a"`)
|
|
382
465
|
|
|
383
|
-
|
|
466
|
+
---
|
|
384
467
|
|
|
385
|
-
|
|
386
|
-
2. Export it from `src/index.tsx`
|
|
387
|
-
3. Create a Storybook story in `src/stories/components/`
|
|
388
|
-
4. Update this documentation
|
|
468
|
+
## Collaboration Features
|
|
389
469
|
|
|
390
|
-
|
|
470
|
+
All field components support real-time collaboration when used within the Process.co flow editor:
|
|
391
471
|
|
|
392
|
-
-
|
|
393
|
-
-
|
|
394
|
-
-
|
|
395
|
-
- Add JSDoc comments for props
|
|
396
|
-
- Create comprehensive Storybook stories
|
|
397
|
-
- Ensure accessibility compliance
|
|
398
|
-
- Use Tailwind CSS with the `ui:` prefix for styling
|
|
472
|
+
- **Cursor sharing**: See where other users are editing
|
|
473
|
+
- **Conflict resolution**: Automatic handling of concurrent edits via Yjs
|
|
474
|
+
- **Presence indicators**: Visual indication of active editors
|
|
399
475
|
|
|
400
|
-
|
|
476
|
+
These features work automatically when components are rendered within a `NodePropertyProvider` context.
|
|
401
477
|
|
|
402
|
-
|
|
478
|
+
---
|
|
403
479
|
|
|
404
|
-
|
|
405
|
-
pnpm test
|
|
406
|
-
```
|
|
480
|
+
## UI Components
|
|
407
481
|
|
|
408
|
-
|
|
482
|
+
The library also includes standard UI components:
|
|
409
483
|
|
|
410
|
-
|
|
484
|
+
```tsx
|
|
485
|
+
import {
|
|
486
|
+
Button,
|
|
487
|
+
Card,
|
|
488
|
+
Alert,
|
|
489
|
+
DropdownMenu,
|
|
490
|
+
DropdownMenuTrigger,
|
|
491
|
+
DropdownMenuContent,
|
|
492
|
+
DropdownMenuItem,
|
|
493
|
+
ConfirmationDropdownMenuItem,
|
|
494
|
+
} from '@process.co/ui';
|
|
495
|
+
```
|
|
411
496
|
|
|
412
|
-
|
|
413
|
-
pnpm build
|
|
414
|
-
```
|
|
497
|
+
See the [Storybook documentation](https://main--674d30e40a127b13419e46ba.chromatic.com) for full component reference.
|