@ic-reactor/candid 3.0.13-beta.0 → 3.0.14-beta.1
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 +28 -0
- package/dist/visitor/arguments/helpers.d.ts +5 -5
- package/dist/visitor/arguments/helpers.d.ts.map +1 -1
- package/dist/visitor/arguments/helpers.js.map +1 -1
- package/dist/visitor/arguments/index.d.ts +12 -2
- package/dist/visitor/arguments/index.d.ts.map +1 -1
- package/dist/visitor/arguments/index.js +140 -25
- package/dist/visitor/arguments/index.js.map +1 -1
- package/dist/visitor/arguments/types.d.ts +76 -419
- package/dist/visitor/arguments/types.d.ts.map +1 -1
- package/dist/visitor/constants.d.ts.map +1 -1
- package/dist/visitor/constants.js +19 -17
- package/dist/visitor/constants.js.map +1 -1
- package/dist/visitor/returns/types.d.ts +3 -4
- package/dist/visitor/returns/types.d.ts.map +1 -1
- package/dist/visitor/types.d.ts +14 -0
- package/dist/visitor/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/visitor/arguments/helpers.ts +7 -7
- package/src/visitor/arguments/index.test.ts +104 -39
- package/src/visitor/arguments/index.ts +170 -42
- package/src/visitor/arguments/schema.test.ts +114 -4
- package/src/visitor/arguments/types.ts +113 -480
- package/src/visitor/constants.ts +24 -15
- package/src/visitor/returns/index.test.ts +1 -1
- package/src/visitor/returns/types.ts +4 -27
- package/src/visitor/types.ts +45 -0
|
@@ -1,44 +1,15 @@
|
|
|
1
1
|
import type { BaseActor, FunctionName, FunctionType } from "@ic-reactor/core"
|
|
2
2
|
import * as z from "zod"
|
|
3
|
+
import type { VisitorDataType, TextFormat, NumberFormat } from "../types"
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
// Field Type Union
|
|
6
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
7
|
-
|
|
8
|
-
export type ArgumentFieldType =
|
|
9
|
-
| "record"
|
|
10
|
-
| "variant"
|
|
11
|
-
| "tuple"
|
|
12
|
-
| "optional"
|
|
13
|
-
| "vector"
|
|
14
|
-
| "blob"
|
|
15
|
-
| "recursive"
|
|
16
|
-
| "principal"
|
|
17
|
-
| "number"
|
|
18
|
-
| "text"
|
|
19
|
-
| "boolean"
|
|
20
|
-
| "null"
|
|
21
|
-
| "unknown"
|
|
5
|
+
export type { VisitorDataType, TextFormat, NumberFormat }
|
|
22
6
|
|
|
23
7
|
// ════════════════════════════════════════════════════════════════════════════
|
|
24
|
-
// Component
|
|
8
|
+
// Component & UI Types
|
|
25
9
|
// ════════════════════════════════════════════════════════════════════════════
|
|
26
10
|
|
|
27
11
|
/**
|
|
28
12
|
* Suggested component type for rendering the field.
|
|
29
|
-
* This eliminates the need for switch statements in the frontend.
|
|
30
|
-
*
|
|
31
|
-
* @example
|
|
32
|
-
* ```tsx
|
|
33
|
-
* const componentMap = {
|
|
34
|
-
* 'text-input': TextField,
|
|
35
|
-
* 'number-input': NumberField,
|
|
36
|
-
* 'boolean-checkbox': BooleanField,
|
|
37
|
-
* // ...
|
|
38
|
-
* }
|
|
39
|
-
* const Component = componentMap[field.component]
|
|
40
|
-
* return <Component field={field} />
|
|
41
|
-
* ```
|
|
42
13
|
*/
|
|
43
14
|
export type FieldComponentType =
|
|
44
15
|
| "record-container"
|
|
@@ -55,13 +26,8 @@ export type FieldComponentType =
|
|
|
55
26
|
| "recursive-lazy"
|
|
56
27
|
| "unknown-fallback"
|
|
57
28
|
|
|
58
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
59
|
-
// Render Hints for UI Rendering Strategy
|
|
60
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
61
|
-
|
|
62
29
|
/**
|
|
63
30
|
* Input type hints for HTML input elements.
|
|
64
|
-
* Used by primitive fields to suggest the appropriate input type.
|
|
65
31
|
*/
|
|
66
32
|
export type InputType =
|
|
67
33
|
| "text"
|
|
@@ -73,66 +39,28 @@ export type InputType =
|
|
|
73
39
|
|
|
74
40
|
/**
|
|
75
41
|
* Rendering hints for the UI.
|
|
76
|
-
* Eliminates the need for frontend to maintain COMPLEX_TYPES arrays.
|
|
77
|
-
*
|
|
78
|
-
* @example
|
|
79
|
-
* ```tsx
|
|
80
|
-
* // Frontend no longer needs:
|
|
81
|
-
* // const COMPLEX_TYPES = ["record", "tuple", "variant", "vector", "optional"]
|
|
82
|
-
*
|
|
83
|
-
* // Instead use:
|
|
84
|
-
* if (field.renderHint.isCompound) {
|
|
85
|
-
* return <CompoundFieldRenderer field={field} />
|
|
86
|
-
* }
|
|
87
|
-
* return <PrimitiveInput field={field} />
|
|
88
|
-
* ```
|
|
89
42
|
*/
|
|
90
43
|
export interface RenderHint {
|
|
91
|
-
/** Whether this field has its own container/card styling (compound types) */
|
|
92
44
|
isCompound: boolean
|
|
93
|
-
/** Whether this is a leaf input (primitive types) */
|
|
94
45
|
isPrimitive: boolean
|
|
95
|
-
/** Suggested input type for HTML input elements */
|
|
96
46
|
inputType?: InputType
|
|
97
|
-
/** Description or help text for the field (derived from Candid) */
|
|
98
47
|
description?: string
|
|
99
48
|
}
|
|
100
49
|
|
|
101
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
102
|
-
// Primitive Input Props
|
|
103
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
104
|
-
|
|
105
50
|
/**
|
|
106
51
|
* Pre-computed HTML input props for primitive fields.
|
|
107
|
-
* Can be spread directly onto an input element.
|
|
108
|
-
*
|
|
109
|
-
* @example
|
|
110
|
-
* ```tsx
|
|
111
|
-
* <input {...field.inputProps} value={value} onChange={handleChange} />
|
|
112
|
-
* ```
|
|
113
52
|
*/
|
|
114
53
|
export interface PrimitiveInputProps {
|
|
115
|
-
|
|
116
|
-
type?: "text" | "number" | "checkbox"
|
|
117
|
-
/** Placeholder text */
|
|
54
|
+
type?: "text" | "number" | "checkbox" | "email" | "url" | "tel"
|
|
118
55
|
placeholder?: string
|
|
119
|
-
/** Minimum value for number inputs */
|
|
120
56
|
min?: string | number
|
|
121
|
-
/** Maximum value for number inputs */
|
|
122
57
|
max?: string | number
|
|
123
|
-
/** Step value for number inputs */
|
|
124
58
|
step?: string | number
|
|
125
|
-
/** Pattern for text inputs */
|
|
126
59
|
pattern?: string
|
|
127
|
-
|
|
128
|
-
inputMode?: "text" | "numeric" | "decimal"
|
|
129
|
-
/** Autocomplete hint */
|
|
60
|
+
inputMode?: "text" | "numeric" | "decimal" | "email" | "tel" | "url"
|
|
130
61
|
autoComplete?: string
|
|
131
|
-
/** Whether to check spelling */
|
|
132
62
|
spellCheck?: boolean
|
|
133
|
-
/** Minimum length for text inputs */
|
|
134
63
|
minLength?: number
|
|
135
|
-
/** Maximum length for text inputs */
|
|
136
64
|
maxLength?: number
|
|
137
65
|
}
|
|
138
66
|
|
|
@@ -140,481 +68,215 @@ export interface PrimitiveInputProps {
|
|
|
140
68
|
// Base Field Interface
|
|
141
69
|
// ════════════════════════════════════════════════════════════════════════════
|
|
142
70
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
type: ArgumentFieldType
|
|
146
|
-
/** Raw label from Candid: "__arg0", "_0_" */
|
|
71
|
+
interface FieldBase<T extends VisitorDataType = VisitorDataType> {
|
|
72
|
+
type: T
|
|
147
73
|
label: string
|
|
148
|
-
/**
|
|
149
|
-
* Pre-formatted display label for UI rendering.
|
|
150
|
-
* Transforms raw labels into human-readable format.
|
|
151
|
-
*
|
|
152
|
-
* @example
|
|
153
|
-
* "__arg0" => "Arg 0"
|
|
154
|
-
* "_0_" => "Item 0"
|
|
155
|
-
* "created_at_time" => "Created At Time"
|
|
156
|
-
*/
|
|
157
74
|
displayLabel: string
|
|
158
|
-
/**
|
|
159
|
-
* Form field name path for binding.
|
|
160
|
-
* Uses bracket notation for array indices: `[0]`, `args[0].owner`, `tags[1]`
|
|
161
|
-
* Compatible with TanStack Form's `form.Field` name prop.
|
|
162
|
-
*
|
|
163
|
-
* @example
|
|
164
|
-
* ```tsx
|
|
165
|
-
* <form.Field name={field.name}>
|
|
166
|
-
* {(fieldApi) => <input {...} />}
|
|
167
|
-
* </form.Field>
|
|
168
|
-
* ```
|
|
169
|
-
*/
|
|
170
75
|
name: string
|
|
171
|
-
/**
|
|
172
|
-
* Suggested component type for rendering this field.
|
|
173
|
-
* Eliminates the need for switch statements in the frontend.
|
|
174
|
-
*/
|
|
175
76
|
component: FieldComponentType
|
|
176
|
-
/**
|
|
177
|
-
* Rendering hints for UI strategy.
|
|
178
|
-
* Use this to determine if the field needs a container or is a simple input.
|
|
179
|
-
*/
|
|
180
77
|
renderHint: RenderHint
|
|
181
|
-
/** Zod schema for field validation */
|
|
182
78
|
schema: z.ZodTypeAny
|
|
183
|
-
|
|
184
|
-
defaultValue:
|
|
185
|
-
/** Original Candid type name for reference */
|
|
186
|
-
candidType?: string
|
|
79
|
+
candidType: string
|
|
80
|
+
defaultValue: unknown
|
|
187
81
|
}
|
|
188
82
|
|
|
189
83
|
// ════════════════════════════════════════════════════════════════════════════
|
|
190
|
-
//
|
|
84
|
+
// Field Extras
|
|
191
85
|
// ════════════════════════════════════════════════════════════════════════════
|
|
192
86
|
|
|
193
|
-
export interface
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
87
|
+
export interface BlobLimits {
|
|
88
|
+
maxHexBytes: number
|
|
89
|
+
maxFileBytes: number
|
|
90
|
+
maxHexDisplayLength: number
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface BlobValidationResult {
|
|
94
|
+
valid: boolean
|
|
95
|
+
error?: string
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
interface RecordExtras {
|
|
99
|
+
fields: FieldNode[]
|
|
100
|
+
defaultValue: Record<string, unknown>
|
|
199
101
|
}
|
|
200
102
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
/** All variant option fields */
|
|
204
|
-
fields: Field[]
|
|
205
|
-
/** List of variant option names */
|
|
206
|
-
options: string[]
|
|
207
|
-
/** Default selected option */
|
|
103
|
+
interface VariantExtras {
|
|
104
|
+
fields: FieldNode[]
|
|
208
105
|
defaultOption: string
|
|
209
|
-
|
|
210
|
-
optionMap: Map<string, Field>
|
|
211
|
-
/**
|
|
212
|
-
* Get default value for a specific option.
|
|
213
|
-
* Useful when switching between variant options.
|
|
214
|
-
*
|
|
215
|
-
* @example
|
|
216
|
-
* ```tsx
|
|
217
|
-
* const handleOptionChange = (newOption: string) => {
|
|
218
|
-
* const newDefault = field.getOptionDefault(newOption)
|
|
219
|
-
* fieldApi.handleChange(newDefault)
|
|
220
|
-
* }
|
|
221
|
-
* ```
|
|
222
|
-
*/
|
|
106
|
+
defaultValue: Record<string, unknown>
|
|
223
107
|
getOptionDefault: (option: string) => Record<string, unknown>
|
|
224
|
-
|
|
225
|
-
* Get the field for a specific option.
|
|
226
|
-
*
|
|
227
|
-
* @example
|
|
228
|
-
* ```tsx
|
|
229
|
-
* const transferField = field.getField("Transfer")
|
|
230
|
-
* ```
|
|
231
|
-
*/
|
|
232
|
-
getField: (option: string) => Field
|
|
233
|
-
/**
|
|
234
|
-
* Get the currently selected option from a value.
|
|
235
|
-
* Returns the first valid key found, or the default option.
|
|
236
|
-
*
|
|
237
|
-
* @example
|
|
238
|
-
* ```tsx
|
|
239
|
-
* const selectedOption = field.getSelectedOption(currentValue)
|
|
240
|
-
* // { Transfer: {...} } => "Transfer"
|
|
241
|
-
* ```
|
|
242
|
-
*/
|
|
108
|
+
getField: (option: string) => FieldNode
|
|
243
109
|
getSelectedOption: (value: Record<string, unknown>) => string
|
|
244
|
-
|
|
245
|
-
* Get the selected field from a value.
|
|
246
|
-
* Combines getSelectedOption and getField for convenience.
|
|
247
|
-
*
|
|
248
|
-
* @example
|
|
249
|
-
* ```tsx
|
|
250
|
-
* // Current (verbose):
|
|
251
|
-
* const validKeys = Object.keys(currentValue).filter(k => field.options.includes(k))
|
|
252
|
-
* const selected = validKeys[0] ?? field.options[0]
|
|
253
|
-
* const selectedIndex = Math.max(0, field.options.indexOf(selected))
|
|
254
|
-
* const selectedField = field.fields[selectedIndex]
|
|
255
|
-
*
|
|
256
|
-
* // Proposed (simple):
|
|
257
|
-
* const selectedField = field.getSelectedField(currentValue)
|
|
258
|
-
* ```
|
|
259
|
-
*/
|
|
260
|
-
getSelectedField: (value: Record<string, unknown>) => Field
|
|
110
|
+
getSelectedField: (value: Record<string, unknown>) => FieldNode
|
|
261
111
|
}
|
|
262
112
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
fields: Field[]
|
|
113
|
+
interface TupleExtras {
|
|
114
|
+
fields: FieldNode[]
|
|
115
|
+
defaultValue: unknown[]
|
|
267
116
|
}
|
|
268
117
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
innerField: Field
|
|
273
|
-
/**
|
|
274
|
-
* Get default value when enabling the optional.
|
|
275
|
-
* Returns the inner field's default value.
|
|
276
|
-
*
|
|
277
|
-
* @example
|
|
278
|
-
* ```tsx
|
|
279
|
-
* const handleToggle = (enabled: boolean) => {
|
|
280
|
-
* if (enabled) {
|
|
281
|
-
* fieldApi.handleChange(field.getInnerDefault())
|
|
282
|
-
* } else {
|
|
283
|
-
* fieldApi.handleChange(null)
|
|
284
|
-
* }
|
|
285
|
-
* }
|
|
286
|
-
* ```
|
|
287
|
-
*/
|
|
118
|
+
interface OptionalExtras {
|
|
119
|
+
innerField: FieldNode
|
|
120
|
+
defaultValue: null
|
|
288
121
|
getInnerDefault: () => unknown
|
|
289
|
-
/**
|
|
290
|
-
* Check if a value represents an enabled optional.
|
|
291
|
-
* Returns true if the value is not null or undefined.
|
|
292
|
-
*
|
|
293
|
-
* @example
|
|
294
|
-
* ```tsx
|
|
295
|
-
* // Current:
|
|
296
|
-
* const enabled = fieldApi.state.value !== null && typeof fieldApi.state.value !== "undefined"
|
|
297
|
-
*
|
|
298
|
-
* // Proposed:
|
|
299
|
-
* const enabled = field.isEnabled(fieldApi.state.value)
|
|
300
|
-
* ```
|
|
301
|
-
*/
|
|
302
122
|
isEnabled: (value: unknown) => boolean
|
|
303
123
|
}
|
|
304
124
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
itemField: Field
|
|
309
|
-
/**
|
|
310
|
-
* Get a new item with default values.
|
|
311
|
-
* Used when adding items to the vector.
|
|
312
|
-
*
|
|
313
|
-
* @example
|
|
314
|
-
* ```tsx
|
|
315
|
-
* <button onClick={() => fieldApi.pushValue(field.getItemDefault())}>
|
|
316
|
-
* Add Item
|
|
317
|
-
* </button>
|
|
318
|
-
* ```
|
|
319
|
-
*/
|
|
125
|
+
interface VectorExtras {
|
|
126
|
+
itemField: FieldNode
|
|
127
|
+
defaultValue: unknown[]
|
|
320
128
|
getItemDefault: () => unknown
|
|
321
|
-
|
|
322
|
-
* Create a properly configured item field for a specific index.
|
|
323
|
-
* Handles name path and label generation.
|
|
324
|
-
*
|
|
325
|
-
* @example
|
|
326
|
-
* ```tsx
|
|
327
|
-
* // Current:
|
|
328
|
-
* renderField({
|
|
329
|
-
* ...field.itemField,
|
|
330
|
-
* label: itemLabel,
|
|
331
|
-
* name: itemFieldName
|
|
332
|
-
* })
|
|
333
|
-
*
|
|
334
|
-
* // Proposed:
|
|
335
|
-
* const itemField = field.createItemField(index, { label: itemLabel })
|
|
336
|
-
* renderField(itemField)
|
|
337
|
-
* ```
|
|
338
|
-
*/
|
|
339
|
-
createItemField: (index: number, overrides?: { label?: string }) => Field
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
/**
|
|
343
|
-
* Blob field size limits.
|
|
344
|
-
*/
|
|
345
|
-
export interface BlobLimits {
|
|
346
|
-
/** Maximum bytes when entering as hex (e.g., 512 bytes) */
|
|
347
|
-
maxHexBytes: number
|
|
348
|
-
/** Maximum file size in bytes (e.g., 2MB ICP limit) */
|
|
349
|
-
maxFileBytes: number
|
|
350
|
-
/** Maximum hex display length before truncation */
|
|
351
|
-
maxHexDisplayLength: number
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
/**
|
|
355
|
-
* Validation result for blob input.
|
|
356
|
-
*/
|
|
357
|
-
export interface BlobValidationResult {
|
|
358
|
-
/** Whether the input is valid */
|
|
359
|
-
valid: boolean
|
|
360
|
-
/** Error message if invalid */
|
|
361
|
-
error?: string
|
|
129
|
+
createItemField: (index: number, overrides?: { label?: string }) => FieldNode
|
|
362
130
|
}
|
|
363
131
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
/** Item field for individual bytes (nat8) */
|
|
367
|
-
itemField: Field
|
|
368
|
-
/** Accepted input formats */
|
|
132
|
+
interface BlobExtras {
|
|
133
|
+
itemField: FieldNode
|
|
369
134
|
acceptedFormats: ("hex" | "base64" | "file")[]
|
|
370
|
-
/** Size limits for blob input */
|
|
371
135
|
limits: BlobLimits
|
|
372
|
-
/**
|
|
373
|
-
* Normalize hex input (remove 0x prefix, lowercase, etc.)
|
|
374
|
-
*
|
|
375
|
-
* @example
|
|
376
|
-
* ```tsx
|
|
377
|
-
* const normalized = field.normalizeHex("0xDEADBEEF")
|
|
378
|
-
* // => "deadbeef"
|
|
379
|
-
* ```
|
|
380
|
-
*/
|
|
381
136
|
normalizeHex: (input: string) => string
|
|
382
|
-
/**
|
|
383
|
-
* Validate blob input value.
|
|
384
|
-
*
|
|
385
|
-
* @example
|
|
386
|
-
* ```tsx
|
|
387
|
-
* const result = field.validateInput(value)
|
|
388
|
-
* if (!result.valid) {
|
|
389
|
-
* setError(result.error)
|
|
390
|
-
* }
|
|
391
|
-
* ```
|
|
392
|
-
*/
|
|
393
137
|
validateInput: (value: string | Uint8Array) => BlobValidationResult
|
|
138
|
+
defaultValue: string
|
|
394
139
|
}
|
|
395
140
|
|
|
396
|
-
|
|
397
|
-
type: "recursive"
|
|
398
|
-
/** Type name for the recursive type */
|
|
141
|
+
interface RecursiveExtras {
|
|
399
142
|
typeName: string
|
|
400
|
-
|
|
401
|
-
extract: () => Field
|
|
402
|
-
/**
|
|
403
|
-
* Get default value for the recursive type.
|
|
404
|
-
* Evaluates the inner type on demand.
|
|
405
|
-
*/
|
|
143
|
+
extract: () => FieldNode
|
|
406
144
|
getInnerDefault: () => unknown
|
|
145
|
+
defaultValue: undefined
|
|
407
146
|
}
|
|
408
147
|
|
|
409
|
-
|
|
410
|
-
// Primitive Types
|
|
411
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
412
|
-
|
|
413
|
-
export interface PrincipalField extends FieldBase<string> {
|
|
414
|
-
type: "principal"
|
|
148
|
+
interface PrincipalExtras {
|
|
415
149
|
maxLength: number
|
|
416
150
|
minLength: number
|
|
417
|
-
|
|
418
|
-
* Pre-computed HTML input props for direct spreading.
|
|
419
|
-
* @example
|
|
420
|
-
* ```tsx
|
|
421
|
-
* <input {...field.inputProps} value={value} onChange={handleChange} />
|
|
422
|
-
* ```
|
|
423
|
-
*/
|
|
151
|
+
format: TextFormat
|
|
424
152
|
inputProps: PrimitiveInputProps
|
|
153
|
+
defaultValue: string
|
|
425
154
|
}
|
|
426
155
|
|
|
427
|
-
|
|
428
|
-
type: "number"
|
|
429
|
-
/**
|
|
430
|
-
* Original Candid type: nat, int, nat8, nat16, nat32, nat64, int8, int16, int32, int64, float32, float64
|
|
431
|
-
*/
|
|
432
|
-
candidType: string
|
|
433
|
-
/** Whether this is an unsigned type */
|
|
156
|
+
interface NumberExtras {
|
|
434
157
|
unsigned: boolean
|
|
435
|
-
/** Whether this is a floating point type */
|
|
436
158
|
isFloat: boolean
|
|
437
|
-
/** Bit width if applicable (8, 16, 32, 64, or undefined for unbounded) */
|
|
438
159
|
bits?: number
|
|
439
|
-
/** Minimum value constraint (for bounded types) */
|
|
440
160
|
min?: string
|
|
441
|
-
/** Maximum value constraint (for bounded types) */
|
|
442
161
|
max?: string
|
|
443
|
-
|
|
444
|
-
* Pre-computed HTML input props for direct spreading.
|
|
445
|
-
* @example
|
|
446
|
-
* ```tsx
|
|
447
|
-
* <input {...field.inputProps} value={value} onChange={handleChange} />
|
|
448
|
-
* ```
|
|
449
|
-
*/
|
|
162
|
+
format: NumberFormat
|
|
450
163
|
inputProps: PrimitiveInputProps
|
|
164
|
+
defaultValue: string
|
|
451
165
|
}
|
|
452
166
|
|
|
453
|
-
|
|
454
|
-
type: "text"
|
|
455
|
-
/** Minimum length constraint */
|
|
167
|
+
interface TextExtras {
|
|
456
168
|
minLength?: number
|
|
457
|
-
/** Maximum length constraint */
|
|
458
169
|
maxLength?: number
|
|
459
|
-
/** Whether to render as multiline textarea */
|
|
460
170
|
multiline?: boolean
|
|
461
|
-
|
|
462
|
-
* Pre-computed HTML input props for direct spreading.
|
|
463
|
-
* @example
|
|
464
|
-
* ```tsx
|
|
465
|
-
* <input {...field.inputProps} value={value} onChange={handleChange} />
|
|
466
|
-
* ```
|
|
467
|
-
*/
|
|
171
|
+
format: TextFormat
|
|
468
172
|
inputProps: PrimitiveInputProps
|
|
173
|
+
defaultValue: string
|
|
469
174
|
}
|
|
470
175
|
|
|
471
|
-
|
|
472
|
-
type: "boolean"
|
|
473
|
-
/**
|
|
474
|
-
* Pre-computed HTML input props for direct spreading.
|
|
475
|
-
* @example
|
|
476
|
-
* ```tsx
|
|
477
|
-
* <input {...field.inputProps} checked={value} onChange={handleChange} />
|
|
478
|
-
* ```
|
|
479
|
-
*/
|
|
176
|
+
interface BooleanExtras {
|
|
480
177
|
inputProps: PrimitiveInputProps
|
|
178
|
+
defaultValue: boolean
|
|
481
179
|
}
|
|
482
180
|
|
|
483
|
-
|
|
484
|
-
|
|
181
|
+
interface NullExtras {
|
|
182
|
+
defaultValue: null
|
|
485
183
|
}
|
|
486
184
|
|
|
487
|
-
|
|
488
|
-
|
|
185
|
+
interface UnknownExtras {
|
|
186
|
+
defaultValue: undefined
|
|
489
187
|
}
|
|
490
188
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
189
|
+
type FieldExtras<T extends VisitorDataType> = T extends "record"
|
|
190
|
+
? RecordExtras
|
|
191
|
+
: T extends "variant"
|
|
192
|
+
? VariantExtras
|
|
193
|
+
: T extends "tuple"
|
|
194
|
+
? TupleExtras
|
|
195
|
+
: T extends "optional"
|
|
196
|
+
? OptionalExtras
|
|
197
|
+
: T extends "vector"
|
|
198
|
+
? VectorExtras
|
|
199
|
+
: T extends "blob"
|
|
200
|
+
? BlobExtras
|
|
201
|
+
: T extends "recursive"
|
|
202
|
+
? RecursiveExtras
|
|
203
|
+
: T extends "principal"
|
|
204
|
+
? PrincipalExtras
|
|
205
|
+
: T extends "number"
|
|
206
|
+
? NumberExtras
|
|
207
|
+
: T extends "text"
|
|
208
|
+
? TextExtras
|
|
209
|
+
: T extends "boolean"
|
|
210
|
+
? BooleanExtras
|
|
211
|
+
: T extends "null"
|
|
212
|
+
? NullExtras
|
|
213
|
+
: T extends "unknown"
|
|
214
|
+
? UnknownExtras
|
|
215
|
+
: {}
|
|
494
216
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
217
|
+
/**
|
|
218
|
+
* A unified field node that contains all metadata needed for rendering.
|
|
219
|
+
*/
|
|
220
|
+
export type FieldNode<T extends VisitorDataType = VisitorDataType> =
|
|
221
|
+
T extends any ? FieldBase<T> & FieldExtras<T> : never
|
|
222
|
+
|
|
223
|
+
export type RecordField = FieldNode<"record">
|
|
224
|
+
export type VariantField = FieldNode<"variant">
|
|
225
|
+
export type TupleField = FieldNode<"tuple">
|
|
226
|
+
export type OptionalField = FieldNode<"optional">
|
|
227
|
+
export type VectorField = FieldNode<"vector">
|
|
228
|
+
export type BlobField = FieldNode<"blob">
|
|
229
|
+
export type RecursiveField = FieldNode<"recursive">
|
|
230
|
+
export type PrincipalField = FieldNode<"principal">
|
|
231
|
+
export type NumberField = FieldNode<"number">
|
|
232
|
+
export type TextField = FieldNode<"text">
|
|
233
|
+
export type BooleanField = FieldNode<"boolean">
|
|
234
|
+
export type NullField = FieldNode<"null">
|
|
235
|
+
export type UnknownField = FieldNode<"unknown">
|
|
509
236
|
|
|
510
237
|
// ════════════════════════════════════════════════════════════════════════════
|
|
511
|
-
// Form Metadata
|
|
238
|
+
// Form Metadata
|
|
512
239
|
// ════════════════════════════════════════════════════════════════════════════
|
|
513
240
|
|
|
514
|
-
/**
|
|
515
|
-
* Form metadata for a Candid method.
|
|
516
|
-
* Contains all information needed to create a TanStack Form instance.
|
|
517
|
-
*
|
|
518
|
-
* @example
|
|
519
|
-
* ```tsx
|
|
520
|
-
* import { useForm } from '@tanstack/react-form'
|
|
521
|
-
*
|
|
522
|
-
* function MethodForm({ meta }: { meta: FormMeta }) {
|
|
523
|
-
* const form = useForm({
|
|
524
|
-
* ...meta.formOptions,
|
|
525
|
-
* onSubmit: async ({ value }) => {
|
|
526
|
-
* await actor[meta.functionName](...value)
|
|
527
|
-
* }
|
|
528
|
-
* })
|
|
529
|
-
*
|
|
530
|
-
* return (
|
|
531
|
-
* <form onSubmit={(e) => { e.preventDefault(); form.handleSubmit() }}>
|
|
532
|
-
* {meta.fields.map(field => (
|
|
533
|
-
* <form.Field key={field.name} name={field.name}>
|
|
534
|
-
* {(fieldApi) => <DynamicInput field={field} fieldApi={fieldApi} />}
|
|
535
|
-
* </form.Field>
|
|
536
|
-
* ))}
|
|
537
|
-
* <button type="submit">Submit</button>
|
|
538
|
-
* </form>
|
|
539
|
-
* )
|
|
540
|
-
* }
|
|
541
|
-
* ```
|
|
542
|
-
*/
|
|
543
241
|
export interface ArgumentsMeta<
|
|
544
242
|
A = BaseActor,
|
|
545
243
|
Name extends FunctionName<A> = FunctionName<A>,
|
|
546
244
|
> {
|
|
547
|
-
/** Whether this is a "query" or "update" function */
|
|
548
245
|
functionType: FunctionType
|
|
549
|
-
/** The function name */
|
|
550
246
|
functionName: Name
|
|
551
|
-
|
|
552
|
-
fields: Field[]
|
|
553
|
-
/** Default values for all arguments (as a tuple) */
|
|
247
|
+
fields: FieldNode[]
|
|
554
248
|
defaultValues: unknown[]
|
|
555
|
-
/** Combined Zod schema for all arguments */
|
|
556
249
|
schema: z.ZodTuple<[z.ZodTypeAny, ...z.ZodTypeAny[]]>
|
|
557
|
-
/** Number of arguments */
|
|
558
250
|
argCount: number
|
|
559
|
-
/** Whether the function takes no arguments */
|
|
560
251
|
isNoArgs: boolean
|
|
561
252
|
}
|
|
562
253
|
|
|
563
|
-
/**
|
|
564
|
-
* Options that can be spread into useForm().
|
|
565
|
-
* Pre-configured with defaultValues and validators.
|
|
566
|
-
*/
|
|
567
254
|
export interface FormOptions {
|
|
568
|
-
/** Initial form values */
|
|
569
255
|
defaultValues: unknown[]
|
|
570
|
-
/** Validators using the Zod schema */
|
|
571
256
|
validators: {
|
|
572
257
|
onChange: z.ZodTypeAny
|
|
573
258
|
onBlur: z.ZodTypeAny
|
|
574
259
|
}
|
|
575
260
|
}
|
|
576
261
|
|
|
577
|
-
/**
|
|
578
|
-
* Service-level form metadata.
|
|
579
|
-
* Maps each method name to its FormMeta.
|
|
580
|
-
*/
|
|
581
262
|
export type ArgumentsServiceMeta<A = BaseActor> = {
|
|
582
263
|
[K in FunctionName<A>]: ArgumentsMeta<A, K>
|
|
583
264
|
}
|
|
584
265
|
|
|
585
266
|
// ════════════════════════════════════════════════════════════════════════════
|
|
586
|
-
// Type Utilities
|
|
267
|
+
// Type Utilities
|
|
587
268
|
// ════════════════════════════════════════════════════════════════════════════
|
|
588
269
|
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
Field,
|
|
270
|
+
export type FieldByType<T extends VisitorDataType> = Extract<
|
|
271
|
+
FieldNode,
|
|
592
272
|
{ type: T }
|
|
593
273
|
>
|
|
594
274
|
|
|
595
|
-
|
|
596
|
-
* Props type helper for field components.
|
|
597
|
-
* Use this to type your field components for better DX.
|
|
598
|
-
*
|
|
599
|
-
* @example
|
|
600
|
-
* ```tsx
|
|
601
|
-
* const VariantField: React.FC<FieldProps<'variant'>> = ({ field, renderField }) => {
|
|
602
|
-
* // field is properly typed as VariantField
|
|
603
|
-
* return (
|
|
604
|
-
* <div>
|
|
605
|
-
* <select>{field.options.map(opt => ...)}</select>
|
|
606
|
-
* {renderField?.(field.getSelectedField(currentValue))}
|
|
607
|
-
* </div>
|
|
608
|
-
* )
|
|
609
|
-
* }
|
|
610
|
-
* ```
|
|
611
|
-
*/
|
|
612
|
-
export type FieldProps<T extends ArgumentFieldType> = {
|
|
275
|
+
export type FieldProps<T extends VisitorDataType> = {
|
|
613
276
|
field: FieldByType<T>
|
|
614
|
-
renderField?: (child:
|
|
277
|
+
renderField?: (child: FieldNode) => React.ReactNode
|
|
615
278
|
}
|
|
616
279
|
|
|
617
|
-
/** Compound field types that contain other fields */
|
|
618
280
|
export type CompoundField =
|
|
619
281
|
| RecordField
|
|
620
282
|
| VariantField
|
|
@@ -623,38 +285,9 @@ export type CompoundField =
|
|
|
623
285
|
| VectorField
|
|
624
286
|
| RecursiveField
|
|
625
287
|
|
|
626
|
-
/** Primitive field types */
|
|
627
288
|
export type PrimitiveField =
|
|
628
289
|
| PrincipalField
|
|
629
290
|
| NumberField
|
|
630
291
|
| TextField
|
|
631
292
|
| BooleanField
|
|
632
293
|
| NullField
|
|
633
|
-
|
|
634
|
-
/**
|
|
635
|
-
* A complete mapping of component types to React components.
|
|
636
|
-
* Use this type when defining your component map.
|
|
637
|
-
*
|
|
638
|
-
* @example
|
|
639
|
-
* ```tsx
|
|
640
|
-
* const componentMap: ComponentMap<typeof MyTextInput, typeof MyNumberInput, ...> = {
|
|
641
|
-
* 'text-input': MyTextInput,
|
|
642
|
-
* 'number-input': MyNumberInput,
|
|
643
|
-
* // ...
|
|
644
|
-
* }
|
|
645
|
-
* ```
|
|
646
|
-
*/
|
|
647
|
-
export type ComponentMap<
|
|
648
|
-
TComponents extends Record<FieldComponentType, unknown>,
|
|
649
|
-
> = {
|
|
650
|
-
[K in FieldComponentType]: TComponents[K]
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
/**
|
|
654
|
-
* Get the component type for a given field component type.
|
|
655
|
-
* Useful for typing dynamic component lookups.
|
|
656
|
-
*/
|
|
657
|
-
export type GetComponentType<
|
|
658
|
-
TMap extends Partial<Record<FieldComponentType, unknown>>,
|
|
659
|
-
TKey extends FieldComponentType,
|
|
660
|
-
> = TKey extends keyof TMap ? TMap[TKey] : never
|