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