@schukai/monster 4.89.0 → 4.91.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/CHANGELOG.md +17 -0
- package/package.json +1 -1
- package/source/components/datatable/save-button.mjs +215 -40
- package/source/components/form/repeat-field-set.mjs +1090 -0
- package/source/components/form/select.mjs +8 -1
- package/source/components/form/style/field-set.pcss +2 -296
- package/source/components/form/style/mixin/field-set-layout.pcss +545 -0
- package/source/components/form/style/repeat-field-set-items.pcss +11 -0
- package/source/components/form/style/repeat-field-set.pcss +45 -0
- package/source/components/form/stylesheet/field-set.mjs +1 -1
- package/source/components/form/stylesheet/mixin/field-set-layout.mjs +38 -0
- package/source/components/form/stylesheet/repeat-field-set-items.mjs +38 -0
- package/source/components/form/stylesheet/repeat-field-set.mjs +38 -0
- package/source/data/diff.mjs +0 -4
- package/source/monster.mjs +1 -0
- package/test/cases/data/diff.mjs +24 -1
|
@@ -0,0 +1,1090 @@
|
|
|
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 { addAttributeToken } from "../../dom/attributes.mjs";
|
|
17
|
+
import {
|
|
18
|
+
ATTRIBUTE_ERRORMESSAGE,
|
|
19
|
+
ATTRIBUTE_ROLE,
|
|
20
|
+
ATTRIBUTE_TEMPLATE_PREFIX,
|
|
21
|
+
ATTRIBUTE_UPDATER_INSERT,
|
|
22
|
+
} from "../../dom/constants.mjs";
|
|
23
|
+
import { CustomControl } from "../../dom/customcontrol.mjs";
|
|
24
|
+
import {
|
|
25
|
+
assembleMethodSymbol,
|
|
26
|
+
registerCustomElement,
|
|
27
|
+
} from "../../dom/customelement.mjs";
|
|
28
|
+
import { fireCustomEvent } from "../../dom/events.mjs";
|
|
29
|
+
import { addErrorAttribute } from "../../dom/error.mjs";
|
|
30
|
+
import { getDocumentTheme } from "../../dom/theme.mjs";
|
|
31
|
+
import { Pathfinder } from "../../data/pathfinder.mjs";
|
|
32
|
+
import { datasourceLinkedElementSymbol } from "../datatable/util.mjs";
|
|
33
|
+
import { Observer } from "../../types/observer.mjs";
|
|
34
|
+
import { isArray, isNumber, isObject, isString } from "../../types/is.mjs";
|
|
35
|
+
import { ID } from "../../types/id.mjs";
|
|
36
|
+
import { clone } from "../../util/clone.mjs";
|
|
37
|
+
import { FieldSetStyleSheet } from "./stylesheet/field-set.mjs";
|
|
38
|
+
import { RepeatFieldSetStyleSheet } from "./stylesheet/repeat-field-set.mjs";
|
|
39
|
+
import { RepeatFieldSetItemsStyleSheet } from "./stylesheet/repeat-field-set-items.mjs";
|
|
40
|
+
|
|
41
|
+
export { RepeatFieldSet };
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @private
|
|
45
|
+
* @type {symbol}
|
|
46
|
+
*/
|
|
47
|
+
const addButtonSymbol = Symbol("addButton");
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @private
|
|
51
|
+
* @type {symbol}
|
|
52
|
+
*/
|
|
53
|
+
const removeButtonSymbol = Symbol("removeButton");
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @private
|
|
57
|
+
* @type {symbol}
|
|
58
|
+
*/
|
|
59
|
+
const itemSlotSymbol = Symbol("itemSlot");
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @private
|
|
63
|
+
* @type {symbol}
|
|
64
|
+
*/
|
|
65
|
+
const itemsContainerSymbol = Symbol("itemsContainer");
|
|
66
|
+
const itemDefaultsSymbol = Symbol("itemDefaults");
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* A repeatable field-set control that manages array data.
|
|
70
|
+
*
|
|
71
|
+
* @fragments /fragments/components/form/repeat-field-set/
|
|
72
|
+
*
|
|
73
|
+
* @since 3.78.0
|
|
74
|
+
* @summary A repeatable field-set control
|
|
75
|
+
* @fires monster-repeat-add
|
|
76
|
+
* @fires monster-repeat-remove
|
|
77
|
+
* @fires monster-repeat-change
|
|
78
|
+
*/
|
|
79
|
+
class RepeatFieldSet extends CustomControl {
|
|
80
|
+
/**
|
|
81
|
+
* This method is called by the `instanceof` operator.
|
|
82
|
+
* @return {symbol}
|
|
83
|
+
*/
|
|
84
|
+
static get [instanceSymbol]() {
|
|
85
|
+
return Symbol.for(
|
|
86
|
+
"@schukai/monster/components/form/repeat-field-set@@instance",
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
*
|
|
92
|
+
* @return {RepeatFieldSet}
|
|
93
|
+
*/
|
|
94
|
+
[assembleMethodSymbol]() {
|
|
95
|
+
super[assembleMethodSymbol]();
|
|
96
|
+
initControlReferences.call(this);
|
|
97
|
+
ensureItemsStyleSheet.call(this);
|
|
98
|
+
initTemplateBridge.call(this);
|
|
99
|
+
syncOptionData.call(this);
|
|
100
|
+
initEventHandler.call(this);
|
|
101
|
+
initObservers.call(this);
|
|
102
|
+
updateColumns.call(this);
|
|
103
|
+
normalizeOnLoad.call(this);
|
|
104
|
+
syncButtons.call(this);
|
|
105
|
+
return this;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* To set the options via the HTML tag, the attribute `data-monster-options` must be used.
|
|
110
|
+
* @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
|
|
111
|
+
*
|
|
112
|
+
* @property {Object} templates Template definitions
|
|
113
|
+
* @property {string} templates.main Main template
|
|
114
|
+
* @property {Object} labels Label definitions
|
|
115
|
+
* @property {string} labels.add Add label
|
|
116
|
+
* @property {string} labels.remove Remove label
|
|
117
|
+
* @property {Object} classes Class definitions
|
|
118
|
+
* @property {string} classes.content Content class
|
|
119
|
+
* @property {string} path Data path relative to the form record
|
|
120
|
+
* @property {number|null} min Minimum items
|
|
121
|
+
* @property {number|null} max Maximum items
|
|
122
|
+
* @property {boolean} disabled Disabled state
|
|
123
|
+
*/
|
|
124
|
+
get defaults() {
|
|
125
|
+
return Object.assign({}, super.defaults, {
|
|
126
|
+
templates: {
|
|
127
|
+
main: getTemplate(),
|
|
128
|
+
},
|
|
129
|
+
labels: {
|
|
130
|
+
add: "Add",
|
|
131
|
+
remove: "Remove",
|
|
132
|
+
},
|
|
133
|
+
classes: {
|
|
134
|
+
content: "collapse-alignment",
|
|
135
|
+
},
|
|
136
|
+
features: {
|
|
137
|
+
multipleColumns: true,
|
|
138
|
+
},
|
|
139
|
+
path: null,
|
|
140
|
+
min: null,
|
|
141
|
+
max: null,
|
|
142
|
+
disabled: false,
|
|
143
|
+
value: null,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
*
|
|
149
|
+
* @return {string}
|
|
150
|
+
*/
|
|
151
|
+
static getTag() {
|
|
152
|
+
return "monster-repeat-field-set";
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
*
|
|
157
|
+
* @return {CSSStyleSheet[]}
|
|
158
|
+
*/
|
|
159
|
+
static getCSSStyleSheet() {
|
|
160
|
+
return [FieldSetStyleSheet, RepeatFieldSetStyleSheet];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* The current value of the control.
|
|
165
|
+
*
|
|
166
|
+
* @return {string}
|
|
167
|
+
*/
|
|
168
|
+
get value() {
|
|
169
|
+
return this.getOption("value");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Set the value of the control.
|
|
174
|
+
*
|
|
175
|
+
* @param {string} value
|
|
176
|
+
* @return {void}
|
|
177
|
+
*/
|
|
178
|
+
set value(value) {
|
|
179
|
+
this.setOption("value", value);
|
|
180
|
+
try {
|
|
181
|
+
this?.setFormValue(this.value);
|
|
182
|
+
} catch (e) {
|
|
183
|
+
addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* @private
|
|
190
|
+
*/
|
|
191
|
+
function initControlReferences() {
|
|
192
|
+
this[addButtonSymbol] = this.shadowRoot.querySelector(
|
|
193
|
+
`[${ATTRIBUTE_ROLE}="add"]`,
|
|
194
|
+
);
|
|
195
|
+
this[removeButtonSymbol] = this.shadowRoot.querySelector(
|
|
196
|
+
`[${ATTRIBUTE_ROLE}="remove"]`,
|
|
197
|
+
);
|
|
198
|
+
this[itemSlotSymbol] = this.shadowRoot.querySelector('slot[name="item"]');
|
|
199
|
+
this[itemsContainerSymbol] = ensureItemsContainer.call(this);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* @private
|
|
204
|
+
* @return {HTMLElement}
|
|
205
|
+
*/
|
|
206
|
+
function ensureItemsContainer() {
|
|
207
|
+
let container = this.querySelector(`[${ATTRIBUTE_ROLE}="items"]`);
|
|
208
|
+
if (!(container instanceof HTMLElement)) {
|
|
209
|
+
container = document.createElement("div");
|
|
210
|
+
container.setAttribute(ATTRIBUTE_ROLE, "items");
|
|
211
|
+
this.appendChild(container);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
container.classList.add("grid-span-full");
|
|
215
|
+
container.setAttribute("slot", "items");
|
|
216
|
+
return container;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* @private
|
|
221
|
+
* @return {HTMLStyleElement}
|
|
222
|
+
*/
|
|
223
|
+
function ensureItemsStyleSheet() {
|
|
224
|
+
const root = this.ownerDocument || document;
|
|
225
|
+
if (!("adoptedStyleSheets" in root)) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
const sheets = root.adoptedStyleSheets || [];
|
|
229
|
+
if (!sheets.includes(RepeatFieldSetItemsStyleSheet)) {
|
|
230
|
+
root.adoptedStyleSheets = [...sheets, RepeatFieldSetItemsStyleSheet];
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* @private
|
|
236
|
+
*/
|
|
237
|
+
function initTemplateBridge() {
|
|
238
|
+
const slot = this[itemSlotSymbol];
|
|
239
|
+
if (slot) {
|
|
240
|
+
slot.addEventListener("slotchange", () => {
|
|
241
|
+
this[itemDefaultsSymbol] = null;
|
|
242
|
+
syncInsertDefinition.call(this);
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
syncInsertDefinition.call(this);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* @private
|
|
250
|
+
*/
|
|
251
|
+
function initEventHandler() {
|
|
252
|
+
if (this[addButtonSymbol]) {
|
|
253
|
+
this[addButtonSymbol].addEventListener("click", (event) => {
|
|
254
|
+
event.stopPropagation();
|
|
255
|
+
addItem.call(this);
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (this[removeButtonSymbol]) {
|
|
260
|
+
this[removeButtonSymbol].addEventListener("click", (event) => {
|
|
261
|
+
event.stopPropagation();
|
|
262
|
+
removeItem.call(this);
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* @private
|
|
269
|
+
*/
|
|
270
|
+
function updateColumns() {
|
|
271
|
+
if (this.getOption("features.multipleColumns") !== true) {
|
|
272
|
+
this.classList.remove("multiple-columns");
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
this.classList.add("multiple-columns");
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* @private
|
|
281
|
+
*/
|
|
282
|
+
function initObservers() {
|
|
283
|
+
this.attachObserver(
|
|
284
|
+
new Observer(() => {
|
|
285
|
+
syncInsertDefinition.call(this);
|
|
286
|
+
syncButtons.call(this);
|
|
287
|
+
}),
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
const form = this.closest("monster-form");
|
|
291
|
+
if (form && typeof form.attachObserver === "function") {
|
|
292
|
+
form.attachObserver(
|
|
293
|
+
new Observer(() => {
|
|
294
|
+
syncOptionData.call(this);
|
|
295
|
+
syncButtons.call(this);
|
|
296
|
+
}),
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const datasource = getDatasourceElement.call(this);
|
|
301
|
+
if (datasource?.datasource?.attachObserver) {
|
|
302
|
+
datasource.datasource.attachObserver(
|
|
303
|
+
new Observer(() => {
|
|
304
|
+
syncOptionData.call(this);
|
|
305
|
+
syncButtons.call(this);
|
|
306
|
+
}),
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (datasource) {
|
|
311
|
+
datasource.addEventListener("monster-datasource-fetched", () => {
|
|
312
|
+
syncOptionData.call(this);
|
|
313
|
+
normalizeOnLoad.call(this);
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* @private
|
|
320
|
+
* @return {string|null}
|
|
321
|
+
*/
|
|
322
|
+
function getPathOption() {
|
|
323
|
+
const path = this.getOption("path");
|
|
324
|
+
if (isString(path) && path.trim() !== "") {
|
|
325
|
+
return path.trim();
|
|
326
|
+
}
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* @private
|
|
332
|
+
* @param {unknown} value
|
|
333
|
+
* @return {number|null}
|
|
334
|
+
*/
|
|
335
|
+
function normalizeLimit(value) {
|
|
336
|
+
if (value === null || value === undefined) {
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
if (isNumber(value) && Number.isFinite(value)) {
|
|
340
|
+
return value;
|
|
341
|
+
}
|
|
342
|
+
if (isString(value)) {
|
|
343
|
+
const trimmed = value.trim();
|
|
344
|
+
if (trimmed === "" || trimmed.toLowerCase() === "null") {
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
const numeric = Number(trimmed);
|
|
348
|
+
return Number.isFinite(numeric) ? numeric : null;
|
|
349
|
+
}
|
|
350
|
+
return null;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* @private
|
|
355
|
+
* @return {number|null}
|
|
356
|
+
*/
|
|
357
|
+
function getMinLimit() {
|
|
358
|
+
return normalizeLimit(this.getOption("min"));
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* @private
|
|
363
|
+
* @return {number|null}
|
|
364
|
+
*/
|
|
365
|
+
function getMaxLimit() {
|
|
366
|
+
return normalizeLimit(this.getOption("max"));
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* @private
|
|
371
|
+
* @return {HTMLTemplateElement|null}
|
|
372
|
+
*/
|
|
373
|
+
function getItemTemplate() {
|
|
374
|
+
const slot = this[itemSlotSymbol];
|
|
375
|
+
if (slot && typeof slot.assignedElements === "function") {
|
|
376
|
+
const assigned = slot.assignedElements({ flatten: true });
|
|
377
|
+
for (const element of assigned) {
|
|
378
|
+
if (element instanceof HTMLTemplateElement) {
|
|
379
|
+
return element;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const fallback = this.querySelector('template[slot="item"]');
|
|
385
|
+
if (fallback instanceof HTMLTemplateElement) {
|
|
386
|
+
return fallback;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* @private
|
|
394
|
+
* @return {string}
|
|
395
|
+
*/
|
|
396
|
+
function ensureTemplatePrefix() {
|
|
397
|
+
const container = this[itemsContainerSymbol];
|
|
398
|
+
if (!(container instanceof HTMLElement)) {
|
|
399
|
+
throw new Error("RepeatFieldSet is missing the items container.");
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
let prefix = container.getAttribute(ATTRIBUTE_TEMPLATE_PREFIX);
|
|
403
|
+
if (!isString(prefix) || prefix.trim() === "") {
|
|
404
|
+
prefix = new ID("repeat-field").toString();
|
|
405
|
+
container.setAttribute(ATTRIBUTE_TEMPLATE_PREFIX, prefix);
|
|
406
|
+
}
|
|
407
|
+
return prefix;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* @private
|
|
412
|
+
*/
|
|
413
|
+
function syncInsertDefinition() {
|
|
414
|
+
const path = getPathOption.call(this);
|
|
415
|
+
if (!path) {
|
|
416
|
+
addErrorAttribute(this, "RepeatFieldSet requires a path option.");
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const container = this[itemsContainerSymbol];
|
|
421
|
+
if (!(container instanceof HTMLElement)) {
|
|
422
|
+
addErrorAttribute(this, "RepeatFieldSet is missing the items container.");
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const template = getItemTemplate.call(this);
|
|
427
|
+
if (!template) {
|
|
428
|
+
addErrorAttribute(
|
|
429
|
+
this,
|
|
430
|
+
'RepeatFieldSet requires a <template slot="item"> definition.',
|
|
431
|
+
);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const prefix = ensureTemplatePrefix.call(this);
|
|
436
|
+
const themeName = getDocumentTheme().getName();
|
|
437
|
+
const nextId = `${prefix}-item-${themeName}`;
|
|
438
|
+
if (template.id !== nextId) {
|
|
439
|
+
template.id = nextId;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const form = this.closest("monster-form");
|
|
443
|
+
let record = null;
|
|
444
|
+
if (form && typeof form.getOption === "function") {
|
|
445
|
+
const formData = form.getOption("data");
|
|
446
|
+
if (isObject(formData) || isArray(formData)) {
|
|
447
|
+
record = formData;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
if (!record) {
|
|
451
|
+
record = resolveFormRecord(form, getDatasourceElement.call(this));
|
|
452
|
+
}
|
|
453
|
+
const normalizedPath = normalizePathForRecord(path, record);
|
|
454
|
+
const dataPath = normalizedPath.startsWith("data.")
|
|
455
|
+
? normalizedPath
|
|
456
|
+
: `data.${normalizedPath}`;
|
|
457
|
+
const insertValue = `item path:${dataPath}`;
|
|
458
|
+
if (container.getAttribute(ATTRIBUTE_UPDATER_INSERT) !== insertValue) {
|
|
459
|
+
container.setAttribute(ATTRIBUTE_UPDATER_INSERT, insertValue);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (this.getAttribute(ATTRIBUTE_UPDATER_INSERT)) {
|
|
463
|
+
this.removeAttribute(ATTRIBUTE_UPDATER_INSERT);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* @private
|
|
470
|
+
* @return {HTMLElement|null}
|
|
471
|
+
*/
|
|
472
|
+
function getDatasourceElement() {
|
|
473
|
+
const form = this.closest("monster-form");
|
|
474
|
+
if (!form) {
|
|
475
|
+
return null;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (form[datasourceLinkedElementSymbol]) {
|
|
479
|
+
return form[datasourceLinkedElementSymbol];
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (typeof form.getOption === "function") {
|
|
483
|
+
const selector = form.getOption("datasource.selector");
|
|
484
|
+
if (isString(selector) && selector.trim() !== "") {
|
|
485
|
+
return document.querySelector(selector);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return null;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* @private
|
|
494
|
+
* @return {object|null}
|
|
495
|
+
*/
|
|
496
|
+
function getTargetContext() {
|
|
497
|
+
const form = this.closest("monster-form");
|
|
498
|
+
const path = getPathOption.call(this);
|
|
499
|
+
if (!path) {
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (form && typeof form.getOption === "function") {
|
|
504
|
+
const formData = form.getOption("data");
|
|
505
|
+
if (isObject(formData) || isArray(formData)) {
|
|
506
|
+
const formPath = normalizePathForForm(path);
|
|
507
|
+
return { type: "form", target: form, path: formPath };
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const datasource = getDatasourceElement.call(this);
|
|
512
|
+
if (datasource) {
|
|
513
|
+
const mappingData = form?.getOption?.("mapping.data");
|
|
514
|
+
const mappingIndex = form?.getOption?.("mapping.index");
|
|
515
|
+
const record = resolveFormRecord(form, datasource);
|
|
516
|
+
const normalizedPath = normalizePathForRecord(path, record);
|
|
517
|
+
let basePath = "";
|
|
518
|
+
|
|
519
|
+
if (isString(mappingData) && mappingData.trim() !== "") {
|
|
520
|
+
basePath = mappingData.trim();
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (mappingIndex !== null && mappingIndex !== undefined) {
|
|
524
|
+
const indexValue = String(mappingIndex);
|
|
525
|
+
basePath = basePath ? `${basePath}.${indexValue}` : indexValue;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const finalPath = basePath
|
|
529
|
+
? `${basePath}.${normalizedPath}`
|
|
530
|
+
: normalizedPath;
|
|
531
|
+
return { type: "datasource", target: datasource, path: finalPath };
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (form && typeof form.getOption === "function") {
|
|
535
|
+
return { type: "form", target: form, path };
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
return null;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* @private
|
|
543
|
+
* @param {string} path
|
|
544
|
+
* @return {string}
|
|
545
|
+
*/
|
|
546
|
+
function normalizePathForForm(path) {
|
|
547
|
+
if (!isString(path)) {
|
|
548
|
+
return path;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
const trimmed = path.trim();
|
|
552
|
+
if (trimmed.startsWith("data.")) {
|
|
553
|
+
return trimmed;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
return `data.${trimmed}`;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* @private
|
|
561
|
+
* @param {string} path
|
|
562
|
+
* @param {object} record
|
|
563
|
+
* @return {string}
|
|
564
|
+
*/
|
|
565
|
+
function normalizePathForRecord(path, record) {
|
|
566
|
+
if (!isString(path)) {
|
|
567
|
+
return path;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
const trimmed = path.trim();
|
|
571
|
+
if (!trimmed.startsWith("data.")) {
|
|
572
|
+
return trimmed;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
if (isObject(record?.data) || isArray(record?.data)) {
|
|
576
|
+
return trimmed;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
return trimmed.slice(5);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* @private
|
|
584
|
+
* @return {object}
|
|
585
|
+
*/
|
|
586
|
+
function getCurrentDataSnapshot() {
|
|
587
|
+
const context = getTargetContext.call(this);
|
|
588
|
+
if (!context) {
|
|
589
|
+
return { context: null, data: {} };
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
let data;
|
|
593
|
+
if (context.type === "datasource") {
|
|
594
|
+
data = context.target?.data;
|
|
595
|
+
} else {
|
|
596
|
+
data = context.target?.getOption?.("data");
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
if (!isObject(data) && !isArray(data)) {
|
|
600
|
+
data = {};
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
return { context, data };
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* @private
|
|
608
|
+
* @return {Array}
|
|
609
|
+
*/
|
|
610
|
+
function getCurrentItems() {
|
|
611
|
+
const { context, data } = getCurrentDataSnapshot.call(this);
|
|
612
|
+
if (!context) {
|
|
613
|
+
return [];
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
try {
|
|
617
|
+
if (context.type === "form") {
|
|
618
|
+
const value = context.target?.getOption?.(context.path);
|
|
619
|
+
return isArray(value) ? [...value] : [];
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
const value = new Pathfinder(data).getVia(context.path);
|
|
623
|
+
return isArray(value) ? [...value] : [];
|
|
624
|
+
} catch {
|
|
625
|
+
return [];
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* @private
|
|
631
|
+
* @return {object}
|
|
632
|
+
*/
|
|
633
|
+
function buildEmptyItem() {
|
|
634
|
+
const defaults = getItemDefaults.call(this);
|
|
635
|
+
return clone(defaults);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* @private
|
|
640
|
+
* @return {object}
|
|
641
|
+
*/
|
|
642
|
+
function getItemDefaults() {
|
|
643
|
+
if (this[itemDefaultsSymbol]) {
|
|
644
|
+
return this[itemDefaultsSymbol];
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
const template = getItemTemplate.call(this);
|
|
648
|
+
const defaults = {};
|
|
649
|
+
if (!template?.content) {
|
|
650
|
+
this[itemDefaultsSymbol] = defaults;
|
|
651
|
+
return defaults;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
const elements = template.content.querySelectorAll(
|
|
655
|
+
"[data-monster-bind],[data-monster-attributes]",
|
|
656
|
+
);
|
|
657
|
+
|
|
658
|
+
for (const element of elements) {
|
|
659
|
+
const bindAttr = element.getAttribute("data-monster-bind");
|
|
660
|
+
const attrAttr = element.getAttribute("data-monster-attributes");
|
|
661
|
+
const paths = new Set([
|
|
662
|
+
...extractItemPaths(bindAttr),
|
|
663
|
+
...extractItemPathsFromAttributes(attrAttr),
|
|
664
|
+
]);
|
|
665
|
+
|
|
666
|
+
if (paths.size === 0) {
|
|
667
|
+
continue;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
const value = getDefaultValueForElement(element);
|
|
671
|
+
for (const path of paths) {
|
|
672
|
+
try {
|
|
673
|
+
new Pathfinder(defaults).setVia(path, value);
|
|
674
|
+
} catch (error) {
|
|
675
|
+
addErrorAttribute(
|
|
676
|
+
this,
|
|
677
|
+
error?.message || "Failed to build default repeat item.",
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
this[itemDefaultsSymbol] = defaults;
|
|
684
|
+
return defaults;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* @private
|
|
689
|
+
* @param {string|null} value
|
|
690
|
+
* @return {string[]}
|
|
691
|
+
*/
|
|
692
|
+
function extractItemPaths(value) {
|
|
693
|
+
if (!isString(value)) {
|
|
694
|
+
return [];
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
const paths = [];
|
|
698
|
+
const regex = /path:item\.([A-Za-z0-9_.-]+)/g;
|
|
699
|
+
for (const match of value.matchAll(regex)) {
|
|
700
|
+
if (match[1]) {
|
|
701
|
+
paths.push(match[1]);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
return paths;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* @private
|
|
709
|
+
* @param {string|null} value
|
|
710
|
+
* @return {string[]}
|
|
711
|
+
*/
|
|
712
|
+
function extractItemPathsFromAttributes(value) {
|
|
713
|
+
if (!isString(value)) {
|
|
714
|
+
return [];
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
const paths = [];
|
|
718
|
+
const entries = value.split(",");
|
|
719
|
+
for (const entry of entries) {
|
|
720
|
+
const def = entry.trim();
|
|
721
|
+
if (
|
|
722
|
+
def.startsWith("value ") ||
|
|
723
|
+
def.startsWith("checked ") ||
|
|
724
|
+
def.startsWith("selected ")
|
|
725
|
+
) {
|
|
726
|
+
paths.push(...extractItemPaths(def));
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
return paths;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* @private
|
|
735
|
+
* @param {Element} element
|
|
736
|
+
* @return {*}
|
|
737
|
+
*/
|
|
738
|
+
function getDefaultValueForElement(element) {
|
|
739
|
+
const bindType = element.getAttribute("data-monster-bind-type");
|
|
740
|
+
const defaultValue = element.getAttribute("data-monster-defaultvalue");
|
|
741
|
+
|
|
742
|
+
if (defaultValue !== null) {
|
|
743
|
+
return coerceDefaultValue(defaultValue, bindType);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
if (bindType === "boolean") {
|
|
747
|
+
return false;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
if (bindType === "number") {
|
|
751
|
+
return null;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
const tagName = element.tagName?.toLowerCase();
|
|
755
|
+
if (tagName === "monster-toggle-switch") {
|
|
756
|
+
return false;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
if (tagName === "monster-select") {
|
|
760
|
+
const optionType = element.getAttribute("data-monster-option-type");
|
|
761
|
+
if (optionType === "checkbox" || element.hasAttribute("multiple")) {
|
|
762
|
+
return [];
|
|
763
|
+
}
|
|
764
|
+
return "";
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
if (tagName === "select" && element.hasAttribute("multiple")) {
|
|
768
|
+
return [];
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
const inputType = element.getAttribute("type");
|
|
772
|
+
if (inputType === "checkbox" || inputType === "radio") {
|
|
773
|
+
return false;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
return "";
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/**
|
|
780
|
+
* @private
|
|
781
|
+
* @param {string} value
|
|
782
|
+
* @param {string|null} bindType
|
|
783
|
+
* @return {*}
|
|
784
|
+
*/
|
|
785
|
+
function coerceDefaultValue(value, bindType) {
|
|
786
|
+
if (bindType === "boolean") {
|
|
787
|
+
const normalized = value.trim().toLowerCase();
|
|
788
|
+
return normalized === "true" || normalized === "1";
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
if (bindType === "number") {
|
|
792
|
+
const numeric = Number(value);
|
|
793
|
+
return Number.isFinite(numeric) ? numeric : null;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
return value;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
/**
|
|
800
|
+
* @private
|
|
801
|
+
* @param {Array} items
|
|
802
|
+
* @return {Array}
|
|
803
|
+
*/
|
|
804
|
+
function enforceLimits(items) {
|
|
805
|
+
let list = isArray(items) ? [...items] : [];
|
|
806
|
+
const min = getMinLimit.call(this);
|
|
807
|
+
const max = getMaxLimit.call(this);
|
|
808
|
+
|
|
809
|
+
if (isNumber(max) && Number.isFinite(max)) {
|
|
810
|
+
if (list.length > max) {
|
|
811
|
+
list = list.slice(0, max);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
if (isNumber(min) && Number.isFinite(min)) {
|
|
816
|
+
while (list.length < min) {
|
|
817
|
+
list.push(buildEmptyItem.call(this));
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
return list;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* @private
|
|
826
|
+
* @param {Array} items
|
|
827
|
+
*/
|
|
828
|
+
function persistItems(items, options = {}) {
|
|
829
|
+
const { context, data } = getCurrentDataSnapshot.call(this);
|
|
830
|
+
if (!context) {
|
|
831
|
+
return;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
const { syncDatasource = true } = options;
|
|
835
|
+
|
|
836
|
+
if (context.type === "form") {
|
|
837
|
+
try {
|
|
838
|
+
context.target.setOption(context.path, items);
|
|
839
|
+
} catch (error) {
|
|
840
|
+
addErrorAttribute(this, error?.message || "Failed to update form data.");
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
if (syncDatasource) {
|
|
845
|
+
const datasourceContext = getDatasourceContext.call(this);
|
|
846
|
+
const datasourceData = datasourceContext?.target?.data;
|
|
847
|
+
if (datasourceContext && (isObject(datasourceData) || isArray(datasourceData))) {
|
|
848
|
+
const nextData = clone(datasourceData);
|
|
849
|
+
try {
|
|
850
|
+
new Pathfinder(nextData).setVia(datasourceContext.path, items);
|
|
851
|
+
datasourceContext.target.data = nextData;
|
|
852
|
+
} catch (error) {
|
|
853
|
+
addErrorAttribute(
|
|
854
|
+
this,
|
|
855
|
+
error?.message || "Failed to update datasource data.",
|
|
856
|
+
);
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
} else {
|
|
862
|
+
const nextData = clone(data);
|
|
863
|
+
try {
|
|
864
|
+
new Pathfinder(nextData).setVia(context.path, items);
|
|
865
|
+
} catch (error) {
|
|
866
|
+
addErrorAttribute(this, error?.message || "Failed to update path.");
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
context.target.data = nextData;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
syncOptionData.call(this);
|
|
874
|
+
syncButtons.call(this);
|
|
875
|
+
fireCustomEvent(this, "monster-repeat-change", {
|
|
876
|
+
detail: {
|
|
877
|
+
length: items.length,
|
|
878
|
+
path: context.path,
|
|
879
|
+
},
|
|
880
|
+
});
|
|
881
|
+
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
* @private
|
|
886
|
+
*/
|
|
887
|
+
function addItem() {
|
|
888
|
+
if (this.getOption("disabled") === true) {
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
const items = getCurrentItems.call(this);
|
|
893
|
+
const max = getMaxLimit.call(this);
|
|
894
|
+
if (isNumber(max) && Number.isFinite(max) && items.length >= max) {
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
const nextItems = [...items, buildEmptyItem.call(this)];
|
|
899
|
+
persistItems.call(this, nextItems);
|
|
900
|
+
fireCustomEvent(this, "monster-repeat-add", {
|
|
901
|
+
detail: { length: nextItems.length },
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
/**
|
|
906
|
+
* @private
|
|
907
|
+
*/
|
|
908
|
+
function removeItem() {
|
|
909
|
+
if (this.getOption("disabled") === true) {
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
const items = getCurrentItems.call(this);
|
|
914
|
+
const min = getMinLimit.call(this);
|
|
915
|
+
if (isNumber(min) && Number.isFinite(min) && items.length <= min) {
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
if (items.length === 0) {
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
const nextItems = items.slice(0, -1);
|
|
923
|
+
persistItems.call(this, nextItems);
|
|
924
|
+
fireCustomEvent(this, "monster-repeat-remove", {
|
|
925
|
+
detail: { length: nextItems.length },
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
/**
|
|
930
|
+
* @private
|
|
931
|
+
*/
|
|
932
|
+
function normalizeOnLoad() {
|
|
933
|
+
const items = getCurrentItems.call(this);
|
|
934
|
+
const normalized = enforceLimits.call(this, items);
|
|
935
|
+
|
|
936
|
+
if (normalized.length !== items.length) {
|
|
937
|
+
persistItems.call(this, normalized, { syncDatasource: false });
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
syncOptionData.call(this);
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
/**
|
|
945
|
+
* @private
|
|
946
|
+
*/
|
|
947
|
+
function syncButtons() {
|
|
948
|
+
const items = getCurrentItems.call(this);
|
|
949
|
+
const min = getMinLimit.call(this);
|
|
950
|
+
const max = getMaxLimit.call(this);
|
|
951
|
+
const isDisabled = this.getOption("disabled") === true;
|
|
952
|
+
|
|
953
|
+
if (this[addButtonSymbol]) {
|
|
954
|
+
const reachedMax =
|
|
955
|
+
isNumber(max) && Number.isFinite(max) && items.length >= max;
|
|
956
|
+
this[addButtonSymbol].disabled = isDisabled || reachedMax;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
if (this[removeButtonSymbol]) {
|
|
960
|
+
const reachedMin =
|
|
961
|
+
isNumber(min) && Number.isFinite(min) && items.length <= min;
|
|
962
|
+
this[removeButtonSymbol].disabled =
|
|
963
|
+
isDisabled || reachedMin || items.length === 0;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
/**
|
|
968
|
+
* @private
|
|
969
|
+
*/
|
|
970
|
+
function syncOptionData() {
|
|
971
|
+
const form = this.closest("monster-form");
|
|
972
|
+
if (!form) {
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
const formData = form.getOption?.("data");
|
|
977
|
+
if (isObject(formData) || isArray(formData)) {
|
|
978
|
+
this.setOption("data", clone(formData));
|
|
979
|
+
} else {
|
|
980
|
+
const datasource = getDatasourceElement.call(this);
|
|
981
|
+
if (datasource) {
|
|
982
|
+
const record = resolveFormRecord.call(this, form, datasource);
|
|
983
|
+
if (record) {
|
|
984
|
+
this.setOption("data", clone(record));
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
/**
|
|
992
|
+
* @private
|
|
993
|
+
* @param {HTMLElement} form
|
|
994
|
+
* @param {HTMLElement} datasource
|
|
995
|
+
* @return {object}
|
|
996
|
+
*/
|
|
997
|
+
function resolveFormRecord(form, datasource) {
|
|
998
|
+
let data = datasource?.data;
|
|
999
|
+
if (!isObject(data) && !isArray(data)) {
|
|
1000
|
+
return {};
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
const mappingData = form.getOption?.("mapping.data");
|
|
1004
|
+
if (isString(mappingData) && mappingData.trim() !== "") {
|
|
1005
|
+
try {
|
|
1006
|
+
data = new Pathfinder(data).getVia(mappingData.trim());
|
|
1007
|
+
} catch {
|
|
1008
|
+
data = {};
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
if (isObject(data)) {
|
|
1013
|
+
data = Object.values(data);
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
const mappingIndex = form.getOption?.("mapping.index");
|
|
1017
|
+
if (mappingIndex !== null && mappingIndex !== undefined) {
|
|
1018
|
+
data = data?.[mappingIndex];
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
if (!isObject(data) && !isArray(data)) {
|
|
1022
|
+
return {};
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
return data;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
/**
|
|
1029
|
+
* @private
|
|
1030
|
+
* @return {{target: HTMLElement, path: string}|null}
|
|
1031
|
+
*/
|
|
1032
|
+
function getDatasourceContext() {
|
|
1033
|
+
const form = this.closest("monster-form");
|
|
1034
|
+
const datasource = getDatasourceElement.call(this);
|
|
1035
|
+
const path = getPathOption.call(this);
|
|
1036
|
+
if (!datasource || !path) {
|
|
1037
|
+
return null;
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
const mappingData = form?.getOption?.("mapping.data");
|
|
1041
|
+
const mappingIndex = form?.getOption?.("mapping.index");
|
|
1042
|
+
const record = resolveFormRecord(form, datasource);
|
|
1043
|
+
const normalizedPath = normalizePathForRecord(path, record);
|
|
1044
|
+
let basePath = "";
|
|
1045
|
+
|
|
1046
|
+
if (isString(mappingData) && mappingData.trim() !== "") {
|
|
1047
|
+
basePath = mappingData.trim();
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
if (mappingIndex !== null && mappingIndex !== undefined) {
|
|
1051
|
+
const indexValue = String(mappingIndex);
|
|
1052
|
+
basePath = basePath ? `${basePath}.${indexValue}` : indexValue;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
const finalPath = basePath ? `${basePath}.${normalizedPath}` : normalizedPath;
|
|
1056
|
+
return { target: datasource, path: finalPath };
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
/**
|
|
1060
|
+
* @private
|
|
1061
|
+
* @return {string}
|
|
1062
|
+
*/
|
|
1063
|
+
function getTemplate() {
|
|
1064
|
+
// language=HTML
|
|
1065
|
+
return `
|
|
1066
|
+
<div data-monster-role="control" part="control">
|
|
1067
|
+
<div data-monster-role="container" part="container">
|
|
1068
|
+
<div data-monster-attributes="class path:classes.content" part="content">
|
|
1069
|
+
<slot></slot>
|
|
1070
|
+
<slot name="items"></slot>
|
|
1071
|
+
</div>
|
|
1072
|
+
<div data-monster-role="actions" part="actions">
|
|
1073
|
+
<slot name="actions"></slot>
|
|
1074
|
+
<button type="button" data-monster-role="add" part="add">
|
|
1075
|
+
<span data-monster-replace="path:labels.add"></span>
|
|
1076
|
+
</button>
|
|
1077
|
+
<button type="button" data-monster-role="remove" part="remove">
|
|
1078
|
+
<span data-monster-replace="path:labels.remove"></span>
|
|
1079
|
+
</button>
|
|
1080
|
+
</div>
|
|
1081
|
+
<slot name="item"></slot>
|
|
1082
|
+
</div>
|
|
1083
|
+
</div>`;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
/**
|
|
1087
|
+
* @private
|
|
1088
|
+
* @return {string}
|
|
1089
|
+
*/
|
|
1090
|
+
registerCustomElement(RepeatFieldSet);
|