@schukai/monster 4.136.29 → 4.137.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/package.json +1 -1
- package/source/components/datatable/dataset.mjs +1 -1
- package/source/components/form/choice-cards.mjs +525 -0
- package/source/components/form/form.mjs +1 -1
- package/source/components/form/style/choice-cards.pcss +172 -0
- package/source/components/form/style/field-layout.pcss +8 -0
- package/source/components/form/stylesheet/choice-cards.mjs +31 -0
- package/source/components/form/stylesheet/field-layout.mjs +7 -14
- package/source/components/form/util/floating-ui.mjs +29 -5
- package/source/monster.mjs +1 -0
- package/test/cases/components/form/choice-cards.mjs +192 -0
- package/test/cases/components/form/floating-ui.mjs +63 -0
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.6"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"4.
|
|
1
|
+
{"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.6"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"4.137.0"}
|
|
@@ -134,7 +134,7 @@ class DataSet extends CustomElement {
|
|
|
134
134
|
*/
|
|
135
135
|
refreshOnMutation: {
|
|
136
136
|
selector:
|
|
137
|
-
"input, select, textarea, monster-select, monster-toggle-switch",
|
|
137
|
+
"input, select, textarea, monster-select, monster-toggle-switch, monster-choice-cards",
|
|
138
138
|
},
|
|
139
139
|
|
|
140
140
|
writeBack: {
|
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © Volker Schukai and all contributing authors, {{copyRightYear}}. All rights reserved.
|
|
3
|
+
* Node module: @schukai/monster
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3).
|
|
6
|
+
* The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html
|
|
7
|
+
*
|
|
8
|
+
* For those who do not wish to adhere to the AGPLv3, a commercial license is available.
|
|
9
|
+
* Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms.
|
|
10
|
+
* For more information about purchasing a commercial license, please contact Volker Schukai.
|
|
11
|
+
*
|
|
12
|
+
* SPDX-License-Identifier: AGPL-3.0
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { instanceSymbol } from "../../constants.mjs";
|
|
16
|
+
import { CustomControl } from "../../dom/customcontrol.mjs";
|
|
17
|
+
import {
|
|
18
|
+
assembleMethodSymbol,
|
|
19
|
+
registerCustomElement,
|
|
20
|
+
} from "../../dom/customelement.mjs";
|
|
21
|
+
import { fireCustomEvent, fireEvent } from "../../dom/events.mjs";
|
|
22
|
+
import { isArray, isObject, isString, isNumber } from "../../types/is.mjs";
|
|
23
|
+
import { Observer } from "../../types/observer.mjs";
|
|
24
|
+
import { clone } from "../../util/clone.mjs";
|
|
25
|
+
import { ChoiceCardsStyleSheet } from "./stylesheet/choice-cards.mjs";
|
|
26
|
+
|
|
27
|
+
export { ChoiceCards };
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @private
|
|
31
|
+
* @type {symbol}
|
|
32
|
+
*/
|
|
33
|
+
const controlElementSymbol = Symbol("controlElement");
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @private
|
|
37
|
+
* @type {symbol}
|
|
38
|
+
*/
|
|
39
|
+
const itemsElementSymbol = Symbol("itemsElement");
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* A card-based single choice control.
|
|
43
|
+
*
|
|
44
|
+
* @fragments /fragments/components/form/choice-cards
|
|
45
|
+
*
|
|
46
|
+
* @example /examples/components/form/choice-cards-simple Simple card choices
|
|
47
|
+
*
|
|
48
|
+
* @since 4.137.0
|
|
49
|
+
* @copyright Volker Schukai
|
|
50
|
+
* @summary A visual radio-card picker for choosing one option from a compact set.
|
|
51
|
+
* @fires monster-selected
|
|
52
|
+
* @fires monster-change
|
|
53
|
+
* @fires monster-changed
|
|
54
|
+
*/
|
|
55
|
+
class ChoiceCards extends CustomControl {
|
|
56
|
+
/**
|
|
57
|
+
* To set the options via the HTML tag, the attribute `data-monster-options` must be used.
|
|
58
|
+
* @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
|
|
59
|
+
*
|
|
60
|
+
* @property {Array<Object|string|number>} items Choice items.
|
|
61
|
+
* @property {string} items[].contentSlot Slot name for custom card content.
|
|
62
|
+
* @property {string|null} value Current selected value.
|
|
63
|
+
* @property {boolean} disabled Disabled state.
|
|
64
|
+
* @property {Object} labels Accessible labels.
|
|
65
|
+
* @property {string} labels.group Radio group label.
|
|
66
|
+
* @property {Object} templates Template definitions.
|
|
67
|
+
* @property {string} templates.main Main template.
|
|
68
|
+
*/
|
|
69
|
+
get defaults() {
|
|
70
|
+
return Object.assign({}, super.defaults, {
|
|
71
|
+
items: [],
|
|
72
|
+
value: null,
|
|
73
|
+
disabled: false,
|
|
74
|
+
eventProcessing: false,
|
|
75
|
+
labels: {
|
|
76
|
+
group: "Choices",
|
|
77
|
+
},
|
|
78
|
+
templates: {
|
|
79
|
+
main: getTemplate(),
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @return {void}
|
|
86
|
+
*/
|
|
87
|
+
[assembleMethodSymbol]() {
|
|
88
|
+
super[assembleMethodSymbol]();
|
|
89
|
+
initControlReferences.call(this);
|
|
90
|
+
initItems.call(this);
|
|
91
|
+
initEventHandler.call(this);
|
|
92
|
+
render.call(this);
|
|
93
|
+
syncFormValue.call(this);
|
|
94
|
+
|
|
95
|
+
this.attachObserver(
|
|
96
|
+
new Observer(() => {
|
|
97
|
+
render.call(this);
|
|
98
|
+
syncFormValue.call(this);
|
|
99
|
+
}),
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @return {CSSStyleSheet[]}
|
|
105
|
+
*/
|
|
106
|
+
static getCSSStyleSheet() {
|
|
107
|
+
return [ChoiceCardsStyleSheet];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* This method is called by the `instanceof` operator.
|
|
112
|
+
* @return {symbol}
|
|
113
|
+
*/
|
|
114
|
+
static get [instanceSymbol]() {
|
|
115
|
+
return Symbol.for(
|
|
116
|
+
"@schukai/monster/components/form/choice-cards@@instance",
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @return {string}
|
|
122
|
+
*/
|
|
123
|
+
static getTag() {
|
|
124
|
+
return "monster-choice-cards";
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @return {string}
|
|
129
|
+
*/
|
|
130
|
+
get value() {
|
|
131
|
+
return this.getOption("value");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* @param {string|null|undefined} value
|
|
136
|
+
*/
|
|
137
|
+
set value(value) {
|
|
138
|
+
const normalized = normalizeValue(value);
|
|
139
|
+
if (this.getOption("value") !== normalized) {
|
|
140
|
+
this.setOption("value", normalized);
|
|
141
|
+
}
|
|
142
|
+
syncValueAttribute.call(this, normalized);
|
|
143
|
+
render.call(this);
|
|
144
|
+
syncFormValue.call(this);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* @return {Array}
|
|
149
|
+
*/
|
|
150
|
+
getItems() {
|
|
151
|
+
return clone(this.getOption("items", []));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* @param {Array} items
|
|
156
|
+
* @return {ChoiceCards}
|
|
157
|
+
*/
|
|
158
|
+
setItems(items) {
|
|
159
|
+
this.setOption("items", normalizeItems(items));
|
|
160
|
+
render.call(this);
|
|
161
|
+
return this;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Select a choice by value.
|
|
166
|
+
*
|
|
167
|
+
* @param {string|null|undefined} value
|
|
168
|
+
* @return {ChoiceCards}
|
|
169
|
+
*/
|
|
170
|
+
select(value) {
|
|
171
|
+
if (this.getOption("disabled") === true || this.hasAttribute("disabled")) {
|
|
172
|
+
return this;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const normalized = normalizeValue(value);
|
|
176
|
+
const item = findItem.call(this, normalized);
|
|
177
|
+
if (item?.disabled === true) {
|
|
178
|
+
return this;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (this.value === normalized) {
|
|
182
|
+
return this;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
this.value = normalized;
|
|
186
|
+
const detail = {
|
|
187
|
+
value: this.value,
|
|
188
|
+
item: item || null,
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
fireEvent(this, "change");
|
|
192
|
+
fireCustomEvent(this, "monster-selected", detail);
|
|
193
|
+
fireCustomEvent(this, "monster-change", detail);
|
|
194
|
+
fireCustomEvent(this, "monster-changed", detail);
|
|
195
|
+
|
|
196
|
+
return this;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* @private
|
|
202
|
+
* @return {void}
|
|
203
|
+
*/
|
|
204
|
+
function initControlReferences() {
|
|
205
|
+
this[controlElementSymbol] = this.shadowRoot?.querySelector(
|
|
206
|
+
"[data-monster-role=control]",
|
|
207
|
+
);
|
|
208
|
+
this[itemsElementSymbol] = this.shadowRoot?.querySelector(
|
|
209
|
+
"[data-monster-role=items]",
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* @private
|
|
215
|
+
* @return {void}
|
|
216
|
+
*/
|
|
217
|
+
function initItems() {
|
|
218
|
+
if (this.hasAttribute("value")) {
|
|
219
|
+
this.setOption("value", normalizeValue(this.getAttribute("value")));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const normalized = normalizeItems(this.getOption("items", []));
|
|
223
|
+
this.setOption("items", normalized);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* @private
|
|
228
|
+
* @return {void}
|
|
229
|
+
*/
|
|
230
|
+
function initEventHandler() {
|
|
231
|
+
this.addEventListener("click", (event) => {
|
|
232
|
+
const button = findChoiceButton(event);
|
|
233
|
+
if (!button) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
this.select(button.getAttribute("data-choice-value"));
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
this.addEventListener("keydown", (event) => {
|
|
240
|
+
if (this.getOption("disabled") === true || this.hasAttribute("disabled")) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const buttons = getEnabledButtons.call(this);
|
|
245
|
+
if (buttons.length === 0) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const current = this.shadowRoot?.activeElement;
|
|
250
|
+
let index = buttons.indexOf(current);
|
|
251
|
+
if (index < 0) {
|
|
252
|
+
index = Math.max(
|
|
253
|
+
0,
|
|
254
|
+
buttons.findIndex(
|
|
255
|
+
(button) => button.getAttribute("aria-checked") === "true",
|
|
256
|
+
),
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (event.key === " " || event.key === "Enter") {
|
|
261
|
+
event.preventDefault();
|
|
262
|
+
this.select(buttons[index].getAttribute("data-choice-value"));
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (event.key === "Home") {
|
|
267
|
+
event.preventDefault();
|
|
268
|
+
focusAndSelect.call(this, buttons[0]);
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (event.key === "End") {
|
|
273
|
+
event.preventDefault();
|
|
274
|
+
focusAndSelect.call(this, buttons[buttons.length - 1]);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (["ArrowRight", "ArrowDown"].includes(event.key)) {
|
|
279
|
+
event.preventDefault();
|
|
280
|
+
focusAndSelect.call(this, buttons[(index + 1) % buttons.length]);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (["ArrowLeft", "ArrowUp"].includes(event.key)) {
|
|
285
|
+
event.preventDefault();
|
|
286
|
+
focusAndSelect.call(
|
|
287
|
+
this,
|
|
288
|
+
buttons[(index - 1 + buttons.length) % buttons.length],
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* @private
|
|
296
|
+
* @param {HTMLButtonElement} button
|
|
297
|
+
* @return {void}
|
|
298
|
+
*/
|
|
299
|
+
function focusAndSelect(button) {
|
|
300
|
+
button.focus();
|
|
301
|
+
this.select(button.getAttribute("data-choice-value"));
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* @private
|
|
306
|
+
* @param {Event} event
|
|
307
|
+
* @return {HTMLButtonElement|null}
|
|
308
|
+
*/
|
|
309
|
+
function findChoiceButton(event) {
|
|
310
|
+
for (const node of event.composedPath()) {
|
|
311
|
+
if (
|
|
312
|
+
node instanceof HTMLButtonElement &&
|
|
313
|
+
node.getAttribute("data-monster-role") === "choice"
|
|
314
|
+
) {
|
|
315
|
+
return node;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* @private
|
|
323
|
+
* @return {HTMLButtonElement[]}
|
|
324
|
+
*/
|
|
325
|
+
function getEnabledButtons() {
|
|
326
|
+
return Array.from(
|
|
327
|
+
this.shadowRoot?.querySelectorAll('[data-monster-role="choice"]') || [],
|
|
328
|
+
).filter((button) => button.disabled !== true);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* @private
|
|
333
|
+
* @return {void}
|
|
334
|
+
*/
|
|
335
|
+
function render() {
|
|
336
|
+
if (!this[itemsElementSymbol]) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const items = normalizeItems(this.getOption("items", []));
|
|
341
|
+
const value = normalizeValue(this.getOption("value"));
|
|
342
|
+
const disabled =
|
|
343
|
+
this.getOption("disabled") === true || this.hasAttribute("disabled");
|
|
344
|
+
const hasSelectedItem = items.some((item) => item.value === value);
|
|
345
|
+
const firstEnabledIndex = items.findIndex((item) => item.disabled !== true);
|
|
346
|
+
|
|
347
|
+
this[itemsElementSymbol].replaceChildren();
|
|
348
|
+
this[controlElementSymbol]?.setAttribute(
|
|
349
|
+
"aria-label",
|
|
350
|
+
this.getOption("labels.group", "Choices"),
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
for (const [index, item] of items.entries()) {
|
|
354
|
+
const selected = value !== null && item.value === value;
|
|
355
|
+
const tabbable =
|
|
356
|
+
selected || (hasSelectedItem === false && index === firstEnabledIndex);
|
|
357
|
+
const button = document.createElement("button");
|
|
358
|
+
button.type = "button";
|
|
359
|
+
button.setAttribute("data-monster-role", "choice");
|
|
360
|
+
button.setAttribute("data-choice-value", item.value);
|
|
361
|
+
button.setAttribute("part", "choice");
|
|
362
|
+
button.setAttribute("role", "radio");
|
|
363
|
+
button.setAttribute("aria-label", item.label);
|
|
364
|
+
button.setAttribute("aria-checked", selected ? "true" : "false");
|
|
365
|
+
button.tabIndex = tabbable ? 0 : -1;
|
|
366
|
+
button.disabled = disabled || item.disabled === true;
|
|
367
|
+
button.classList.toggle("selected", selected);
|
|
368
|
+
button.classList.toggle("custom-content", !!item.contentSlot);
|
|
369
|
+
|
|
370
|
+
if (item.id) {
|
|
371
|
+
button.setAttribute("data-choice-id", item.id);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const indicator = document.createElement("span");
|
|
375
|
+
indicator.setAttribute("data-monster-role", "indicator");
|
|
376
|
+
indicator.setAttribute("part", "indicator");
|
|
377
|
+
button.appendChild(indicator);
|
|
378
|
+
|
|
379
|
+
if (item.contentSlot) {
|
|
380
|
+
const content = document.createElement("span");
|
|
381
|
+
content.setAttribute("data-monster-role", "content");
|
|
382
|
+
content.setAttribute("part", "content");
|
|
383
|
+
const slot = document.createElement("slot");
|
|
384
|
+
slot.name = item.contentSlot;
|
|
385
|
+
content.appendChild(slot);
|
|
386
|
+
button.appendChild(content);
|
|
387
|
+
} else {
|
|
388
|
+
const visual = document.createElement("span");
|
|
389
|
+
visual.setAttribute("data-monster-role", "visual");
|
|
390
|
+
visual.setAttribute("part", "visual");
|
|
391
|
+
if (item.iconSlot) {
|
|
392
|
+
const slot = document.createElement("slot");
|
|
393
|
+
slot.name = item.iconSlot;
|
|
394
|
+
visual.appendChild(slot);
|
|
395
|
+
} else if (item.icon) {
|
|
396
|
+
visual.setAttribute("data-choice-icon", item.icon);
|
|
397
|
+
} else {
|
|
398
|
+
visual.classList.add("empty");
|
|
399
|
+
}
|
|
400
|
+
button.appendChild(visual);
|
|
401
|
+
|
|
402
|
+
const label = document.createElement("span");
|
|
403
|
+
label.setAttribute("data-monster-role", "label");
|
|
404
|
+
label.setAttribute("part", "label");
|
|
405
|
+
label.textContent = item.label;
|
|
406
|
+
button.appendChild(label);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
this[itemsElementSymbol].appendChild(button);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* @private
|
|
415
|
+
* @param {string|null} value
|
|
416
|
+
* @return {object|null}
|
|
417
|
+
*/
|
|
418
|
+
function findItem(value) {
|
|
419
|
+
return normalizeItems(this.getOption("items", [])).find(
|
|
420
|
+
(item) => item.value === value,
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* @private
|
|
426
|
+
* @param {*} value
|
|
427
|
+
* @return {string|null}
|
|
428
|
+
*/
|
|
429
|
+
function normalizeValue(value) {
|
|
430
|
+
if (value === undefined || value === null || value === "") {
|
|
431
|
+
return null;
|
|
432
|
+
}
|
|
433
|
+
return String(value);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* @private
|
|
438
|
+
* @param {*} items
|
|
439
|
+
* @return {Array}
|
|
440
|
+
*/
|
|
441
|
+
function normalizeItems(items) {
|
|
442
|
+
if (!isArray(items)) {
|
|
443
|
+
return [];
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return items.map((entry, index) => {
|
|
447
|
+
if (isString(entry) || isNumber(entry)) {
|
|
448
|
+
return {
|
|
449
|
+
id: `item-${index}`,
|
|
450
|
+
value: String(entry),
|
|
451
|
+
label: String(entry),
|
|
452
|
+
icon: "",
|
|
453
|
+
iconSlot: "",
|
|
454
|
+
contentSlot: "",
|
|
455
|
+
disabled: false,
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (!isObject(entry)) {
|
|
460
|
+
return {
|
|
461
|
+
id: `item-${index}`,
|
|
462
|
+
value: `item-${index}`,
|
|
463
|
+
label: "",
|
|
464
|
+
icon: "",
|
|
465
|
+
iconSlot: "",
|
|
466
|
+
contentSlot: "",
|
|
467
|
+
disabled: false,
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const value = normalizeValue(entry.value ?? entry.id ?? `item-${index}`);
|
|
472
|
+
return {
|
|
473
|
+
id: String(entry.id ?? value ?? `item-${index}`),
|
|
474
|
+
value: value ?? `item-${index}`,
|
|
475
|
+
label: String(entry.label ?? value ?? ""),
|
|
476
|
+
icon: String(entry.icon ?? ""),
|
|
477
|
+
iconSlot: String(entry.iconSlot ?? ""),
|
|
478
|
+
contentSlot: String(entry.contentSlot ?? entry.slot ?? ""),
|
|
479
|
+
disabled: entry.disabled === true,
|
|
480
|
+
};
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* @private
|
|
486
|
+
* @return {void}
|
|
487
|
+
*/
|
|
488
|
+
function syncFormValue() {
|
|
489
|
+
if (typeof this.setFormValue === "function") {
|
|
490
|
+
this.setFormValue(this.value ?? "");
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* @private
|
|
496
|
+
* @param {string|null} value
|
|
497
|
+
* @return {void}
|
|
498
|
+
*/
|
|
499
|
+
function syncValueAttribute(value) {
|
|
500
|
+
if (value === null) {
|
|
501
|
+
if (this.hasAttribute("value")) {
|
|
502
|
+
this.removeAttribute("value");
|
|
503
|
+
}
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if (this.getAttribute("value") !== value) {
|
|
508
|
+
this.setAttribute("value", value);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* @private
|
|
514
|
+
* @return {string}
|
|
515
|
+
*/
|
|
516
|
+
function getTemplate() {
|
|
517
|
+
// language=HTML
|
|
518
|
+
return `
|
|
519
|
+
<div data-monster-role="control" part="control" role="radiogroup">
|
|
520
|
+
<div data-monster-role="items" part="items"></div>
|
|
521
|
+
</div>
|
|
522
|
+
`;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
registerCustomElement(ChoiceCards);
|
|
@@ -97,7 +97,7 @@ class Form extends DataSet {
|
|
|
97
97
|
|
|
98
98
|
reportValidity: {
|
|
99
99
|
selector:
|
|
100
|
-
"input,select,textarea,monster-select,monster-toggle-switch,monster-password",
|
|
100
|
+
"input,select,textarea,monster-select,monster-toggle-switch,monster-password,monster-choice-cards",
|
|
101
101
|
},
|
|
102
102
|
|
|
103
103
|
eventProcessing: true,
|