@aglyn/plugins-mui 1.0.0-beta.246 → 1.0.0-beta.249
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/package.json +7 -7
- package/src/lib/components/button.js +24 -0
- package/src/lib/components/button.js.map +1 -1
- package/src/lib/components/form-controls.d.ts +120 -0
- package/src/lib/components/form-controls.js +845 -0
- package/src/lib/components/form-controls.js.map +1 -0
- package/src/lib/element-tier.d.ts +1 -0
- package/src/lib/element-tier.js +1 -0
- package/src/lib/element-tier.js.map +1 -1
- package/src/lib/plugin.js +58 -0
- package/src/lib/plugin.js.map +1 -1
|
@@ -0,0 +1,845 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
|
|
3
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
4
|
+
/**
|
|
5
|
+
* @license
|
|
6
|
+
* Copyright 2026 Aglyn LLC
|
|
7
|
+
*
|
|
8
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
9
|
+
* you may not use this file except in compliance with the License.
|
|
10
|
+
* You may obtain a copy of the License at
|
|
11
|
+
*
|
|
12
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
13
|
+
*
|
|
14
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
15
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
16
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
17
|
+
* See the License for the specific language governing permissions and
|
|
18
|
+
* limitations under the License.
|
|
19
|
+
*/ import * as Aglyn from "@aglyn/aglyn";
|
|
20
|
+
import { mdiCheckboxMarkedOutline, mdiFormDropdown, mdiFormTextbox, mdiFormatText, mdiRadioboxMarked, mdiStarOutline, mdiTextShort, mdiTagOutline, mdiFormSelect } from "@aglyn/shared-data-mdi";
|
|
21
|
+
import Checkbox from "@mui/material/Checkbox";
|
|
22
|
+
import FormControl from "@mui/material/FormControl";
|
|
23
|
+
import FormControlLabel from "@mui/material/FormControlLabel";
|
|
24
|
+
import FormHelperText from "@mui/material/FormHelperText";
|
|
25
|
+
import FilledInput from "@mui/material/FilledInput";
|
|
26
|
+
import FormLabel from "@mui/material/FormLabel";
|
|
27
|
+
import NativeSelect from "@mui/material/NativeSelect";
|
|
28
|
+
import OutlinedInput from "@mui/material/OutlinedInput";
|
|
29
|
+
import Radio from "@mui/material/Radio";
|
|
30
|
+
import RadioGroup from "@mui/material/RadioGroup";
|
|
31
|
+
import Rating from "@mui/material/Rating";
|
|
32
|
+
import { alpha, useTheme } from "@mui/material/styles";
|
|
33
|
+
import { createContext, forwardRef, useContext, useId } from "react";
|
|
34
|
+
import { BUNDLE_ID } from "../constants/bundle-common.js";
|
|
35
|
+
import { FIELD_TEXT_CONTENT } from "../constants/field-presets.js";
|
|
36
|
+
import { dropClearedProps } from "../utils/drop-cleared-props.js";
|
|
37
|
+
import { generatePresetId } from "../utils/generate-preset-id.js";
|
|
38
|
+
/*
|
|
39
|
+
* MUI's FORM ELEMENTS, ONE BESIGNER ELEMENT EACH.
|
|
40
|
+
*
|
|
41
|
+
* A field is not one element: it is a Form Control holding a label, an input
|
|
42
|
+
* and a line of help text, exactly as MUI builds it. Each piece is placed,
|
|
43
|
+
* selected and styled on its own — a label's color, an input's fill and
|
|
44
|
+
* corners, the help text's size — with the ordinary Styles panel, and the
|
|
45
|
+
* presets at the bottom of this file are just ready-made arrangements of
|
|
46
|
+
* them. Nothing here knows about forms submitting: an input carries a NAME,
|
|
47
|
+
* and whatever form it sits in sends what the browser sends.
|
|
48
|
+
*
|
|
49
|
+
* Component ids are persisted in documents; never rename them.
|
|
50
|
+
*/ export const FORM_CONTROL_ID = 'muiFormControl';
|
|
51
|
+
export const FORM_LABEL_ID = 'muiFormLabel';
|
|
52
|
+
export const INPUT_ID = 'muiInput';
|
|
53
|
+
export const FORM_HELPER_TEXT_ID = 'muiFormHelperText';
|
|
54
|
+
export const SELECT_ID = 'muiSelect';
|
|
55
|
+
export const SELECT_OPTION_ID = 'muiSelectOption';
|
|
56
|
+
export const CHECKBOX_ID = 'muiCheckbox';
|
|
57
|
+
export const RADIO_GROUP_ID = 'muiRadioGroup';
|
|
58
|
+
export const RADIO_ID = 'muiRadio';
|
|
59
|
+
export const CHOICE_LABEL_ID = 'muiFormControlLabel';
|
|
60
|
+
export const RATING_ID = 'muiRating';
|
|
61
|
+
/**
|
|
62
|
+
* The id a Form Control gives the input inside it, so the Form Label beside
|
|
63
|
+
* that input names it (`for=`) without the author wiring anything.
|
|
64
|
+
*/ const FieldIdContext = /*#__PURE__*/ createContext(undefined);
|
|
65
|
+
/** A field's frame: label, input and help text together. */ export const AglynFormControl = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
66
|
+
const id = `field-${useId().replace(/:/g, '')}`;
|
|
67
|
+
return /*#__PURE__*/ _jsx(FieldIdContext.Provider, {
|
|
68
|
+
value: id,
|
|
69
|
+
children: /*#__PURE__*/ _jsx(FormControl, _extends({
|
|
70
|
+
ref: ref
|
|
71
|
+
}, dropClearedProps(props)))
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
AglynFormControl.displayName = 'AglynFormControl';
|
|
75
|
+
/** The label above an input, linked to it by the Form Control. */ export const AglynFormLabel = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
76
|
+
const id = useContext(FieldIdContext);
|
|
77
|
+
return /*#__PURE__*/ _jsx(FormLabel, _extends({
|
|
78
|
+
ref: ref,
|
|
79
|
+
component: "label"
|
|
80
|
+
}, id ? {
|
|
81
|
+
htmlFor: id
|
|
82
|
+
} : {}, dropClearedProps(props)));
|
|
83
|
+
});
|
|
84
|
+
AglynFormLabel.displayName = 'AglynFormLabel';
|
|
85
|
+
/**
|
|
86
|
+
* The outline an Input or Select draws, ON ITS OWN BOX.
|
|
87
|
+
*
|
|
88
|
+
* MUI paints an outlined input's border with an inner `<fieldset>`, so the
|
|
89
|
+
* Styles panel's Border Color, Border and Corner Radius — which reach the
|
|
90
|
+
* element's own box — would change nothing a visitor can see. Hiding that
|
|
91
|
+
* fieldset and drawing the same outline (MUI's resting, hover, focus and
|
|
92
|
+
* error colors) on the box itself puts every border and corner field on the
|
|
93
|
+
* outline you actually see. The node's own styles come after, so whatever an
|
|
94
|
+
* author sets wins; focus and error still show through their own rules.
|
|
95
|
+
*
|
|
96
|
+
* The resting outline sits under `:where()`, which weighs nothing, so the
|
|
97
|
+
* site theme's input border color (the Style components card) outranks it,
|
|
98
|
+
* as MUI's own corner radius and the theme's corners already do.
|
|
99
|
+
*/ const outlinedBoxSx = (theme)=>({
|
|
100
|
+
'*:where(&)': {
|
|
101
|
+
border: '1px solid',
|
|
102
|
+
borderColor: alpha(theme.palette.text.primary, 0.23)
|
|
103
|
+
},
|
|
104
|
+
'& .MuiOutlinedInput-notchedOutline': {
|
|
105
|
+
display: 'none'
|
|
106
|
+
},
|
|
107
|
+
'&:hover': {
|
|
108
|
+
borderColor: 'text.primary'
|
|
109
|
+
},
|
|
110
|
+
'&.Mui-focused': {
|
|
111
|
+
borderColor: 'primary.main',
|
|
112
|
+
boxShadow: `0 0 0 1px ${theme.palette.primary.main}`
|
|
113
|
+
},
|
|
114
|
+
'&.Mui-error': {
|
|
115
|
+
borderColor: 'error.main'
|
|
116
|
+
},
|
|
117
|
+
'&.Mui-disabled': {
|
|
118
|
+
borderColor: 'action.disabled'
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
/**
|
|
122
|
+
* Whether the site theme draws inputs filled: the Input style its Style
|
|
123
|
+
* components card sets, which is MUI's own text field default.
|
|
124
|
+
*/ const useFilledInputs = ()=>{
|
|
125
|
+
var _useTheme_components_MuiTextField_defaultProps, _useTheme_components_MuiTextField, _useTheme_components;
|
|
126
|
+
return ((_useTheme_components = useTheme().components) == null ? void 0 : (_useTheme_components_MuiTextField = _useTheme_components.MuiTextField) == null ? void 0 : (_useTheme_components_MuiTextField_defaultProps = _useTheme_components_MuiTextField.defaultProps) == null ? void 0 : _useTheme_components_MuiTextField_defaultProps.variant) === 'filled';
|
|
127
|
+
};
|
|
128
|
+
/** A node's `sx`, as a list the outline can go in front of. */ const nodeSxList = (sx)=>sx === undefined || sx === null ? [] : Array.isArray(sx) ? sx : [
|
|
129
|
+
sx
|
|
130
|
+
];
|
|
131
|
+
/** The box a visitor types in. */ export const AglynInput = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
132
|
+
const _dropClearedProps = dropClearedProps(props), { name, type, multiline, minRows, placeholder, required, ariaLabel, datasetFieldId: _datasetFieldId } = _dropClearedProps, rest = _object_without_properties_loose(_dropClearedProps, [
|
|
133
|
+
"name",
|
|
134
|
+
"type",
|
|
135
|
+
"multiline",
|
|
136
|
+
"minRows",
|
|
137
|
+
"placeholder",
|
|
138
|
+
"required",
|
|
139
|
+
"ariaLabel",
|
|
140
|
+
"datasetFieldId"
|
|
141
|
+
]);
|
|
142
|
+
const id = useContext(FieldIdContext);
|
|
143
|
+
const filled = useFilledInputs();
|
|
144
|
+
const inputProps = _extends({}, id ? {
|
|
145
|
+
id
|
|
146
|
+
} : {}, {
|
|
147
|
+
name: name || undefined,
|
|
148
|
+
type: multiline ? undefined : type || 'text',
|
|
149
|
+
multiline: Boolean(multiline)
|
|
150
|
+
}, multiline ? {
|
|
151
|
+
minRows: minRows || 3
|
|
152
|
+
} : {}, {
|
|
153
|
+
placeholder: placeholder || undefined,
|
|
154
|
+
required: Boolean(required),
|
|
155
|
+
inputProps: ariaLabel ? {
|
|
156
|
+
'aria-label': ariaLabel
|
|
157
|
+
} : undefined
|
|
158
|
+
});
|
|
159
|
+
if (filled) return /*#__PURE__*/ _jsx(FilledInput, _extends({
|
|
160
|
+
ref: ref
|
|
161
|
+
}, inputProps, rest));
|
|
162
|
+
return /*#__PURE__*/ _jsx(OutlinedInput, _extends({
|
|
163
|
+
ref: ref,
|
|
164
|
+
// The label is its own element above the box, so the outline never
|
|
165
|
+
// carries a notch for a floating one.
|
|
166
|
+
notched: false
|
|
167
|
+
}, inputProps, rest, {
|
|
168
|
+
sx: [
|
|
169
|
+
outlinedBoxSx,
|
|
170
|
+
...nodeSxList(rest['sx'])
|
|
171
|
+
]
|
|
172
|
+
}));
|
|
173
|
+
});
|
|
174
|
+
AglynInput.displayName = 'AglynInput';
|
|
175
|
+
/** One line of help or guidance under a field. */ export const AglynFormHelperText = /*#__PURE__*/ forwardRef((props, ref)=>/*#__PURE__*/ _jsx(FormHelperText, _extends({
|
|
176
|
+
ref: ref
|
|
177
|
+
}, dropClearedProps(props))));
|
|
178
|
+
AglynFormHelperText.displayName = 'AglynFormHelperText';
|
|
179
|
+
/**
|
|
180
|
+
* A dropdown. Its choices are Select Option elements inside it — the
|
|
181
|
+
* browser's own list, so every choice is a real element a page can edit.
|
|
182
|
+
*/ export const AglynSelect = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
183
|
+
const _dropClearedProps = dropClearedProps(props), { name, required, placeholder, ariaLabel, children, size, datasetFieldId: _datasetFieldId } = _dropClearedProps, rest = _object_without_properties_loose(_dropClearedProps, [
|
|
184
|
+
"name",
|
|
185
|
+
"required",
|
|
186
|
+
"placeholder",
|
|
187
|
+
"ariaLabel",
|
|
188
|
+
"children",
|
|
189
|
+
"size",
|
|
190
|
+
"datasetFieldId"
|
|
191
|
+
]);
|
|
192
|
+
const id = useContext(FieldIdContext);
|
|
193
|
+
const filled = useFilledInputs();
|
|
194
|
+
return /*#__PURE__*/ _jsxs(NativeSelect, _extends({
|
|
195
|
+
ref: ref,
|
|
196
|
+
input: filled ? /*#__PURE__*/ _jsx(FilledInput, {
|
|
197
|
+
size: size,
|
|
198
|
+
sx: rest['sx']
|
|
199
|
+
}) : /*#__PURE__*/ _jsx(OutlinedInput, {
|
|
200
|
+
notched: false,
|
|
201
|
+
size: size,
|
|
202
|
+
sx: [
|
|
203
|
+
outlinedBoxSx,
|
|
204
|
+
...nodeSxList(rest['sx'])
|
|
205
|
+
]
|
|
206
|
+
}),
|
|
207
|
+
defaultValue: "",
|
|
208
|
+
inputProps: _extends({}, id ? {
|
|
209
|
+
id
|
|
210
|
+
} : {}, {
|
|
211
|
+
name: name || undefined,
|
|
212
|
+
required: Boolean(required)
|
|
213
|
+
}, ariaLabel ? {
|
|
214
|
+
'aria-label': ariaLabel
|
|
215
|
+
} : {})
|
|
216
|
+
}, rest, {
|
|
217
|
+
// The node's styles ride the outlined box above, where they reach
|
|
218
|
+
// what a visitor sees.
|
|
219
|
+
sx: undefined,
|
|
220
|
+
children: [
|
|
221
|
+
/*#__PURE__*/ _jsx("option", {
|
|
222
|
+
value: "",
|
|
223
|
+
disabled: Boolean(required),
|
|
224
|
+
children: placeholder || ''
|
|
225
|
+
}),
|
|
226
|
+
children
|
|
227
|
+
]
|
|
228
|
+
}));
|
|
229
|
+
});
|
|
230
|
+
AglynSelect.displayName = 'AglynSelect';
|
|
231
|
+
/** One choice in a Select. */ export const AglynSelectOption = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
232
|
+
const _dropClearedProps = dropClearedProps(props), { value, children } = _dropClearedProps, rest = _object_without_properties_loose(_dropClearedProps, [
|
|
233
|
+
"value",
|
|
234
|
+
"children"
|
|
235
|
+
]);
|
|
236
|
+
const text = typeof children === 'string' ? children : '';
|
|
237
|
+
return /*#__PURE__*/ _jsx("option", _extends({
|
|
238
|
+
ref: ref,
|
|
239
|
+
value: value != null ? value : text
|
|
240
|
+
}, rest, {
|
|
241
|
+
children: children
|
|
242
|
+
}));
|
|
243
|
+
});
|
|
244
|
+
AglynSelectOption.displayName = 'AglynSelectOption';
|
|
245
|
+
/**
|
|
246
|
+
* The props an answer element hands MUI: cleared values dropped, and the
|
|
247
|
+
* dataset mapping — authoring data read off the node, never a DOM attribute —
|
|
248
|
+
* taken out.
|
|
249
|
+
*/ function withoutMapping(props) {
|
|
250
|
+
const _dropClearedProps = dropClearedProps(props), { datasetFieldId: _datasetFieldId } = _dropClearedProps, rest = _object_without_properties_loose(_dropClearedProps, [
|
|
251
|
+
"datasetFieldId"
|
|
252
|
+
]);
|
|
253
|
+
return rest;
|
|
254
|
+
}
|
|
255
|
+
/** A tick box. Put it in a Choice Label to give it words. */ export const AglynCheckbox = /*#__PURE__*/ forwardRef((props, ref)=>/*#__PURE__*/ _jsx(Checkbox, _extends({
|
|
256
|
+
ref: ref
|
|
257
|
+
}, withoutMapping(props))));
|
|
258
|
+
AglynCheckbox.displayName = 'AglynCheckbox';
|
|
259
|
+
/** A set of Radio choices sharing one name; one may be picked. */ export const AglynRadioGroup = /*#__PURE__*/ forwardRef((props, ref)=>/*#__PURE__*/ _jsx(RadioGroup, _extends({
|
|
260
|
+
ref: ref
|
|
261
|
+
}, withoutMapping(props))));
|
|
262
|
+
AglynRadioGroup.displayName = 'AglynRadioGroup';
|
|
263
|
+
/** One choice in a Radio Group. Put it in a Choice Label to give it words. */ export const AglynRadio = /*#__PURE__*/ forwardRef((props, ref)=>/*#__PURE__*/ _jsx(Radio, _extends({
|
|
264
|
+
ref: ref
|
|
265
|
+
}, dropClearedProps(props))));
|
|
266
|
+
AglynRadio.displayName = 'AglynRadio';
|
|
267
|
+
/**
|
|
268
|
+
* The words beside a Checkbox or Radio, clickable as one with it — MUI's
|
|
269
|
+
* FormControlLabel, holding the control as its child element.
|
|
270
|
+
*/ export const AglynChoiceLabel = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
271
|
+
const _dropClearedProps = dropClearedProps(props), { label, children } = _dropClearedProps, rest = _object_without_properties_loose(_dropClearedProps, [
|
|
272
|
+
"label",
|
|
273
|
+
"children"
|
|
274
|
+
]);
|
|
275
|
+
return /*#__PURE__*/ _jsx(FormControlLabel, _extends({
|
|
276
|
+
ref: ref,
|
|
277
|
+
label: label != null ? label : '',
|
|
278
|
+
// The control is whatever element the author placed inside; a
|
|
279
|
+
// `display: contents` holder keeps it the label's direct box.
|
|
280
|
+
control: /*#__PURE__*/ _jsx("span", {
|
|
281
|
+
style: {
|
|
282
|
+
display: 'contents'
|
|
283
|
+
},
|
|
284
|
+
children: children
|
|
285
|
+
})
|
|
286
|
+
}, rest));
|
|
287
|
+
});
|
|
288
|
+
AglynChoiceLabel.displayName = 'AglynChoiceLabel';
|
|
289
|
+
/** Stars a visitor picks; sends the number chosen. */ export const AglynRating = /*#__PURE__*/ forwardRef((props, ref)=>/*#__PURE__*/ _jsx(Rating, _extends({
|
|
290
|
+
ref: ref
|
|
291
|
+
}, withoutMapping(props))));
|
|
292
|
+
AglynRating.displayName = 'AglynRating';
|
|
293
|
+
// ── Attributes ──────────────────────────────────────────────────────────────
|
|
294
|
+
const FIELD_NAME = {
|
|
295
|
+
name: 'name',
|
|
296
|
+
label: 'Field name',
|
|
297
|
+
description: 'What the answer is sent as — the key it is stored under in submissions. ' + 'Two inputs in one form need two names (radios share their group’s).',
|
|
298
|
+
component: Aglyn.FieldComponentType.TEXT_FIELD
|
|
299
|
+
};
|
|
300
|
+
/**
|
|
301
|
+
* Which field of the form's dataset this answer is stored under — the same
|
|
302
|
+
* picker the all-in-one Form Field had, listing the fields of the dataset
|
|
303
|
+
* chosen on the Form around it. Stored by id, so renaming the dataset field
|
|
304
|
+
* never breaks the mapping; left empty, the answer matches by name.
|
|
305
|
+
*/ const FIELD_DATASET_FIELD = {
|
|
306
|
+
name: 'datasetFieldId',
|
|
307
|
+
label: 'Maps to schema field',
|
|
308
|
+
description: 'The field of the form’s dataset this answer is stored under. Left ' + 'empty, it matches a dataset field by its Field name.',
|
|
309
|
+
component: Aglyn.FieldComponentType.DATASET_FIELD_SELECT
|
|
310
|
+
};
|
|
311
|
+
const FIELD_INPUT_SIZE = {
|
|
312
|
+
name: 'size',
|
|
313
|
+
label: 'Size',
|
|
314
|
+
description: 'Normal is 56px tall; Small is 40px, to sit beside a button.',
|
|
315
|
+
component: Aglyn.FieldComponentType.SELECT,
|
|
316
|
+
options: [
|
|
317
|
+
{
|
|
318
|
+
value: 'medium',
|
|
319
|
+
label: 'Normal'
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
value: 'small',
|
|
323
|
+
label: 'Small'
|
|
324
|
+
}
|
|
325
|
+
]
|
|
326
|
+
};
|
|
327
|
+
const FIELD_REQUIRED = {
|
|
328
|
+
name: 'required',
|
|
329
|
+
label: 'Required?',
|
|
330
|
+
description: 'The form will not send until this is answered.',
|
|
331
|
+
component: Aglyn.FieldComponentType.SWITCH
|
|
332
|
+
};
|
|
333
|
+
const FIELD_ARIA_LABEL = {
|
|
334
|
+
name: 'ariaLabel',
|
|
335
|
+
label: 'Accessible label',
|
|
336
|
+
description: 'Names the input for screen readers when there is no Form Label beside ' + 'it. Not shown on the page.',
|
|
337
|
+
component: Aglyn.FieldComponentType.TEXT_FIELD
|
|
338
|
+
};
|
|
339
|
+
const FIELD_FULL_WIDTH = {
|
|
340
|
+
name: 'fullWidth',
|
|
341
|
+
label: 'Full width?',
|
|
342
|
+
description: 'Fills the width of whatever holds it.',
|
|
343
|
+
component: Aglyn.FieldComponentType.SWITCH
|
|
344
|
+
};
|
|
345
|
+
const FIELD_PLACEHOLDER = {
|
|
346
|
+
name: 'placeholder',
|
|
347
|
+
label: 'Placeholder',
|
|
348
|
+
description: 'A gray hint inside the empty input, such as you@company.com. It is ' + 'not a label: it disappears as soon as someone types.',
|
|
349
|
+
component: Aglyn.FieldComponentType.TEXT_FIELD
|
|
350
|
+
};
|
|
351
|
+
const FIELD_CHOICE_VALUE = {
|
|
352
|
+
name: 'value',
|
|
353
|
+
label: 'Value',
|
|
354
|
+
description: 'What is sent when this is chosen.',
|
|
355
|
+
component: Aglyn.FieldComponentType.TEXT_FIELD
|
|
356
|
+
};
|
|
357
|
+
const category = Aglyn.ComponentCategory.FORMS;
|
|
358
|
+
export const formControlSchema = {
|
|
359
|
+
$id: FORM_CONTROL_ID,
|
|
360
|
+
pluginId: BUNDLE_ID,
|
|
361
|
+
displayName: 'Form Control',
|
|
362
|
+
description: 'One field’s frame: holds a Form Label, an input and Helper Text, and ' + 'links the label to the input.',
|
|
363
|
+
category,
|
|
364
|
+
icon: {
|
|
365
|
+
path: mdiFormTextbox.path,
|
|
366
|
+
sx: {
|
|
367
|
+
color: 'info.main'
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
attributes: [
|
|
371
|
+
FIELD_FULL_WIDTH,
|
|
372
|
+
_extends({}, FIELD_REQUIRED, {
|
|
373
|
+
description: 'Marks the label with an asterisk. Set Required? on the input too.'
|
|
374
|
+
})
|
|
375
|
+
]
|
|
376
|
+
};
|
|
377
|
+
export const formLabelSchema = {
|
|
378
|
+
$id: FORM_LABEL_ID,
|
|
379
|
+
pluginId: BUNDLE_ID,
|
|
380
|
+
displayName: 'Form Label',
|
|
381
|
+
description: 'The words that name a field, above its input.',
|
|
382
|
+
category,
|
|
383
|
+
icon: {
|
|
384
|
+
path: mdiTagOutline.path,
|
|
385
|
+
sx: {
|
|
386
|
+
color: 'info.main'
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
flags: {
|
|
390
|
+
textEditable: Aglyn.FEATURE_FLAG.ENABLED
|
|
391
|
+
},
|
|
392
|
+
attributes: [
|
|
393
|
+
FIELD_TEXT_CONTENT
|
|
394
|
+
]
|
|
395
|
+
};
|
|
396
|
+
export const inputSchema = {
|
|
397
|
+
$id: INPUT_ID,
|
|
398
|
+
pluginId: BUNDLE_ID,
|
|
399
|
+
displayName: 'Input',
|
|
400
|
+
description: 'A box a visitor types in: text, email, phone, link or number.',
|
|
401
|
+
category,
|
|
402
|
+
icon: {
|
|
403
|
+
path: mdiTextShort.path,
|
|
404
|
+
sx: {
|
|
405
|
+
color: 'info.main'
|
|
406
|
+
}
|
|
407
|
+
},
|
|
408
|
+
flags: {
|
|
409
|
+
selfClosing: Aglyn.FEATURE_FLAG.ENABLED
|
|
410
|
+
},
|
|
411
|
+
attributes: [
|
|
412
|
+
FIELD_NAME,
|
|
413
|
+
FIELD_DATASET_FIELD,
|
|
414
|
+
{
|
|
415
|
+
name: 'type',
|
|
416
|
+
label: 'Type',
|
|
417
|
+
description: 'What goes in it. Email, phone and link bring the right phone keyboard.',
|
|
418
|
+
component: Aglyn.FieldComponentType.SELECT,
|
|
419
|
+
options: [
|
|
420
|
+
{
|
|
421
|
+
value: 'text',
|
|
422
|
+
label: 'Text'
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
value: 'email',
|
|
426
|
+
label: 'Email'
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
value: 'tel',
|
|
430
|
+
label: 'Phone'
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
value: 'url',
|
|
434
|
+
label: 'Link'
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
value: 'number',
|
|
438
|
+
label: 'Number'
|
|
439
|
+
}
|
|
440
|
+
]
|
|
441
|
+
},
|
|
442
|
+
FIELD_PLACEHOLDER,
|
|
443
|
+
FIELD_REQUIRED,
|
|
444
|
+
{
|
|
445
|
+
name: 'multiline',
|
|
446
|
+
label: 'Several lines?',
|
|
447
|
+
description: 'A message box instead of a single line.',
|
|
448
|
+
component: Aglyn.FieldComponentType.SWITCH
|
|
449
|
+
},
|
|
450
|
+
FIELD_INPUT_SIZE,
|
|
451
|
+
FIELD_FULL_WIDTH,
|
|
452
|
+
FIELD_ARIA_LABEL
|
|
453
|
+
]
|
|
454
|
+
};
|
|
455
|
+
export const formHelperTextSchema = {
|
|
456
|
+
$id: FORM_HELPER_TEXT_ID,
|
|
457
|
+
pluginId: BUNDLE_ID,
|
|
458
|
+
displayName: 'Helper Text',
|
|
459
|
+
description: 'A line of guidance under a field.',
|
|
460
|
+
category,
|
|
461
|
+
icon: {
|
|
462
|
+
path: mdiFormatText.path,
|
|
463
|
+
sx: {
|
|
464
|
+
color: 'info.main'
|
|
465
|
+
}
|
|
466
|
+
},
|
|
467
|
+
flags: {
|
|
468
|
+
textEditable: Aglyn.FEATURE_FLAG.ENABLED
|
|
469
|
+
},
|
|
470
|
+
attributes: [
|
|
471
|
+
FIELD_TEXT_CONTENT
|
|
472
|
+
]
|
|
473
|
+
};
|
|
474
|
+
export const selectSchema = {
|
|
475
|
+
$id: SELECT_ID,
|
|
476
|
+
pluginId: BUNDLE_ID,
|
|
477
|
+
displayName: 'Select',
|
|
478
|
+
description: 'A dropdown of Select Options; one may be picked.',
|
|
479
|
+
category,
|
|
480
|
+
icon: {
|
|
481
|
+
path: mdiFormDropdown.path,
|
|
482
|
+
sx: {
|
|
483
|
+
color: 'info.main'
|
|
484
|
+
}
|
|
485
|
+
},
|
|
486
|
+
restrictChildren: [
|
|
487
|
+
Aglyn.LinealDirectiveFlag.LIMIT_TO,
|
|
488
|
+
{
|
|
489
|
+
components: [
|
|
490
|
+
SELECT_OPTION_ID
|
|
491
|
+
]
|
|
492
|
+
}
|
|
493
|
+
],
|
|
494
|
+
attributes: [
|
|
495
|
+
FIELD_NAME,
|
|
496
|
+
FIELD_DATASET_FIELD,
|
|
497
|
+
_extends({}, FIELD_PLACEHOLDER, {
|
|
498
|
+
description: 'Shown until a choice is made; never sent.'
|
|
499
|
+
}),
|
|
500
|
+
FIELD_REQUIRED,
|
|
501
|
+
FIELD_INPUT_SIZE,
|
|
502
|
+
FIELD_FULL_WIDTH,
|
|
503
|
+
FIELD_ARIA_LABEL
|
|
504
|
+
]
|
|
505
|
+
};
|
|
506
|
+
export const selectOptionSchema = {
|
|
507
|
+
$id: SELECT_OPTION_ID,
|
|
508
|
+
pluginId: BUNDLE_ID,
|
|
509
|
+
displayName: 'Select Option',
|
|
510
|
+
description: 'One choice in a Select.',
|
|
511
|
+
category,
|
|
512
|
+
icon: {
|
|
513
|
+
path: mdiFormSelect.path,
|
|
514
|
+
sx: {
|
|
515
|
+
color: 'info.main'
|
|
516
|
+
}
|
|
517
|
+
},
|
|
518
|
+
flags: {
|
|
519
|
+
selfClosing: Aglyn.FEATURE_FLAG.ENABLED,
|
|
520
|
+
textEditable: Aglyn.FEATURE_FLAG.ENABLED
|
|
521
|
+
},
|
|
522
|
+
attributes: [
|
|
523
|
+
FIELD_TEXT_CONTENT,
|
|
524
|
+
_extends({}, FIELD_CHOICE_VALUE, {
|
|
525
|
+
description: 'What is sent. Leave empty to send the text.'
|
|
526
|
+
})
|
|
527
|
+
]
|
|
528
|
+
};
|
|
529
|
+
export const checkboxSchema = {
|
|
530
|
+
$id: CHECKBOX_ID,
|
|
531
|
+
pluginId: BUNDLE_ID,
|
|
532
|
+
displayName: 'Checkbox',
|
|
533
|
+
description: 'A tick box. Put it in a Choice Label to give it words.',
|
|
534
|
+
category,
|
|
535
|
+
icon: {
|
|
536
|
+
path: mdiCheckboxMarkedOutline.path,
|
|
537
|
+
sx: {
|
|
538
|
+
color: 'info.main'
|
|
539
|
+
}
|
|
540
|
+
},
|
|
541
|
+
flags: {
|
|
542
|
+
selfClosing: Aglyn.FEATURE_FLAG.ENABLED
|
|
543
|
+
},
|
|
544
|
+
attributes: [
|
|
545
|
+
FIELD_NAME,
|
|
546
|
+
FIELD_DATASET_FIELD,
|
|
547
|
+
_extends({}, FIELD_CHOICE_VALUE, {
|
|
548
|
+
description: 'What is sent when ticked, such as Yes.'
|
|
549
|
+
}),
|
|
550
|
+
FIELD_REQUIRED
|
|
551
|
+
]
|
|
552
|
+
};
|
|
553
|
+
export const radioGroupSchema = {
|
|
554
|
+
$id: RADIO_GROUP_ID,
|
|
555
|
+
pluginId: BUNDLE_ID,
|
|
556
|
+
displayName: 'Radio Group',
|
|
557
|
+
description: 'A set of Radio choices; one may be picked.',
|
|
558
|
+
category,
|
|
559
|
+
icon: {
|
|
560
|
+
path: mdiRadioboxMarked.path,
|
|
561
|
+
sx: {
|
|
562
|
+
color: 'info.main'
|
|
563
|
+
}
|
|
564
|
+
},
|
|
565
|
+
attributes: [
|
|
566
|
+
FIELD_NAME,
|
|
567
|
+
FIELD_DATASET_FIELD,
|
|
568
|
+
{
|
|
569
|
+
name: 'row',
|
|
570
|
+
label: 'In a row?',
|
|
571
|
+
description: 'Side by side instead of one under another.',
|
|
572
|
+
component: Aglyn.FieldComponentType.SWITCH
|
|
573
|
+
}
|
|
574
|
+
]
|
|
575
|
+
};
|
|
576
|
+
export const radioSchema = {
|
|
577
|
+
$id: RADIO_ID,
|
|
578
|
+
pluginId: BUNDLE_ID,
|
|
579
|
+
displayName: 'Radio',
|
|
580
|
+
description: 'One choice in a Radio Group. Put it in a Choice Label to give it words.',
|
|
581
|
+
category,
|
|
582
|
+
icon: {
|
|
583
|
+
path: mdiRadioboxMarked.path,
|
|
584
|
+
sx: {
|
|
585
|
+
color: 'info.main'
|
|
586
|
+
}
|
|
587
|
+
},
|
|
588
|
+
flags: {
|
|
589
|
+
selfClosing: Aglyn.FEATURE_FLAG.ENABLED
|
|
590
|
+
},
|
|
591
|
+
attributes: [
|
|
592
|
+
FIELD_CHOICE_VALUE,
|
|
593
|
+
FIELD_REQUIRED
|
|
594
|
+
]
|
|
595
|
+
};
|
|
596
|
+
export const choiceLabelSchema = {
|
|
597
|
+
$id: CHOICE_LABEL_ID,
|
|
598
|
+
pluginId: BUNDLE_ID,
|
|
599
|
+
displayName: 'Choice Label',
|
|
600
|
+
description: 'The words beside a Checkbox or Radio placed inside it; clicking them ticks it.',
|
|
601
|
+
category,
|
|
602
|
+
icon: {
|
|
603
|
+
path: mdiTagOutline.path,
|
|
604
|
+
sx: {
|
|
605
|
+
color: 'info.main'
|
|
606
|
+
}
|
|
607
|
+
},
|
|
608
|
+
restrictChildren: [
|
|
609
|
+
Aglyn.LinealDirectiveFlag.LIMIT_TO,
|
|
610
|
+
{
|
|
611
|
+
components: [
|
|
612
|
+
CHECKBOX_ID,
|
|
613
|
+
RADIO_ID
|
|
614
|
+
]
|
|
615
|
+
}
|
|
616
|
+
],
|
|
617
|
+
attributes: [
|
|
618
|
+
{
|
|
619
|
+
name: 'label',
|
|
620
|
+
label: 'Text',
|
|
621
|
+
description: 'The words beside the box.',
|
|
622
|
+
component: Aglyn.FieldComponentType.TEXT_FIELD
|
|
623
|
+
}
|
|
624
|
+
]
|
|
625
|
+
};
|
|
626
|
+
export const ratingSchema = {
|
|
627
|
+
$id: RATING_ID,
|
|
628
|
+
pluginId: BUNDLE_ID,
|
|
629
|
+
displayName: 'Rating',
|
|
630
|
+
description: 'Stars a visitor picks; sends the number chosen.',
|
|
631
|
+
category,
|
|
632
|
+
icon: {
|
|
633
|
+
path: mdiStarOutline.path,
|
|
634
|
+
sx: {
|
|
635
|
+
color: 'info.main'
|
|
636
|
+
}
|
|
637
|
+
},
|
|
638
|
+
flags: {
|
|
639
|
+
selfClosing: Aglyn.FEATURE_FLAG.ENABLED
|
|
640
|
+
},
|
|
641
|
+
attributes: [
|
|
642
|
+
FIELD_NAME,
|
|
643
|
+
FIELD_DATASET_FIELD,
|
|
644
|
+
{
|
|
645
|
+
name: 'max',
|
|
646
|
+
label: 'Stars',
|
|
647
|
+
description: 'How many stars to show.',
|
|
648
|
+
component: Aglyn.FieldComponentType.TEXT_FIELD,
|
|
649
|
+
type: 'number'
|
|
650
|
+
}
|
|
651
|
+
]
|
|
652
|
+
};
|
|
653
|
+
// ── Presets: ready-made arrangements of the pieces above ────────────────────
|
|
654
|
+
const node = (componentId, props, nodes, sx)=>_extends({
|
|
655
|
+
$id: null,
|
|
656
|
+
componentId,
|
|
657
|
+
pluginId: BUNDLE_ID
|
|
658
|
+
}, props ? {
|
|
659
|
+
props
|
|
660
|
+
} : {}, nodes ? {
|
|
661
|
+
nodes
|
|
662
|
+
} : {}, sx ? {
|
|
663
|
+
sx
|
|
664
|
+
} : {});
|
|
665
|
+
/** A labelled text field: Form Control › Form Label + Input. */ export const textFieldPreset = (label, input)=>node(FORM_CONTROL_ID, {
|
|
666
|
+
fullWidth: true
|
|
667
|
+
}, [
|
|
668
|
+
node(FORM_LABEL_ID, {
|
|
669
|
+
children: label
|
|
670
|
+
}),
|
|
671
|
+
node(INPUT_ID, _extends({
|
|
672
|
+
fullWidth: true
|
|
673
|
+
}, input))
|
|
674
|
+
]);
|
|
675
|
+
const preset = ($id, displayName, description, icon, data)=>({
|
|
676
|
+
$id,
|
|
677
|
+
type: Aglyn.NodeType.PRESET,
|
|
678
|
+
displayName,
|
|
679
|
+
description,
|
|
680
|
+
pluginId: BUNDLE_ID,
|
|
681
|
+
category,
|
|
682
|
+
icon: {
|
|
683
|
+
path: icon,
|
|
684
|
+
sx: {
|
|
685
|
+
color: 'info.main'
|
|
686
|
+
}
|
|
687
|
+
},
|
|
688
|
+
data
|
|
689
|
+
});
|
|
690
|
+
/**
|
|
691
|
+
* Each element on its own, so any piece can be inserted by itself — a bare
|
|
692
|
+
* Input beside a Button in a sign-up row, a Form Label above a field the
|
|
693
|
+
* author is assembling, one more Radio in a group. The field presets below
|
|
694
|
+
* are ready-made arrangements of the same pieces.
|
|
695
|
+
*/ const singleElementPresets = [
|
|
696
|
+
preset(generatePresetId(FORM_CONTROL_ID), 'Form Control', 'An empty field frame: put a Form Label, an input and Helper Text in it.', mdiFormTextbox.path, node(FORM_CONTROL_ID, {
|
|
697
|
+
fullWidth: true
|
|
698
|
+
})),
|
|
699
|
+
preset(generatePresetId(FORM_LABEL_ID), 'Form Label', 'The words that name a field, above its input.', mdiTagOutline.path, node(FORM_LABEL_ID, {
|
|
700
|
+
children: 'Label'
|
|
701
|
+
})),
|
|
702
|
+
preset(generatePresetId(INPUT_ID), 'Input', 'A box a visitor types in, on its own.', mdiTextShort.path, node(INPUT_ID, {
|
|
703
|
+
name: 'field',
|
|
704
|
+
placeholder: 'Type here'
|
|
705
|
+
})),
|
|
706
|
+
preset(generatePresetId(SELECT_ID), 'Select', 'A dropdown with two options.', mdiFormDropdown.path, node(SELECT_ID, {
|
|
707
|
+
name: 'choice',
|
|
708
|
+
placeholder: 'Choose…'
|
|
709
|
+
}, [
|
|
710
|
+
node(SELECT_OPTION_ID, {
|
|
711
|
+
children: 'Option one'
|
|
712
|
+
}),
|
|
713
|
+
node(SELECT_OPTION_ID, {
|
|
714
|
+
children: 'Option two'
|
|
715
|
+
})
|
|
716
|
+
])),
|
|
717
|
+
preset(generatePresetId(SELECT_OPTION_ID), 'Select Option', 'One more choice for a Select.', mdiFormSelect.path, node(SELECT_OPTION_ID, {
|
|
718
|
+
children: 'Option'
|
|
719
|
+
})),
|
|
720
|
+
preset(generatePresetId(RADIO_GROUP_ID), 'Radio Group', 'A set of Radio choices sharing one Field name.', mdiRadioboxMarked.path, node(RADIO_GROUP_ID, {
|
|
721
|
+
name: 'pick'
|
|
722
|
+
}, [
|
|
723
|
+
node(CHOICE_LABEL_ID, {
|
|
724
|
+
label: 'First'
|
|
725
|
+
}, [
|
|
726
|
+
node(RADIO_ID, {
|
|
727
|
+
value: 'First'
|
|
728
|
+
})
|
|
729
|
+
]),
|
|
730
|
+
node(CHOICE_LABEL_ID, {
|
|
731
|
+
label: 'Second'
|
|
732
|
+
}, [
|
|
733
|
+
node(RADIO_ID, {
|
|
734
|
+
value: 'Second'
|
|
735
|
+
})
|
|
736
|
+
])
|
|
737
|
+
])),
|
|
738
|
+
preset(generatePresetId(RADIO_ID), 'Radio', 'One more choice for a Radio Group, with its words.', mdiRadioboxMarked.path, node(CHOICE_LABEL_ID, {
|
|
739
|
+
label: 'Another choice'
|
|
740
|
+
}, [
|
|
741
|
+
node(RADIO_ID, {
|
|
742
|
+
value: 'Another choice'
|
|
743
|
+
})
|
|
744
|
+
])),
|
|
745
|
+
preset(generatePresetId(CHOICE_LABEL_ID), 'Choice Label', 'Words beside a tick box; holds a Checkbox.', mdiTagOutline.path, node(CHOICE_LABEL_ID, {
|
|
746
|
+
label: 'Yes, please'
|
|
747
|
+
}, [
|
|
748
|
+
node(CHECKBOX_ID, {
|
|
749
|
+
name: 'agree',
|
|
750
|
+
value: 'Yes'
|
|
751
|
+
})
|
|
752
|
+
])),
|
|
753
|
+
preset(generatePresetId(RATING_ID), 'Rating', 'Five stars, on their own.', mdiStarOutline.path, node(RATING_ID, {
|
|
754
|
+
name: 'rating'
|
|
755
|
+
}))
|
|
756
|
+
];
|
|
757
|
+
export const formControlPresets = [
|
|
758
|
+
...singleElementPresets,
|
|
759
|
+
preset(generatePresetId(FORM_CONTROL_ID, 'text'), 'Text field', 'A label and a one-line input.', mdiFormTextbox.path, textFieldPreset('Name', {
|
|
760
|
+
name: 'name'
|
|
761
|
+
})),
|
|
762
|
+
preset(generatePresetId(FORM_CONTROL_ID, 'email'), 'Email field', 'A label and an email input.', mdiFormTextbox.path, textFieldPreset('Email', {
|
|
763
|
+
name: 'email',
|
|
764
|
+
type: 'email',
|
|
765
|
+
required: true
|
|
766
|
+
})),
|
|
767
|
+
preset(generatePresetId(FORM_CONTROL_ID, 'message'), 'Message field', 'A label and a several-line input.', mdiFormTextbox.path, textFieldPreset('Message', {
|
|
768
|
+
name: 'message',
|
|
769
|
+
multiline: true,
|
|
770
|
+
minRows: 4
|
|
771
|
+
})),
|
|
772
|
+
preset(generatePresetId(FORM_CONTROL_ID, 'select'), 'Dropdown field', 'A label and a Select with three options.', mdiFormDropdown.path, node(FORM_CONTROL_ID, {
|
|
773
|
+
fullWidth: true
|
|
774
|
+
}, [
|
|
775
|
+
node(FORM_LABEL_ID, {
|
|
776
|
+
children: 'Choose one'
|
|
777
|
+
}),
|
|
778
|
+
node(SELECT_ID, {
|
|
779
|
+
name: 'choice',
|
|
780
|
+
fullWidth: true,
|
|
781
|
+
placeholder: 'Choose…'
|
|
782
|
+
}, [
|
|
783
|
+
node(SELECT_OPTION_ID, {
|
|
784
|
+
children: 'Option one'
|
|
785
|
+
}),
|
|
786
|
+
node(SELECT_OPTION_ID, {
|
|
787
|
+
children: 'Option two'
|
|
788
|
+
}),
|
|
789
|
+
node(SELECT_OPTION_ID, {
|
|
790
|
+
children: 'Option three'
|
|
791
|
+
})
|
|
792
|
+
])
|
|
793
|
+
])),
|
|
794
|
+
preset(generatePresetId(FORM_CONTROL_ID, 'radio'), 'Radio choice field', 'A label and a Radio Group with three choices.', mdiRadioboxMarked.path, node(FORM_CONTROL_ID, {}, [
|
|
795
|
+
node(FORM_LABEL_ID, {
|
|
796
|
+
children: 'Pick one'
|
|
797
|
+
}),
|
|
798
|
+
node(RADIO_GROUP_ID, {
|
|
799
|
+
name: 'pick'
|
|
800
|
+
}, [
|
|
801
|
+
node(CHOICE_LABEL_ID, {
|
|
802
|
+
label: 'First'
|
|
803
|
+
}, [
|
|
804
|
+
node(RADIO_ID, {
|
|
805
|
+
value: 'First'
|
|
806
|
+
})
|
|
807
|
+
]),
|
|
808
|
+
node(CHOICE_LABEL_ID, {
|
|
809
|
+
label: 'Second'
|
|
810
|
+
}, [
|
|
811
|
+
node(RADIO_ID, {
|
|
812
|
+
value: 'Second'
|
|
813
|
+
})
|
|
814
|
+
]),
|
|
815
|
+
node(CHOICE_LABEL_ID, {
|
|
816
|
+
label: 'Third'
|
|
817
|
+
}, [
|
|
818
|
+
node(RADIO_ID, {
|
|
819
|
+
value: 'Third'
|
|
820
|
+
})
|
|
821
|
+
])
|
|
822
|
+
])
|
|
823
|
+
])),
|
|
824
|
+
preset(generatePresetId(FORM_CONTROL_ID, 'checkbox'), 'Checkbox', 'A tick box with words beside it.', mdiCheckboxMarkedOutline.path, node(CHOICE_LABEL_ID, {
|
|
825
|
+
label: 'Yes, please'
|
|
826
|
+
}, [
|
|
827
|
+
node(CHECKBOX_ID, {
|
|
828
|
+
name: 'agree',
|
|
829
|
+
value: 'Yes'
|
|
830
|
+
})
|
|
831
|
+
])),
|
|
832
|
+
preset(generatePresetId(FORM_HELPER_TEXT_ID), 'Helper Text', 'A line of guidance under a field.', mdiFormatText.path, node(FORM_HELPER_TEXT_ID, {
|
|
833
|
+
children: 'We never share your address.'
|
|
834
|
+
})),
|
|
835
|
+
preset(generatePresetId(FORM_CONTROL_ID, 'rating'), 'Rating field', 'A label and five stars.', mdiStarOutline.path, node(FORM_CONTROL_ID, {}, [
|
|
836
|
+
node(FORM_LABEL_ID, {
|
|
837
|
+
children: 'How did we do?'
|
|
838
|
+
}),
|
|
839
|
+
node(RATING_ID, {
|
|
840
|
+
name: 'rating'
|
|
841
|
+
})
|
|
842
|
+
]))
|
|
843
|
+
];
|
|
844
|
+
|
|
845
|
+
//# sourceMappingURL=form-controls.js.map
|