@preset.nz/facets 0.1.1 → 0.1.3
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 +35 -1
- package/package.json +7 -3
- package/schema/v0.1.json +801 -0
- package/src/field-renderers.tsx +38 -1
- package/src/panel.tsx +44 -10
- package/src/types.ts +27 -0
package/README.md
CHANGED
|
@@ -14,6 +14,10 @@ switch statement over your types.
|
|
|
14
14
|
Built for image and map editors, where the same panel has to render a layer, a
|
|
15
15
|
filter, a document or a tool depending on what is selected.
|
|
16
16
|
|
|
17
|
+
The playground at [facets.preset.nz](https://facets.preset.nz) shows the field
|
|
18
|
+
kinds, the schema as JSON, and the panel it draws, side by side. Its source is
|
|
19
|
+
in `playground/`.
|
|
20
|
+
|
|
17
21
|
---
|
|
18
22
|
|
|
19
23
|
## Installing
|
|
@@ -159,7 +163,7 @@ keep the mapping inside `read`, where your domain types are still in scope.
|
|
|
159
163
|
|
|
160
164
|
## Field kinds
|
|
161
165
|
|
|
162
|
-
|
|
166
|
+
Eleven ship built in: nine that hold a value, and two that only show something.
|
|
163
167
|
|
|
164
168
|
| Kind | Value | Notes |
|
|
165
169
|
|---|---|---|
|
|
@@ -172,6 +176,36 @@ Nine ship built in.
|
|
|
172
176
|
| `color` | hex string | `presets`; shows a swatch |
|
|
173
177
|
| `vector` | number array | N scalars in one row — Houdini's `float3` / `int2`. `components` sets arity and per-slot label, suffix and range |
|
|
174
178
|
| `file` | `{ name }` | display only |
|
|
179
|
+
| `separator` | none | a horizontal rule, with `label` as an optional caption. No `path` |
|
|
180
|
+
| `label` | none | static text: a hint or a sub-heading. The text is `label`. No `path` |
|
|
181
|
+
|
|
182
|
+
Groups take `collapsible: true` to fold under their title, and
|
|
183
|
+
`defaultCollapsed: true` to start folded. The open or closed state belongs to
|
|
184
|
+
the group, so it resets when the panel remounts.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## JSON Schema
|
|
189
|
+
|
|
190
|
+
A schema for editors and agents is published at
|
|
191
|
+
`https://facets.preset.nz/schema/v0.1.json`, and ships in the package as
|
|
192
|
+
`@preset.nz/facets/schema/v0.1.json`. Point a JSON file at it:
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"$schema": "https://facets.preset.nz/schema/v0.1.json",
|
|
197
|
+
"version": 1,
|
|
198
|
+
"groups": []
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Built-in kinds are checked strictly: a misspelt prop or a missing `path` is an
|
|
203
|
+
error. Any other kind passes as a custom kind, so a misspelt *kind* is not
|
|
204
|
+
caught here; the panel logs a warning and skips the field. `optionsProvider` is
|
|
205
|
+
code, so it has no JSON form.
|
|
206
|
+
|
|
207
|
+
One file covers every 0.1.x release. Patches may add kinds and props to it,
|
|
208
|
+
never remove or tighten them.
|
|
175
209
|
|
|
176
210
|
---
|
|
177
211
|
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@preset.nz/facets",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Schema-as-data property/inspector panels for React: FieldDef schemas, a scope registry, an open field-renderer registry, and a generic PropertyPanel.",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"homepage": "https://facets.preset.nz",
|
|
6
7
|
"repository": {
|
|
7
8
|
"type": "git",
|
|
8
9
|
"url": "git+https://github.com/preset-nz/facets.git"
|
|
@@ -10,10 +11,12 @@
|
|
|
10
11
|
"type": "module",
|
|
11
12
|
"sideEffects": false,
|
|
12
13
|
"exports": {
|
|
13
|
-
".": "./src/index.ts"
|
|
14
|
+
".": "./src/index.ts",
|
|
15
|
+
"./schema/v0.1.json": "./schema/v0.1.json"
|
|
14
16
|
},
|
|
15
17
|
"files": [
|
|
16
|
-
"src"
|
|
18
|
+
"src",
|
|
19
|
+
"schema"
|
|
17
20
|
],
|
|
18
21
|
"publishConfig": {
|
|
19
22
|
"access": "public"
|
|
@@ -23,6 +26,7 @@
|
|
|
23
26
|
},
|
|
24
27
|
"devDependencies": {
|
|
25
28
|
"@types/react": "^19",
|
|
29
|
+
"ajv": "^8.20.0",
|
|
26
30
|
"typescript": "^5.8.3"
|
|
27
31
|
}
|
|
28
32
|
}
|
package/schema/v0.1.json
ADDED
|
@@ -0,0 +1,801 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://facets.preset.nz/schema/v0.1.json",
|
|
4
|
+
"title": "@preset.nz/facets PropertySchema, 0.1",
|
|
5
|
+
"description": "An inspector panel as data: groups of rows of fields. Valid for every 0.1.x release; later 0.1 patches only add kinds and props. Playground: https://facets.preset.nz",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": [
|
|
8
|
+
"version",
|
|
9
|
+
"groups"
|
|
10
|
+
],
|
|
11
|
+
"additionalProperties": false,
|
|
12
|
+
"properties": {
|
|
13
|
+
"$schema": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "This file's URL, for editors."
|
|
16
|
+
},
|
|
17
|
+
"version": {
|
|
18
|
+
"type": "number",
|
|
19
|
+
"description": "The host's own version of this schema. Bump it when the shape changes."
|
|
20
|
+
},
|
|
21
|
+
"groups": {
|
|
22
|
+
"type": "array",
|
|
23
|
+
"description": "Rendered top to bottom, with a rule between groups.",
|
|
24
|
+
"items": {
|
|
25
|
+
"$ref": "#/definitions/group"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"definitions": {
|
|
30
|
+
"group": {
|
|
31
|
+
"type": "object",
|
|
32
|
+
"required": [
|
|
33
|
+
"id",
|
|
34
|
+
"rows"
|
|
35
|
+
],
|
|
36
|
+
"additionalProperties": false,
|
|
37
|
+
"properties": {
|
|
38
|
+
"id": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"description": "Unique within the schema."
|
|
41
|
+
},
|
|
42
|
+
"title": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"description": "The heading. Omit it to render the group without one."
|
|
45
|
+
},
|
|
46
|
+
"description": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"description": "A line under the title."
|
|
49
|
+
},
|
|
50
|
+
"collapsible": {
|
|
51
|
+
"type": "boolean",
|
|
52
|
+
"description": "Fold under the title. Needs a title."
|
|
53
|
+
},
|
|
54
|
+
"defaultCollapsed": {
|
|
55
|
+
"type": "boolean",
|
|
56
|
+
"description": "Start folded."
|
|
57
|
+
},
|
|
58
|
+
"rows": {
|
|
59
|
+
"type": "array",
|
|
60
|
+
"description": "A field is a full-width row; an array of fields is one row laid out as a grid (usually two).",
|
|
61
|
+
"items": {
|
|
62
|
+
"anyOf": [
|
|
63
|
+
{
|
|
64
|
+
"$ref": "#/definitions/field"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"type": "array",
|
|
68
|
+
"items": {
|
|
69
|
+
"$ref": "#/definitions/field"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"disabledWhen": {
|
|
78
|
+
"type": "object",
|
|
79
|
+
"description": "Greys the field out depending on another field's value. Set one operator; if several are set, the first of equals, notEquals, in, notIn wins.",
|
|
80
|
+
"required": [
|
|
81
|
+
"path"
|
|
82
|
+
],
|
|
83
|
+
"additionalProperties": false,
|
|
84
|
+
"properties": {
|
|
85
|
+
"path": {
|
|
86
|
+
"type": "string",
|
|
87
|
+
"description": "The field value to watch, by path."
|
|
88
|
+
},
|
|
89
|
+
"equals": {
|
|
90
|
+
"description": "Disabled when the value equals this."
|
|
91
|
+
},
|
|
92
|
+
"notEquals": {
|
|
93
|
+
"description": "Disabled unless the value equals this."
|
|
94
|
+
},
|
|
95
|
+
"in": {
|
|
96
|
+
"type": "array",
|
|
97
|
+
"description": "Disabled when the value is one of these."
|
|
98
|
+
},
|
|
99
|
+
"notIn": {
|
|
100
|
+
"type": "array",
|
|
101
|
+
"description": "Disabled unless the value is one of these."
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
"selectOption": {
|
|
106
|
+
"type": "object",
|
|
107
|
+
"required": [
|
|
108
|
+
"value",
|
|
109
|
+
"label"
|
|
110
|
+
],
|
|
111
|
+
"additionalProperties": false,
|
|
112
|
+
"properties": {
|
|
113
|
+
"value": {
|
|
114
|
+
"type": "string",
|
|
115
|
+
"description": "Stored in the value record."
|
|
116
|
+
},
|
|
117
|
+
"label": {
|
|
118
|
+
"type": "string",
|
|
119
|
+
"description": "Shown to the user."
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
"vectorComponent": {
|
|
124
|
+
"type": "object",
|
|
125
|
+
"additionalProperties": false,
|
|
126
|
+
"properties": {
|
|
127
|
+
"label": {
|
|
128
|
+
"type": "string",
|
|
129
|
+
"description": "Shown above the slot."
|
|
130
|
+
},
|
|
131
|
+
"suffix": {
|
|
132
|
+
"type": "string",
|
|
133
|
+
"description": "Unit after the number, e.g. px or °."
|
|
134
|
+
},
|
|
135
|
+
"min": {
|
|
136
|
+
"type": "number",
|
|
137
|
+
"description": "Lower bound."
|
|
138
|
+
},
|
|
139
|
+
"max": {
|
|
140
|
+
"type": "number",
|
|
141
|
+
"description": "Upper bound."
|
|
142
|
+
},
|
|
143
|
+
"step": {
|
|
144
|
+
"type": "number",
|
|
145
|
+
"description": "Increment. Default 1."
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
"field": {
|
|
150
|
+
"type": "object",
|
|
151
|
+
"description": "One field. kind picks the renderer: a built-in kind is checked strictly; any other kind is a custom kind registered by the host, which may carry extra props.",
|
|
152
|
+
"required": [
|
|
153
|
+
"kind",
|
|
154
|
+
"id"
|
|
155
|
+
],
|
|
156
|
+
"properties": {
|
|
157
|
+
"kind": {
|
|
158
|
+
"type": "string",
|
|
159
|
+
"description": "The renderer to use. Built in: text, textarea, number, slider, select, color, checkbox, file, vector, separator, label. Anything else must be registered by the host with registerFieldRenderer().",
|
|
160
|
+
"examples": [
|
|
161
|
+
"text",
|
|
162
|
+
"textarea",
|
|
163
|
+
"number",
|
|
164
|
+
"slider",
|
|
165
|
+
"select",
|
|
166
|
+
"color",
|
|
167
|
+
"checkbox",
|
|
168
|
+
"file",
|
|
169
|
+
"vector",
|
|
170
|
+
"separator",
|
|
171
|
+
"label"
|
|
172
|
+
]
|
|
173
|
+
},
|
|
174
|
+
"id": {
|
|
175
|
+
"type": "string",
|
|
176
|
+
"description": "Unique within the schema. Keys the field in the panel."
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
"allOf": [
|
|
180
|
+
{
|
|
181
|
+
"if": {
|
|
182
|
+
"required": [
|
|
183
|
+
"kind"
|
|
184
|
+
],
|
|
185
|
+
"properties": {
|
|
186
|
+
"kind": {
|
|
187
|
+
"const": "text"
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
"then": {
|
|
192
|
+
"$ref": "#/definitions/text"
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"if": {
|
|
197
|
+
"required": [
|
|
198
|
+
"kind"
|
|
199
|
+
],
|
|
200
|
+
"properties": {
|
|
201
|
+
"kind": {
|
|
202
|
+
"const": "textarea"
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
"then": {
|
|
207
|
+
"$ref": "#/definitions/textarea"
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
"if": {
|
|
212
|
+
"required": [
|
|
213
|
+
"kind"
|
|
214
|
+
],
|
|
215
|
+
"properties": {
|
|
216
|
+
"kind": {
|
|
217
|
+
"const": "number"
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
"then": {
|
|
222
|
+
"$ref": "#/definitions/number"
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"if": {
|
|
227
|
+
"required": [
|
|
228
|
+
"kind"
|
|
229
|
+
],
|
|
230
|
+
"properties": {
|
|
231
|
+
"kind": {
|
|
232
|
+
"const": "slider"
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
"then": {
|
|
237
|
+
"$ref": "#/definitions/slider"
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"if": {
|
|
242
|
+
"required": [
|
|
243
|
+
"kind"
|
|
244
|
+
],
|
|
245
|
+
"properties": {
|
|
246
|
+
"kind": {
|
|
247
|
+
"const": "select"
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
"then": {
|
|
252
|
+
"$ref": "#/definitions/select"
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
"if": {
|
|
257
|
+
"required": [
|
|
258
|
+
"kind"
|
|
259
|
+
],
|
|
260
|
+
"properties": {
|
|
261
|
+
"kind": {
|
|
262
|
+
"const": "color"
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
"then": {
|
|
267
|
+
"$ref": "#/definitions/color"
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
"if": {
|
|
272
|
+
"required": [
|
|
273
|
+
"kind"
|
|
274
|
+
],
|
|
275
|
+
"properties": {
|
|
276
|
+
"kind": {
|
|
277
|
+
"const": "checkbox"
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
"then": {
|
|
282
|
+
"$ref": "#/definitions/checkbox"
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
"if": {
|
|
287
|
+
"required": [
|
|
288
|
+
"kind"
|
|
289
|
+
],
|
|
290
|
+
"properties": {
|
|
291
|
+
"kind": {
|
|
292
|
+
"const": "file"
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
"then": {
|
|
297
|
+
"$ref": "#/definitions/file"
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
"if": {
|
|
302
|
+
"required": [
|
|
303
|
+
"kind"
|
|
304
|
+
],
|
|
305
|
+
"properties": {
|
|
306
|
+
"kind": {
|
|
307
|
+
"const": "vector"
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
"then": {
|
|
312
|
+
"$ref": "#/definitions/vector"
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
"if": {
|
|
317
|
+
"required": [
|
|
318
|
+
"kind"
|
|
319
|
+
],
|
|
320
|
+
"properties": {
|
|
321
|
+
"kind": {
|
|
322
|
+
"const": "separator"
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
"then": {
|
|
327
|
+
"$ref": "#/definitions/separator"
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
"if": {
|
|
332
|
+
"required": [
|
|
333
|
+
"kind"
|
|
334
|
+
],
|
|
335
|
+
"properties": {
|
|
336
|
+
"kind": {
|
|
337
|
+
"const": "label"
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
"then": {
|
|
342
|
+
"$ref": "#/definitions/label"
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
"if": {
|
|
347
|
+
"required": [
|
|
348
|
+
"kind"
|
|
349
|
+
],
|
|
350
|
+
"properties": {
|
|
351
|
+
"kind": {
|
|
352
|
+
"not": {
|
|
353
|
+
"enum": [
|
|
354
|
+
"text",
|
|
355
|
+
"textarea",
|
|
356
|
+
"number",
|
|
357
|
+
"slider",
|
|
358
|
+
"select",
|
|
359
|
+
"color",
|
|
360
|
+
"checkbox",
|
|
361
|
+
"file",
|
|
362
|
+
"vector",
|
|
363
|
+
"separator",
|
|
364
|
+
"label"
|
|
365
|
+
]
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
"then": {
|
|
371
|
+
"$ref": "#/definitions/custom"
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
]
|
|
375
|
+
},
|
|
376
|
+
"text": {
|
|
377
|
+
"type": "object",
|
|
378
|
+
"description": "A single line of text. Value: string.",
|
|
379
|
+
"required": [
|
|
380
|
+
"kind",
|
|
381
|
+
"id",
|
|
382
|
+
"path"
|
|
383
|
+
],
|
|
384
|
+
"properties": {
|
|
385
|
+
"kind": {
|
|
386
|
+
"const": "text"
|
|
387
|
+
},
|
|
388
|
+
"id": {
|
|
389
|
+
"type": "string",
|
|
390
|
+
"description": "Unique within the schema. Keys the field in the panel."
|
|
391
|
+
},
|
|
392
|
+
"label": {
|
|
393
|
+
"type": "string",
|
|
394
|
+
"description": "Shown above the field. Defaults to id."
|
|
395
|
+
},
|
|
396
|
+
"path": {
|
|
397
|
+
"type": "string",
|
|
398
|
+
"description": "A key into the record the scope's read() returns. A flat lookup, not a nested traversal: \"transform.x\" is one key."
|
|
399
|
+
},
|
|
400
|
+
"disabledWhen": {
|
|
401
|
+
"$ref": "#/definitions/disabledWhen"
|
|
402
|
+
},
|
|
403
|
+
"placeholder": {
|
|
404
|
+
"type": "string",
|
|
405
|
+
"description": "Shown while empty."
|
|
406
|
+
}
|
|
407
|
+
},
|
|
408
|
+
"additionalProperties": false
|
|
409
|
+
},
|
|
410
|
+
"textarea": {
|
|
411
|
+
"type": "object",
|
|
412
|
+
"description": "Several lines of text. Value: string.",
|
|
413
|
+
"required": [
|
|
414
|
+
"kind",
|
|
415
|
+
"id",
|
|
416
|
+
"path"
|
|
417
|
+
],
|
|
418
|
+
"properties": {
|
|
419
|
+
"kind": {
|
|
420
|
+
"const": "textarea"
|
|
421
|
+
},
|
|
422
|
+
"id": {
|
|
423
|
+
"type": "string",
|
|
424
|
+
"description": "Unique within the schema. Keys the field in the panel."
|
|
425
|
+
},
|
|
426
|
+
"label": {
|
|
427
|
+
"type": "string",
|
|
428
|
+
"description": "Shown above the field. Defaults to id."
|
|
429
|
+
},
|
|
430
|
+
"path": {
|
|
431
|
+
"type": "string",
|
|
432
|
+
"description": "A key into the record the scope's read() returns. A flat lookup, not a nested traversal: \"transform.x\" is one key."
|
|
433
|
+
},
|
|
434
|
+
"disabledWhen": {
|
|
435
|
+
"$ref": "#/definitions/disabledWhen"
|
|
436
|
+
},
|
|
437
|
+
"placeholder": {
|
|
438
|
+
"type": "string",
|
|
439
|
+
"description": "Shown while empty."
|
|
440
|
+
},
|
|
441
|
+
"rows": {
|
|
442
|
+
"type": "number",
|
|
443
|
+
"description": "Visible lines. Default 3."
|
|
444
|
+
}
|
|
445
|
+
},
|
|
446
|
+
"additionalProperties": false
|
|
447
|
+
},
|
|
448
|
+
"number": {
|
|
449
|
+
"type": "object",
|
|
450
|
+
"description": "A number typed into a box. Value: number.",
|
|
451
|
+
"required": [
|
|
452
|
+
"kind",
|
|
453
|
+
"id",
|
|
454
|
+
"path"
|
|
455
|
+
],
|
|
456
|
+
"properties": {
|
|
457
|
+
"kind": {
|
|
458
|
+
"const": "number"
|
|
459
|
+
},
|
|
460
|
+
"id": {
|
|
461
|
+
"type": "string",
|
|
462
|
+
"description": "Unique within the schema. Keys the field in the panel."
|
|
463
|
+
},
|
|
464
|
+
"label": {
|
|
465
|
+
"type": "string",
|
|
466
|
+
"description": "Shown above the field. Defaults to id."
|
|
467
|
+
},
|
|
468
|
+
"path": {
|
|
469
|
+
"type": "string",
|
|
470
|
+
"description": "A key into the record the scope's read() returns. A flat lookup, not a nested traversal: \"transform.x\" is one key."
|
|
471
|
+
},
|
|
472
|
+
"disabledWhen": {
|
|
473
|
+
"$ref": "#/definitions/disabledWhen"
|
|
474
|
+
},
|
|
475
|
+
"min": {
|
|
476
|
+
"type": "number",
|
|
477
|
+
"description": "Lower bound."
|
|
478
|
+
},
|
|
479
|
+
"max": {
|
|
480
|
+
"type": "number",
|
|
481
|
+
"description": "Upper bound."
|
|
482
|
+
},
|
|
483
|
+
"step": {
|
|
484
|
+
"type": "number",
|
|
485
|
+
"description": "Increment. Default 1."
|
|
486
|
+
}
|
|
487
|
+
},
|
|
488
|
+
"additionalProperties": false
|
|
489
|
+
},
|
|
490
|
+
"slider": {
|
|
491
|
+
"type": "object",
|
|
492
|
+
"description": "A number dragged along a range. Value: number.",
|
|
493
|
+
"required": [
|
|
494
|
+
"kind",
|
|
495
|
+
"id",
|
|
496
|
+
"path"
|
|
497
|
+
],
|
|
498
|
+
"properties": {
|
|
499
|
+
"kind": {
|
|
500
|
+
"const": "slider"
|
|
501
|
+
},
|
|
502
|
+
"id": {
|
|
503
|
+
"type": "string",
|
|
504
|
+
"description": "Unique within the schema. Keys the field in the panel."
|
|
505
|
+
},
|
|
506
|
+
"label": {
|
|
507
|
+
"type": "string",
|
|
508
|
+
"description": "Shown above the field. Defaults to id."
|
|
509
|
+
},
|
|
510
|
+
"path": {
|
|
511
|
+
"type": "string",
|
|
512
|
+
"description": "A key into the record the scope's read() returns. A flat lookup, not a nested traversal: \"transform.x\" is one key."
|
|
513
|
+
},
|
|
514
|
+
"disabledWhen": {
|
|
515
|
+
"$ref": "#/definitions/disabledWhen"
|
|
516
|
+
},
|
|
517
|
+
"min": {
|
|
518
|
+
"type": "number",
|
|
519
|
+
"description": "Lower bound."
|
|
520
|
+
},
|
|
521
|
+
"max": {
|
|
522
|
+
"type": "number",
|
|
523
|
+
"description": "Upper bound."
|
|
524
|
+
},
|
|
525
|
+
"step": {
|
|
526
|
+
"type": "number",
|
|
527
|
+
"description": "Increment. Default 1."
|
|
528
|
+
}
|
|
529
|
+
},
|
|
530
|
+
"additionalProperties": false
|
|
531
|
+
},
|
|
532
|
+
"select": {
|
|
533
|
+
"type": "object",
|
|
534
|
+
"description": "One choice from a list. Value: the chosen option's value (string). Options can also come from optionsProvider(ctx), a function, which is set in code and has no JSON form.",
|
|
535
|
+
"required": [
|
|
536
|
+
"kind",
|
|
537
|
+
"id",
|
|
538
|
+
"path"
|
|
539
|
+
],
|
|
540
|
+
"properties": {
|
|
541
|
+
"kind": {
|
|
542
|
+
"const": "select"
|
|
543
|
+
},
|
|
544
|
+
"id": {
|
|
545
|
+
"type": "string",
|
|
546
|
+
"description": "Unique within the schema. Keys the field in the panel."
|
|
547
|
+
},
|
|
548
|
+
"label": {
|
|
549
|
+
"type": "string",
|
|
550
|
+
"description": "Shown above the field. Defaults to id."
|
|
551
|
+
},
|
|
552
|
+
"path": {
|
|
553
|
+
"type": "string",
|
|
554
|
+
"description": "A key into the record the scope's read() returns. A flat lookup, not a nested traversal: \"transform.x\" is one key."
|
|
555
|
+
},
|
|
556
|
+
"disabledWhen": {
|
|
557
|
+
"$ref": "#/definitions/disabledWhen"
|
|
558
|
+
},
|
|
559
|
+
"options": {
|
|
560
|
+
"type": "array",
|
|
561
|
+
"description": "The choices.",
|
|
562
|
+
"items": {
|
|
563
|
+
"$ref": "#/definitions/selectOption"
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
},
|
|
567
|
+
"additionalProperties": false
|
|
568
|
+
},
|
|
569
|
+
"color": {
|
|
570
|
+
"type": "object",
|
|
571
|
+
"description": "A colour as a hex string, e.g. \"#2b7c76\". Value: string.",
|
|
572
|
+
"required": [
|
|
573
|
+
"kind",
|
|
574
|
+
"id",
|
|
575
|
+
"path"
|
|
576
|
+
],
|
|
577
|
+
"properties": {
|
|
578
|
+
"kind": {
|
|
579
|
+
"const": "color"
|
|
580
|
+
},
|
|
581
|
+
"id": {
|
|
582
|
+
"type": "string",
|
|
583
|
+
"description": "Unique within the schema. Keys the field in the panel."
|
|
584
|
+
},
|
|
585
|
+
"label": {
|
|
586
|
+
"type": "string",
|
|
587
|
+
"description": "Shown above the field. Defaults to id."
|
|
588
|
+
},
|
|
589
|
+
"path": {
|
|
590
|
+
"type": "string",
|
|
591
|
+
"description": "A key into the record the scope's read() returns. A flat lookup, not a nested traversal: \"transform.x\" is one key."
|
|
592
|
+
},
|
|
593
|
+
"disabledWhen": {
|
|
594
|
+
"$ref": "#/definitions/disabledWhen"
|
|
595
|
+
},
|
|
596
|
+
"presets": {
|
|
597
|
+
"type": "array",
|
|
598
|
+
"items": {
|
|
599
|
+
"type": "string"
|
|
600
|
+
},
|
|
601
|
+
"description": "Hex strings. Declared in 0.1; the built-in renderer does not draw them."
|
|
602
|
+
}
|
|
603
|
+
},
|
|
604
|
+
"additionalProperties": false
|
|
605
|
+
},
|
|
606
|
+
"checkbox": {
|
|
607
|
+
"type": "object",
|
|
608
|
+
"description": "On or off. Value: boolean.",
|
|
609
|
+
"required": [
|
|
610
|
+
"kind",
|
|
611
|
+
"id",
|
|
612
|
+
"path"
|
|
613
|
+
],
|
|
614
|
+
"properties": {
|
|
615
|
+
"kind": {
|
|
616
|
+
"const": "checkbox"
|
|
617
|
+
},
|
|
618
|
+
"id": {
|
|
619
|
+
"type": "string",
|
|
620
|
+
"description": "Unique within the schema. Keys the field in the panel."
|
|
621
|
+
},
|
|
622
|
+
"label": {
|
|
623
|
+
"type": "string",
|
|
624
|
+
"description": "Shown above the field. Defaults to id."
|
|
625
|
+
},
|
|
626
|
+
"path": {
|
|
627
|
+
"type": "string",
|
|
628
|
+
"description": "A key into the record the scope's read() returns. A flat lookup, not a nested traversal: \"transform.x\" is one key."
|
|
629
|
+
},
|
|
630
|
+
"disabledWhen": {
|
|
631
|
+
"$ref": "#/definitions/disabledWhen"
|
|
632
|
+
}
|
|
633
|
+
},
|
|
634
|
+
"additionalProperties": false
|
|
635
|
+
},
|
|
636
|
+
"file": {
|
|
637
|
+
"type": "object",
|
|
638
|
+
"description": "Shows a file's name; always read-only. Value: { \"name\": string }.",
|
|
639
|
+
"required": [
|
|
640
|
+
"kind",
|
|
641
|
+
"id",
|
|
642
|
+
"path"
|
|
643
|
+
],
|
|
644
|
+
"properties": {
|
|
645
|
+
"kind": {
|
|
646
|
+
"const": "file"
|
|
647
|
+
},
|
|
648
|
+
"id": {
|
|
649
|
+
"type": "string",
|
|
650
|
+
"description": "Unique within the schema. Keys the field in the panel."
|
|
651
|
+
},
|
|
652
|
+
"label": {
|
|
653
|
+
"type": "string",
|
|
654
|
+
"description": "Shown above the field. Defaults to id."
|
|
655
|
+
},
|
|
656
|
+
"path": {
|
|
657
|
+
"type": "string",
|
|
658
|
+
"description": "A key into the record the scope's read() returns. A flat lookup, not a nested traversal: \"transform.x\" is one key."
|
|
659
|
+
},
|
|
660
|
+
"disabledWhen": {
|
|
661
|
+
"$ref": "#/definitions/disabledWhen"
|
|
662
|
+
},
|
|
663
|
+
"accept": {
|
|
664
|
+
"type": "string",
|
|
665
|
+
"description": "File types, as on <input>. Declared in 0.1; the built-in renderer ignores it."
|
|
666
|
+
},
|
|
667
|
+
"helperText": {
|
|
668
|
+
"type": "string",
|
|
669
|
+
"description": "A hint. Declared in 0.1; the built-in renderer does not draw it."
|
|
670
|
+
}
|
|
671
|
+
},
|
|
672
|
+
"additionalProperties": false
|
|
673
|
+
},
|
|
674
|
+
"vector": {
|
|
675
|
+
"type": "object",
|
|
676
|
+
"description": "Several numbers in one row: position, size, RGB, LCh. Value: number[], one per component.",
|
|
677
|
+
"required": [
|
|
678
|
+
"kind",
|
|
679
|
+
"id",
|
|
680
|
+
"path",
|
|
681
|
+
"components"
|
|
682
|
+
],
|
|
683
|
+
"properties": {
|
|
684
|
+
"kind": {
|
|
685
|
+
"const": "vector"
|
|
686
|
+
},
|
|
687
|
+
"id": {
|
|
688
|
+
"type": "string",
|
|
689
|
+
"description": "Unique within the schema. Keys the field in the panel."
|
|
690
|
+
},
|
|
691
|
+
"label": {
|
|
692
|
+
"type": "string",
|
|
693
|
+
"description": "Shown above the field. Defaults to id."
|
|
694
|
+
},
|
|
695
|
+
"path": {
|
|
696
|
+
"type": "string",
|
|
697
|
+
"description": "A key into the record the scope's read() returns. A flat lookup, not a nested traversal: \"transform.x\" is one key."
|
|
698
|
+
},
|
|
699
|
+
"disabledWhen": {
|
|
700
|
+
"$ref": "#/definitions/disabledWhen"
|
|
701
|
+
},
|
|
702
|
+
"components": {
|
|
703
|
+
"type": "array",
|
|
704
|
+
"description": "One entry per slot; sets the arity.",
|
|
705
|
+
"items": {
|
|
706
|
+
"$ref": "#/definitions/vectorComponent"
|
|
707
|
+
}
|
|
708
|
+
},
|
|
709
|
+
"integer": {
|
|
710
|
+
"type": "boolean",
|
|
711
|
+
"description": "Truncate to whole numbers."
|
|
712
|
+
},
|
|
713
|
+
"precision": {
|
|
714
|
+
"type": "number",
|
|
715
|
+
"description": "Decimals shown when read-only."
|
|
716
|
+
}
|
|
717
|
+
},
|
|
718
|
+
"additionalProperties": false
|
|
719
|
+
},
|
|
720
|
+
"separator": {
|
|
721
|
+
"type": "object",
|
|
722
|
+
"description": "A horizontal rule between rows, with label as an optional caption. Holds no value, so it has no path.",
|
|
723
|
+
"required": [
|
|
724
|
+
"kind",
|
|
725
|
+
"id"
|
|
726
|
+
],
|
|
727
|
+
"properties": {
|
|
728
|
+
"kind": {
|
|
729
|
+
"const": "separator"
|
|
730
|
+
},
|
|
731
|
+
"id": {
|
|
732
|
+
"type": "string",
|
|
733
|
+
"description": "Unique within the schema. Keys the field in the panel."
|
|
734
|
+
},
|
|
735
|
+
"label": {
|
|
736
|
+
"type": "string",
|
|
737
|
+
"description": "Shown above the field. Defaults to id."
|
|
738
|
+
},
|
|
739
|
+
"disabledWhen": {
|
|
740
|
+
"$ref": "#/definitions/disabledWhen"
|
|
741
|
+
}
|
|
742
|
+
},
|
|
743
|
+
"additionalProperties": false
|
|
744
|
+
},
|
|
745
|
+
"label": {
|
|
746
|
+
"type": "object",
|
|
747
|
+
"description": "Static text: a hint, a note, a sub-heading. The text is label. Holds no value, so it has no path.",
|
|
748
|
+
"required": [
|
|
749
|
+
"kind",
|
|
750
|
+
"id",
|
|
751
|
+
"label"
|
|
752
|
+
],
|
|
753
|
+
"properties": {
|
|
754
|
+
"kind": {
|
|
755
|
+
"const": "label"
|
|
756
|
+
},
|
|
757
|
+
"id": {
|
|
758
|
+
"type": "string",
|
|
759
|
+
"description": "Unique within the schema. Keys the field in the panel."
|
|
760
|
+
},
|
|
761
|
+
"label": {
|
|
762
|
+
"type": "string",
|
|
763
|
+
"description": "Shown above the field. Defaults to id."
|
|
764
|
+
},
|
|
765
|
+
"disabledWhen": {
|
|
766
|
+
"$ref": "#/definitions/disabledWhen"
|
|
767
|
+
}
|
|
768
|
+
},
|
|
769
|
+
"additionalProperties": false
|
|
770
|
+
},
|
|
771
|
+
"custom": {
|
|
772
|
+
"type": "object",
|
|
773
|
+
"description": "A custom kind. The host's renderer decides which extra props it reads.",
|
|
774
|
+
"required": [
|
|
775
|
+
"kind",
|
|
776
|
+
"id",
|
|
777
|
+
"path"
|
|
778
|
+
],
|
|
779
|
+
"properties": {
|
|
780
|
+
"kind": {
|
|
781
|
+
"type": "string"
|
|
782
|
+
},
|
|
783
|
+
"id": {
|
|
784
|
+
"type": "string",
|
|
785
|
+
"description": "Unique within the schema. Keys the field in the panel."
|
|
786
|
+
},
|
|
787
|
+
"label": {
|
|
788
|
+
"type": "string",
|
|
789
|
+
"description": "Shown above the field. Defaults to id."
|
|
790
|
+
},
|
|
791
|
+
"path": {
|
|
792
|
+
"type": "string",
|
|
793
|
+
"description": "A key into the record the scope's read() returns. A flat lookup, not a nested traversal: \"transform.x\" is one key."
|
|
794
|
+
},
|
|
795
|
+
"disabledWhen": {
|
|
796
|
+
"$ref": "#/definitions/disabledWhen"
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
package/src/field-renderers.tsx
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
SelectValue,
|
|
15
15
|
} from "@/components/ui/select"
|
|
16
16
|
import { Checkbox } from "@/components/ui/checkbox"
|
|
17
|
+
import { Separator } from "@/components/ui/separator"
|
|
17
18
|
import { registerFieldRenderer } from "./registry"
|
|
18
19
|
import type {
|
|
19
20
|
CheckboxFieldDef,
|
|
@@ -26,6 +27,8 @@ import type {
|
|
|
26
27
|
TextFieldDef,
|
|
27
28
|
TextareaFieldDef,
|
|
28
29
|
FileFieldDef,
|
|
30
|
+
LabelFieldDef,
|
|
31
|
+
SeparatorFieldDef,
|
|
29
32
|
VectorFieldDef,
|
|
30
33
|
} from "./types"
|
|
31
34
|
|
|
@@ -226,7 +229,9 @@ const SelectRenderer: FieldRenderer<SelectFieldDef> = ({
|
|
|
226
229
|
disabled={disabled}
|
|
227
230
|
>
|
|
228
231
|
<SelectTrigger>
|
|
229
|
-
|
|
232
|
+
{/* Children, not the primitive's own lookup: Base UI's Value shows
|
|
233
|
+
the raw value unless it's told the label. */}
|
|
234
|
+
<SelectValue>{matched ? matched.label : current}</SelectValue>
|
|
230
235
|
</SelectTrigger>
|
|
231
236
|
<SelectContent>
|
|
232
237
|
{options.map((o) => (
|
|
@@ -425,7 +430,39 @@ const VectorRenderer: FieldRenderer<VectorFieldDef> = ({
|
|
|
425
430
|
)
|
|
426
431
|
}
|
|
427
432
|
|
|
433
|
+
const SeparatorRenderer: FieldRenderer<SeparatorFieldDef> = ({ field }) =>
|
|
434
|
+
field.label ? (
|
|
435
|
+
<div className="my-1 flex items-center gap-2">
|
|
436
|
+
<span className="text-[10px] uppercase tracking-wide text-muted-foreground">
|
|
437
|
+
{field.label}
|
|
438
|
+
</span>
|
|
439
|
+
<Separator className="flex-1" />
|
|
440
|
+
</div>
|
|
441
|
+
) : (
|
|
442
|
+
<Separator className="my-1" />
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
// The same in every mode: there is nothing to edit. Greys out under
|
|
446
|
+
// disabledWhen so a hint can follow the field it describes.
|
|
447
|
+
const LabelRenderer: FieldRenderer<LabelFieldDef> = ({ field, disabled }) => (
|
|
448
|
+
<p
|
|
449
|
+
className={
|
|
450
|
+
"text-xs text-muted-foreground" + (disabled ? " opacity-50" : "")
|
|
451
|
+
}
|
|
452
|
+
>
|
|
453
|
+
{field.label}
|
|
454
|
+
</p>
|
|
455
|
+
)
|
|
456
|
+
|
|
428
457
|
export function registerBuiltinRenderers(): void {
|
|
458
|
+
registerFieldRenderer(
|
|
459
|
+
"separator",
|
|
460
|
+
SeparatorRenderer as FieldRenderer<import("./types").FieldDef>,
|
|
461
|
+
)
|
|
462
|
+
registerFieldRenderer(
|
|
463
|
+
"label",
|
|
464
|
+
LabelRenderer as FieldRenderer<import("./types").FieldDef>,
|
|
465
|
+
)
|
|
429
466
|
registerFieldRenderer(
|
|
430
467
|
"text",
|
|
431
468
|
TextRenderer as FieldRenderer<import("./types").FieldDef>,
|
package/src/panel.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useMemo } from "react"
|
|
1
|
+
import { useId, useMemo, useState } from "react"
|
|
2
2
|
import { Separator } from "@/components/ui/separator"
|
|
3
3
|
import { getFieldRenderer, getScope } from "./registry"
|
|
4
4
|
import type {
|
|
@@ -69,23 +69,54 @@ function PropertyGroup({
|
|
|
69
69
|
onChange?: (path: string, val: unknown) => void
|
|
70
70
|
isLast: boolean
|
|
71
71
|
}) {
|
|
72
|
+
// Collapsing needs a title to click. Open or closed is the group's own
|
|
73
|
+
// state; a host that wants it remembered across selections keys the panel.
|
|
74
|
+
const collapsible = Boolean(group.collapsible && group.title)
|
|
75
|
+
const [collapsed, setCollapsed] = useState(
|
|
76
|
+
collapsible && Boolean(group.defaultCollapsed),
|
|
77
|
+
)
|
|
78
|
+
const bodyId = useId()
|
|
79
|
+
const titleClass =
|
|
80
|
+
"text-[11px] font-semibold uppercase tracking-wider text-muted-foreground"
|
|
81
|
+
|
|
72
82
|
return (
|
|
73
83
|
<section className="flex flex-col gap-3 px-3 py-3">
|
|
74
84
|
{(group.title || group.description) && (
|
|
75
85
|
<header>
|
|
76
|
-
{group.title &&
|
|
77
|
-
|
|
78
|
-
{
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
86
|
+
{group.title &&
|
|
87
|
+
(collapsible ? (
|
|
88
|
+
<h3 className={titleClass}>
|
|
89
|
+
<button
|
|
90
|
+
type="button"
|
|
91
|
+
aria-expanded={!collapsed}
|
|
92
|
+
aria-controls={bodyId}
|
|
93
|
+
onClick={() => setCollapsed((c) => !c)}
|
|
94
|
+
className="-mx-1 flex w-[calc(100%+0.5rem)] items-center gap-1 px-1 text-left uppercase hover:text-foreground"
|
|
95
|
+
>
|
|
96
|
+
<svg
|
|
97
|
+
aria-hidden
|
|
98
|
+
viewBox="0 0 16 16"
|
|
99
|
+
className={
|
|
100
|
+
"size-3 shrink-0 transition-transform " +
|
|
101
|
+
(collapsed ? "-rotate-90" : "")
|
|
102
|
+
}
|
|
103
|
+
>
|
|
104
|
+
<path d="M4 6l4 4 4-4" fill="none" stroke="currentColor" strokeWidth="1.5" />
|
|
105
|
+
</svg>
|
|
106
|
+
{group.title}
|
|
107
|
+
</button>
|
|
108
|
+
</h3>
|
|
109
|
+
) : (
|
|
110
|
+
<h3 className={titleClass}>{group.title}</h3>
|
|
111
|
+
))}
|
|
112
|
+
{group.description && !(collapsible && collapsed) && (
|
|
82
113
|
<p className="mt-1 text-xs text-muted-foreground">
|
|
83
114
|
{group.description}
|
|
84
115
|
</p>
|
|
85
116
|
)}
|
|
86
117
|
</header>
|
|
87
118
|
)}
|
|
88
|
-
<div className="flex flex-col gap-3">
|
|
119
|
+
<div id={bodyId} hidden={collapsible && collapsed} className="flex flex-col gap-3">
|
|
89
120
|
{group.rows.map((row, idx) => {
|
|
90
121
|
const fields = Array.isArray(row) ? row : [row]
|
|
91
122
|
return (
|
|
@@ -142,11 +173,14 @@ function FieldSlot({
|
|
|
142
173
|
return false
|
|
143
174
|
})()
|
|
144
175
|
|
|
176
|
+
// Display-only kinds (separator, label) have no path: no value, no writes.
|
|
177
|
+
const path = "path" in field ? field.path : undefined
|
|
145
178
|
return renderer({
|
|
146
179
|
field,
|
|
147
|
-
value: values[
|
|
180
|
+
value: path === undefined ? undefined : values[path],
|
|
148
181
|
disabled,
|
|
149
|
-
onChange:
|
|
182
|
+
onChange:
|
|
183
|
+
onChange && path !== undefined ? (val) => onChange(path, val) : undefined,
|
|
150
184
|
ctx,
|
|
151
185
|
})
|
|
152
186
|
}
|
package/src/types.ts
CHANGED
|
@@ -9,6 +9,9 @@ export type BuiltinFieldKind =
|
|
|
9
9
|
| "color"
|
|
10
10
|
| "checkbox"
|
|
11
11
|
| "file"
|
|
12
|
+
| "vector"
|
|
13
|
+
| "separator"
|
|
14
|
+
| "label"
|
|
12
15
|
|
|
13
16
|
export interface BaseField {
|
|
14
17
|
kind: string
|
|
@@ -109,6 +112,28 @@ export interface FileFieldDef extends BaseField {
|
|
|
109
112
|
helperText?: string
|
|
110
113
|
}
|
|
111
114
|
|
|
115
|
+
// Display-only rows: they show something but hold no value, so they have no
|
|
116
|
+
// `path`. Houdini's Separator and Label parameter types.
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* A horizontal rule between rows, with an optional caption. Every kind keeps
|
|
120
|
+
* `label` so hosts can read `field.label` on any FieldDef without narrowing.
|
|
121
|
+
*/
|
|
122
|
+
export interface SeparatorFieldDef {
|
|
123
|
+
kind: "separator"
|
|
124
|
+
id: string
|
|
125
|
+
label?: string
|
|
126
|
+
disabledWhen?: DisabledWhen
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Static text: a hint, a note, a sub-heading. The text is `label`. */
|
|
130
|
+
export interface LabelFieldDef {
|
|
131
|
+
kind: "label"
|
|
132
|
+
id: string
|
|
133
|
+
label: string
|
|
134
|
+
disabledWhen?: DisabledWhen
|
|
135
|
+
}
|
|
136
|
+
|
|
112
137
|
export type BuiltinFieldDef =
|
|
113
138
|
| SelectFieldDef
|
|
114
139
|
| ColorFieldDef
|
|
@@ -119,6 +144,8 @@ export type BuiltinFieldDef =
|
|
|
119
144
|
| CheckboxFieldDef
|
|
120
145
|
| FileFieldDef
|
|
121
146
|
| VectorFieldDef
|
|
147
|
+
| SeparatorFieldDef
|
|
148
|
+
| LabelFieldDef
|
|
122
149
|
|
|
123
150
|
export type FieldDef = BuiltinFieldDef | CustomFieldDef
|
|
124
151
|
|