@es-joy/jsoe 0.5.1 → 0.6.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/.eslintignore +2 -0
- package/.eslintrc.cjs +2 -0
- package/.nyc_output/out.json +121296 -0
- package/CHANGES.md +6 -0
- package/README.md +47 -21
- package/cypress.config.ts +13 -0
- package/demo/index-instrumented.html +29 -0
- package/demo/index.html +10 -1
- package/demo/index.js +482 -126
- package/dist/formatAndTypeChoices.d.ts +2 -2
- package/dist/formatAndTypeChoices.d.ts.map +1 -1
- package/dist/formats/json.d.ts.map +1 -1
- package/dist/formats/structuredCloning.d.ts +2 -1
- package/dist/formats/structuredCloning.d.ts.map +1 -1
- package/dist/fundamentalTypes/arrayType.d.ts +6 -37
- package/dist/fundamentalTypes/arrayType.d.ts.map +1 -1
- package/dist/fundamentalTypes/dateType.d.ts.map +1 -1
- package/dist/fundamentalTypes/errorType.d.ts +6 -0
- package/dist/fundamentalTypes/errorType.d.ts.map +1 -0
- package/dist/fundamentalTypes/mapType.d.ts +6 -0
- package/dist/fundamentalTypes/mapType.d.ts.map +1 -0
- package/dist/fundamentalTypes/nullType.d.ts.map +1 -1
- package/dist/fundamentalTypes/objectReferenceType.d.ts.map +1 -1
- package/dist/fundamentalTypes/setType.d.ts +6 -0
- package/dist/fundamentalTypes/setType.d.ts.map +1 -0
- package/dist/fundamentalTypes/sparseUndefinedType.d.ts.map +1 -1
- package/dist/fundamentalTypes/undefinedType.d.ts.map +1 -1
- package/dist/subTypes/blobHTMLType.d.ts +12 -6
- package/dist/subTypes/blobHTMLType.d.ts.map +1 -1
- package/dist/superTypes/SpecialNumberType.d.ts +14 -0
- package/dist/superTypes/SpecialNumberType.d.ts.map +1 -0
- package/dist/superTypes/SpecialRealNumberType.d.ts +6 -0
- package/dist/superTypes/SpecialRealNumberType.d.ts.map +1 -0
- package/dist/superTypes/errorsSpecialType.d.ts +8 -0
- package/dist/superTypes/errorsSpecialType.d.ts.map +1 -0
- package/dist/typeChoices.d.ts +4 -2
- package/dist/typeChoices.d.ts.map +1 -1
- package/dist/types.d.ts +83 -231
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/dialogs.d.ts +22 -22
- package/dist/utils/dialogs.d.ts.map +1 -1
- package/dist/utils/templateUtils.d.ts +6 -6
- package/dist/utils/templateUtils.d.ts.map +1 -1
- package/dist/vendor-imports.d.ts +1 -1
- package/package.json +39 -34
- package/rollup.config.js +14 -2
- package/server.js +1 -1
- package/src/formatAndTypeChoices.js +9 -9
- package/src/formats/indexedDBKey.js +2 -2
- package/src/formats/json.js +1 -2
- package/src/formats/structuredCloning.js +104 -29
- package/src/formats.js +2 -2
- package/src/fundamentalTypes/arrayReferenceType.js +3 -3
- package/src/fundamentalTypes/arrayType.js +384 -79
- package/src/fundamentalTypes/dateType.js +4 -6
- package/src/fundamentalTypes/errorType.js +416 -0
- package/src/fundamentalTypes/mapType.js +34 -0
- package/src/fundamentalTypes/nullType.js +1 -2
- package/src/fundamentalTypes/objectReferenceType.js +2 -1
- package/src/fundamentalTypes/regexpType.js +2 -2
- package/src/fundamentalTypes/setType.js +34 -0
- package/src/fundamentalTypes/sparseUndefinedType.js +2 -3
- package/src/fundamentalTypes/undefinedType.js +2 -3
- package/src/jsoe.css +3 -0
- package/src/subTypes/blobHTMLType.js +37 -33
- package/src/subTypes/falseType.js +2 -2
- package/src/subTypes/trueType.js +2 -2
- package/src/superTypes/{SpecialNumberSuperType.js → SpecialNumberType.js} +1 -1
- package/src/superTypes/{SpecialRealNumberSuperType.js → SpecialRealNumberType.js} +3 -2
- package/src/superTypes/errorsSpecialType.js +768 -0
- package/src/typeChoices.js +27 -20
- package/src/types.js +71 -39
- package/src/utils/dialogs.js +11 -12
- package/src/utils/jsonPointer.js +3 -3
- package/src/utils/templateUtils.js +4 -4
- package/src/vendor-imports.js +2 -2
- package/tsconfig.json +1 -1
- package/typings/CypressPlugins.d.ts +10 -0
- package/vendor/jamilih/dist/jml-es.js +126 -106
- package/vendor/typeson-registry/dist/index.js +188 -44
|
@@ -18,6 +18,14 @@ import {
|
|
|
18
18
|
/**
|
|
19
19
|
* @typedef {() => HTMLInputElement[]} InputsExceedingLength
|
|
20
20
|
*/
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {() => (HTMLSelectElement & {
|
|
23
|
+
* $getValue: import('../typeChoices.js').GetValue
|
|
24
|
+
* })[]} GetMapKeySelects
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {() => void} ValidateMapKey
|
|
28
|
+
*/
|
|
21
29
|
/**
|
|
22
30
|
* @callback GetPropertyInputs
|
|
23
31
|
* @returns {HTMLInputElement[]}
|
|
@@ -31,7 +39,8 @@ import {
|
|
|
31
39
|
* @typedef {() => HTMLDivElement & {
|
|
32
40
|
* $inputsExceedingLength: InputsExceedingLength,
|
|
33
41
|
* $getPropertyInputs: GetPropertyInputs,
|
|
34
|
-
* $redrawMoveArrows: RedrawMoveArrows
|
|
42
|
+
* $redrawMoveArrows: RedrawMoveArrows,
|
|
43
|
+
* $getMapKeySelects: GetMapKeySelects
|
|
35
44
|
* }} GetArrayItems
|
|
36
45
|
*/
|
|
37
46
|
/**
|
|
@@ -48,6 +57,26 @@ import {
|
|
|
48
57
|
* @typedef {() => HTMLInputElement|undefined} GetPropertyInput
|
|
49
58
|
*/
|
|
50
59
|
|
|
60
|
+
/**
|
|
61
|
+
* @typedef {any} AnyValue
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Algorithm used for checking key identity in `Map`'s.
|
|
66
|
+
* @param {AnyValue} x
|
|
67
|
+
* @param {AnyValue} y
|
|
68
|
+
* @returns {boolean}
|
|
69
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness#same-value-zero_equality
|
|
70
|
+
*/
|
|
71
|
+
function sameValueZero (x, y) {
|
|
72
|
+
if (typeof x === 'number' && typeof y === 'number') {
|
|
73
|
+
// x and y are equal (may be -0 and 0) or they are both NaN
|
|
74
|
+
// eslint-disable-next-line no-self-compare -- Not pointless with -0
|
|
75
|
+
return x === y || (x !== x && y !== y);
|
|
76
|
+
}
|
|
77
|
+
return x === y;
|
|
78
|
+
}
|
|
79
|
+
|
|
51
80
|
/**
|
|
52
81
|
* @type {import('../types.js').TypeObject & {sparse?: boolean|undefined}}
|
|
53
82
|
*/
|
|
@@ -87,7 +116,11 @@ const arrayType = {
|
|
|
87
116
|
const checkEnd = (beginOnly) => {
|
|
88
117
|
if (beginOnly && endMatchTypeObjs.length) {
|
|
89
118
|
const endMatch = stringVal.match(
|
|
90
|
-
/** @type {RegExp} */ (
|
|
119
|
+
/** @type {RegExp} */ (
|
|
120
|
+
/** @type {import('../types.js').TypeObject} */ (
|
|
121
|
+
endMatchTypeObjs.at(-1)
|
|
122
|
+
).stringRegexEnd
|
|
123
|
+
)
|
|
91
124
|
);
|
|
92
125
|
if (endMatch) {
|
|
93
126
|
endMatchTypeObjs.pop(); // Safe now to extract
|
|
@@ -139,7 +172,14 @@ const arrayType = {
|
|
|
139
172
|
if (sparse && idx && idx > retObj.length - 1) {
|
|
140
173
|
retObj.length = idx + 1;
|
|
141
174
|
}
|
|
142
|
-
return {
|
|
175
|
+
return {
|
|
176
|
+
value: this.set && Array.isArray(retObj)
|
|
177
|
+
? new Set(retObj)
|
|
178
|
+
: this.map
|
|
179
|
+
? new Map(/** @type {Array<any>} */ (retObj))
|
|
180
|
+
: retObj,
|
|
181
|
+
remnant: stringVal
|
|
182
|
+
};
|
|
143
183
|
}
|
|
144
184
|
|
|
145
185
|
/**
|
|
@@ -178,7 +218,7 @@ const arrayType = {
|
|
|
178
218
|
}
|
|
179
219
|
// console.log('vvv', stringVal, beginOnly, v);
|
|
180
220
|
if (assign) {
|
|
181
|
-
// eslint-disable-next-line max-len -- Long
|
|
221
|
+
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
182
222
|
/** @type {{[key: string]: import('../formats.js').StructuredCloneValue}} */ (
|
|
183
223
|
retObj
|
|
184
224
|
)[pr] = v;
|
|
@@ -193,8 +233,13 @@ const arrayType = {
|
|
|
193
233
|
},
|
|
194
234
|
getValue ({root, stateObj = {}, currentPath = ''}) {
|
|
195
235
|
const top = currentPath === '';
|
|
196
|
-
const arrayItems =
|
|
197
|
-
|
|
236
|
+
const arrayItems =
|
|
237
|
+
/**
|
|
238
|
+
* @type {HTMLElement & {
|
|
239
|
+
* $getMapKeySelects: GetMapKeySelects
|
|
240
|
+
* }}
|
|
241
|
+
*/ ($e(root, '.arrayItems'));
|
|
242
|
+
const fieldsets = [...arrayItems.children];
|
|
198
243
|
|
|
199
244
|
/**
|
|
200
245
|
* @type {({[key: string]: any})|any[]}
|
|
@@ -216,7 +261,7 @@ const arrayType = {
|
|
|
216
261
|
/**
|
|
217
262
|
* @param {HTMLDivElement} root
|
|
218
263
|
* @param {string} currentPathPart
|
|
219
|
-
* @returns {[boolean,
|
|
264
|
+
* @returns {[boolean, any?]}
|
|
220
265
|
*/
|
|
221
266
|
const getValOrRef = (root, currentPathPart) => {
|
|
222
267
|
const value = Types.getValueForRoot(
|
|
@@ -229,26 +274,33 @@ const arrayType = {
|
|
|
229
274
|
}
|
|
230
275
|
return [true, value];
|
|
231
276
|
};
|
|
232
|
-
|
|
233
|
-
const legend =
|
|
234
|
-
|
|
235
|
-
/** @type {HTMLLegendElement} */ (legend)
|
|
277
|
+
fieldsets.forEach((fieldset) => {
|
|
278
|
+
const legend = /** @type {HTMLLegendElement} */ (
|
|
279
|
+
fieldset.firstElementChild
|
|
236
280
|
);
|
|
281
|
+
const propVal = getPropertyValueFromLegend(legend);
|
|
237
282
|
const root = /** @type {HTMLDivElement} */ (
|
|
238
|
-
|
|
283
|
+
DOM.filterChildElements(
|
|
284
|
+
/** @type {HTMLFieldSetElement} */
|
|
285
|
+
(fieldset),
|
|
286
|
+
[
|
|
287
|
+
'.typeContainer',
|
|
288
|
+
'div[data-type]'
|
|
289
|
+
]
|
|
290
|
+
)[0]
|
|
239
291
|
);
|
|
240
292
|
/* istanbul ignore if -- Should err first? */
|
|
241
293
|
if (!root) {
|
|
242
294
|
return;
|
|
243
295
|
}
|
|
244
296
|
|
|
245
|
-
const objectProperty =
|
|
297
|
+
const objectProperty = DOM.filterChildElements(legend, ['input']);
|
|
246
298
|
const [isVal, value] = getValOrRef(
|
|
247
299
|
root,
|
|
248
300
|
propVal
|
|
249
301
|
);
|
|
250
302
|
if (isVal) {
|
|
251
|
-
if (objectProperty) {
|
|
303
|
+
if (objectProperty.length) {
|
|
252
304
|
/** @type {{[key: string]: any}} */ (ret)[propVal] = value;
|
|
253
305
|
} else {
|
|
254
306
|
ret.push(value);
|
|
@@ -264,11 +316,12 @@ const arrayType = {
|
|
|
264
316
|
path: referentPath,
|
|
265
317
|
obj: ret
|
|
266
318
|
});
|
|
267
|
-
|
|
268
319
|
const referencePathJsonPtr = getJSONPointerParts(referencePath);
|
|
269
320
|
const referenceFinalPathPart = referencePathJsonPtr.pop();
|
|
270
321
|
const referenceParentObj = referencePathJsonPtr.reduce(
|
|
271
|
-
(obj, pathPart) =>
|
|
322
|
+
(obj, pathPart) => {
|
|
323
|
+
return reduceJSONPointerParts(obj, pathPart);
|
|
324
|
+
},
|
|
272
325
|
ret
|
|
273
326
|
);
|
|
274
327
|
/** @type {{[key: string]: any}} */ (referenceParentObj)[
|
|
@@ -277,7 +330,14 @@ const arrayType = {
|
|
|
277
330
|
}
|
|
278
331
|
);
|
|
279
332
|
}
|
|
280
|
-
return ret
|
|
333
|
+
return this.set && Array.isArray(ret)
|
|
334
|
+
? new Set(ret)
|
|
335
|
+
: this.map
|
|
336
|
+
? new Map(arrayItems.$getMapKeySelects().map((select, idx) => {
|
|
337
|
+
const key = select.$getValue();
|
|
338
|
+
return [key, /** @type {any[]} */ (ret)[idx]];
|
|
339
|
+
}))
|
|
340
|
+
: ret;
|
|
281
341
|
},
|
|
282
342
|
|
|
283
343
|
// Try to keep in sync with basic structure of `editUI`
|
|
@@ -285,11 +345,13 @@ const arrayType = {
|
|
|
285
345
|
// const {sparse} = this;
|
|
286
346
|
let itemIndex = -1;
|
|
287
347
|
|
|
348
|
+
const parentType = type;
|
|
349
|
+
|
|
288
350
|
/**
|
|
289
351
|
* @param {{
|
|
290
352
|
* itemIndex: number,
|
|
291
353
|
* typeNamespace?: string,
|
|
292
|
-
* propName
|
|
354
|
+
* propName?: string
|
|
293
355
|
* }} cfg
|
|
294
356
|
* @returns {import('jamilih').JamilihArray}
|
|
295
357
|
*/
|
|
@@ -308,7 +370,7 @@ const arrayType = {
|
|
|
308
370
|
}, [
|
|
309
371
|
propName !== undefined
|
|
310
372
|
? propName
|
|
311
|
-
// eslint-disable-next-line max-len -- Long
|
|
373
|
+
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
312
374
|
/* istanbul ignore next -- Won't reach here as typeson will always give keypath? */
|
|
313
375
|
: itemIndex
|
|
314
376
|
]]
|
|
@@ -321,7 +383,7 @@ const arrayType = {
|
|
|
321
383
|
$custom: {
|
|
322
384
|
/**
|
|
323
385
|
* @param {{
|
|
324
|
-
* propName:
|
|
386
|
+
* propName: string,
|
|
325
387
|
* type: import('../types.js').AvailableType,
|
|
326
388
|
* value: import('../formats.js').StructuredCloneValue,
|
|
327
389
|
* bringIntoFocus: boolean,
|
|
@@ -335,6 +397,37 @@ const arrayType = {
|
|
|
335
397
|
propName, type, value, bringIntoFocus,
|
|
336
398
|
schemaContent, schemaState
|
|
337
399
|
}) {
|
|
400
|
+
if (parentType === 'map') {
|
|
401
|
+
const root = Types.getUIForModeAndType({
|
|
402
|
+
resultType,
|
|
403
|
+
readonly: true,
|
|
404
|
+
typeNamespace, type, topRoot,
|
|
405
|
+
bringIntoFocus,
|
|
406
|
+
format, schemaContent, schemaState,
|
|
407
|
+
value,
|
|
408
|
+
hasValue: true // type === 'sparseArrays' && value
|
|
409
|
+
});
|
|
410
|
+
if (propName === '0') {
|
|
411
|
+
const fieldset = this.$addMapElement();
|
|
412
|
+
this._lastFieldset = fieldset;
|
|
413
|
+
const keyFieldset = /** @type {HTMLFieldSetElement} */ (jml(
|
|
414
|
+
'fieldset', [
|
|
415
|
+
['legend', ['Key']]
|
|
416
|
+
], fieldset
|
|
417
|
+
));
|
|
418
|
+
jml(root, keyFieldset);
|
|
419
|
+
} else { // propName === '1'
|
|
420
|
+
const valueFieldset = /** @type {HTMLFieldSetElement} */ (jml(
|
|
421
|
+
'fieldset', [
|
|
422
|
+
['legend', ['Value']]
|
|
423
|
+
], this._lastFieldset
|
|
424
|
+
));
|
|
425
|
+
this._lastFieldset = null;
|
|
426
|
+
jml(root, valueFieldset);
|
|
427
|
+
}
|
|
428
|
+
return root;
|
|
429
|
+
}
|
|
430
|
+
|
|
338
431
|
const fieldset = this.$addArrayElement({propName});
|
|
339
432
|
const root = Types.getUIForModeAndType({
|
|
340
433
|
resultType,
|
|
@@ -348,6 +441,27 @@ const arrayType = {
|
|
|
348
441
|
jml(root, fieldset);
|
|
349
442
|
return root;
|
|
350
443
|
},
|
|
444
|
+
/**
|
|
445
|
+
* @returns {HTMLFieldSetElement}
|
|
446
|
+
*/
|
|
447
|
+
$addMapElement () {
|
|
448
|
+
itemIndex++;
|
|
449
|
+
const arrayItems = this.$getArrayItems();
|
|
450
|
+
// const className = `${type}Item`;
|
|
451
|
+
const fieldset = /** @type {HTMLFieldSetElement} */ (
|
|
452
|
+
jml('fieldset', [
|
|
453
|
+
buildLegend({
|
|
454
|
+
// className,
|
|
455
|
+
// type,
|
|
456
|
+
// arrayItems,
|
|
457
|
+
itemIndex,
|
|
458
|
+
typeNamespace,
|
|
459
|
+
propName: undefined
|
|
460
|
+
})
|
|
461
|
+
], arrayItems)
|
|
462
|
+
);
|
|
463
|
+
return fieldset;
|
|
464
|
+
},
|
|
351
465
|
/**
|
|
352
466
|
* @param {{propName: string}} cfg
|
|
353
467
|
* @returns {HTMLFieldSetElement}
|
|
@@ -378,9 +492,7 @@ const arrayType = {
|
|
|
378
492
|
DOM.initialCaps(/** @type {import('../types.js').AvailableType} */ (
|
|
379
493
|
type
|
|
380
494
|
)).replace(/s$/u, ''), nbsp.repeat(2),
|
|
381
|
-
|
|
382
|
-
/** @type {import('jamilih').JamilihArray} */
|
|
383
|
-
(['button', {$on: {click (/** @type {Event} */ e) {
|
|
495
|
+
['button', {$on: {click (/** @type {Event} */ e) {
|
|
384
496
|
e.preventDefault();
|
|
385
497
|
const {target} = e;
|
|
386
498
|
const arrayContents = /** @type {HTMLDivElement} */ ($e(
|
|
@@ -392,13 +504,19 @@ const arrayType = {
|
|
|
392
504
|
/** @type {HTMLElement} */ (
|
|
393
505
|
target
|
|
394
506
|
).textContent = arrayContents.hidden ? '+' : '-';
|
|
395
|
-
}}}, ['-']]
|
|
507
|
+
}}}, ['-']],
|
|
396
508
|
['div', {class: 'arrayContents'}, [
|
|
397
509
|
this.array
|
|
398
510
|
? ['div', [
|
|
399
|
-
|
|
511
|
+
type === 'set'
|
|
512
|
+
? 'Set size: '
|
|
513
|
+
: type === 'map'
|
|
514
|
+
? 'Map size: '
|
|
515
|
+
: 'Array length: ',
|
|
400
516
|
['span', [
|
|
401
|
-
(value &&
|
|
517
|
+
(value && (type === 'set' || type === 'map')
|
|
518
|
+
? value.size
|
|
519
|
+
: value.length) || 0
|
|
402
520
|
]]
|
|
403
521
|
]]
|
|
404
522
|
: '',
|
|
@@ -418,13 +536,17 @@ const arrayType = {
|
|
|
418
536
|
// an array (or object if called by that method) and we handle the
|
|
419
537
|
// population of the array in the callback
|
|
420
538
|
editUI ({
|
|
421
|
-
typeNamespace, buildTypeChoices, format, resultType,
|
|
422
|
-
type, topRoot, value, bringIntoFocus = true
|
|
539
|
+
typeNamespace, buildTypeChoices, format, // resultType,
|
|
540
|
+
type, arrayState, topRoot, value, bringIntoFocus = true
|
|
423
541
|
}) {
|
|
424
542
|
const {sparse} = this;
|
|
543
|
+
// eslint-disable-next-line consistent-this
|
|
544
|
+
const parentTypeObject = this;
|
|
425
545
|
const itemAdjust = type === 'object' ? 1 : 0;
|
|
426
546
|
let itemIndex = itemAdjust - 1;
|
|
427
|
-
const editableProperties = type !== 'array'
|
|
547
|
+
const editableProperties = type !== 'array' &&
|
|
548
|
+
type !== 'set' && type !== 'map';
|
|
549
|
+
const mapProperties = type === 'map';
|
|
428
550
|
|
|
429
551
|
/**
|
|
430
552
|
* @param {HTMLInputElement} input
|
|
@@ -471,14 +593,14 @@ const arrayType = {
|
|
|
471
593
|
swapCountElem.textContent = base;
|
|
472
594
|
baseCountElem.textContent = swap;
|
|
473
595
|
} else {
|
|
474
|
-
// eslint-disable-next-line max-len -- Long
|
|
596
|
+
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
475
597
|
const swapCountElem = /** @type {HTMLInputElement & {$parseInt: ParseInt}} */
|
|
476
598
|
(/**
|
|
477
599
|
* @type {HTMLFieldSetElement & {$getPropertyInput: GetPropertyInput}}
|
|
478
600
|
*/ (
|
|
479
601
|
group
|
|
480
602
|
).$getPropertyInput());
|
|
481
|
-
// eslint-disable-next-line max-len -- Long
|
|
603
|
+
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
482
604
|
const baseCountElem = /** @type {HTMLInputElement & {$parseInt: ParseInt}} */
|
|
483
605
|
(/**
|
|
484
606
|
* @type {HTMLFieldSetElement & {$getPropertyInput: GetPropertyInput}}
|
|
@@ -602,6 +724,7 @@ const arrayType = {
|
|
|
602
724
|
* $inputsExceedingLength: InputsExceedingLength,
|
|
603
725
|
* $getPropertyInputs: GetPropertyInputs,
|
|
604
726
|
* $redrawMoveArrows: RedrawMoveArrows
|
|
727
|
+
* $getMapKeySelects: GetMapKeySelects
|
|
605
728
|
* }} ArrayItems
|
|
606
729
|
*/
|
|
607
730
|
|
|
@@ -652,12 +775,82 @@ const arrayType = {
|
|
|
652
775
|
'input'
|
|
653
776
|
)
|
|
654
777
|
);
|
|
655
|
-
const highest = inputsExceedingLength.map(
|
|
778
|
+
const highest = /** @type {number} */ (inputsExceedingLength.map(
|
|
656
779
|
(i) => Number.parseInt(i.value)
|
|
657
|
-
).sort().
|
|
780
|
+
).sort().at(-1));
|
|
658
781
|
arrLengthInput.value = String(highest + 1);
|
|
659
782
|
return this.$validateLength(true);
|
|
660
783
|
};
|
|
784
|
+
if (mapProperties) {
|
|
785
|
+
const keyTypeSelection =
|
|
786
|
+
/** @type {import('../typeChoices.js').BuildTypeChoices} */ (
|
|
787
|
+
buildTypeChoices
|
|
788
|
+
)({
|
|
789
|
+
format: 'structuredCloning',
|
|
790
|
+
typeNamespace: 'key-type-choices-only'
|
|
791
|
+
});
|
|
792
|
+
return ['legend', [
|
|
793
|
+
'Key ',
|
|
794
|
+
['span', {
|
|
795
|
+
dataset: {prop: 'true'},
|
|
796
|
+
className
|
|
797
|
+
}, [String(itemIndex)]],
|
|
798
|
+
':',
|
|
799
|
+
nbsp.repeat(2),
|
|
800
|
+
|
|
801
|
+
['span', {
|
|
802
|
+
class: 'mapKey',
|
|
803
|
+
$on: {
|
|
804
|
+
change: [function () {
|
|
805
|
+
/**
|
|
806
|
+
* @type {HTMLSpanElement & {
|
|
807
|
+
* $validateMapKey: ValidateMapKey
|
|
808
|
+
* }}
|
|
809
|
+
*/
|
|
810
|
+
(this).$validateMapKey();
|
|
811
|
+
|
|
812
|
+
// Needed?
|
|
813
|
+
Types.validateAllReferences({
|
|
814
|
+
// eslint-disable-next-line object-shorthand -- TS
|
|
815
|
+
topRoot: /** @type {HTMLDivElement} */ (topRoot)
|
|
816
|
+
});
|
|
817
|
+
}, true]
|
|
818
|
+
},
|
|
819
|
+
$custom: {
|
|
820
|
+
/** @type {ValidateMapKey} */
|
|
821
|
+
$validateMapKey () {
|
|
822
|
+
const selects = arrayItems.$getMapKeySelects();
|
|
823
|
+
|
|
824
|
+
setTimeout(() => {
|
|
825
|
+
const values = selects.map((select) => {
|
|
826
|
+
return select.$getValue();
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
const dupeIndex = values.findLastIndex((value, idx) => {
|
|
830
|
+
return values.some((val, index) => {
|
|
831
|
+
return idx !== index && sameValueZero(value, val);
|
|
832
|
+
});
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
if (dupeIndex === -1) {
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
const select = selects[dupeIndex];
|
|
839
|
+
/* istanbul ignore if -- Should exist */
|
|
840
|
+
if (!select) {
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
select.setCustomValidity(
|
|
845
|
+
'Duplicate Map key value'
|
|
846
|
+
);
|
|
847
|
+
select.reportValidity();
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
}, keyTypeSelection.domArray]
|
|
852
|
+
]];
|
|
853
|
+
}
|
|
661
854
|
if (editableProperties) {
|
|
662
855
|
return /** @type {import('jamilih').JamilihArray} */ (['legend', [
|
|
663
856
|
sparse
|
|
@@ -752,7 +945,7 @@ const arrayType = {
|
|
|
752
945
|
el.closest('fieldset')
|
|
753
946
|
);
|
|
754
947
|
const thisFieldset = getFieldset(this);
|
|
755
|
-
// eslint-disable-next-line max-len -- Long
|
|
948
|
+
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
756
949
|
const intVal = /** @type {HTMLInputElement & {$parseInt: ParseInt}} */ (
|
|
757
950
|
this
|
|
758
951
|
).$parseInt();
|
|
@@ -825,7 +1018,7 @@ const arrayType = {
|
|
|
825
1018
|
const method = latest ? 'after' : 'before';
|
|
826
1019
|
// Ensure move *after* splice that will occur after this
|
|
827
1020
|
setTimeout(() => {
|
|
828
|
-
/*
|
|
1021
|
+
/* istanbul ignore if */
|
|
829
1022
|
if (!nearest) {
|
|
830
1023
|
return;
|
|
831
1024
|
}
|
|
@@ -879,7 +1072,7 @@ const arrayType = {
|
|
|
879
1072
|
*/
|
|
880
1073
|
const decrementItemIndex = (arrayItems) => {
|
|
881
1074
|
if (sparse) {
|
|
882
|
-
// eslint-disable-next-line max-len -- Long
|
|
1075
|
+
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
883
1076
|
itemIndex = /** @type {(HTMLInputElement & {$parseInt: ParseInt})[]} */ (
|
|
884
1077
|
arrayItems.$getPropertyInputs()
|
|
885
1078
|
).reduce(
|
|
@@ -908,7 +1101,8 @@ const arrayType = {
|
|
|
908
1101
|
* @type {AddArrayElement}
|
|
909
1102
|
* @this {HTMLButtonElement & {
|
|
910
1103
|
* $getArrayItems: GetArrayItems,
|
|
911
|
-
* $addArrayElement: AddArrayElement
|
|
1104
|
+
* $addArrayElement: AddArrayElement,
|
|
1105
|
+
* $getMapKeySelects?: GetMapKeySelects
|
|
912
1106
|
* }}
|
|
913
1107
|
*/
|
|
914
1108
|
const $addArrayElement = function ({propName, splice, alwaysFocus}) {
|
|
@@ -923,9 +1117,9 @@ const arrayType = {
|
|
|
923
1117
|
} else if (typeof splice === 'number') {
|
|
924
1118
|
itemIndex = splice + 1;
|
|
925
1119
|
} else if (typeof splice !== 'string') {
|
|
926
|
-
// eslint-disable-next-line max-len -- Long
|
|
1120
|
+
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
927
1121
|
itemIndex = /** @type {(HTMLInputElement & {$parseInt: ParseInt})[]} */ (
|
|
928
|
-
// eslint-disable-next-line max-len -- Long
|
|
1122
|
+
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
929
1123
|
/** @type {HTMLDivElement & {$getPropertyInputs: GetPropertyInputs}} */
|
|
930
1124
|
(arrayItems).$getPropertyInputs()
|
|
931
1125
|
).reduce(
|
|
@@ -941,27 +1135,84 @@ const arrayType = {
|
|
|
941
1135
|
}
|
|
942
1136
|
const thisButton = this; // eslint-disable-line consistent-this
|
|
943
1137
|
const className = `${type}Item`;
|
|
944
|
-
const fieldset =
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
1138
|
+
const fieldset =
|
|
1139
|
+
/** @type {HTMLFieldSetElement} */ (
|
|
1140
|
+
jml('fieldset',
|
|
1141
|
+
{
|
|
1142
|
+
$on: {
|
|
1143
|
+
change: [() => {
|
|
1144
|
+
if (type !== 'set') {
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
setTimeout(() => {
|
|
1148
|
+
const root = div;
|
|
1149
|
+
const values = /** @type {any[]} */ (
|
|
1150
|
+
parentTypeObject.getValue.call({
|
|
1151
|
+
...parentTypeObject,
|
|
1152
|
+
set: false
|
|
1153
|
+
}, {root})
|
|
1154
|
+
);
|
|
1155
|
+
// console.log('values', values);
|
|
1156
|
+
|
|
1157
|
+
const dupeIndex = values.findLastIndex((value, idx) => {
|
|
1158
|
+
return values.some((val, index) => {
|
|
1159
|
+
return idx !== index && value === val;
|
|
1160
|
+
});
|
|
1161
|
+
});
|
|
1162
|
+
|
|
1163
|
+
if (dupeIndex === -1) {
|
|
1164
|
+
return;
|
|
1165
|
+
}
|
|
1166
|
+
const fieldsets = arrayItems.children;
|
|
1167
|
+
const controls = [...fieldsets].map((fieldset) => {
|
|
1168
|
+
const root = /** @type {HTMLDivElement} */ (
|
|
1169
|
+
$e(fieldset, 'div[data-type]')
|
|
1170
|
+
);
|
|
1171
|
+
/* istanbul ignore if -- Should err first? */
|
|
1172
|
+
if (!root) {
|
|
1173
|
+
return null;
|
|
1174
|
+
}
|
|
1175
|
+
return Types.getFormControlForRoot(root);
|
|
1176
|
+
});
|
|
1177
|
+
|
|
1178
|
+
const control = controls[dupeIndex];
|
|
1179
|
+
/* istanbul ignore if -- Should exist */
|
|
1180
|
+
if (!control) {
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
control.setCustomValidity(
|
|
1185
|
+
'Duplicate Set value'
|
|
1186
|
+
);
|
|
1187
|
+
control.reportValidity();
|
|
1188
|
+
});
|
|
1189
|
+
}, true]
|
|
1190
|
+
},
|
|
1191
|
+
$custom: {
|
|
1192
|
+
/** @type {GetPropertyInput} */
|
|
1193
|
+
$getPropertyInput () {
|
|
1194
|
+
return /** @type {HTMLInputElement} */ (
|
|
1195
|
+
DOM.filterChildElements(
|
|
1196
|
+
/** @type {HTMLFieldSetElement} */ (this),
|
|
1197
|
+
['legend', `input.propertyName-${typeNamespace}`]
|
|
1198
|
+
).pop()
|
|
1199
|
+
);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
},
|
|
1203
|
+
[
|
|
1204
|
+
buildLegend({
|
|
1205
|
+
className,
|
|
1206
|
+
splice,
|
|
1207
|
+
itemIndex,
|
|
1208
|
+
// type,
|
|
1209
|
+
typeNamespace,
|
|
1210
|
+
arrayItems,
|
|
1211
|
+
propName
|
|
1212
|
+
})
|
|
1213
|
+
],
|
|
1214
|
+
arrayItems)
|
|
1215
|
+
);
|
|
965
1216
|
// We must ensure fieldset is built before passing it
|
|
966
1217
|
jml({'#': [
|
|
967
1218
|
...(/** @type {import('../typeChoices.js').BuildTypeChoices} */ (
|
|
@@ -970,9 +1221,9 @@ const arrayType = {
|
|
|
970
1221
|
// resultType,
|
|
971
1222
|
// eslint-disable-next-line object-shorthand -- TS
|
|
972
1223
|
topRoot: /** @type {HTMLDivElement} */ (topRoot),
|
|
973
|
-
// eslint-disable-next-line object-shorthand, max-len -- TS
|
|
1224
|
+
// eslint-disable-next-line object-shorthand, @stylistic/max-len -- TS
|
|
974
1225
|
format: /** @type {import('../formats.js').AvailableFormat} */ (format),
|
|
975
|
-
state: type,
|
|
1226
|
+
state: arrayState ?? type,
|
|
976
1227
|
// itemIndex,
|
|
977
1228
|
typeNamespace
|
|
978
1229
|
}).domArray),
|
|
@@ -1032,7 +1283,6 @@ const arrayType = {
|
|
|
1032
1283
|
*/
|
|
1033
1284
|
([
|
|
1034
1285
|
'button',
|
|
1035
|
-
// @ts-ignore Can't seem to force optional Event
|
|
1036
1286
|
{
|
|
1037
1287
|
$on: {
|
|
1038
1288
|
click (/** @type {Event} */ e) {
|
|
@@ -1143,7 +1393,8 @@ const arrayType = {
|
|
|
1143
1393
|
* @type {HTMLDivElement & {
|
|
1144
1394
|
* $inputsExceedingLength: InputsExceedingLength,
|
|
1145
1395
|
* $getPropertyInputs: GetPropertyInputs,
|
|
1146
|
-
* $redrawMoveArrows: RedrawMoveArrows
|
|
1396
|
+
* $redrawMoveArrows: RedrawMoveArrows,
|
|
1397
|
+
* $getMapKeySelects: GetMapKeySelects
|
|
1147
1398
|
* }}
|
|
1148
1399
|
*/
|
|
1149
1400
|
(this.previousElementSibling)
|
|
@@ -1234,6 +1485,29 @@ const arrayType = {
|
|
|
1234
1485
|
$swapGroup, $redrawMoveArrows, $getArrayLength,
|
|
1235
1486
|
$inputsExceedingLength,
|
|
1236
1487
|
|
|
1488
|
+
/**
|
|
1489
|
+
* Only relevant for maps.
|
|
1490
|
+
* @type {GetMapKeySelects}
|
|
1491
|
+
* @this {HTMLDivElement}
|
|
1492
|
+
*/
|
|
1493
|
+
$getMapKeySelects () {
|
|
1494
|
+
const selects =
|
|
1495
|
+
/**
|
|
1496
|
+
* @type {(HTMLSelectElement & {
|
|
1497
|
+
* $getValue: import('../typeChoices.js').GetValue}
|
|
1498
|
+
* )[]}
|
|
1499
|
+
*/ (DOM.filterChildElements(
|
|
1500
|
+
this,
|
|
1501
|
+
[
|
|
1502
|
+
'fieldset',
|
|
1503
|
+
'legend:first-child',
|
|
1504
|
+
'span.mapKey',
|
|
1505
|
+
'select.typeChoices-key-type-choices-only'
|
|
1506
|
+
]
|
|
1507
|
+
));
|
|
1508
|
+
return selects;
|
|
1509
|
+
},
|
|
1510
|
+
|
|
1237
1511
|
/**
|
|
1238
1512
|
* @type {GetPropertyInputs}
|
|
1239
1513
|
* @this {HTMLDivElement}
|
|
@@ -1266,11 +1540,10 @@ const arrayType = {
|
|
|
1266
1540
|
addArrayElement,
|
|
1267
1541
|
['button', {$on: {click () {
|
|
1268
1542
|
const arrayContents = /** @type {HTMLElement} */ (
|
|
1269
|
-
// @ts-ignore Erring out of IDE
|
|
1270
1543
|
this.closest('.arrayContents')
|
|
1271
1544
|
);
|
|
1272
1545
|
const arrayItems =
|
|
1273
|
-
// eslint-disable-next-line max-len -- Long
|
|
1546
|
+
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
1274
1547
|
/** @type {HTMLDivElement & {$getPropertyInputs: GetPropertyInputs;}} */ (
|
|
1275
1548
|
$e(/** @type {HTMLElement} */ (arrayContents), '.arrayItems')
|
|
1276
1549
|
);
|
|
@@ -1278,7 +1551,7 @@ const arrayType = {
|
|
|
1278
1551
|
if (lastElement) {
|
|
1279
1552
|
lastElement.remove();
|
|
1280
1553
|
decrementItemIndex(arrayItems);
|
|
1281
|
-
// eslint-disable-next-line max-len -- Long
|
|
1554
|
+
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
1282
1555
|
/** @type {HTMLDivElement & {$redrawMoveArrows: RedrawMoveArrows}} */ (
|
|
1283
1556
|
$e(/** @type {HTMLElement} */ (arrayContents), '.arrayItems')
|
|
1284
1557
|
).$redrawMoveArrows();
|
|
@@ -1292,7 +1565,7 @@ const arrayType = {
|
|
|
1292
1565
|
// We could only add this when there was more than one
|
|
1293
1566
|
['button', {$on: {click () {
|
|
1294
1567
|
const arrayItems =
|
|
1295
|
-
// eslint-disable-next-line max-len -- Long
|
|
1568
|
+
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
1296
1569
|
/** @type {HTMLDivElement & {$getPropertyInputs: GetPropertyInputs}} */ ($e(
|
|
1297
1570
|
/** @type {HTMLElement} */ (
|
|
1298
1571
|
/** @type {HTMLElement} */ (this).closest('.arrayContents')
|
|
@@ -1314,26 +1587,58 @@ const arrayType = {
|
|
|
1314
1587
|
]]
|
|
1315
1588
|
);
|
|
1316
1589
|
|
|
1590
|
+
const parentType = type;
|
|
1591
|
+
|
|
1317
1592
|
const div = /** @type {HTMLDivElement} */ (
|
|
1318
1593
|
jml('div', /** @type {import('jamilih').JamilihAttributes} */ ({
|
|
1319
1594
|
dataset: {type},
|
|
1320
1595
|
// is: 'array-or-object-editor',
|
|
1321
1596
|
$custom: {
|
|
1322
|
-
// eslint-disable-next-line max-len -- Long
|
|
1597
|
+
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
1323
1598
|
/** @type {import('../formats/structuredCloning.js').AddAndSetArrayElement} */
|
|
1324
1599
|
$addAndSetArrayElement ({
|
|
1325
|
-
propName, type, value, bringIntoFocus
|
|
1600
|
+
propName, type, value, bringIntoFocus, setAValue
|
|
1326
1601
|
// , schemaContent, schemaState
|
|
1327
1602
|
}) {
|
|
1328
|
-
|
|
1603
|
+
if (parentType === 'map') {
|
|
1604
|
+
if (propName === '0') {
|
|
1605
|
+
this.$addArrayElement({propName});
|
|
1606
|
+
const arrayItems = this.$getArrayItems();
|
|
1607
|
+
const keyTypeChoices = /**
|
|
1608
|
+
* @type {HTMLSelectElement & {
|
|
1609
|
+
* $setType: import('../typeChoices.js').SetType,
|
|
1610
|
+
* $getTypeRoot: import('../formatAndTypeChoices.js').
|
|
1611
|
+
* TypeRootGetter
|
|
1612
|
+
* }}
|
|
1613
|
+
*/ (DOM.filterChildElements(
|
|
1614
|
+
arrayItems,
|
|
1615
|
+
['fieldset:last-of-type', 'legend', '.mapKey', 'select']
|
|
1616
|
+
)[0]);
|
|
1617
|
+
keyTypeChoices.$setType({
|
|
1618
|
+
type, baseValue: value, bringIntoFocus
|
|
1619
|
+
});
|
|
1620
|
+
|
|
1621
|
+
// The key may itself be a map, etc.
|
|
1622
|
+
return keyTypeChoices.$getTypeRoot();
|
|
1623
|
+
}
|
|
1624
|
+
} else {
|
|
1625
|
+
this.$addArrayElement({propName});
|
|
1626
|
+
}
|
|
1329
1627
|
const typeChoices = this.$getTypeChoices();
|
|
1330
1628
|
typeChoices.$setType({type, baseValue: value, bringIntoFocus});
|
|
1331
1629
|
const root = typeChoices.$getTypeRoot();
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1630
|
+
// Reapply this repeated setting of value if setting within the
|
|
1631
|
+
// array is not enough for `idb-manager`! But causes problems
|
|
1632
|
+
// with running `Error.cause` type twice and is inefficient;
|
|
1633
|
+
// currently put behind `setAValue` as we need to set a value
|
|
1634
|
+
// from `errorsSpecialType`
|
|
1635
|
+
if (setAValue) {
|
|
1636
|
+
const typeObj = /** @type {import('../types.js').TypeObject} */ (
|
|
1637
|
+
Types.availableTypes[type]
|
|
1638
|
+
);
|
|
1639
|
+
if (typeObj.setValue) {
|
|
1640
|
+
typeObj.setValue({root, value});
|
|
1641
|
+
}
|
|
1337
1642
|
}
|
|
1338
1643
|
Types.validate({type, root, topRoot});
|
|
1339
1644
|
return root;
|
|
@@ -1345,8 +1650,8 @@ const arrayType = {
|
|
|
1345
1650
|
|
|
1346
1651
|
/** @type {GetAddArrayElement} */
|
|
1347
1652
|
$getAddArrayElement () {
|
|
1348
|
-
const el = this
|
|
1349
|
-
|
|
1653
|
+
const el = this.
|
|
1654
|
+
lastElementChild.firstElementChild.nextElementSibling;
|
|
1350
1655
|
return sparse ? el.nextElementSibling : el;
|
|
1351
1656
|
},
|
|
1352
1657
|
/**
|
|
@@ -1374,7 +1679,7 @@ const arrayType = {
|
|
|
1374
1679
|
const arrayItems = this.$getArrayItems();
|
|
1375
1680
|
return $e(
|
|
1376
1681
|
arrayItems.lastElementChild,
|
|
1377
|
-
|
|
1682
|
+
`fieldset > .typeChoices-${typeNamespace}` // Avoid keys
|
|
1378
1683
|
);
|
|
1379
1684
|
}
|
|
1380
1685
|
},
|