html-form-field 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.
- package/LICENSE +21 -0
- package/README.md +102 -0
- package/dist/html-form-field.cjs +353 -0
- package/dist/html-form-field.min.js +1 -0
- package/dist/html-form-field.mjs +351 -0
- package/dist/package.json +1 -0
- package/package.json +64 -0
- package/src/form-field.ts +207 -0
- package/src/form-item.ts +148 -0
- package/src/index.ts +5 -0
- package/types/html-form-field.d.ts +186 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Yusuke Kawasaki
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# html-form-field
|
|
2
|
+
|
|
3
|
+
Unified interface for HTML form fields with synchronized binding to object properties
|
|
4
|
+
|
|
5
|
+
[](https://github.com/kawanet/html-form-field/actions/)
|
|
6
|
+
[](https://www.npmjs.com/package/html-form-field)
|
|
7
|
+
[](https://cdn.jsdelivr.net/npm/html-form-field/dist/html-form-field.min.js)
|
|
8
|
+
|
|
9
|
+
- Unified getter/setter for text inputs, checkboxes, radio buttons, and select elements
|
|
10
|
+
- Two-way binding between form fields and object properties (synchronized updates in both directions)
|
|
11
|
+
- Built-in change detection with `onChange` and `onWrite` callbacks
|
|
12
|
+
- Small browser build: [html-form-field.min.js](https://cdn.jsdelivr.net/npm/html-form-field/dist/html-form-field.min.js) under 4KB minified, under 2KB gzipped
|
|
13
|
+
- Full TypeScript support - [html-form-field.d.ts](https://github.com/kawanet/html-form-field/blob/main/types/html-form-field.d.ts) for detailed specifications
|
|
14
|
+
|
|
15
|
+
## SYNOPSIS
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import {formField} from "html-form-field"
|
|
19
|
+
|
|
20
|
+
interface Context {
|
|
21
|
+
nickname: string
|
|
22
|
+
email: string
|
|
23
|
+
favo: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const form = document.querySelector("form")
|
|
27
|
+
|
|
28
|
+
const ctx = {} as Context
|
|
29
|
+
|
|
30
|
+
formField({form, bindTo: ctx, name: "nickname"})
|
|
31
|
+
|
|
32
|
+
console.log(ctx.nickname) // reads from form field
|
|
33
|
+
|
|
34
|
+
ctx.nickname = "John" // updates form field
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
#### HTML Example
|
|
38
|
+
|
|
39
|
+
```html
|
|
40
|
+
|
|
41
|
+
<form>
|
|
42
|
+
<ul>
|
|
43
|
+
<li>Nickname: <input type="text" name="nickname" value="Alice"></li>
|
|
44
|
+
<li>Email: <input type="email" name="email" value="alice@example.com"></li>
|
|
45
|
+
<li>Favorites:
|
|
46
|
+
<label><input type="checkbox" name="favo" value="tech">Tech</label>
|
|
47
|
+
<label><input type="checkbox" name="favo" value="travel">Travel</label>
|
|
48
|
+
<label><input type="checkbox" name="favo" value="trading">Trading</label>
|
|
49
|
+
</li>
|
|
50
|
+
</ul>
|
|
51
|
+
</form>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
#### Value Access
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
const email = formField({form, name: "email"})
|
|
58
|
+
|
|
59
|
+
console.log(email.value) // current value
|
|
60
|
+
|
|
61
|
+
email.value = "john@example.com" // update value
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
#### Multiple Selections
|
|
65
|
+
|
|
66
|
+
```js
|
|
67
|
+
const favo = formField({form, name: "favo", delim: ","})
|
|
68
|
+
|
|
69
|
+
favo.toggle("tech") // toggle checkbox
|
|
70
|
+
|
|
71
|
+
favo.toggle("travel", true)
|
|
72
|
+
|
|
73
|
+
favo.toggle("trading", false)
|
|
74
|
+
|
|
75
|
+
console.log(favo.has("travel")) // check if selected
|
|
76
|
+
|
|
77
|
+
// Shortcut to item by index. Equivalent to items().at(index))
|
|
78
|
+
const firstItem = favo.itemAt(0)
|
|
79
|
+
console.log(firstItem.checked)
|
|
80
|
+
|
|
81
|
+
// Shortcut to item by value. Equivalent to items().find(v => v.value === value)
|
|
82
|
+
const travelItem = favo.itemOf("travel")
|
|
83
|
+
console.log(travelItem.checked)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
#### Change Handling and Default Values
|
|
87
|
+
|
|
88
|
+
```js
|
|
89
|
+
formField({
|
|
90
|
+
form,
|
|
91
|
+
bindTo: ctx,
|
|
92
|
+
name: "email",
|
|
93
|
+
onWrite: ({name, value}) => sessionStorage.setItem(name, value),
|
|
94
|
+
onChange: ({name, value}) => submitForm(),
|
|
95
|
+
defaults: [sessionStorage.getItem("email")],
|
|
96
|
+
})
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## LINKS
|
|
100
|
+
|
|
101
|
+
- https://www.npmjs.com/package/html-form-field
|
|
102
|
+
- https://github.com/kawanet/html-form-field
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const triggerOnWrite$1 = (field) => {
|
|
4
|
+
const onWrite = field.options.onWrite;
|
|
5
|
+
if (onWrite) onWrite(field);
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const checkableMap = {
|
|
9
|
+
option: true,
|
|
10
|
+
radio: true,
|
|
11
|
+
checkbox: true,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
const isElementOf = (v, tagName) => ((v ).tagName === tagName);
|
|
17
|
+
|
|
18
|
+
const formItemList = (field, nodeList) => {
|
|
19
|
+
const list = [];
|
|
20
|
+
|
|
21
|
+
for (const node of nodeList) {
|
|
22
|
+
if (isElementOf(node, "SELECT")) {
|
|
23
|
+
for (const option of node.options) {
|
|
24
|
+
list.push(new OptionItem(field, option));
|
|
25
|
+
}
|
|
26
|
+
} else {
|
|
27
|
+
list.push(new InputItem(field, node));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return list
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
class BaseFormItem {
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
constructor(field, node) {
|
|
40
|
+
this.field = field;
|
|
41
|
+
this.node = node;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get value() {
|
|
45
|
+
return this.node.value
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
set value(value) {
|
|
49
|
+
this.setValue(value);
|
|
50
|
+
triggerOnWrite$1(this.field);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
setValue(value) {
|
|
54
|
+
this.node.value = value;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get checked() {
|
|
58
|
+
return this.getChecked()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
set checked(checked) {
|
|
64
|
+
const prev = this.checked;
|
|
65
|
+
if (prev !== checked) {
|
|
66
|
+
this.setChecked(checked);
|
|
67
|
+
triggerOnWrite$1(this.field);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
get disabled() {
|
|
74
|
+
return this.node.disabled
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
set disabled(disabled) {
|
|
78
|
+
this.node.disabled = disabled;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
class InputItem extends BaseFormItem {
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
constructor(entry, node) {
|
|
88
|
+
super(entry, node);
|
|
89
|
+
this.checkable = checkableMap[node.type];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
getChecked() {
|
|
93
|
+
return (this.node ).checked
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
setChecked(checked) {
|
|
97
|
+
(this.node ).checked = checked;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
get label() {
|
|
101
|
+
const node = this.node;
|
|
102
|
+
const label = node.closest("label");
|
|
103
|
+
const siblings = label && label.querySelectorAll(node.tagName);
|
|
104
|
+
|
|
105
|
+
if (siblings && siblings.length === 1) {
|
|
106
|
+
const text = pickText(label);
|
|
107
|
+
if (text) return text
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const labels = node.labels;
|
|
111
|
+
for (const label of labels) {
|
|
112
|
+
const text = pickText(label);
|
|
113
|
+
if (text) return text
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const text = pickText(node);
|
|
117
|
+
if (text) return text
|
|
118
|
+
|
|
119
|
+
function pickText(node) {
|
|
120
|
+
const text = node.innerText || node.textContent;
|
|
121
|
+
if (text) return text.trim()
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
class OptionItem extends BaseFormItem {
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
constructor(entry, node) {
|
|
130
|
+
super(entry, node);
|
|
131
|
+
this.checkable = true;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
getChecked() {
|
|
135
|
+
return this.node.selected
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
setChecked(checked) {
|
|
139
|
+
// Note: Setting false may trigger auto-selection of the first option on select-one
|
|
140
|
+
this.node.selected = checked;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
get label() {
|
|
144
|
+
const node = this.node;
|
|
145
|
+
const text = node.label || node.textContent;
|
|
146
|
+
if (text) return text.trim()
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const DELIM = ","; // or use Unit Separator `\x1F` instead
|
|
151
|
+
|
|
152
|
+
const isString = (v) => ("string" === typeof v);
|
|
153
|
+
|
|
154
|
+
const noNullString = (v) => (v == null) ? "" : isString(v) ? v : String(v);
|
|
155
|
+
|
|
156
|
+
const splitString = (v, delim) => (v == null) ? [] : Array.isArray(v) ? v.map(noNullString) : noNullString(v).split(delim);
|
|
157
|
+
|
|
158
|
+
const isHTMLElement = (v) => ("function" === typeof (v ).matches);
|
|
159
|
+
|
|
160
|
+
const formField = (options) => {
|
|
161
|
+
return new FormBridgeImpl(options)
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const getNodeList = ({form, name}) => {
|
|
165
|
+
const safeName = JSON.stringify(name);
|
|
166
|
+
if (!name) {
|
|
167
|
+
throw new Error(`Invalid name=${safeName}`)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const selector = `input[name=${safeName}], textarea[name=${safeName}], button[name=${safeName}], select[name=${safeName}]`;
|
|
171
|
+
|
|
172
|
+
const nodeList = form.querySelectorAll(selector);
|
|
173
|
+
if (!nodeList.length) {
|
|
174
|
+
if (isHTMLElement(form) && form.matches(selector)) {
|
|
175
|
+
return [form]
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
throw new Error(`Not found: name=${safeName}`)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return nodeList
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const updateEventListener = (nodeList, handler) => {
|
|
185
|
+
for (const node of nodeList) {
|
|
186
|
+
node.removeEventListener("change", handler);
|
|
187
|
+
node.addEventListener("change", handler);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const triggerOnWrite = (field) => {
|
|
192
|
+
const onWrite = field.options.onWrite;
|
|
193
|
+
if (onWrite) onWrite(field);
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const triggerOnChange = (field) => {
|
|
197
|
+
const onChange = field.options.onChange;
|
|
198
|
+
if (onChange) onChange(field);
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const applyBindTo = (bindTo, name, field) => {
|
|
202
|
+
if (!bindTo) return // nothing to be bound
|
|
203
|
+
|
|
204
|
+
delete bindTo[name ];
|
|
205
|
+
|
|
206
|
+
const getValue = () => field.value;
|
|
207
|
+
|
|
208
|
+
const setValue = (value) => {
|
|
209
|
+
field.value = value;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
Object.defineProperty(bindTo, name, {
|
|
213
|
+
get: getValue,
|
|
214
|
+
set: setValue,
|
|
215
|
+
enumerable: true,
|
|
216
|
+
configurable: true,
|
|
217
|
+
});
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
class FormBridgeImpl {
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
constructor(options = {} ) {
|
|
228
|
+
this.options = options;
|
|
229
|
+
const name = this.name = options.name;
|
|
230
|
+
applyBindTo(options.bindTo, name, this);
|
|
231
|
+
|
|
232
|
+
const _onChange = this._onChange = () => {
|
|
233
|
+
triggerOnWrite(this);
|
|
234
|
+
triggerOnChange(this);
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const nodeList = getNodeList(options);
|
|
238
|
+
updateEventListener(nodeList, _onChange);
|
|
239
|
+
const items = this._items = formItemList(this, nodeList);
|
|
240
|
+
|
|
241
|
+
const defaults = options.defaults;
|
|
242
|
+
if (defaults) {
|
|
243
|
+
if (items[0].checkable) {
|
|
244
|
+
for (const v of defaults) {
|
|
245
|
+
if (this.setValue(v)) break
|
|
246
|
+
}
|
|
247
|
+
} else {
|
|
248
|
+
this.setValue(defaults);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
get value() {
|
|
254
|
+
const values = this.current().map(v => v.value);
|
|
255
|
+
if (values.length > 1) {
|
|
256
|
+
const delim = this.options.delim || DELIM;
|
|
257
|
+
return values.join(delim)
|
|
258
|
+
} else {
|
|
259
|
+
return values[0]
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
set value(value) {
|
|
264
|
+
this.setValue(value);
|
|
265
|
+
triggerOnWrite(this);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
setValue(value) {
|
|
269
|
+
const items = this.items();
|
|
270
|
+
|
|
271
|
+
if (items.length === 1 && !items[0].checkable && isString(value)) {
|
|
272
|
+
value = [value];
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const delim = this.options.delim || DELIM;
|
|
276
|
+
const values = splitString(value, delim);
|
|
277
|
+
let i = 0;
|
|
278
|
+
let assigned = 0;
|
|
279
|
+
|
|
280
|
+
for (const item of items) {
|
|
281
|
+
if (item.checkable) {
|
|
282
|
+
const checked = values.includes(item.value);
|
|
283
|
+
if (checked) assigned++;
|
|
284
|
+
if (item.checked !== checked) {
|
|
285
|
+
item.setChecked(checked);
|
|
286
|
+
}
|
|
287
|
+
} else {
|
|
288
|
+
const value = values[i++];
|
|
289
|
+
const isNull = (value == null);
|
|
290
|
+
item.setValue(isNull ? "" : value);
|
|
291
|
+
if (!isNull) assigned++;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return !!assigned
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
current() {
|
|
299
|
+
return this.items().filter(v => (!v.disabled && (!v.checkable || v.checked)))
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
reload() {
|
|
303
|
+
const nodeList = getNodeList(this.options);
|
|
304
|
+
const _onChange = this._onChange;
|
|
305
|
+
updateEventListener(nodeList, _onChange);
|
|
306
|
+
this._items = formItemList(this, nodeList);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
items() {
|
|
310
|
+
return this._items
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
toggle(value, checked) {
|
|
314
|
+
let result;
|
|
315
|
+
const delim = this.options.delim || DELIM;
|
|
316
|
+
const values = splitString(value, delim);
|
|
317
|
+
|
|
318
|
+
for (const item of this.items()) {
|
|
319
|
+
if (values.includes(item.value)) {
|
|
320
|
+
if (checked != null) {
|
|
321
|
+
result = checked;
|
|
322
|
+
} else {
|
|
323
|
+
result = !item.checked;
|
|
324
|
+
}
|
|
325
|
+
item.checked = result;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return result
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
has(value) {
|
|
333
|
+
return !!this.current().find(v => v.value === value)
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Shortcut to access an item by index (operates on items()).
|
|
338
|
+
* Equivalent to items().at(index). Returns undefined if out of range.
|
|
339
|
+
*/
|
|
340
|
+
itemAt(index) {
|
|
341
|
+
return this.items().at(index)
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Shortcut to access an item by value (operates on items()).
|
|
346
|
+
* Returns the first FormItem whose value equals the given value, or undefined if not found.
|
|
347
|
+
*/
|
|
348
|
+
itemOf(value) {
|
|
349
|
+
return this.items().find(v => v.value === value)
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
exports.formField = formField;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var formField=function(e){"use strict";const t=e=>{const t=e.options.onWrite;t&&t(e)},s={option:!0,radio:!0,checkbox:!0},n=(e,t)=>e.tagName===t,i=(e,t)=>{const s=[];for(const i of t)if(n(i,"SELECT"))for(const t of i.options)s.push(new c(e,t));else s.push(new r(e,i));return s};class o{constructor(e,t){this.field=e,this.node=t}get value(){return this.node.value}set value(e){this.setValue(e),t(this.field)}setValue(e){this.node.value=e}get checked(){return this.getChecked()}set checked(e){this.checked!==e&&(this.setChecked(e),t(this.field))}get disabled(){return this.node.disabled}set disabled(e){this.node.disabled=e}}class r extends o{constructor(e,t){super(e,t),this.checkable=s[t.type]}getChecked(){return this.node.checked}setChecked(e){this.node.checked=e}get label(){const e=this.node,t=e.closest("label"),s=t&&t.querySelectorAll(e.tagName);if(s&&1===s.length){const e=o(t);if(e)return e}const n=e.labels;for(const e of n){const t=o(e);if(t)return t}const i=o(e);if(i)return i;function o(e){const t=e.innerText||e.textContent;if(t)return t.trim()}}}class c extends o{constructor(e,t){super(e,t),this.checkable=!0}getChecked(){return this.node.selected}setChecked(e){this.node.selected=e}get label(){const e=this.node,t=e.label||e.textContent;if(t)return t.trim()}}const l=e=>"string"==typeof e,h=e=>null==e?"":l(e)?e:String(e),a=(e,t)=>null==e?[]:Array.isArray(e)?e.map(h):h(e).split(t),u=({form:e,name:t})=>{const s=JSON.stringify(t);if(!t)throw new Error(`Invalid name=${s}`);const n=`input[name=${s}], textarea[name=${s}], button[name=${s}], select[name=${s}]`,i=e.querySelectorAll(n);if(!i.length){if("function"==typeof e.matches&&e.matches(n))return[e];throw new Error(`Not found: name=${s}`)}return i},d=(e,t)=>{for(const s of e)s.removeEventListener("change",t),s.addEventListener("change",t)},f=e=>{const t=e.options.onWrite;t&&t(e)};class m{constructor(e={}){this.options=e;const t=this.name=e.name;((e,t,s)=>{if(!e)return;delete e[t],Object.defineProperty(e,t,{get:()=>s.value,set:e=>{s.value=e},enumerable:!0,configurable:!0})})(e.bindTo,t,this);const s=this._onChange=()=>{f(this),(e=>{const t=e.options.onChange;t&&t(e)})(this)},n=u(e);d(n,s);const o=this._items=i(this,n),r=e.defaults;if(r)if(o[0].checkable){for(const e of r)if(this.setValue(e))break}else this.setValue(r)}get value(){const e=this.current().map(e=>e.value);if(e.length>1){const t=this.options.delim||",";return e.join(t)}return e[0]}set value(e){this.setValue(e),f(this)}setValue(e){const t=this.items();1===t.length&&!t[0].checkable&&l(e)&&(e=[e]);const s=this.options.delim||",",n=a(e,s);let i=0,o=0;for(const e of t)if(e.checkable){const t=n.includes(e.value);t&&o++,e.checked!==t&&e.setChecked(t)}else{const t=n[i++],s=null==t;e.setValue(s?"":t),s||o++}return!!o}current(){return this.items().filter(e=>!e.disabled&&(!e.checkable||e.checked))}reload(){const e=u(this.options),t=this._onChange;d(e,t),this._items=i(this,e)}items(){return this._items}toggle(e,t){let s;const n=this.options.delim||",",i=a(e,n);for(const e of this.items())i.includes(e.value)&&(s=null!=t?t:!e.checked,e.checked=s);return s}has(e){return!!this.current().find(t=>t.value===e)}itemAt(e){return this.items().at(e)}itemOf(e){return this.items().find(t=>t.value===e)}}return e.formField=e=>new m(e),"undefined"!=typeof module&&(module.exports=e),e.formField}({});
|