@theredhead/lucid-forms 0.1.0
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.
|
@@ -0,0 +1,1138 @@
|
|
|
1
|
+
import * as _angular_core from '@angular/core';
|
|
2
|
+
import { WritableSignal, Signal, Type, InjectionToken, EnvironmentProviders } from '@angular/core';
|
|
3
|
+
import { TextAdapter, SelectOption } from '@theredhead/lucid-kit';
|
|
4
|
+
import * as i1 from '@theredhead/lucid-foundation';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Operators available for field/group conditions.
|
|
8
|
+
*
|
|
9
|
+
* These compare the **live** value of a referenced field against
|
|
10
|
+
* a target value to determine visibility or enabled state.
|
|
11
|
+
*/
|
|
12
|
+
type ConditionOperator = "equals" | "notEquals" | "contains" | "notContains" | "empty" | "notEmpty" | "greaterThan" | "lessThan" | "greaterThanOrEqual" | "lessThanOrEqual" | "in" | "notIn";
|
|
13
|
+
/**
|
|
14
|
+
* A single condition that controls whether a field or group is
|
|
15
|
+
* visible / enabled, based on the live value of another field.
|
|
16
|
+
*
|
|
17
|
+
* Conditions are JSON-serializable so a form schema can be stored
|
|
18
|
+
* as pure JSON and restored later.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```json
|
|
22
|
+
* { "field": "country", "operator": "equals", "value": "NL" }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
interface FieldCondition {
|
|
26
|
+
/** ID of the field whose value is evaluated. */
|
|
27
|
+
readonly field: string;
|
|
28
|
+
/** Comparison operator. */
|
|
29
|
+
readonly operator: ConditionOperator;
|
|
30
|
+
/**
|
|
31
|
+
* Target value to compare against. Omitted for unary operators
|
|
32
|
+
* like `"empty"` and `"notEmpty"`.
|
|
33
|
+
*/
|
|
34
|
+
readonly value?: unknown;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Logical combination of multiple conditions.
|
|
38
|
+
*
|
|
39
|
+
* When `mode` is `"every"` (default), **all** conditions must pass.
|
|
40
|
+
* When `mode` is `"some"`, **at least one** must pass.
|
|
41
|
+
*/
|
|
42
|
+
interface ConditionGroup {
|
|
43
|
+
/** How to combine the conditions. Defaults to `"every"`. */
|
|
44
|
+
readonly mode?: "every" | "some";
|
|
45
|
+
/** The individual conditions to evaluate. */
|
|
46
|
+
readonly conditions: readonly FieldCondition[];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Either a single condition or a group of conditions.
|
|
50
|
+
* Used for both visibility and enabled state.
|
|
51
|
+
*/
|
|
52
|
+
type Condition = FieldCondition | ConditionGroup;
|
|
53
|
+
/**
|
|
54
|
+
* Check whether a `Condition` is a `ConditionGroup` (has nested
|
|
55
|
+
* `conditions` array) rather than a single `FieldCondition`.
|
|
56
|
+
*/
|
|
57
|
+
declare function isConditionGroup(c: Condition): c is ConditionGroup;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Built-in validation rule identifiers.
|
|
61
|
+
*
|
|
62
|
+
* The form engine ships validators for each of these. Custom
|
|
63
|
+
* validators can extend this set via the `"custom"` type.
|
|
64
|
+
*/
|
|
65
|
+
type ValidationRuleType = "required" | "minLength" | "maxLength" | "min" | "max" | "pattern" | "email" | "custom";
|
|
66
|
+
/**
|
|
67
|
+
* A single validation rule, JSON-serializable.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```json
|
|
71
|
+
* { "type": "required", "message": "This field is required." }
|
|
72
|
+
* { "type": "minLength", "params": { "min": 3 }, "message": "At least 3 characters." }
|
|
73
|
+
* { "type": "pattern", "params": { "pattern": "^[A-Z]" }, "message": "Must start with uppercase." }
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
interface ValidationRule {
|
|
77
|
+
/** The validator to apply. */
|
|
78
|
+
readonly type: ValidationRuleType;
|
|
79
|
+
/**
|
|
80
|
+
* Additional parameters for the validator. The shape depends on
|
|
81
|
+
* the `type`:
|
|
82
|
+
*
|
|
83
|
+
* | Type | Params |
|
|
84
|
+
* |-------------|-------------------------------|
|
|
85
|
+
* | required | — |
|
|
86
|
+
* | minLength | `{ min: number }` |
|
|
87
|
+
* | maxLength | `{ max: number }` |
|
|
88
|
+
* | min | `{ min: number }` |
|
|
89
|
+
* | max | `{ max: number }` |
|
|
90
|
+
* | pattern | `{ pattern: string }` |
|
|
91
|
+
* | email | — |
|
|
92
|
+
* | custom | `{ validatorId: string, … }` |
|
|
93
|
+
*/
|
|
94
|
+
readonly params?: Readonly<Record<string, unknown>>;
|
|
95
|
+
/** Human-readable error message shown when the rule fails. */
|
|
96
|
+
readonly message?: string;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* A single validation error produced by the validation engine.
|
|
100
|
+
*/
|
|
101
|
+
interface ValidationError {
|
|
102
|
+
/** The rule type that failed. */
|
|
103
|
+
readonly type: string;
|
|
104
|
+
/** Human-readable error message. */
|
|
105
|
+
readonly message: string;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Complete validation result for a single field.
|
|
109
|
+
*/
|
|
110
|
+
interface ValidationResult {
|
|
111
|
+
/** Whether all rules passed. */
|
|
112
|
+
readonly valid: boolean;
|
|
113
|
+
/** Errors for every failed rule (empty when valid). */
|
|
114
|
+
readonly errors: readonly ValidationError[];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* An option for select / radio / autocomplete fields.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```json
|
|
122
|
+
* { "label": "Netherlands", "value": "NL" }
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
interface FormFieldOption {
|
|
126
|
+
readonly label: string;
|
|
127
|
+
readonly value: unknown;
|
|
128
|
+
/** Whether the option is disabled. */
|
|
129
|
+
readonly disabled?: boolean;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Definition of a single form field. Fully JSON-serializable.
|
|
133
|
+
*
|
|
134
|
+
* The `component` key is resolved at runtime through the
|
|
135
|
+
* {@link FormFieldRegistry} to an actual Angular component that
|
|
136
|
+
* exposes a two-way `value` (or mapped model) signal.
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```json
|
|
140
|
+
* {
|
|
141
|
+
* "id": "email",
|
|
142
|
+
* "title": "E-mail address",
|
|
143
|
+
* "component": "text",
|
|
144
|
+
* "config": { "type": "email", "placeholder": "you@example.com" },
|
|
145
|
+
* "validation": [
|
|
146
|
+
* { "type": "required", "message": "E-mail is required." },
|
|
147
|
+
* { "type": "email", "message": "Enter a valid e-mail address." }
|
|
148
|
+
* ]
|
|
149
|
+
* }
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
interface FormFieldDefinition {
|
|
153
|
+
/** Unique ID — used as the key in the form values object. */
|
|
154
|
+
readonly id: string;
|
|
155
|
+
/** Human-readable label shown above the field. */
|
|
156
|
+
readonly title: string;
|
|
157
|
+
/** Optional helper text shown below the field. */
|
|
158
|
+
readonly description?: string;
|
|
159
|
+
/**
|
|
160
|
+
* Registry key that maps to an Angular component.
|
|
161
|
+
*
|
|
162
|
+
* Built-in keys: `"text"`, `"select"`, `"checkbox"`, `"toggle"`,
|
|
163
|
+
* `"radio"`, `"autocomplete"`, `"date"`, `"time"`, `"datetime"`,
|
|
164
|
+
* `"color"`, `"slider"`, `"richtext"`, `"file"`.
|
|
165
|
+
*/
|
|
166
|
+
readonly component: string;
|
|
167
|
+
/**
|
|
168
|
+
* Arbitrary key–value config forwarded as inputs to the
|
|
169
|
+
* resolved component. Keys must match the component's `input()`
|
|
170
|
+
* signal names.
|
|
171
|
+
*/
|
|
172
|
+
readonly config?: Readonly<Record<string, unknown>>;
|
|
173
|
+
/**
|
|
174
|
+
* Options for select, radio, and autocomplete fields.
|
|
175
|
+
* Passed to the component's `options` or equivalent input.
|
|
176
|
+
*/
|
|
177
|
+
readonly options?: readonly FormFieldOption[];
|
|
178
|
+
/** Validation rules evaluated by the form engine. */
|
|
179
|
+
readonly validation?: readonly ValidationRule[];
|
|
180
|
+
/**
|
|
181
|
+
* When set, the field is only visible if the condition is met.
|
|
182
|
+
* Hidden fields are excluded from the output JSON.
|
|
183
|
+
*/
|
|
184
|
+
readonly visibleWhen?: Condition;
|
|
185
|
+
/**
|
|
186
|
+
* When set, the field is disabled (non-interactive) unless the
|
|
187
|
+
* condition is met.
|
|
188
|
+
*/
|
|
189
|
+
readonly enabledWhen?: Condition;
|
|
190
|
+
/** Default value used when the form is first created. */
|
|
191
|
+
readonly defaultValue?: unknown;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* A named group of fields. Rendered as a fieldset / section, or
|
|
195
|
+
* as a single step in wizard mode.
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```json
|
|
199
|
+
* {
|
|
200
|
+
* "id": "personal",
|
|
201
|
+
* "title": "Personal information",
|
|
202
|
+
* "fields": [
|
|
203
|
+
* { "id": "firstName", "title": "First name", "component": "text" },
|
|
204
|
+
* { "id": "lastName", "title": "Last name", "component": "text" }
|
|
205
|
+
* ]
|
|
206
|
+
* }
|
|
207
|
+
* ```
|
|
208
|
+
*/
|
|
209
|
+
interface FormGroupDefinition {
|
|
210
|
+
/** Unique ID for the group. */
|
|
211
|
+
readonly id: string;
|
|
212
|
+
/** Group heading. */
|
|
213
|
+
readonly title?: string;
|
|
214
|
+
/** Optional description shown below the heading. */
|
|
215
|
+
readonly description?: string;
|
|
216
|
+
/** Ordered list of fields in this group. */
|
|
217
|
+
readonly fields: readonly FormFieldDefinition[];
|
|
218
|
+
/** Condition controlling group visibility. */
|
|
219
|
+
readonly visibleWhen?: Condition;
|
|
220
|
+
/** Condition controlling group enabled state. */
|
|
221
|
+
readonly enabledWhen?: Condition;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Top-level form schema. This is the JSON document that fully
|
|
225
|
+
* describes a form — its structure, fields, validation, and
|
|
226
|
+
* conditional logic.
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* ```json
|
|
230
|
+
* {
|
|
231
|
+
* "id": "contact",
|
|
232
|
+
* "title": "Contact form",
|
|
233
|
+
* "groups": [
|
|
234
|
+
* {
|
|
235
|
+
* "id": "main",
|
|
236
|
+
* "title": "Your details",
|
|
237
|
+
* "fields": [
|
|
238
|
+
* { "id": "name", "title": "Name", "component": "text" },
|
|
239
|
+
* { "id": "email", "title": "E-mail", "component": "text",
|
|
240
|
+
* "config": { "type": "email" } }
|
|
241
|
+
* ]
|
|
242
|
+
* }
|
|
243
|
+
* ]
|
|
244
|
+
* }
|
|
245
|
+
* ```
|
|
246
|
+
*/
|
|
247
|
+
interface FormSchema {
|
|
248
|
+
/** Unique form identifier. */
|
|
249
|
+
readonly id: string;
|
|
250
|
+
/** Form title (rendered as a heading). */
|
|
251
|
+
readonly title?: string;
|
|
252
|
+
/** Optional description shown below the title. */
|
|
253
|
+
readonly description?: string;
|
|
254
|
+
/** Ordered list of field groups. */
|
|
255
|
+
readonly groups: readonly FormGroupDefinition[];
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* The plain-object output produced by the form engine.
|
|
259
|
+
*
|
|
260
|
+
* Keys are field IDs, values are the current field values.
|
|
261
|
+
* Only visible fields are included.
|
|
262
|
+
*/
|
|
263
|
+
type FormValues = Record<string, unknown>;
|
|
264
|
+
/**
|
|
265
|
+
* Known flair component keys. Flair items are purely presentational
|
|
266
|
+
* elements that do not collect user input.
|
|
267
|
+
*/
|
|
268
|
+
declare const FLAIR_COMPONENTS: readonly ["flair:richtext", "flair:image", "flair:media"];
|
|
269
|
+
/** A flair component key. */
|
|
270
|
+
type FlairComponent = (typeof FLAIR_COMPONENTS)[number];
|
|
271
|
+
/**
|
|
272
|
+
* Returns `true` if the given component key is a flair (non-data)
|
|
273
|
+
* component.
|
|
274
|
+
*/
|
|
275
|
+
declare function isFlairComponent(component: string): component is FlairComponent;
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Signature for a custom validator function.
|
|
279
|
+
*
|
|
280
|
+
* Returns `null` when valid, or a `ValidationError` on failure.
|
|
281
|
+
*/
|
|
282
|
+
type ValidatorFn = (value: unknown, params: Readonly<Record<string, unknown>>) => ValidationError | null;
|
|
283
|
+
/**
|
|
284
|
+
* Register a custom validator that can be referenced by
|
|
285
|
+
* `{ type: "custom", params: { validatorId: "myId" } }` in a
|
|
286
|
+
* form schema.
|
|
287
|
+
*/
|
|
288
|
+
declare function registerCustomValidator(id: string, fn: ValidatorFn): void;
|
|
289
|
+
/**
|
|
290
|
+
* Validate a value against an array of rules and return a
|
|
291
|
+
* {@link ValidationResult}.
|
|
292
|
+
*/
|
|
293
|
+
declare function validate(rules: readonly ValidationRule[], value: unknown): ValidationResult;
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Evaluate a {@link Condition} (single or group) against the
|
|
297
|
+
* current form values.
|
|
298
|
+
*
|
|
299
|
+
* @returns `true` when the condition is satisfied.
|
|
300
|
+
*/
|
|
301
|
+
declare function evaluateCondition(condition: Condition, values: FormValues): boolean;
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Runtime state for a single field managed by the form engine.
|
|
305
|
+
*/
|
|
306
|
+
interface FieldState {
|
|
307
|
+
/** The field definition from the schema. */
|
|
308
|
+
readonly definition: FormFieldDefinition;
|
|
309
|
+
/** Current value (writable signal). */
|
|
310
|
+
readonly value: WritableSignal<unknown>;
|
|
311
|
+
/** Whether the field is currently visible. */
|
|
312
|
+
readonly visible: Signal<boolean>;
|
|
313
|
+
/** Whether the field is currently enabled (interactive). */
|
|
314
|
+
readonly enabled: Signal<boolean>;
|
|
315
|
+
/** Live validation result. */
|
|
316
|
+
readonly validation: Signal<ValidationResult>;
|
|
317
|
+
/** Whether the field has been interacted with. */
|
|
318
|
+
readonly touched: WritableSignal<boolean>;
|
|
319
|
+
/** Whether the field has been changed from its default. */
|
|
320
|
+
readonly dirty: Signal<boolean>;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Runtime state for a field group.
|
|
324
|
+
*/
|
|
325
|
+
interface GroupState {
|
|
326
|
+
/** The group definition from the schema. */
|
|
327
|
+
readonly definition: FormGroupDefinition;
|
|
328
|
+
/** Runtime states for every field in this group. */
|
|
329
|
+
readonly fields: readonly FieldState[];
|
|
330
|
+
/** Whether the group is currently visible. */
|
|
331
|
+
readonly visible: Signal<boolean>;
|
|
332
|
+
/** Whether the group is currently enabled. */
|
|
333
|
+
readonly enabled: Signal<boolean>;
|
|
334
|
+
/** Whether every visible field in the group is valid. */
|
|
335
|
+
readonly valid: Signal<boolean>;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Signal-based form engine. Takes a {@link FormSchema}, creates
|
|
339
|
+
* reactive state for every field and group, evaluates conditions,
|
|
340
|
+
* runs validation, and produces a JSON output object.
|
|
341
|
+
*
|
|
342
|
+
* The engine is framework-agnostic (no Angular DI required) — it
|
|
343
|
+
* operates purely on signals and plain objects.
|
|
344
|
+
*
|
|
345
|
+
* @example
|
|
346
|
+
* ```ts
|
|
347
|
+
* const engine = new FormEngine(schema);
|
|
348
|
+
* engine.setValue("email", "test@example.com");
|
|
349
|
+
* console.log(engine.values()); // { email: "test@example.com", … }
|
|
350
|
+
* console.log(engine.valid()); // true / false
|
|
351
|
+
* ```
|
|
352
|
+
*/
|
|
353
|
+
declare class FormEngine {
|
|
354
|
+
readonly schema: FormSchema;
|
|
355
|
+
/** All field states indexed by field ID. */
|
|
356
|
+
private readonly fieldMap;
|
|
357
|
+
/** Ordered group states. */
|
|
358
|
+
readonly groups: readonly GroupState[];
|
|
359
|
+
/** Live snapshot of all field values (including hidden fields). */
|
|
360
|
+
readonly values: Signal<FormValues>;
|
|
361
|
+
/** Whether every visible field passes validation. */
|
|
362
|
+
readonly valid: Signal<boolean>;
|
|
363
|
+
/** Whether any field has been interacted with. */
|
|
364
|
+
readonly touched: Signal<boolean>;
|
|
365
|
+
/** Whether any field value differs from its default. */
|
|
366
|
+
readonly dirty: Signal<boolean>;
|
|
367
|
+
constructor(schema: FormSchema);
|
|
368
|
+
/**
|
|
369
|
+
* Get the {@link FieldState} for a field by ID.
|
|
370
|
+
* Throws if the field is not found.
|
|
371
|
+
*/
|
|
372
|
+
getField(id: string): FieldState;
|
|
373
|
+
/** Set the value of a single field by ID. */
|
|
374
|
+
setValue(id: string, value: unknown): void;
|
|
375
|
+
/** Mark a field as touched. */
|
|
376
|
+
markTouched(id: string): void;
|
|
377
|
+
/** Mark all fields as touched (e.g. on submit attempt). */
|
|
378
|
+
markAllTouched(): void;
|
|
379
|
+
/**
|
|
380
|
+
* Reset the form to its initial default values and clear
|
|
381
|
+
* touched/dirty state.
|
|
382
|
+
*/
|
|
383
|
+
reset(): void;
|
|
384
|
+
/**
|
|
385
|
+
* Produce the JSON output — a plain object containing only
|
|
386
|
+
* **visible** field values.
|
|
387
|
+
*/
|
|
388
|
+
output(): Signal<FormValues>;
|
|
389
|
+
private buildGroupState;
|
|
390
|
+
private buildFieldState;
|
|
391
|
+
/**
|
|
392
|
+
* Read all current field values reactively. When called inside a
|
|
393
|
+
* `computed()`, it tracks every field's value signal so the
|
|
394
|
+
* computed re-evaluates whenever any field changes.
|
|
395
|
+
*/
|
|
396
|
+
private readFieldValues;
|
|
397
|
+
/**
|
|
398
|
+
* Produce a sensible default value for a given component type so
|
|
399
|
+
* fields without explicit `defaultValue` still have a typed zero.
|
|
400
|
+
*/
|
|
401
|
+
private defaultForComponent;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Transforms a raw config value (typically a JSON-serializable string
|
|
406
|
+
* or number) into the runtime value expected by the component input.
|
|
407
|
+
*
|
|
408
|
+
* Used by {@link FormFieldRegistration.configTransforms} to bridge
|
|
409
|
+
* JSON-friendly config keys to complex runtime objects.
|
|
410
|
+
*/
|
|
411
|
+
interface ConfigTransform {
|
|
412
|
+
/** The actual component input name to set. */
|
|
413
|
+
readonly inputKey: string;
|
|
414
|
+
/** Convert the raw config value to the runtime input value. */
|
|
415
|
+
readonly transform: (value: unknown) => unknown;
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Describes how a component is integrated into the form system.
|
|
419
|
+
*/
|
|
420
|
+
interface FormFieldRegistration {
|
|
421
|
+
/**
|
|
422
|
+
* The Angular component class to instantiate.
|
|
423
|
+
* Must be a standalone component.
|
|
424
|
+
*/
|
|
425
|
+
readonly component: Type<unknown>;
|
|
426
|
+
/**
|
|
427
|
+
* Name of the model signal used for two-way value binding.
|
|
428
|
+
*
|
|
429
|
+
* Most ui-kit components use `"value"`. Exceptions:
|
|
430
|
+
* - `UICheckbox` → `"checked"`
|
|
431
|
+
* - `UIFileUpload` → `"files"`
|
|
432
|
+
*/
|
|
433
|
+
readonly modelProperty: string;
|
|
434
|
+
/**
|
|
435
|
+
* Static inputs to apply by default (e.g. `{ type: "email" }`).
|
|
436
|
+
* Merged with (overridden by) the field definition's `config`.
|
|
437
|
+
*/
|
|
438
|
+
readonly defaultConfig?: Readonly<Record<string, unknown>>;
|
|
439
|
+
/**
|
|
440
|
+
* Optional map from config keys to {@link ConfigTransform} entries.
|
|
441
|
+
*
|
|
442
|
+
* When a config key appears in this map, the raw JSON value is
|
|
443
|
+
* passed through the transform and set on the component input
|
|
444
|
+
* specified by `inputKey` instead of the config key itself.
|
|
445
|
+
*
|
|
446
|
+
* @example
|
|
447
|
+
* ```ts
|
|
448
|
+
* configTransforms: {
|
|
449
|
+
* textAdapter: {
|
|
450
|
+
* inputKey: 'adapter',
|
|
451
|
+
* transform: (key) => resolveTextAdapter(key as string),
|
|
452
|
+
* },
|
|
453
|
+
* }
|
|
454
|
+
* ```
|
|
455
|
+
*/
|
|
456
|
+
readonly configTransforms?: Readonly<Record<string, ConfigTransform>>;
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Multi-provider token that collects field registrations from
|
|
460
|
+
* across the application.
|
|
461
|
+
*/
|
|
462
|
+
declare const FORM_FIELD_REGISTRATIONS: InjectionToken<ReadonlyMap<string, FormFieldRegistration>>;
|
|
463
|
+
/**
|
|
464
|
+
* Register one or more field types for use in form schemas.
|
|
465
|
+
*
|
|
466
|
+
* @example
|
|
467
|
+
* ```ts
|
|
468
|
+
* // app.config.ts
|
|
469
|
+
* import { provideFormFields } from '@theredhead/lucid-forms';
|
|
470
|
+
* import { UIInput, UISelect, UICheckbox } from '@theredhead/lucid-kit';
|
|
471
|
+
*
|
|
472
|
+
* export const appConfig = {
|
|
473
|
+
* providers: [
|
|
474
|
+
* provideFormFields({
|
|
475
|
+
* text: { component: UIInput, modelProperty: 'value' },
|
|
476
|
+
* select: { component: UISelect, modelProperty: 'value' },
|
|
477
|
+
* checkbox: { component: UICheckbox, modelProperty: 'checked' },
|
|
478
|
+
* }),
|
|
479
|
+
* ],
|
|
480
|
+
* };
|
|
481
|
+
* ```
|
|
482
|
+
*/
|
|
483
|
+
declare function provideFormFields(fields: Readonly<Record<string, FormFieldRegistration>>): EnvironmentProviders;
|
|
484
|
+
/**
|
|
485
|
+
* Injectable service that resolves a component key (e.g. `"text"`)
|
|
486
|
+
* to a {@link FormFieldRegistration}.
|
|
487
|
+
*
|
|
488
|
+
* It merges all maps provided via `FORM_FIELD_REGISTRATIONS`.
|
|
489
|
+
*/
|
|
490
|
+
declare class FormFieldRegistry {
|
|
491
|
+
private readonly maps;
|
|
492
|
+
private merged;
|
|
493
|
+
private getMerged;
|
|
494
|
+
/** Resolve a component key to its registration, or `null`. */
|
|
495
|
+
resolve(key: string): FormFieldRegistration | null;
|
|
496
|
+
/** All registered keys. */
|
|
497
|
+
keys(): string[];
|
|
498
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormFieldRegistry, never>;
|
|
499
|
+
static ɵprov: _angular_core.ɵɵInjectableDeclaration<FormFieldRegistry>;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Built-in field registrations that map common component keys
|
|
504
|
+
* to `@theredhead/lucid-kit` components.
|
|
505
|
+
*
|
|
506
|
+
* | Key | Component | Model property |
|
|
507
|
+
* |-----------------|---------------------|----------------|
|
|
508
|
+
* | `"text"` | `UIInput` | `value` |
|
|
509
|
+
* | `"select"` | `UIDropdownList` | `value` |
|
|
510
|
+
* | `"checkbox"` | `UICheckbox` | `checked` |
|
|
511
|
+
* | `"toggle"` | `UIToggle` | `value` |
|
|
512
|
+
* | `"radio"` | `UIRadioGroup` | `value` |
|
|
513
|
+
* | `"autocomplete"`| `UIAutocomplete` | `value` |
|
|
514
|
+
* | `"date"` | `UIInput` + `DateInputAdapter` | `value` |
|
|
515
|
+
* | `"time"` | `UIInput` + `TimeTextAdapter` | `value` |
|
|
516
|
+
* | `"datetime"` | `UIInput` + `DateInputAdapter` | `value` |
|
|
517
|
+
* | `"color"` | `UIColorPicker` | `value` |
|
|
518
|
+
* | `"slider"` | `UISlider` | `value` |
|
|
519
|
+
* | `"richtext"` | `UIRichTextEditor` | `value` |
|
|
520
|
+
* | `"file"` | `UIFileUpload` | `files` |
|
|
521
|
+
* | `"flair:richtext"` | `UIRichTextView` | `content` |
|
|
522
|
+
* | `"flair:image"` | `UIImage` | `src` |
|
|
523
|
+
* | `"flair:media"` | `UIMediaPlayer` | `source` |
|
|
524
|
+
*/
|
|
525
|
+
declare const BUILT_IN_FIELDS: Readonly<Record<string, FormFieldRegistration>>;
|
|
526
|
+
/**
|
|
527
|
+
* Convenience provider that registers all built-in
|
|
528
|
+
* `@theredhead/lucid-kit` field types.
|
|
529
|
+
*
|
|
530
|
+
* @example
|
|
531
|
+
* ```ts
|
|
532
|
+
* import { provideBuiltInFormFields } from '@theredhead/lucid-forms';
|
|
533
|
+
*
|
|
534
|
+
* export const appConfig = {
|
|
535
|
+
* providers: [provideBuiltInFormFields()],
|
|
536
|
+
* };
|
|
537
|
+
* ```
|
|
538
|
+
*/
|
|
539
|
+
declare function provideBuiltInFormFields(): EnvironmentProviders;
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* All available text adapter keys, in display order.
|
|
543
|
+
*/
|
|
544
|
+
declare const TEXT_ADAPTER_KEYS: readonly string[];
|
|
545
|
+
/**
|
|
546
|
+
* Resolve a text adapter key to a {@link TextAdapter} instance.
|
|
547
|
+
*
|
|
548
|
+
* Returns `undefined` for empty/unknown keys.
|
|
549
|
+
*
|
|
550
|
+
* @param key Adapter key (e.g. `"email"`, `"phone"`, `"money"`).
|
|
551
|
+
*/
|
|
552
|
+
declare function resolveTextAdapter(key: unknown): TextAdapter | undefined;
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* The result of an export operation.
|
|
556
|
+
*
|
|
557
|
+
* Contains the generated content as a string together with metadata
|
|
558
|
+
* about the suggested file name and MIME type.
|
|
559
|
+
*/
|
|
560
|
+
interface ExportResult {
|
|
561
|
+
/** MIME type for the exported content (e.g. `"application/json"`, `"text/typescript"`). */
|
|
562
|
+
readonly mimeType: string;
|
|
563
|
+
/** Suggested file name for the exported artefact. */
|
|
564
|
+
readonly fileName: string;
|
|
565
|
+
/** The exported content as a string. */
|
|
566
|
+
readonly content: string;
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Strategy interface for exporting a {@link FormSchema} into a
|
|
570
|
+
* specific format.
|
|
571
|
+
*
|
|
572
|
+
* Implementations must be plain classes — no Angular DI required.
|
|
573
|
+
*
|
|
574
|
+
* @example
|
|
575
|
+
* ```ts
|
|
576
|
+
* class MyExportStrategy implements ExportStrategy {
|
|
577
|
+
* readonly label = 'My Format';
|
|
578
|
+
* readonly description = 'Export as My Format.';
|
|
579
|
+
*
|
|
580
|
+
* export(schema: FormSchema): ExportResult {
|
|
581
|
+
* return {
|
|
582
|
+
* mimeType: 'text/plain',
|
|
583
|
+
* fileName: 'form.txt',
|
|
584
|
+
* content: JSON.stringify(schema),
|
|
585
|
+
* };
|
|
586
|
+
* }
|
|
587
|
+
* }
|
|
588
|
+
* ```
|
|
589
|
+
*/
|
|
590
|
+
interface ExportStrategy {
|
|
591
|
+
/** Human-readable label displayed in the export format selector. */
|
|
592
|
+
readonly label: string;
|
|
593
|
+
/** Short description of the export format. */
|
|
594
|
+
readonly description: string;
|
|
595
|
+
/** Generate an {@link ExportResult} from the given schema. */
|
|
596
|
+
export(schema: FormSchema): ExportResult;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* Exports a {@link FormSchema} as a formatted JSON file.
|
|
601
|
+
*
|
|
602
|
+
* The output is a standard JSON document — identical to
|
|
603
|
+
* `JSON.stringify(schema, null, 2)`.
|
|
604
|
+
*/
|
|
605
|
+
declare class JsonExportStrategy implements ExportStrategy {
|
|
606
|
+
readonly label = "JSON Schema";
|
|
607
|
+
readonly description = "Export the form schema as a JSON file.";
|
|
608
|
+
export(schema: FormSchema): ExportResult;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Exports a {@link FormSchema} as a standalone Angular component
|
|
613
|
+
* with a fully declarative HTML template.
|
|
614
|
+
*
|
|
615
|
+
* The generated `.component.ts` file contains:
|
|
616
|
+
*
|
|
617
|
+
* - A typed `<Title>FormValues` interface with one property per field
|
|
618
|
+
* - Individual `signal()` fields for two-way binding
|
|
619
|
+
* - A `computed()` `formValues` signal that assembles the full typed object
|
|
620
|
+
* - A template that directly uses `@theredhead/lucid-kit` components
|
|
621
|
+
* (`<ui-input>`, `<ui-select>`, …) — no `FormEngine` or `<ui-form>`
|
|
622
|
+
*
|
|
623
|
+
* The component is almost entirely declarative: the TypeScript class
|
|
624
|
+
* is just signals and a single computed.
|
|
625
|
+
*/
|
|
626
|
+
declare class AngularComponentExportStrategy implements ExportStrategy {
|
|
627
|
+
readonly label = "Angular Component";
|
|
628
|
+
readonly description = "Standalone Angular component with declarative template.";
|
|
629
|
+
export(schema: FormSchema): ExportResult;
|
|
630
|
+
/** @internal Build the import statements. */
|
|
631
|
+
private buildImports;
|
|
632
|
+
/** @internal Build the values interface. */
|
|
633
|
+
private buildInterface;
|
|
634
|
+
/** @internal Build the full @Component class. */
|
|
635
|
+
private buildComponent;
|
|
636
|
+
/** @internal Build the HTML template with fieldsets and components. */
|
|
637
|
+
private buildTemplate;
|
|
638
|
+
/** @internal Build the class body: signals, options, computed. */
|
|
639
|
+
private buildClassBody;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Renders a single form field by dynamically creating the component
|
|
644
|
+
* registered for the field's `component` key and wiring up two-way
|
|
645
|
+
* value binding, config inputs, and validation display.
|
|
646
|
+
*
|
|
647
|
+
* This component is used internally by {@link UIFormGroup} and
|
|
648
|
+
* {@link UIForm}. It can also be used standalone for custom layouts.
|
|
649
|
+
*
|
|
650
|
+
* @example
|
|
651
|
+
* ```html
|
|
652
|
+
* <ui-form-field [state]="fieldState" />
|
|
653
|
+
* ```
|
|
654
|
+
*/
|
|
655
|
+
declare class UIFormField {
|
|
656
|
+
/** The field state managed by the {@link FormEngine}. */
|
|
657
|
+
readonly state: _angular_core.InputSignal<FieldState>;
|
|
658
|
+
private readonly registry;
|
|
659
|
+
private readonly log;
|
|
660
|
+
private readonly outlet;
|
|
661
|
+
/** @internal Show errors only when the field has been touched. */
|
|
662
|
+
protected readonly showErrors: _angular_core.Signal<boolean>;
|
|
663
|
+
/** @internal Whether this field is a flair (non-data) component. */
|
|
664
|
+
protected readonly isFlair: _angular_core.Signal<boolean>;
|
|
665
|
+
/** @internal Whether the field has a `required` validation rule. */
|
|
666
|
+
protected readonly isRequired: _angular_core.Signal<boolean>;
|
|
667
|
+
constructor();
|
|
668
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<UIFormField, never>;
|
|
669
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<UIFormField, "ui-form-field", never, { "state": { "alias": "state"; "required": true; "isSignal": true; }; }, {}, never, never, true, [{ directive: typeof i1.UISurface; inputs: { "surfaceType": "surfaceType"; }; outputs: {}; }]>;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Renders a group of form fields as a visual section (fieldset).
|
|
674
|
+
*
|
|
675
|
+
* In sequential (non-wizard) mode every group is displayed
|
|
676
|
+
* vertically. In wizard mode, {@link UIFormWizard} controls which
|
|
677
|
+
* group is visible.
|
|
678
|
+
*
|
|
679
|
+
* @example
|
|
680
|
+
* ```html
|
|
681
|
+
* <ui-form-group [state]="groupState" />
|
|
682
|
+
* ```
|
|
683
|
+
*/
|
|
684
|
+
declare class UIFormGroup {
|
|
685
|
+
/** The group state managed by the {@link FormEngine}. */
|
|
686
|
+
readonly state: _angular_core.InputSignal<GroupState>;
|
|
687
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<UIFormGroup, never>;
|
|
688
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<UIFormGroup, "ui-form-group", never, { "state": { "alias": "state"; "required": true; "isSignal": true; }; }, {}, never, never, true, [{ directive: typeof i1.UISurface; inputs: { "surfaceType": "surfaceType"; }; outputs: {}; }]>;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* Top-level form component that renders all groups sequentially.
|
|
693
|
+
*
|
|
694
|
+
* Takes a {@link FormEngine} instance and displays every group's
|
|
695
|
+
* fields in order, with validation and conditional visibility.
|
|
696
|
+
*
|
|
697
|
+
* @example
|
|
698
|
+
* ```html
|
|
699
|
+
* <ui-form [engine]="engine" (formSubmit)="onSubmit($event)" />
|
|
700
|
+
* ```
|
|
701
|
+
*/
|
|
702
|
+
declare class UIForm {
|
|
703
|
+
/** The form engine instance that drives this form. */
|
|
704
|
+
readonly engine: _angular_core.InputSignal<FormEngine>;
|
|
705
|
+
/** Label for the submit button. Defaults to `"Submit"`. */
|
|
706
|
+
readonly submitLabel: _angular_core.InputSignal<string>;
|
|
707
|
+
/** Whether to show the built-in submit button. Defaults to `true`. */
|
|
708
|
+
readonly showSubmit: _angular_core.InputSignal<boolean>;
|
|
709
|
+
/** Minimum width (in pixels) for form field controls. Defaults to `200`. */
|
|
710
|
+
readonly fieldMinWidth: _angular_core.InputSignal<number>;
|
|
711
|
+
/** Emitted when the submit button is clicked and the form is valid. */
|
|
712
|
+
readonly formSubmit: _angular_core.OutputEmitterRef<FormValues>;
|
|
713
|
+
/** @internal */
|
|
714
|
+
protected readonly isValid: _angular_core.Signal<boolean>;
|
|
715
|
+
/** @internal — collects all invalid visible fields for the summary. */
|
|
716
|
+
protected readonly validationSummary: _angular_core.Signal<{
|
|
717
|
+
fieldId: string;
|
|
718
|
+
title: string;
|
|
719
|
+
errors: string[];
|
|
720
|
+
}[]>;
|
|
721
|
+
/** @internal */
|
|
722
|
+
protected onSubmit(): void;
|
|
723
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<UIForm, never>;
|
|
724
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<UIForm, "ui-form", never, { "engine": { "alias": "engine"; "required": true; "isSignal": true; }; "submitLabel": { "alias": "submitLabel"; "required": false; "isSignal": true; }; "showSubmit": { "alias": "showSubmit"; "required": false; "isSignal": true; }; "fieldMinWidth": { "alias": "fieldMinWidth"; "required": false; "isSignal": true; }; }, { "formSubmit": "formSubmit"; }, never, never, true, [{ directive: typeof i1.UISurface; inputs: { "surfaceType": "surfaceType"; }; outputs: {}; }]>;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* Renders a {@link FormEngine}'s groups as wizard steps — one group
|
|
729
|
+
* at a time with previous / next / submit navigation.
|
|
730
|
+
*
|
|
731
|
+
* Only visible groups are included as steps. The wizard validates
|
|
732
|
+
* the current step before allowing navigation to the next.
|
|
733
|
+
*
|
|
734
|
+
* @example
|
|
735
|
+
* ```html
|
|
736
|
+
* <ui-form-wizard
|
|
737
|
+
* [engine]="engine"
|
|
738
|
+
* (formSubmit)="onSubmit($event)"
|
|
739
|
+
* />
|
|
740
|
+
* ```
|
|
741
|
+
*/
|
|
742
|
+
declare class UIFormWizard {
|
|
743
|
+
/** The form engine instance that drives this wizard. */
|
|
744
|
+
readonly engine: _angular_core.InputSignal<FormEngine>;
|
|
745
|
+
/** Label for the "Next" button. */
|
|
746
|
+
readonly nextLabel: _angular_core.InputSignal<string>;
|
|
747
|
+
/** Label for the "Previous" button. */
|
|
748
|
+
readonly prevLabel: _angular_core.InputSignal<string>;
|
|
749
|
+
/** Label for the "Submit" button (last step). */
|
|
750
|
+
readonly submitLabel: _angular_core.InputSignal<string>;
|
|
751
|
+
/** Minimum width (in pixels) for form field controls. Defaults to `200`. */
|
|
752
|
+
readonly fieldMinWidth: _angular_core.InputSignal<number>;
|
|
753
|
+
/** Emitted when the form is submitted (last step, valid). */
|
|
754
|
+
readonly formSubmit: _angular_core.OutputEmitterRef<FormValues>;
|
|
755
|
+
/** Current step index. */
|
|
756
|
+
protected readonly currentIndex: _angular_core.WritableSignal<number>;
|
|
757
|
+
/** Only visible groups are wizard steps. */
|
|
758
|
+
protected readonly visibleGroups: _angular_core.Signal<GroupState[]>;
|
|
759
|
+
/** The currently displayed group state. */
|
|
760
|
+
protected readonly currentGroup: _angular_core.Signal<GroupState | null>;
|
|
761
|
+
/** Whether the current step is the last one. */
|
|
762
|
+
protected readonly isLastStep: _angular_core.Signal<boolean>;
|
|
763
|
+
/** Whether the current step's fields are all valid. */
|
|
764
|
+
protected readonly currentStepValid: _angular_core.Signal<boolean>;
|
|
765
|
+
/** Navigate to the next step (marks current fields touched). */
|
|
766
|
+
next(): void;
|
|
767
|
+
/** Navigate to the previous step. */
|
|
768
|
+
prev(): void;
|
|
769
|
+
/** Jump to a specific step. */
|
|
770
|
+
goTo(index: number): void;
|
|
771
|
+
/** @internal Submit handler. */
|
|
772
|
+
protected onSubmit(): void;
|
|
773
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<UIFormWizard, never>;
|
|
774
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<UIFormWizard, "ui-form-wizard", never, { "engine": { "alias": "engine"; "required": true; "isSignal": true; }; "nextLabel": { "alias": "nextLabel"; "required": false; "isSignal": true; }; "prevLabel": { "alias": "prevLabel"; "required": false; "isSignal": true; }; "submitLabel": { "alias": "submitLabel"; "required": false; "isSignal": true; }; "fieldMinWidth": { "alias": "fieldMinWidth"; "required": false; "isSignal": true; }; }, { "formSubmit": "formSubmit"; }, never, never, true, [{ directive: typeof i1.UISurface; inputs: { "surfaceType": "surfaceType"; }; outputs: {}; }]>;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
/**
|
|
778
|
+
* Mutable counterpart of {@link FormFieldDefinition} used
|
|
779
|
+
* exclusively inside the designer. Signal-driven so every
|
|
780
|
+
* property change is immediately reflected in the live preview
|
|
781
|
+
* and schema output.
|
|
782
|
+
*/
|
|
783
|
+
interface MutableFieldDefinition {
|
|
784
|
+
readonly uid: string;
|
|
785
|
+
readonly id: WritableSignal<string>;
|
|
786
|
+
readonly title: WritableSignal<string>;
|
|
787
|
+
readonly description: WritableSignal<string>;
|
|
788
|
+
readonly component: WritableSignal<string>;
|
|
789
|
+
readonly config: WritableSignal<Record<string, unknown>>;
|
|
790
|
+
readonly options: WritableSignal<FormFieldOption[]>;
|
|
791
|
+
readonly validation: WritableSignal<ValidationRule[]>;
|
|
792
|
+
readonly visibleWhen: WritableSignal<Condition | null>;
|
|
793
|
+
readonly enabledWhen: WritableSignal<Condition | null>;
|
|
794
|
+
readonly defaultValue: WritableSignal<unknown>;
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Mutable counterpart of {@link FormGroupDefinition}.
|
|
798
|
+
*/
|
|
799
|
+
interface MutableGroupDefinition {
|
|
800
|
+
readonly uid: string;
|
|
801
|
+
readonly id: WritableSignal<string>;
|
|
802
|
+
readonly title: WritableSignal<string>;
|
|
803
|
+
readonly description: WritableSignal<string>;
|
|
804
|
+
readonly fields: WritableSignal<MutableFieldDefinition[]>;
|
|
805
|
+
readonly visibleWhen: WritableSignal<Condition | null>;
|
|
806
|
+
readonly enabledWhen: WritableSignal<Condition | null>;
|
|
807
|
+
}
|
|
808
|
+
/** What kind of item is currently selected in the designer. */
|
|
809
|
+
type DesignerSelectionKind = "field" | "group" | "form";
|
|
810
|
+
/**
|
|
811
|
+
* Describes the currently selected item in the designer canvas.
|
|
812
|
+
*/
|
|
813
|
+
interface DesignerSelection {
|
|
814
|
+
readonly kind: DesignerSelectionKind;
|
|
815
|
+
readonly groupUid: string | null;
|
|
816
|
+
readonly fieldUid: string | null;
|
|
817
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* Signal-based engine that maintains the mutable designer state
|
|
820
|
+
* and produces a readonly {@link FormSchema} snapshot on demand.
|
|
821
|
+
*
|
|
822
|
+
* All mutations go through public methods so the UI stays in sync
|
|
823
|
+
* via Angular's signal-based change detection.
|
|
824
|
+
*
|
|
825
|
+
* @example
|
|
826
|
+
* ```ts
|
|
827
|
+
* const engine = new FormDesignerEngine();
|
|
828
|
+
* engine.addGroup();
|
|
829
|
+
* engine.addField(engine.groups()[0].uid, 'text');
|
|
830
|
+
* const schema = engine.schema();
|
|
831
|
+
* ```
|
|
832
|
+
*/
|
|
833
|
+
declare class FormDesignerEngine {
|
|
834
|
+
/** Form ID. */
|
|
835
|
+
readonly formId: WritableSignal<string>;
|
|
836
|
+
/** Form title. */
|
|
837
|
+
readonly formTitle: WritableSignal<string>;
|
|
838
|
+
/** Form description. */
|
|
839
|
+
readonly formDescription: WritableSignal<string>;
|
|
840
|
+
/** Ordered list of mutable group definitions. */
|
|
841
|
+
readonly groups: WritableSignal<MutableGroupDefinition[]>;
|
|
842
|
+
/** Currently selected item in the canvas. */
|
|
843
|
+
readonly selection: WritableSignal<DesignerSelection | null>;
|
|
844
|
+
/** The currently selected mutable field (convenience). */
|
|
845
|
+
readonly selectedField: Signal<MutableFieldDefinition | null>;
|
|
846
|
+
/** The currently selected mutable group (convenience). */
|
|
847
|
+
readonly selectedGroup: Signal<MutableGroupDefinition | null>;
|
|
848
|
+
/**
|
|
849
|
+
* Produces a readonly {@link FormSchema} snapshot from the current
|
|
850
|
+
* mutable state. Fully JSON-serializable.
|
|
851
|
+
*/
|
|
852
|
+
readonly schema: Signal<FormSchema>;
|
|
853
|
+
/** Add a new empty group at the end. Returns the new group's uid. */
|
|
854
|
+
addGroup(): string;
|
|
855
|
+
/** Remove a group by uid. Clears selection if it pointed at the group. */
|
|
856
|
+
removeGroup(uid: string): void;
|
|
857
|
+
/** Move a group to a new index. */
|
|
858
|
+
moveGroup(uid: string, newIndex: number): void;
|
|
859
|
+
/**
|
|
860
|
+
* Add a new field to a group. Returns the new field's uid.
|
|
861
|
+
*
|
|
862
|
+
* @param groupUid — Target group
|
|
863
|
+
* @param component — Field component key (e.g. `"text"`, `"select"`)
|
|
864
|
+
* @param atIndex — Optional insertion index (appends if omitted)
|
|
865
|
+
*/
|
|
866
|
+
addField(groupUid: string, component: string, atIndex?: number): string;
|
|
867
|
+
/** Remove a field by uid from its containing group. */
|
|
868
|
+
removeField(groupUid: string, fieldUid: string): void;
|
|
869
|
+
/** Move a field to a new position within its group, or to another group. */
|
|
870
|
+
moveField(sourceGroupUid: string, fieldUid: string, targetGroupUid: string, targetIndex: number): void;
|
|
871
|
+
/** Duplicate a field within its group (inserted right after the original). */
|
|
872
|
+
duplicateField(groupUid: string, fieldUid: string): string | null;
|
|
873
|
+
/** Select a field for editing in the inspector. */
|
|
874
|
+
selectField(groupUid: string, fieldUid: string): void;
|
|
875
|
+
/** Select a group for editing in the inspector. */
|
|
876
|
+
selectGroup(groupUid: string): void;
|
|
877
|
+
/** Select the form-level properties for editing. */
|
|
878
|
+
selectForm(): void;
|
|
879
|
+
/** Clear the selection. */
|
|
880
|
+
clearSelection(): void;
|
|
881
|
+
/**
|
|
882
|
+
* Load an existing {@link FormSchema} into the designer,
|
|
883
|
+
* replacing all current state.
|
|
884
|
+
*/
|
|
885
|
+
loadSchema(schema: FormSchema): void;
|
|
886
|
+
/**
|
|
887
|
+
* Produce a pretty-printed JSON string of the current schema.
|
|
888
|
+
*/
|
|
889
|
+
toJSON(): string;
|
|
890
|
+
private createMutableField;
|
|
891
|
+
private createMutableGroup;
|
|
892
|
+
private buildSchema;
|
|
893
|
+
/**
|
|
894
|
+
* Generate a sensible default label from a component key.
|
|
895
|
+
*/
|
|
896
|
+
private labelForComponent;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
/**
|
|
900
|
+
* Metadata for a draggable field type shown in the palette.
|
|
901
|
+
*/
|
|
902
|
+
interface PaletteFieldType {
|
|
903
|
+
/** Registry component key (e.g. `"text"`, `"select"`). */
|
|
904
|
+
readonly key: string;
|
|
905
|
+
/** Display label. */
|
|
906
|
+
readonly label: string;
|
|
907
|
+
/** Short description shown on hover. */
|
|
908
|
+
readonly description: string;
|
|
909
|
+
/** SVG icon content from the `UIIcons` registry. */
|
|
910
|
+
readonly icon: string;
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* Palette sidebar that lists available field types. Clicking or
|
|
914
|
+
* dragging a type emits the component key so the parent can add
|
|
915
|
+
* it to the canvas.
|
|
916
|
+
*
|
|
917
|
+
* @example
|
|
918
|
+
* ```html
|
|
919
|
+
* <ui-field-palette (fieldRequested)="onAddField($event)" />
|
|
920
|
+
* ```
|
|
921
|
+
*/
|
|
922
|
+
declare class UIFieldPalette {
|
|
923
|
+
/** Emitted when the user clicks a field type to add it. */
|
|
924
|
+
readonly fieldRequested: _angular_core.OutputEmitterRef<string>;
|
|
925
|
+
/** @internal Available field types. */
|
|
926
|
+
protected readonly fieldTypes: readonly PaletteFieldType[];
|
|
927
|
+
/** @internal Available flair types. */
|
|
928
|
+
protected readonly flairTypes: readonly PaletteFieldType[];
|
|
929
|
+
/** @internal */
|
|
930
|
+
protected onDragStart(event: DragEvent, key: string): void;
|
|
931
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<UIFieldPalette, never>;
|
|
932
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<UIFieldPalette, "ui-field-palette", never, {}, { "fieldRequested": "fieldRequested"; }, never, never, true, [{ directive: typeof i1.UISurface; inputs: { "surfaceType": "surfaceType"; }; outputs: {}; }]>;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/**
|
|
936
|
+
* The central canvas of the form designer. Displays all groups
|
|
937
|
+
* and their fields as interactive cards that can be selected,
|
|
938
|
+
* reordered, and deleted.
|
|
939
|
+
*
|
|
940
|
+
* @example
|
|
941
|
+
* ```html
|
|
942
|
+
* <ui-designer-canvas [engine]="engine" />
|
|
943
|
+
* ```
|
|
944
|
+
*/
|
|
945
|
+
declare class UIDesignerCanvas {
|
|
946
|
+
/** The designer engine driving this canvas. */
|
|
947
|
+
readonly engine: _angular_core.InputSignal<FormDesignerEngine>;
|
|
948
|
+
/** Emitted when "Add field" is clicked on a group (passes group uid). */
|
|
949
|
+
readonly addFieldRequest: _angular_core.OutputEmitterRef<string>;
|
|
950
|
+
/** @internal Icon references exposed to the template. */
|
|
951
|
+
protected readonly icons: {
|
|
952
|
+
readonly ChevronUp: "<path d=\"m18 15-6-6-6 6\" />";
|
|
953
|
+
readonly ChevronDown: "<path d=\"m6 9 6 6 6-6\" />";
|
|
954
|
+
readonly Copy: "<rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\" /><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\" />";
|
|
955
|
+
readonly X: "<path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" />";
|
|
956
|
+
};
|
|
957
|
+
/** @internal Whether a component key is a flair type. */
|
|
958
|
+
protected readonly isFlair: typeof isFlairComponent;
|
|
959
|
+
/** @internal */
|
|
960
|
+
protected moveGroupUp(event: Event, uid: string, index: number): void;
|
|
961
|
+
/** @internal */
|
|
962
|
+
protected moveGroupDown(event: Event, uid: string, index: number): void;
|
|
963
|
+
/** @internal */
|
|
964
|
+
protected removeGroup(event: Event, uid: string): void;
|
|
965
|
+
/** @internal */
|
|
966
|
+
protected moveFieldUp(event: Event, groupUid: string, fieldUid: string, index: number): void;
|
|
967
|
+
/** @internal */
|
|
968
|
+
protected moveFieldDown(event: Event, groupUid: string, fieldUid: string, index: number): void;
|
|
969
|
+
/** @internal */
|
|
970
|
+
protected duplicateField(event: Event, groupUid: string, fieldUid: string): void;
|
|
971
|
+
/** @internal */
|
|
972
|
+
protected removeField(event: Event, groupUid: string, fieldUid: string): void;
|
|
973
|
+
/** @internal */
|
|
974
|
+
protected onDragOver(event: DragEvent): void;
|
|
975
|
+
/** @internal */
|
|
976
|
+
protected onDropField(event: DragEvent, group: MutableGroupDefinition): void;
|
|
977
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<UIDesignerCanvas, never>;
|
|
978
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<UIDesignerCanvas, "ui-designer-canvas", never, { "engine": { "alias": "engine"; "required": true; "isSignal": true; }; }, { "addFieldRequest": "addFieldRequest"; }, never, never, true, [{ directive: typeof i1.UISurface; inputs: { "surfaceType": "surfaceType"; }; outputs: {}; }]>;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
/**
|
|
982
|
+
* Describes a single config property that the property inspector
|
|
983
|
+
* renders as a structured form control.
|
|
984
|
+
*/
|
|
985
|
+
interface ConfigPropertySchema {
|
|
986
|
+
/** The config key name (e.g. `"type"`, `"multiline"`). */
|
|
987
|
+
readonly key: string;
|
|
988
|
+
/** Human-readable label shown in the inspector. */
|
|
989
|
+
readonly label: string;
|
|
990
|
+
/** The editor to use. */
|
|
991
|
+
readonly editor: "text" | "number" | "boolean" | "select" | "richtext";
|
|
992
|
+
/**
|
|
993
|
+
* For `"select"` editors — the list of allowed values.
|
|
994
|
+
* Each entry is either a plain string (used as both label and value)
|
|
995
|
+
* or a `{ label, value }` pair.
|
|
996
|
+
*/
|
|
997
|
+
readonly options?: readonly (string | {
|
|
998
|
+
label: string;
|
|
999
|
+
value: string;
|
|
1000
|
+
})[];
|
|
1001
|
+
/** Default value when the property is not set. */
|
|
1002
|
+
readonly defaultValue?: unknown;
|
|
1003
|
+
/** Optional short hint shown as placeholder text. */
|
|
1004
|
+
readonly placeholder?: string;
|
|
1005
|
+
/**
|
|
1006
|
+
* When provided, the property is only shown in the inspector if this
|
|
1007
|
+
* predicate returns `true` for the current config values.
|
|
1008
|
+
*/
|
|
1009
|
+
readonly visibleWhen?: (config: Record<string, unknown>) => boolean;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
/**
|
|
1013
|
+
* Property inspector panel for editing the properties of the
|
|
1014
|
+
* currently selected field, group, or form.
|
|
1015
|
+
*
|
|
1016
|
+
* @example
|
|
1017
|
+
* ```html
|
|
1018
|
+
* <ui-property-inspector [engine]="engine" />
|
|
1019
|
+
* ```
|
|
1020
|
+
*/
|
|
1021
|
+
declare class UIPropertyInspector {
|
|
1022
|
+
/** The designer engine driving this inspector. */
|
|
1023
|
+
readonly engine: _angular_core.InputSignal<FormDesignerEngine>;
|
|
1024
|
+
/** @internal */
|
|
1025
|
+
protected readonly componentOptions: SelectOption[];
|
|
1026
|
+
/** @internal */
|
|
1027
|
+
protected readonly validationTypeOptions: SelectOption[];
|
|
1028
|
+
/** @internal */
|
|
1029
|
+
protected readonly configError: _angular_core.WritableSignal<string>;
|
|
1030
|
+
/** @internal X icon for remove buttons. */
|
|
1031
|
+
protected readonly iconX: "<path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" />";
|
|
1032
|
+
/** @internal Edit icon for the rich text button. */
|
|
1033
|
+
protected readonly iconEdit: "<path d=\"M12 4v16\" /><path d=\"M4 7V5a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2\" /><path d=\"M9 20h6\" />";
|
|
1034
|
+
private readonly modalService;
|
|
1035
|
+
/** @internal Extract input value from a DOM event. */
|
|
1036
|
+
protected inputValue(event: Event): string;
|
|
1037
|
+
/** @internal Whether the field type supports options. */
|
|
1038
|
+
protected showOptions(field: MutableFieldDefinition): boolean;
|
|
1039
|
+
/** @internal Whether the field is a flair (non-data) component. */
|
|
1040
|
+
protected isFieldFlair(field: MutableFieldDefinition): boolean;
|
|
1041
|
+
/** @internal */
|
|
1042
|
+
protected addOption(field: MutableFieldDefinition): void;
|
|
1043
|
+
/** @internal */
|
|
1044
|
+
protected removeOption(field: MutableFieldDefinition, index: number): void;
|
|
1045
|
+
/** @internal */
|
|
1046
|
+
protected updateOption(field: MutableFieldDefinition, index: number, key: "label" | "value", value: string): void;
|
|
1047
|
+
/** @internal */
|
|
1048
|
+
protected addValidationRule(field: MutableFieldDefinition): void;
|
|
1049
|
+
/** @internal */
|
|
1050
|
+
protected removeValidationRule(field: MutableFieldDefinition, index: number): void;
|
|
1051
|
+
/** @internal */
|
|
1052
|
+
protected updateValidationRule(field: MutableFieldDefinition, index: number, key: string, value: string): void;
|
|
1053
|
+
/** @internal */
|
|
1054
|
+
protected ruleHasParam(type: string): boolean;
|
|
1055
|
+
/** @internal */
|
|
1056
|
+
protected paramPlaceholder(type: string): string;
|
|
1057
|
+
/** @internal */
|
|
1058
|
+
protected ruleParamValue(rule: ValidationRule): string;
|
|
1059
|
+
/** @internal */
|
|
1060
|
+
protected updateValidationParam(field: MutableFieldDefinition, index: number, type: string, value: string): void;
|
|
1061
|
+
/** @internal Returns the config property schemas for the field's component. */
|
|
1062
|
+
protected configSchemaFor(field: MutableFieldDefinition): readonly ConfigPropertySchema[];
|
|
1063
|
+
/** @internal Read a single config value. */
|
|
1064
|
+
protected configValue(field: MutableFieldDefinition, key: string): unknown;
|
|
1065
|
+
/** @internal Set a string config value (removes key if empty). */
|
|
1066
|
+
protected setConfigValue(field: MutableFieldDefinition, key: string, value: string): void;
|
|
1067
|
+
/** @internal Set a numeric config value (removes key if empty/NaN). */
|
|
1068
|
+
protected setConfigNumber(field: MutableFieldDefinition, key: string, raw: string): void;
|
|
1069
|
+
/** @internal Set a boolean config value. */
|
|
1070
|
+
protected setConfigBoolean(field: MutableFieldDefinition, key: string, checked: boolean): void;
|
|
1071
|
+
/** @internal Normalize select options to SelectOption[] with empty option. */
|
|
1072
|
+
protected configSelectOptions(prop: ConfigPropertySchema): SelectOption[];
|
|
1073
|
+
/** @internal Open a richtext editor dialog for the given config key. */
|
|
1074
|
+
protected openRichTextEditor(field: MutableFieldDefinition, key: string): void;
|
|
1075
|
+
/** @internal Whether the field has config keys not covered by the schema. */
|
|
1076
|
+
protected hasExtraConfig(field: MutableFieldDefinition): boolean;
|
|
1077
|
+
/** @internal JSON string of config keys NOT in the structured schema. */
|
|
1078
|
+
protected extraConfigJSON(field: MutableFieldDefinition): string;
|
|
1079
|
+
/** @internal Parse and merge extra config JSON back into the field. */
|
|
1080
|
+
protected onExtraConfigChange(field: MutableFieldDefinition, event: Event): void;
|
|
1081
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<UIPropertyInspector, never>;
|
|
1082
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<UIPropertyInspector, "ui-property-inspector", never, { "engine": { "alias": "engine"; "required": true; "isSignal": true; }; }, {}, never, never, true, [{ directive: typeof i1.UISurface; inputs: { "surfaceType": "surfaceType"; }; outputs: {}; }]>;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
/**
|
|
1086
|
+
* Full-featured form designer that lets users visually build
|
|
1087
|
+
* a {@link FormSchema} via a palette + canvas + inspector layout.
|
|
1088
|
+
*
|
|
1089
|
+
* Includes a live preview panel that renders the designed form
|
|
1090
|
+
* using {@link UIForm} and {@link FormEngine}.
|
|
1091
|
+
*
|
|
1092
|
+
* @example
|
|
1093
|
+
* ```html
|
|
1094
|
+
* <ui-form-designer
|
|
1095
|
+
* [schema]="existingSchema"
|
|
1096
|
+
* (schemaChange)="onSave($event)"
|
|
1097
|
+
* />
|
|
1098
|
+
* ```
|
|
1099
|
+
*/
|
|
1100
|
+
declare class UIFormDesigner {
|
|
1101
|
+
/**
|
|
1102
|
+
* Optional initial schema to load into the designer.
|
|
1103
|
+
* When set, the designer engine imports it on init.
|
|
1104
|
+
*/
|
|
1105
|
+
readonly schema: _angular_core.InputSignal<FormSchema | null>;
|
|
1106
|
+
/** Emitted when the user clicks "Export" — always emits the raw schema. */
|
|
1107
|
+
readonly schemaChange: _angular_core.OutputEmitterRef<FormSchema>;
|
|
1108
|
+
/** @internal Icon SVG for the copy button. */
|
|
1109
|
+
protected readonly copyIcon: "<rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\" /><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\" />";
|
|
1110
|
+
/** @internal Active tab: design, preview, or json. */
|
|
1111
|
+
protected readonly activeTab: _angular_core.WritableSignal<"json" | "design" | "preview">;
|
|
1112
|
+
/** @internal The designer engine instance. */
|
|
1113
|
+
protected readonly designerEngine: FormDesignerEngine;
|
|
1114
|
+
/**
|
|
1115
|
+
* @internal Preview engine — rebuilt whenever the schema changes.
|
|
1116
|
+
* Returns null if the schema has no groups/fields.
|
|
1117
|
+
*/
|
|
1118
|
+
protected readonly previewEngine: _angular_core.Signal<FormEngine | null>;
|
|
1119
|
+
/** @internal Live output from the preview engine. */
|
|
1120
|
+
protected readonly previewOutput: _angular_core.Signal<FormValues | null>;
|
|
1121
|
+
/** @internal The group uid to add a field to when palette is clicked. */
|
|
1122
|
+
private lastGroupUid;
|
|
1123
|
+
constructor();
|
|
1124
|
+
/** @internal Handle palette field click — add to first or last-used group. */
|
|
1125
|
+
protected onFieldRequested(componentKey: string): void;
|
|
1126
|
+
/** @internal Handle "Add field" click within a specific group. */
|
|
1127
|
+
protected onAddFieldToGroup(groupUid: string): void;
|
|
1128
|
+
/** @internal Copy the current JSON schema to the clipboard. */
|
|
1129
|
+
protected onCopyJson(): void;
|
|
1130
|
+
/** @internal Show submitted values in a native dialog. */
|
|
1131
|
+
protected onPreviewSubmit(values: FormValues): void;
|
|
1132
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<UIFormDesigner, never>;
|
|
1133
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<UIFormDesigner, "ui-form-designer", never, { "schema": { "alias": "schema"; "required": false; "isSignal": true; }; }, { "schemaChange": "schemaChange"; }, never, never, true, [{ directive: typeof i1.UISurface; inputs: { "surfaceType": "surfaceType"; }; outputs: {}; }]>;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
export { AngularComponentExportStrategy, BUILT_IN_FIELDS, FLAIR_COMPONENTS, FORM_FIELD_REGISTRATIONS, FormDesignerEngine, FormEngine, FormFieldRegistry, JsonExportStrategy, TEXT_ADAPTER_KEYS, UIDesignerCanvas, UIFieldPalette, UIForm, UIFormDesigner, UIFormField, UIFormGroup, UIFormWizard, UIPropertyInspector, evaluateCondition, isConditionGroup, isFlairComponent, provideBuiltInFormFields, provideFormFields, registerCustomValidator, resolveTextAdapter, validate };
|
|
1137
|
+
export type { Condition, ConditionGroup, ConditionOperator, ConfigTransform, DesignerSelection, DesignerSelectionKind, ExportResult, ExportStrategy, FieldCondition, FieldState, FlairComponent, FormFieldDefinition, FormFieldOption, FormFieldRegistration, FormGroupDefinition, FormSchema, FormValues, GroupState, MutableFieldDefinition, MutableGroupDefinition, PaletteFieldType, ValidationError, ValidationResult, ValidationRule, ValidationRuleType, ValidatorFn };
|
|
1138
|
+
//# sourceMappingURL=theredhead-lucid-forms.d.ts.map
|