@dxos/react-ui-form 0.7.3-staging.cc8dd3e → 0.7.3-staging.d887a4b
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/dist/lib/browser/index.mjs +191 -31
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +191 -30
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +191 -31
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/Form/Defaults.d.ts +4 -4
- package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +8 -0
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +11 -0
- package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/Input.d.ts +5 -4
- package/dist/types/src/components/Form/Input.d.ts.map +1 -1
- package/dist/types/src/hooks/useForm.d.ts +7 -6
- package/dist/types/src/hooks/useForm.d.ts.map +1 -1
- package/package.json +23 -23
- package/src/components/Form/Defaults.tsx +28 -13
- package/src/components/Form/Form.stories.tsx +42 -10
- package/src/components/Form/Form.tsx +204 -28
- package/src/components/Form/Input.tsx +5 -4
- package/src/components/ViewEditor/ViewEditor.tsx +2 -2
- package/src/hooks/useForm.ts +36 -18
|
@@ -91,7 +91,7 @@ import { pipe } from "effect";
|
|
|
91
91
|
import { capitalize } from "effect/String";
|
|
92
92
|
import React5, { useEffect as useEffect2, useMemo as useMemo2 } from "react";
|
|
93
93
|
import { AST, S } from "@dxos/echo-schema";
|
|
94
|
-
import { findNode, getDiscriminatedType, isDiscriminatedUnion } from "@dxos/effect";
|
|
94
|
+
import { findNode, getDiscriminatedType, isDiscriminatedUnion, SimpleType } from "@dxos/effect";
|
|
95
95
|
import { log as log2 } from "@dxos/log";
|
|
96
96
|
import { IconButton, useTranslation as useTranslation2 } from "@dxos/react-ui";
|
|
97
97
|
import { mx } from "@dxos/react-ui-theme";
|
|
@@ -122,11 +122,11 @@ var InputHeader = ({ children, error }) => {
|
|
|
122
122
|
};
|
|
123
123
|
|
|
124
124
|
// packages/ui/react-ui-form/src/components/Form/Defaults.tsx
|
|
125
|
-
var TextInput = ({ property, type, label, disabled, placeholder, getStatus, getValue, onValueChange, onBlur }) => {
|
|
125
|
+
var TextInput = ({ property, type, label, inputOnly, disabled, placeholder, getStatus, getValue, onValueChange, onBlur }) => {
|
|
126
126
|
const { status, error } = getStatus?.(property);
|
|
127
127
|
return /* @__PURE__ */ React3.createElement(Input2.Root, {
|
|
128
128
|
validationValence: status
|
|
129
|
-
}, /* @__PURE__ */ React3.createElement(InputHeader, {
|
|
129
|
+
}, !inputOnly && /* @__PURE__ */ React3.createElement(InputHeader, {
|
|
130
130
|
error
|
|
131
131
|
}, /* @__PURE__ */ React3.createElement(Input2.Label, null, label)), /* @__PURE__ */ React3.createElement(Input2.TextInput, {
|
|
132
132
|
disabled,
|
|
@@ -134,13 +134,13 @@ var TextInput = ({ property, type, label, disabled, placeholder, getStatus, getV
|
|
|
134
134
|
value: getValue(property) ?? "",
|
|
135
135
|
onChange: (event) => onValueChange(property, type, event.target.value),
|
|
136
136
|
onBlur
|
|
137
|
-
}));
|
|
137
|
+
}), inputOnly && /* @__PURE__ */ React3.createElement(Input2.Validation, null, error));
|
|
138
138
|
};
|
|
139
|
-
var NumberInput = ({ property, type, label, disabled, placeholder, getStatus, getValue, onValueChange, onBlur }) => {
|
|
139
|
+
var NumberInput = ({ property, type, label, inputOnly, disabled, placeholder, getStatus, getValue, onValueChange, onBlur }) => {
|
|
140
140
|
const { status, error } = getStatus?.(property);
|
|
141
141
|
return /* @__PURE__ */ React3.createElement(Input2.Root, {
|
|
142
142
|
validationValence: status
|
|
143
|
-
}, /* @__PURE__ */ React3.createElement(InputHeader, {
|
|
143
|
+
}, !inputOnly && /* @__PURE__ */ React3.createElement(InputHeader, {
|
|
144
144
|
error
|
|
145
145
|
}, /* @__PURE__ */ React3.createElement(Input2.Label, null, label)), /* @__PURE__ */ React3.createElement(Input2.TextInput, {
|
|
146
146
|
type: "number",
|
|
@@ -149,24 +149,24 @@ var NumberInput = ({ property, type, label, disabled, placeholder, getStatus, ge
|
|
|
149
149
|
value: getValue(property) ?? 0,
|
|
150
150
|
onChange: (event) => onValueChange(property, type, event.target.value),
|
|
151
151
|
onBlur
|
|
152
|
-
}));
|
|
152
|
+
}), inputOnly && /* @__PURE__ */ React3.createElement(Input2.DescriptionAndValidation, null, error));
|
|
153
153
|
};
|
|
154
|
-
var BooleanInput = ({ property, type, label, getStatus, getValue, onValueChange }) => {
|
|
154
|
+
var BooleanInput = ({ property, type, label, inputOnly, getStatus, getValue, onValueChange }) => {
|
|
155
155
|
const { status, error } = getStatus?.(property);
|
|
156
156
|
return /* @__PURE__ */ React3.createElement(Input2.Root, {
|
|
157
157
|
validationValence: status
|
|
158
|
-
}, /* @__PURE__ */ React3.createElement(InputHeader, {
|
|
158
|
+
}, !inputOnly && /* @__PURE__ */ React3.createElement(InputHeader, {
|
|
159
159
|
error
|
|
160
160
|
}, /* @__PURE__ */ React3.createElement(Input2.Label, null, label)), /* @__PURE__ */ React3.createElement(Input2.Switch, {
|
|
161
161
|
checked: getValue(property),
|
|
162
162
|
onCheckedChange: (value) => onValueChange(property, type, value)
|
|
163
|
-
}));
|
|
163
|
+
}), inputOnly && /* @__PURE__ */ React3.createElement(Input2.DescriptionAndValidation, null, error));
|
|
164
164
|
};
|
|
165
|
-
var SelectInput = ({ property, type, label, disabled, placeholder, options, getStatus, getValue, onValueChange }) => {
|
|
165
|
+
var SelectInput = ({ property, type, label, inputOnly, disabled, placeholder, options, getStatus, getValue, onValueChange }) => {
|
|
166
166
|
const { status, error } = getStatus?.(property);
|
|
167
167
|
return /* @__PURE__ */ React3.createElement(Input2.Root, {
|
|
168
168
|
validationValence: status
|
|
169
|
-
}, /* @__PURE__ */ React3.createElement(InputHeader, {
|
|
169
|
+
}, !inputOnly && /* @__PURE__ */ React3.createElement(InputHeader, {
|
|
170
170
|
error
|
|
171
171
|
}, /* @__PURE__ */ React3.createElement(Input2.Label, null, label)), /* @__PURE__ */ React3.createElement(Select.Root, {
|
|
172
172
|
value: getValue(property),
|
|
@@ -181,7 +181,7 @@ var SelectInput = ({ property, type, label, disabled, placeholder, options, getS
|
|
|
181
181
|
key: String(value),
|
|
182
182
|
value: String(value)
|
|
183
183
|
}, label2 ?? String(value))
|
|
184
|
-
)))))));
|
|
184
|
+
)))))), inputOnly && /* @__PURE__ */ React3.createElement(Input2.DescriptionAndValidation, null, error));
|
|
185
185
|
};
|
|
186
186
|
|
|
187
187
|
// packages/ui/react-ui-form/src/components/Form/factory.tsx
|
|
@@ -317,7 +317,7 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
|
|
|
317
317
|
errors
|
|
318
318
|
]);
|
|
319
319
|
const getValue = (property) => {
|
|
320
|
-
return values
|
|
320
|
+
return getByPath(values, property);
|
|
321
321
|
};
|
|
322
322
|
const onValueChange = (property, type, value) => {
|
|
323
323
|
let parsedValue = value;
|
|
@@ -328,16 +328,13 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
|
|
|
328
328
|
} catch (err) {
|
|
329
329
|
log.catch(err, void 0, {
|
|
330
330
|
F: __dxlog_file,
|
|
331
|
-
L:
|
|
331
|
+
L: 183,
|
|
332
332
|
S: void 0,
|
|
333
333
|
C: (f, a) => f(...a)
|
|
334
334
|
});
|
|
335
335
|
parsedValue = void 0;
|
|
336
336
|
}
|
|
337
|
-
const newValues =
|
|
338
|
-
...values,
|
|
339
|
-
[property]: parsedValue
|
|
340
|
-
};
|
|
337
|
+
const newValues = setByPath(values, property, parsedValue);
|
|
341
338
|
setValues(newValues);
|
|
342
339
|
setChanged((prev) => ({
|
|
343
340
|
...prev,
|
|
@@ -383,7 +380,7 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
|
|
|
383
380
|
var createKeySet = (obj, value) => {
|
|
384
381
|
invariant(obj, void 0, {
|
|
385
382
|
F: __dxlog_file,
|
|
386
|
-
L:
|
|
383
|
+
L: 234,
|
|
387
384
|
S: void 0,
|
|
388
385
|
A: [
|
|
389
386
|
"obj",
|
|
@@ -403,12 +400,25 @@ var flatMap = (errors) => {
|
|
|
403
400
|
return result;
|
|
404
401
|
}, {});
|
|
405
402
|
};
|
|
403
|
+
var getByPath = (obj, path) => {
|
|
404
|
+
const [key, index] = path.split(".");
|
|
405
|
+
return index === void 0 ? obj[key] : obj[key][parseInt(index)];
|
|
406
|
+
};
|
|
407
|
+
var setByPath = (obj, path, value) => {
|
|
408
|
+
const [key, index] = path.split(".");
|
|
409
|
+
const newValue = index === void 0 ? value : [
|
|
410
|
+
...obj[key]
|
|
411
|
+
].map((v, i) => i === parseInt(index) ? value : v);
|
|
412
|
+
return {
|
|
413
|
+
...obj,
|
|
414
|
+
[key]: newValue
|
|
415
|
+
};
|
|
416
|
+
};
|
|
406
417
|
|
|
407
418
|
// packages/ui/react-ui-form/src/components/Form/Form.tsx
|
|
408
419
|
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/components/Form/Form.tsx";
|
|
409
420
|
var padding = "px-2";
|
|
410
421
|
var Form = ({ classNames, schema, values: initialValues, path = [], readonly, filter, sort, autoSave, testId, onValuesChanged, onValidate, onSave, onCancel, Custom }) => {
|
|
411
|
-
const { t } = useTranslation2(translationKey);
|
|
412
422
|
const onValid = useMemo2(() => autoSave ? onSave : void 0, [
|
|
413
423
|
autoSave,
|
|
414
424
|
onSave
|
|
@@ -440,7 +450,7 @@ var Form = ({ classNames, schema, values: initialValues, path = [], readonly, fi
|
|
|
440
450
|
errors
|
|
441
451
|
}, {
|
|
442
452
|
F: __dxlog_file2,
|
|
443
|
-
L:
|
|
453
|
+
L: 100,
|
|
444
454
|
S: void 0,
|
|
445
455
|
C: (f, a) => f(...a)
|
|
446
456
|
});
|
|
@@ -453,7 +463,7 @@ var Form = ({ classNames, schema, values: initialValues, path = [], readonly, fi
|
|
|
453
463
|
className: mx("flex flex-col w-full gap-2 py-2", classNames),
|
|
454
464
|
"data-testid": testId
|
|
455
465
|
}, properties.map((property) => {
|
|
456
|
-
const { ast, name, type, format, title, description, examples, options } = property;
|
|
466
|
+
const { ast, name, type, format, title, description, examples, options, array } = property;
|
|
457
467
|
const key = [
|
|
458
468
|
...path,
|
|
459
469
|
name
|
|
@@ -482,6 +492,22 @@ var Form = ({ classNames, schema, values: initialValues, path = [], readonly, fi
|
|
|
482
492
|
}));
|
|
483
493
|
}
|
|
484
494
|
InputComponent = getInputComponent(type, format);
|
|
495
|
+
if (array) {
|
|
496
|
+
return /* @__PURE__ */ React5.createElement(ArrayField, {
|
|
497
|
+
key: name,
|
|
498
|
+
name,
|
|
499
|
+
type,
|
|
500
|
+
array: true,
|
|
501
|
+
label,
|
|
502
|
+
ast,
|
|
503
|
+
values,
|
|
504
|
+
format,
|
|
505
|
+
readonly,
|
|
506
|
+
placeholder,
|
|
507
|
+
inputProps,
|
|
508
|
+
Custom
|
|
509
|
+
});
|
|
510
|
+
}
|
|
485
511
|
}
|
|
486
512
|
if (!InputComponent) {
|
|
487
513
|
if (type === "object") {
|
|
@@ -510,7 +536,7 @@ var Form = ({ classNames, schema, values: initialValues, path = [], readonly, fi
|
|
|
510
536
|
type
|
|
511
537
|
}, {
|
|
512
538
|
F: __dxlog_file2,
|
|
513
|
-
L:
|
|
539
|
+
L: 185,
|
|
514
540
|
S: void 0,
|
|
515
541
|
C: (f, a) => f(...a)
|
|
516
542
|
});
|
|
@@ -529,7 +555,140 @@ var Form = ({ classNames, schema, values: initialValues, path = [], readonly, fi
|
|
|
529
555
|
placeholder,
|
|
530
556
|
...inputProps
|
|
531
557
|
}));
|
|
532
|
-
}).filter(isNotFalsy), (onCancel || onSave) && !autoSave && /* @__PURE__ */ React5.createElement(
|
|
558
|
+
}).filter(isNotFalsy), (onCancel || onSave) && !autoSave && /* @__PURE__ */ React5.createElement(FormActions, {
|
|
559
|
+
onCancel,
|
|
560
|
+
onSubmit: handleSave,
|
|
561
|
+
canSubmit: canSave,
|
|
562
|
+
readonly
|
|
563
|
+
}));
|
|
564
|
+
};
|
|
565
|
+
var ArrayField = ({ name, type, label, ast, values, format, readonly, placeholder, inputProps, Custom }) => {
|
|
566
|
+
const { t } = useTranslation2(translationKey);
|
|
567
|
+
const arrayValues = values[name] ?? [];
|
|
568
|
+
const key = [
|
|
569
|
+
name
|
|
570
|
+
];
|
|
571
|
+
const tupleType = findNode(ast, AST.isTupleType);
|
|
572
|
+
const elementType = tupleType?.rest[0]?.type;
|
|
573
|
+
if (type === "object" && elementType) {
|
|
574
|
+
const baseNode = findNode(elementType, isDiscriminatedUnion);
|
|
575
|
+
const typeLiteral = baseNode ? getDiscriminatedType(baseNode, values[name]) : findNode(elementType, AST.isTypeLiteral);
|
|
576
|
+
if (typeLiteral) {
|
|
577
|
+
const schema = S.make(typeLiteral);
|
|
578
|
+
return /* @__PURE__ */ React5.createElement("div", {
|
|
579
|
+
key: name,
|
|
580
|
+
role: "none",
|
|
581
|
+
className: mx(padding)
|
|
582
|
+
}, /* @__PURE__ */ React5.createElement(InputHeader, null, label), /* @__PURE__ */ React5.createElement("div", {
|
|
583
|
+
role: "none",
|
|
584
|
+
className: "flex flex-col gap-1"
|
|
585
|
+
}, arrayValues.map((value, index) => {
|
|
586
|
+
return /* @__PURE__ */ React5.createElement("div", {
|
|
587
|
+
key: index,
|
|
588
|
+
role: "none",
|
|
589
|
+
className: "flex items-center gap-1"
|
|
590
|
+
}, /* @__PURE__ */ React5.createElement("div", {
|
|
591
|
+
role: "none",
|
|
592
|
+
className: "flex-1"
|
|
593
|
+
}, /* @__PURE__ */ React5.createElement(Form, {
|
|
594
|
+
schema,
|
|
595
|
+
path: [
|
|
596
|
+
...key,
|
|
597
|
+
index.toString()
|
|
598
|
+
],
|
|
599
|
+
values: value,
|
|
600
|
+
onValuesChanged: (childValues) => {
|
|
601
|
+
const newValues = [
|
|
602
|
+
...arrayValues
|
|
603
|
+
];
|
|
604
|
+
newValues[index] = childValues;
|
|
605
|
+
inputProps.onValueChange(name, "object", newValues);
|
|
606
|
+
},
|
|
607
|
+
Custom
|
|
608
|
+
})), /* @__PURE__ */ React5.createElement(IconButton, {
|
|
609
|
+
icon: "ph--trash--regular",
|
|
610
|
+
iconOnly: true,
|
|
611
|
+
label: t("button remove"),
|
|
612
|
+
onClick: () => {
|
|
613
|
+
const newValues = arrayValues.filter((_, i) => i !== index);
|
|
614
|
+
inputProps.onValueChange(name, type, newValues);
|
|
615
|
+
}
|
|
616
|
+
}));
|
|
617
|
+
})), /* @__PURE__ */ React5.createElement("div", {
|
|
618
|
+
role: "none",
|
|
619
|
+
className: "flex justify-between items-center plb-1"
|
|
620
|
+
}, /* @__PURE__ */ React5.createElement(IconButton, {
|
|
621
|
+
icon: "ph--plus--regular",
|
|
622
|
+
iconOnly: true,
|
|
623
|
+
label: t("button add"),
|
|
624
|
+
onClick: () => {
|
|
625
|
+
const newValues = [
|
|
626
|
+
...arrayValues,
|
|
627
|
+
{}
|
|
628
|
+
];
|
|
629
|
+
inputProps.onValueChange(name, type, newValues);
|
|
630
|
+
}
|
|
631
|
+
})));
|
|
632
|
+
}
|
|
633
|
+
return /* @__PURE__ */ React5.createElement("div", {
|
|
634
|
+
key: name,
|
|
635
|
+
role: "none"
|
|
636
|
+
}, "Nested form not supported in arrays, yet.");
|
|
637
|
+
}
|
|
638
|
+
const InputComponent = getInputComponent(type, format);
|
|
639
|
+
if (!InputComponent) {
|
|
640
|
+
return null;
|
|
641
|
+
}
|
|
642
|
+
return /* @__PURE__ */ React5.createElement("div", {
|
|
643
|
+
role: "none",
|
|
644
|
+
key: "meta-input",
|
|
645
|
+
className: mx(padding)
|
|
646
|
+
}, /* @__PURE__ */ React5.createElement(InputHeader, null, label), /* @__PURE__ */ React5.createElement("div", {
|
|
647
|
+
role: "none",
|
|
648
|
+
className: "flex flex-col gap-1"
|
|
649
|
+
}, arrayValues.map((_, index) => /* @__PURE__ */ React5.createElement("div", {
|
|
650
|
+
key: index,
|
|
651
|
+
role: "none",
|
|
652
|
+
className: "flex items-start gap-1"
|
|
653
|
+
}, /* @__PURE__ */ React5.createElement("div", {
|
|
654
|
+
role: "none",
|
|
655
|
+
className: "flex-1"
|
|
656
|
+
}, /* @__PURE__ */ React5.createElement(InputComponent, {
|
|
657
|
+
type,
|
|
658
|
+
format,
|
|
659
|
+
label,
|
|
660
|
+
inputOnly: true,
|
|
661
|
+
property: `${name}.${index}`,
|
|
662
|
+
disabled: readonly,
|
|
663
|
+
placeholder,
|
|
664
|
+
...inputProps
|
|
665
|
+
})), /* @__PURE__ */ React5.createElement(IconButton, {
|
|
666
|
+
icon: "ph--trash--regular",
|
|
667
|
+
iconOnly: true,
|
|
668
|
+
label: t("button remove"),
|
|
669
|
+
onClick: () => {
|
|
670
|
+
const newValues = arrayValues.filter((_2, i) => i !== index);
|
|
671
|
+
inputProps.onValueChange(name, type, newValues);
|
|
672
|
+
}
|
|
673
|
+
})))), /* @__PURE__ */ React5.createElement("div", {
|
|
674
|
+
role: "none",
|
|
675
|
+
className: "flex justify-between items-center plb-1"
|
|
676
|
+
}, /* @__PURE__ */ React5.createElement(IconButton, {
|
|
677
|
+
icon: "ph--plus--regular",
|
|
678
|
+
iconOnly: true,
|
|
679
|
+
label: t("button add"),
|
|
680
|
+
onClick: () => {
|
|
681
|
+
const newValues = [
|
|
682
|
+
...arrayValues,
|
|
683
|
+
SimpleType.getDefaultValue(type)
|
|
684
|
+
];
|
|
685
|
+
inputProps.onValueChange(name, type, newValues);
|
|
686
|
+
}
|
|
687
|
+
})));
|
|
688
|
+
};
|
|
689
|
+
var FormActions = ({ onCancel, onSubmit, canSubmit, readonly }) => {
|
|
690
|
+
const { t } = useTranslation2(translationKey);
|
|
691
|
+
return /* @__PURE__ */ React5.createElement("div", {
|
|
533
692
|
role: "none",
|
|
534
693
|
className: "flex justify-center"
|
|
535
694
|
}, /* @__PURE__ */ React5.createElement("div", {
|
|
@@ -540,14 +699,14 @@ var Form = ({ classNames, schema, values: initialValues, path = [], readonly, fi
|
|
|
540
699
|
icon: "ph--x--regular",
|
|
541
700
|
label: t("button cancel"),
|
|
542
701
|
onClick: onCancel
|
|
543
|
-
}),
|
|
702
|
+
}), onSubmit && /* @__PURE__ */ React5.createElement(IconButton, {
|
|
544
703
|
type: "submit",
|
|
545
704
|
"data-testid": "save-button",
|
|
546
|
-
disabled: !
|
|
705
|
+
disabled: !canSubmit,
|
|
547
706
|
icon: "ph--check--regular",
|
|
548
707
|
label: t("button save"),
|
|
549
|
-
onClick:
|
|
550
|
-
})))
|
|
708
|
+
onClick: onSubmit
|
|
709
|
+
})));
|
|
551
710
|
};
|
|
552
711
|
|
|
553
712
|
// packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx
|
|
@@ -718,7 +877,7 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
|
|
|
718
877
|
name: view.name,
|
|
719
878
|
// TODO(burdon): Need to warn user of possible consequences of editing.
|
|
720
879
|
// TODO(burdon): Settings should have domain name owned by user.
|
|
721
|
-
typename: view.query.
|
|
880
|
+
typename: view.query.type
|
|
722
881
|
};
|
|
723
882
|
}, [
|
|
724
883
|
view
|
|
@@ -726,7 +885,7 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
|
|
|
726
885
|
const handleViewUpdate = useCallback3(({ name, typename }) => {
|
|
727
886
|
requestAnimationFrame(() => {
|
|
728
887
|
view.name = name;
|
|
729
|
-
view.query.
|
|
888
|
+
view.query.type = typename;
|
|
730
889
|
schema.updateTypename(typename);
|
|
731
890
|
});
|
|
732
891
|
}, [
|
|
@@ -1026,6 +1185,7 @@ export {
|
|
|
1026
1185
|
DeprecatedFormInput,
|
|
1027
1186
|
FieldEditor,
|
|
1028
1187
|
Form,
|
|
1188
|
+
FormActions,
|
|
1029
1189
|
GeiPointInput,
|
|
1030
1190
|
InputHeader,
|
|
1031
1191
|
NumberInput,
|