@es-joy/jsoe 0.15.0 → 0.17.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/.ncurc.cjs +4 -5
- package/CHANGES.md +9 -0
- package/README.md +28 -4
- package/badges/licenses-badge.svg +1 -0
- package/dist/formatAndTypeChoices.d.ts +39 -19
- package/dist/formatAndTypeChoices.d.ts.map +1 -1
- package/dist/formats/indexedDBKey.d.ts +1 -1
- package/dist/formats/indexedDBKey.d.ts.map +1 -1
- package/dist/formats/json.d.ts +1 -1
- package/dist/formats/json.d.ts.map +1 -1
- package/dist/formats/schema.d.ts +11 -0
- package/dist/formats/schema.d.ts.map +1 -0
- package/dist/formats/structuredCloning.d.ts +11 -13
- package/dist/formats/structuredCloning.d.ts.map +1 -1
- package/dist/formats.d.ts +86 -25
- package/dist/formats.d.ts.map +1 -1
- package/dist/fundamentalTypes/BooleanObjectType.d.ts +1 -1
- package/dist/fundamentalTypes/BooleanObjectType.d.ts.map +1 -1
- package/dist/fundamentalTypes/NumberObjectType.d.ts +1 -1
- package/dist/fundamentalTypes/NumberObjectType.d.ts.map +1 -1
- package/dist/fundamentalTypes/StringObjectType.d.ts +1 -1
- package/dist/fundamentalTypes/StringObjectType.d.ts.map +1 -1
- package/dist/fundamentalTypes/arrayReferenceType.d.ts +1 -1
- package/dist/fundamentalTypes/arrayType.d.ts +3 -3
- package/dist/fundamentalTypes/arrayType.d.ts.map +1 -1
- package/dist/fundamentalTypes/bigintObjectType.d.ts +6 -0
- package/dist/fundamentalTypes/bigintObjectType.d.ts.map +1 -0
- package/dist/fundamentalTypes/bigintType.d.ts +1 -1
- package/dist/fundamentalTypes/bigintType.d.ts.map +1 -1
- package/dist/fundamentalTypes/blobType.d.ts +1 -1
- package/dist/fundamentalTypes/blobType.d.ts.map +1 -1
- package/dist/fundamentalTypes/booleanType.d.ts +8 -0
- package/dist/fundamentalTypes/booleanType.d.ts.map +1 -0
- package/dist/fundamentalTypes/catchType.d.ts +6 -0
- package/dist/fundamentalTypes/catchType.d.ts.map +1 -0
- package/dist/fundamentalTypes/dateType.d.ts +4 -4
- package/dist/fundamentalTypes/dateType.d.ts.map +1 -1
- package/dist/fundamentalTypes/domexceptionType.d.ts +1 -1
- package/dist/fundamentalTypes/domexceptionType.d.ts.map +1 -1
- package/dist/fundamentalTypes/enumType.d.ts +6 -0
- package/dist/fundamentalTypes/enumType.d.ts.map +1 -0
- package/dist/fundamentalTypes/errorType.d.ts +1 -1
- package/dist/fundamentalTypes/errorType.d.ts.map +1 -1
- package/dist/fundamentalTypes/fileType.d.ts +1 -1
- package/dist/fundamentalTypes/fileType.d.ts.map +1 -1
- package/dist/fundamentalTypes/filelistType.d.ts +1 -1
- package/dist/fundamentalTypes/filelistType.d.ts.map +1 -1
- package/dist/fundamentalTypes/functionType.d.ts +6 -0
- package/dist/fundamentalTypes/functionType.d.ts.map +1 -0
- package/dist/fundamentalTypes/literalType.d.ts +6 -0
- package/dist/fundamentalTypes/literalType.d.ts.map +1 -0
- package/dist/fundamentalTypes/mapType.d.ts +1 -1
- package/dist/fundamentalTypes/mapType.d.ts.map +1 -1
- package/dist/fundamentalTypes/nanType.d.ts +6 -0
- package/dist/fundamentalTypes/nanType.d.ts.map +1 -0
- package/dist/fundamentalTypes/nativeEnumType.d.ts +6 -0
- package/dist/fundamentalTypes/nativeEnumType.d.ts.map +1 -0
- package/dist/fundamentalTypes/neverType.d.ts +6 -0
- package/dist/fundamentalTypes/neverType.d.ts.map +1 -0
- package/dist/fundamentalTypes/noneditableType.d.ts +1 -1
- package/dist/fundamentalTypes/noneditableType.d.ts.map +1 -1
- package/dist/fundamentalTypes/nullType.d.ts +1 -1
- package/dist/fundamentalTypes/nullType.d.ts.map +1 -1
- package/dist/fundamentalTypes/numberType.d.ts +1 -1
- package/dist/fundamentalTypes/numberType.d.ts.map +1 -1
- package/dist/fundamentalTypes/objectReferenceType.d.ts +1 -1
- package/dist/fundamentalTypes/objectType.d.ts +1 -1
- package/dist/fundamentalTypes/objectType.d.ts.map +1 -1
- package/dist/fundamentalTypes/promiseType.d.ts +6 -0
- package/dist/fundamentalTypes/promiseType.d.ts.map +1 -0
- package/dist/fundamentalTypes/recordType.d.ts +6 -0
- package/dist/fundamentalTypes/recordType.d.ts.map +1 -0
- package/dist/fundamentalTypes/regexpType.d.ts +1 -1
- package/dist/fundamentalTypes/regexpType.d.ts.map +1 -1
- package/dist/fundamentalTypes/setType.d.ts +1 -1
- package/dist/fundamentalTypes/setType.d.ts.map +1 -1
- package/dist/fundamentalTypes/sparseUndefinedType.d.ts +1 -1
- package/dist/fundamentalTypes/sparseUndefinedType.d.ts.map +1 -1
- package/dist/fundamentalTypes/stringType.d.ts +1 -1
- package/dist/fundamentalTypes/stringType.d.ts.map +1 -1
- package/dist/fundamentalTypes/symbolType.d.ts +8 -0
- package/dist/fundamentalTypes/symbolType.d.ts.map +1 -0
- package/dist/fundamentalTypes/tupleType.d.ts +6 -0
- package/dist/fundamentalTypes/tupleType.d.ts.map +1 -0
- package/dist/fundamentalTypes/undefinedType.d.ts +1 -1
- package/dist/fundamentalTypes/undefinedType.d.ts.map +1 -1
- package/dist/fundamentalTypes/voidType.d.ts +6 -0
- package/dist/fundamentalTypes/voidType.d.ts.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9661 -1587
- package/dist/subTypes/blobHTMLType.d.ts.map +1 -1
- package/dist/subTypes/falseType.d.ts +1 -1
- package/dist/subTypes/trueType.d.ts +1 -1
- package/dist/superTypes/SpecialNumberType.d.ts +1 -1
- package/dist/superTypes/SpecialRealNumberType.d.ts +1 -1
- package/dist/superTypes/SpecialRealNumberType.d.ts.map +1 -1
- package/dist/superTypes/buffersourceType.d.ts +1 -1
- package/dist/superTypes/buffersourceType.d.ts.map +1 -1
- package/dist/superTypes/dommatrixType.d.ts +1 -1
- package/dist/superTypes/dommatrixType.d.ts.map +1 -1
- package/dist/superTypes/dompointType.d.ts +1 -1
- package/dist/superTypes/dompointType.d.ts.map +1 -1
- package/dist/superTypes/domrectType.d.ts +1 -1
- package/dist/superTypes/domrectType.d.ts.map +1 -1
- package/dist/superTypes/errorsSpecialType.d.ts +1 -1
- package/dist/superTypes/errorsSpecialType.d.ts.map +1 -1
- package/dist/typeChoices.d.ts +33 -16
- package/dist/typeChoices.d.ts.map +1 -1
- package/dist/types.d.ts +161 -157
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/dialogs.d.ts +3 -3
- package/dist/utils/dialogs.d.ts.map +1 -1
- package/dist/utils/jsonPointer.d.ts.map +1 -1
- package/dist/utils/media.d.ts.map +1 -1
- package/dist/utils/objects.d.ts +16 -0
- package/dist/utils/objects.d.ts.map +1 -0
- package/dist/utils/types.d.ts +1 -1
- package/dist/utils/types.d.ts.map +1 -1
- package/dist/vendor-imports.d.ts +1 -1
- package/eslint.config.js +74 -0
- package/licenseInfo.json +4 -0
- package/package.json +59 -69
- package/rollup.config.js +16 -1
- package/src/formatAndTypeChoices.js +68 -54
- package/src/formats/indexedDBKey.js +9 -4
- package/src/formats/json.js +7 -4
- package/src/formats/schema.js +772 -0
- package/src/formats/structuredCloning.js +187 -86
- package/src/formats.js +61 -26
- package/src/fundamentalTypes/BooleanObjectType.js +18 -6
- package/src/fundamentalTypes/NumberObjectType.js +18 -5
- package/src/fundamentalTypes/StringObjectType.js +20 -8
- package/src/fundamentalTypes/arrayType.js +1241 -533
- package/src/fundamentalTypes/bigintObjectType.js +64 -0
- package/src/fundamentalTypes/bigintType.js +31 -5
- package/src/fundamentalTypes/blobType.js +201 -190
- package/src/fundamentalTypes/booleanType.js +63 -0
- package/src/fundamentalTypes/catchType.js +140 -0
- package/src/fundamentalTypes/dateType.js +57 -19
- package/src/fundamentalTypes/domexceptionType.js +61 -63
- package/src/fundamentalTypes/enumType.js +52 -0
- package/src/fundamentalTypes/errorType.js +20 -7
- package/src/fundamentalTypes/fileType.js +195 -189
- package/src/fundamentalTypes/filelistType.js +4 -0
- package/src/fundamentalTypes/functionType.js +150 -0
- package/src/fundamentalTypes/literalType.js +81 -0
- package/src/fundamentalTypes/mapType.js +4 -0
- package/src/fundamentalTypes/nanType.js +41 -0
- package/src/fundamentalTypes/nativeEnumType.js +155 -0
- package/src/fundamentalTypes/neverType.js +44 -0
- package/src/fundamentalTypes/noneditableType.js +19 -5
- package/src/fundamentalTypes/nullType.js +16 -6
- package/src/fundamentalTypes/numberType.js +49 -6
- package/src/fundamentalTypes/objectType.js +10 -2
- package/src/fundamentalTypes/promiseType.js +60 -0
- package/src/fundamentalTypes/recordType.js +42 -0
- package/src/fundamentalTypes/regexpType.js +52 -34
- package/src/fundamentalTypes/setType.js +4 -0
- package/src/fundamentalTypes/sparseUndefinedType.js +3 -0
- package/src/fundamentalTypes/stringType.js +254 -6
- package/src/fundamentalTypes/symbolType.js +81 -0
- package/src/fundamentalTypes/tupleType.js +39 -0
- package/src/fundamentalTypes/undefinedType.js +14 -4
- package/src/fundamentalTypes/voidType.js +48 -0
- package/src/jsoe.css +37 -3
- package/src/subTypes/blobHTMLType.js +33 -24
- package/src/superTypes/SpecialRealNumberType.js +22 -6
- package/src/superTypes/buffersourceType.js +86 -30
- package/src/superTypes/dommatrixType.js +24 -7
- package/src/superTypes/dompointType.js +27 -7
- package/src/superTypes/domrectType.js +33 -7
- package/src/superTypes/errorsSpecialType.js +30 -14
- package/src/typeChoices.js +115 -44
- package/src/types.js +129 -45
- package/src/utils/FileList.js +3 -3
- package/src/utils/dialogs.js +6 -8
- package/src/utils/objects.js +22 -0
- package/src/vendor-imports.js +1 -1
- package/tsconfig.json +2 -2
- package/vendor/jamilih/dist/jml-es.js +23 -6
- package/vendor/typeson-registry/dist/index.js +77 -47
- package/vendor/zod/lib/index.mjs +4236 -0
- package/vendor/zodex/dist/esm/dezerialize.d.ts +26 -0
- package/vendor/zodex/dist/esm/dezerialize.js +393 -0
- package/vendor/zodex/dist/esm/index.d.ts +9 -0
- package/vendor/zodex/dist/esm/index.js +4 -0
- package/vendor/zodex/dist/esm/infer.d.ts +29 -0
- package/vendor/zodex/dist/esm/infer.js +1 -0
- package/vendor/zodex/dist/esm/package.json +1 -0
- package/vendor/zodex/dist/esm/types.d.ts +201 -0
- package/vendor/zodex/dist/esm/types.js +13 -0
- package/vendor/zodex/dist/esm/ui.d.ts +23 -0
- package/vendor/zodex/dist/esm/ui.js +10 -0
- package/vendor/zodex/dist/esm/zerialize.d.ts +50 -0
- package/vendor/zodex/dist/esm/zerialize.js +417 -0
- package/vendor/zodex/dist/esm/zod-types.d.ts +11 -0
- package/vendor/zodex/dist/esm/zod-types.js +1 -0
- package/.eslintignore +0 -13
- package/.eslintrc.cjs +0 -145
- package/.nyc_output/out.json +0 -114984
- package/cypress.config.ts +0 -15
- package/demo/index-instrumented.html +0 -29
- package/demo/index.html +0 -29
- package/demo/index.js +0 -644
- package/src/formats/schemaAndArbitrary.js +0 -20
- package/src/formats/schemaOnly.js +0 -539
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import {jml, nbsp} from '../vendor-imports.js';
|
|
2
2
|
|
|
3
3
|
import {getPropertyValueFromLegend} from '../types.js';
|
|
4
|
-
import {$e, U, DOM} from '../utils/templateUtils.js';
|
|
4
|
+
import {$e, $$e, U, DOM} from '../utils/templateUtils.js';
|
|
5
5
|
import dialogs from '../utils/dialogs.js';
|
|
6
6
|
import {
|
|
7
7
|
resolveJSONPointer, getJSONPointerParts, reduceJSONPointerParts
|
|
8
8
|
} from '../utils/jsonPointer.js';
|
|
9
9
|
import FileList from '../utils/FileList.js';
|
|
10
10
|
|
|
11
|
+
let optionalPropertyId = 0;
|
|
12
|
+
|
|
11
13
|
/**
|
|
12
14
|
* @typedef {number} Integer
|
|
13
15
|
*/
|
|
@@ -88,12 +90,16 @@ const arrayType = {
|
|
|
88
90
|
regexEndings: [',', ']'],
|
|
89
91
|
stringRegexBegin: /^\[/u,
|
|
90
92
|
stringRegexEnd: /^\]/u,
|
|
93
|
+
valueMatch (x) {
|
|
94
|
+
return Array.isArray(x);
|
|
95
|
+
},
|
|
91
96
|
toValue (s, info) {
|
|
92
97
|
const {
|
|
93
98
|
/* istanbul ignore next -- Just a guard */
|
|
94
99
|
endMatchTypeObjs = [],
|
|
95
100
|
remnant: innerContents,
|
|
96
|
-
rootHolder
|
|
101
|
+
rootHolder,
|
|
102
|
+
schemaObject
|
|
97
103
|
} = /** @type {import('../types.js').RootInfo} */ (info);
|
|
98
104
|
// eslint-disable-next-line prefer-destructuring -- TS
|
|
99
105
|
const format = /** @type {import('../types.js').RootInfo} */ (info).format;
|
|
@@ -157,6 +163,7 @@ const arrayType = {
|
|
|
157
163
|
firstRun: false,
|
|
158
164
|
format,
|
|
159
165
|
state,
|
|
166
|
+
schemaObject,
|
|
160
167
|
endMatchTypeObjs, rootHolder, parent: retObj, parentPath: idx
|
|
161
168
|
}
|
|
162
169
|
);
|
|
@@ -211,11 +218,11 @@ const arrayType = {
|
|
|
211
218
|
[v, stringVal, beginOnly, assign] = types.getValueForString(
|
|
212
219
|
stringVal,
|
|
213
220
|
{
|
|
214
|
-
firstRun: false, format, state,
|
|
221
|
+
firstRun: false, format, state, schemaObject,
|
|
215
222
|
endMatchTypeObjs, rootHolder, parent: retObj, parentPath: pr
|
|
216
223
|
}
|
|
217
224
|
);
|
|
218
|
-
} catch
|
|
225
|
+
} catch {
|
|
219
226
|
// console.log(
|
|
220
227
|
// 'errrrr', stringVal,
|
|
221
228
|
// JSON.stringify(endMatchTypeObjs)
|
|
@@ -299,6 +306,14 @@ const arrayType = {
|
|
|
299
306
|
'.typeContainer',
|
|
300
307
|
'div[data-type]'
|
|
301
308
|
]
|
|
309
|
+
)[0] || DOM.filterChildElements(
|
|
310
|
+
/** @type {HTMLFieldSetElement} */
|
|
311
|
+
(fieldset),
|
|
312
|
+
[
|
|
313
|
+
'[class^=optionalProperties-placeholder]',
|
|
314
|
+
'.typeContainer',
|
|
315
|
+
'div[data-type]'
|
|
316
|
+
]
|
|
302
317
|
)[0]
|
|
303
318
|
);
|
|
304
319
|
/* istanbul ignore if -- Should err first? */
|
|
@@ -351,11 +366,26 @@ const arrayType = {
|
|
|
351
366
|
const key = select.$getValue();
|
|
352
367
|
return [key, /** @type {any[]} */ (ret)[idx]];
|
|
353
368
|
}))
|
|
354
|
-
:
|
|
369
|
+
: this.record
|
|
370
|
+
? arrayItems.$getMapKeySelects().reduce((obj, select, idx) => {
|
|
371
|
+
const key = select.$getValue();
|
|
372
|
+
const val = /** @type {any[]} */ (ret)[idx];
|
|
373
|
+
obj[key] = val;
|
|
374
|
+
// Todo: Reenable for native enums
|
|
375
|
+
// The nature of native enums
|
|
376
|
+
// if (typeof val === 'number') {
|
|
377
|
+
// obj[val] = key;
|
|
378
|
+
// }
|
|
379
|
+
return obj;
|
|
380
|
+
}, /** @type {{[key: string]: any}} */ ({}))
|
|
381
|
+
: ret;
|
|
355
382
|
},
|
|
356
383
|
|
|
357
384
|
// Try to keep in sync with basic structure of `editUI`
|
|
358
|
-
viewUI ({
|
|
385
|
+
viewUI ({
|
|
386
|
+
typeNamespace, type, types, value, topRoot, resultType, format,
|
|
387
|
+
specificSchemaObject
|
|
388
|
+
}) {
|
|
359
389
|
// const {sparse} = this;
|
|
360
390
|
let itemIndex = -1;
|
|
361
391
|
|
|
@@ -373,175 +403,238 @@ const arrayType = {
|
|
|
373
403
|
/* className, type, arrayItems, */
|
|
374
404
|
itemIndex, typeNamespace, propName
|
|
375
405
|
}) => {
|
|
406
|
+
const tupleItem = /** @type {import('zodex').SzTuple} */ (
|
|
407
|
+
specificSchemaObject
|
|
408
|
+
)?.items?.[itemIndex];
|
|
409
|
+
const restItem = /** @type {import('zodex').SzTuple} */ (
|
|
410
|
+
specificSchemaObject
|
|
411
|
+
)?.rest;
|
|
376
412
|
return ['legend', [
|
|
377
|
-
this.array
|
|
378
|
-
? '
|
|
379
|
-
|
|
380
|
-
|
|
413
|
+
this.array && type !== 'record'
|
|
414
|
+
? /** @type {import('zodex').SzArray} */ (
|
|
415
|
+
specificSchemaObject
|
|
416
|
+
)?.element?.description ??
|
|
417
|
+
((type === 'set' && /** @type {import('zodex').SzSet} */ (
|
|
418
|
+
specificSchemaObject
|
|
419
|
+
)?.value?.description)
|
|
420
|
+
? /** @type {import('zodex').SzSet} */ (
|
|
421
|
+
specificSchemaObject
|
|
422
|
+
)?.value?.description
|
|
423
|
+
: (
|
|
424
|
+
tupleItem?.description ?? restItem?.description
|
|
425
|
+
)) ?? 'Item'
|
|
426
|
+
: specificSchemaObject ? '' : 'Property',
|
|
427
|
+
specificSchemaObject ? '' : ':',
|
|
381
428
|
nbsp.repeat(2),
|
|
382
429
|
['span', {
|
|
383
|
-
class: `propertyName-${typeNamespace}
|
|
430
|
+
class: `propertyName-${typeNamespace}`,
|
|
431
|
+
title: type === 'record' && /** @type {import('zodex').SzRecord} */ (
|
|
432
|
+
specificSchemaObject
|
|
433
|
+
)?.key?.description
|
|
434
|
+
? /** @type {import('zodex').SzRecord} */ (
|
|
435
|
+
specificSchemaObject
|
|
436
|
+
)?.key?.description
|
|
437
|
+
: specificSchemaObject ? propName : undefined
|
|
384
438
|
}, [
|
|
385
439
|
propName !== undefined
|
|
386
|
-
?
|
|
440
|
+
? /** @type {import('zodex').SzObject} */ (
|
|
441
|
+
specificSchemaObject
|
|
442
|
+
)?.properties?.[propName]?.description ?? propName
|
|
387
443
|
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
388
444
|
/* istanbul ignore next -- Won't reach here as typeson will always give keypath? */
|
|
389
445
|
: itemIndex
|
|
390
446
|
]]
|
|
391
447
|
]];
|
|
392
448
|
};
|
|
393
|
-
const div = /** @type {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
schemaContent, schemaState
|
|
413
|
-
}) {
|
|
414
|
-
if (parentType === 'map') {
|
|
415
|
-
const root = types.getUIForModeAndType({
|
|
416
|
-
resultType,
|
|
417
|
-
readonly: true,
|
|
418
|
-
typeNamespace, type, topRoot,
|
|
419
|
-
bringIntoFocus,
|
|
420
|
-
format, schemaContent, schemaState,
|
|
421
|
-
value,
|
|
422
|
-
hasValue: true // type === 'sparseArrays' && value
|
|
423
|
-
});
|
|
424
|
-
if (propName === '0') {
|
|
425
|
-
const fieldset = this.$addMapElement();
|
|
426
|
-
this._lastFieldset = fieldset;
|
|
427
|
-
const keyFieldset = /** @type {HTMLFieldSetElement} */ (jml(
|
|
428
|
-
'fieldset', [
|
|
429
|
-
['legend', ['Key']]
|
|
430
|
-
], fieldset
|
|
431
|
-
));
|
|
432
|
-
jml(root, keyFieldset);
|
|
433
|
-
} else { // propName === '1'
|
|
434
|
-
const valueFieldset = /** @type {HTMLFieldSetElement} */ (jml(
|
|
435
|
-
'fieldset', [
|
|
436
|
-
['legend', ['Value']]
|
|
437
|
-
], this._lastFieldset
|
|
438
|
-
));
|
|
439
|
-
this._lastFieldset = null;
|
|
440
|
-
jml(root, valueFieldset);
|
|
441
|
-
}
|
|
442
|
-
return root;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
const fieldset = this.$addArrayElement({propName});
|
|
449
|
+
const div = jml('div', /** @type {import('jamilih').JamilihAttributes} */ ({
|
|
450
|
+
class: 'arrayHolder',
|
|
451
|
+
dataset: {type},
|
|
452
|
+
$custom: {
|
|
453
|
+
/**
|
|
454
|
+
* @param {{
|
|
455
|
+
* propName: string,
|
|
456
|
+
* type: import('../types.js').AvailableType,
|
|
457
|
+
* value: import('../formats.js').StructuredCloneValue,
|
|
458
|
+
* bringIntoFocus: boolean,
|
|
459
|
+
* schemaContent: import('../formats/schema.js').ZodexSchema,
|
|
460
|
+
* }} cfg
|
|
461
|
+
* @returns {Element}
|
|
462
|
+
*/
|
|
463
|
+
$addAndSetArrayElement ({
|
|
464
|
+
propName, type, value, bringIntoFocus,
|
|
465
|
+
schemaContent
|
|
466
|
+
}) {
|
|
467
|
+
if (parentType === 'map') {
|
|
446
468
|
const root = types.getUIForModeAndType({
|
|
447
469
|
resultType,
|
|
448
470
|
readonly: true,
|
|
449
471
|
typeNamespace, type, topRoot,
|
|
450
472
|
bringIntoFocus,
|
|
451
|
-
format, schemaContent,
|
|
473
|
+
format, schemaContent,
|
|
452
474
|
value,
|
|
453
475
|
hasValue: true // type === 'sparseArrays' && value
|
|
454
476
|
});
|
|
455
|
-
|
|
477
|
+
if (propName === '0') {
|
|
478
|
+
const fieldset = this.$addMapElement();
|
|
479
|
+
this._lastFieldset = fieldset;
|
|
480
|
+
const keyFieldset = jml(
|
|
481
|
+
'fieldset', [
|
|
482
|
+
['legend', {
|
|
483
|
+
title: /** @type {import('zodex').SzMap<any, any>} */ (
|
|
484
|
+
specificSchemaObject
|
|
485
|
+
)?.key?.description
|
|
486
|
+
? '(map key)'
|
|
487
|
+
: undefined
|
|
488
|
+
}, [
|
|
489
|
+
/** @type {import('zodex').SzMap<any, any>} */ (
|
|
490
|
+
specificSchemaObject
|
|
491
|
+
)?.key?.description ?? 'Key'
|
|
492
|
+
]]
|
|
493
|
+
], fieldset
|
|
494
|
+
);
|
|
495
|
+
jml(root, keyFieldset);
|
|
496
|
+
} else { // propName === '1'
|
|
497
|
+
const valueFieldset = jml(
|
|
498
|
+
'fieldset', [
|
|
499
|
+
['legend', {
|
|
500
|
+
title: /** @type {import('zodex').SzMap<any, any>} */ (
|
|
501
|
+
specificSchemaObject
|
|
502
|
+
)?.value?.description
|
|
503
|
+
? '(map value)'
|
|
504
|
+
: undefined
|
|
505
|
+
}, [
|
|
506
|
+
/** @type {import('zodex').SzMap<any, any>} */ (
|
|
507
|
+
specificSchemaObject
|
|
508
|
+
)?.value?.description ??
|
|
509
|
+
'Value'
|
|
510
|
+
]]
|
|
511
|
+
], this._lastFieldset
|
|
512
|
+
);
|
|
513
|
+
this._lastFieldset = null;
|
|
514
|
+
jml(root, valueFieldset);
|
|
515
|
+
}
|
|
456
516
|
return root;
|
|
457
|
-
},
|
|
458
|
-
/**
|
|
459
|
-
* @returns {HTMLFieldSetElement}
|
|
460
|
-
*/
|
|
461
|
-
$addMapElement () {
|
|
462
|
-
itemIndex++;
|
|
463
|
-
const arrayItems = this.$getArrayItems();
|
|
464
|
-
// const className = `${type}Item`;
|
|
465
|
-
const fieldset = /** @type {HTMLFieldSetElement} */ (
|
|
466
|
-
jml('fieldset', [
|
|
467
|
-
buildLegend({
|
|
468
|
-
// className,
|
|
469
|
-
// type,
|
|
470
|
-
// arrayItems,
|
|
471
|
-
itemIndex,
|
|
472
|
-
typeNamespace,
|
|
473
|
-
propName: undefined
|
|
474
|
-
})
|
|
475
|
-
], arrayItems)
|
|
476
|
-
);
|
|
477
|
-
return fieldset;
|
|
478
|
-
},
|
|
479
|
-
/**
|
|
480
|
-
* @param {{propName: string}} cfg
|
|
481
|
-
* @returns {HTMLFieldSetElement}
|
|
482
|
-
*/
|
|
483
|
-
$addArrayElement ({propName}) {
|
|
484
|
-
itemIndex++;
|
|
485
|
-
const arrayItems = this.$getArrayItems();
|
|
486
|
-
// const className = `${type}Item`;
|
|
487
|
-
const fieldset = /** @type {HTMLFieldSetElement} */ (
|
|
488
|
-
jml('fieldset', [
|
|
489
|
-
buildLegend({
|
|
490
|
-
// className,
|
|
491
|
-
// type,
|
|
492
|
-
// arrayItems,
|
|
493
|
-
itemIndex,
|
|
494
|
-
typeNamespace,
|
|
495
|
-
propName
|
|
496
|
-
})
|
|
497
|
-
], arrayItems)
|
|
498
|
-
);
|
|
499
|
-
return fieldset;
|
|
500
|
-
},
|
|
501
|
-
$getArrayItems () {
|
|
502
|
-
return this.lastElementChild.lastElementChild;
|
|
503
517
|
}
|
|
518
|
+
|
|
519
|
+
const fieldset = this.$addArrayElement({propName});
|
|
520
|
+
const root = types.getUIForModeAndType({
|
|
521
|
+
resultType,
|
|
522
|
+
readonly: true,
|
|
523
|
+
typeNamespace, type, topRoot,
|
|
524
|
+
bringIntoFocus,
|
|
525
|
+
format,
|
|
526
|
+
schemaContent,
|
|
527
|
+
specificSchemaObject: schemaContent,
|
|
528
|
+
value,
|
|
529
|
+
hasValue: true // type === 'sparseArrays' && value
|
|
530
|
+
});
|
|
531
|
+
jml(root, fieldset);
|
|
532
|
+
return root;
|
|
533
|
+
},
|
|
534
|
+
/**
|
|
535
|
+
* @returns {HTMLFieldSetElement}
|
|
536
|
+
*/
|
|
537
|
+
$addMapElement () {
|
|
538
|
+
itemIndex++;
|
|
539
|
+
const arrayItems = this.$getArrayItems();
|
|
540
|
+
// const className = `${type}Item`;
|
|
541
|
+
const fieldset = jml('fieldset', [
|
|
542
|
+
buildLegend({
|
|
543
|
+
// className,
|
|
544
|
+
// type,
|
|
545
|
+
// arrayItems,
|
|
546
|
+
itemIndex,
|
|
547
|
+
typeNamespace,
|
|
548
|
+
propName: undefined
|
|
549
|
+
})
|
|
550
|
+
], arrayItems);
|
|
551
|
+
return fieldset;
|
|
552
|
+
},
|
|
553
|
+
/**
|
|
554
|
+
* @param {{propName: string}} cfg
|
|
555
|
+
* @returns {HTMLFieldSetElement}
|
|
556
|
+
*/
|
|
557
|
+
$addArrayElement ({propName}) {
|
|
558
|
+
itemIndex++;
|
|
559
|
+
const arrayItems = this.$getArrayItems();
|
|
560
|
+
// const className = `${type}Item`;
|
|
561
|
+
const fieldset = jml('fieldset', [
|
|
562
|
+
buildLegend({
|
|
563
|
+
// className,
|
|
564
|
+
// type,
|
|
565
|
+
// arrayItems,
|
|
566
|
+
itemIndex,
|
|
567
|
+
typeNamespace,
|
|
568
|
+
propName
|
|
569
|
+
})
|
|
570
|
+
], arrayItems);
|
|
571
|
+
return fieldset;
|
|
572
|
+
},
|
|
573
|
+
$getArrayItems () {
|
|
574
|
+
return this.lastElementChild.lastElementChild;
|
|
504
575
|
}
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
576
|
+
}
|
|
577
|
+
}), [
|
|
578
|
+
['span', {
|
|
579
|
+
title: specificSchemaObject?.description
|
|
580
|
+
}, [
|
|
581
|
+
specificSchemaObject
|
|
582
|
+
? '—'
|
|
583
|
+
: DOM.initialCaps(/** @type {import('../types.js').AvailableType} */ (
|
|
584
|
+
type
|
|
585
|
+
)).replace(/s$/u, '')
|
|
586
|
+
]],
|
|
587
|
+
nbsp.repeat(2),
|
|
588
|
+
['button', {$on: {click (/** @type {Event} */ e) {
|
|
589
|
+
e.preventDefault();
|
|
590
|
+
const {target} = e;
|
|
591
|
+
const arrayContents = /** @type {HTMLDivElement} */ ($e(
|
|
592
|
+
/** @type {HTMLElement} */
|
|
593
|
+
(/** @type {HTMLElement} */ (target).closest('.arrayHolder')),
|
|
594
|
+
'.arrayContents'
|
|
595
|
+
));
|
|
596
|
+
arrayContents.hidden = !arrayContents.hidden;
|
|
597
|
+
/** @type {HTMLElement} */ (
|
|
598
|
+
target
|
|
599
|
+
).textContent = arrayContents.hidden ? '+' : '-';
|
|
600
|
+
}}}, ['-']],
|
|
601
|
+
['div', {class: 'arrayContents'}, [
|
|
602
|
+
this.array && type !== 'record'
|
|
603
|
+
? ['div', {
|
|
604
|
+
title: specificSchemaObject
|
|
605
|
+
? (type === 'filelist'
|
|
606
|
+
? '(a FileList)'
|
|
527
607
|
: type === 'set'
|
|
528
|
-
? 'Set
|
|
608
|
+
? '(a Set)'
|
|
529
609
|
: type === 'map'
|
|
530
|
-
? 'Map
|
|
531
|
-
:
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
610
|
+
? '(a Map)'
|
|
611
|
+
: type === 'tuple'
|
|
612
|
+
? '(a tuple)'
|
|
613
|
+
: '(an Array)')
|
|
614
|
+
: undefined
|
|
615
|
+
}, [
|
|
616
|
+
type === 'filelist'
|
|
617
|
+
? (specificSchemaObject?.description ?? 'FileList') + ' length: '
|
|
618
|
+
: type === 'set'
|
|
619
|
+
? (specificSchemaObject?.description ?? 'Set') + ' size: '
|
|
620
|
+
: type === 'map'
|
|
621
|
+
? (specificSchemaObject?.description ?? 'Map') + ' size: '
|
|
622
|
+
: (specificSchemaObject?.description ?? 'Array') +
|
|
623
|
+
' length: ',
|
|
624
|
+
['span', [
|
|
625
|
+
(value && (type === 'set' || type === 'map')
|
|
626
|
+
? value.size
|
|
627
|
+
: value.length) || 0
|
|
537
628
|
]]
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
629
|
+
]]
|
|
630
|
+
: (type === 'record'
|
|
631
|
+
? specificSchemaObject?.description ?? 'Record'
|
|
632
|
+
: ''),
|
|
633
|
+
['div', {
|
|
634
|
+
class: 'arrayItems'
|
|
635
|
+
}]
|
|
636
|
+
]]
|
|
637
|
+
]);
|
|
545
638
|
return [div];
|
|
546
639
|
},
|
|
547
640
|
getInput ({root}) {
|
|
@@ -553,17 +646,26 @@ const arrayType = {
|
|
|
553
646
|
// population of the array in the callback
|
|
554
647
|
editUI ({
|
|
555
648
|
typeNamespace, buildTypeChoices, format, // resultType,
|
|
556
|
-
formats, types,
|
|
557
|
-
type,
|
|
649
|
+
formats, types, specificSchemaObject, schemaFallingBack, schemaContent,
|
|
650
|
+
type, forcedState, topRoot, value: objectValue, bringIntoFocus = true
|
|
558
651
|
}) {
|
|
559
652
|
const {sparse} = this;
|
|
560
|
-
// eslint-disable-next-line consistent-this
|
|
653
|
+
// eslint-disable-next-line consistent-this -- Clearer
|
|
561
654
|
const parentTypeObject = this;
|
|
562
655
|
const itemAdjust = type === 'object' ? 1 : 0;
|
|
563
656
|
let itemIndex = itemAdjust - 1;
|
|
564
657
|
const editableProperties = type !== 'array' &&
|
|
565
|
-
type !== 'set' && type !== 'map' && type !== 'filelist'
|
|
566
|
-
|
|
658
|
+
type !== 'set' && type !== 'map' && type !== 'filelist' &&
|
|
659
|
+
type !== 'tuple' && type !== 'record'; // arrayNonindexKeys and object?
|
|
660
|
+
const mapProperties = type === 'map' || type === 'record';
|
|
661
|
+
|
|
662
|
+
const elementDesc = /** @type {import('zodex').SzArray} */ (
|
|
663
|
+
specificSchemaObject
|
|
664
|
+
)?.element?.description ?? (type === 'set'
|
|
665
|
+
? /** @type {import('zodex').SzSet} */ (
|
|
666
|
+
specificSchemaObject
|
|
667
|
+
)?.value?.description
|
|
668
|
+
: undefined);
|
|
567
669
|
|
|
568
670
|
/**
|
|
569
671
|
* @param {HTMLInputElement} input
|
|
@@ -597,7 +699,7 @@ const arrayType = {
|
|
|
597
699
|
if (!swapGroup || swapGroup.nodeName.toLowerCase() !== 'fieldset') {
|
|
598
700
|
return;
|
|
599
701
|
}
|
|
600
|
-
if (!sparse) {
|
|
702
|
+
if (!sparse && (!specificSchemaObject || parentTypeObject.array)) {
|
|
601
703
|
const swapCountElem = DOM.filterChildElements(
|
|
602
704
|
swapGroup, ['legend', `.${type}Item`]
|
|
603
705
|
)[0];
|
|
@@ -659,6 +761,9 @@ const arrayType = {
|
|
|
659
761
|
* @this {HTMLDivElement & {$swapGroup: SwapGroup}}
|
|
660
762
|
*/
|
|
661
763
|
const $redrawMoveArrows = function () {
|
|
764
|
+
if (type === 'tuple') { // Don't want to move non-rest items at least
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
662
767
|
DOM.filterChildElements(this, [
|
|
663
768
|
'fieldset', `.${type}Item-arrowHolder-${typeNamespace}`
|
|
664
769
|
]).forEach((holder, j, arr) => {
|
|
@@ -752,13 +857,15 @@ const arrayType = {
|
|
|
752
857
|
* itemIndex: number,
|
|
753
858
|
* typeNamespace: string|undefined,
|
|
754
859
|
* arrayItems: ArrayItems,
|
|
755
|
-
* propName: string|undefined
|
|
860
|
+
* propName: string|undefined,
|
|
861
|
+
* required?: true,
|
|
862
|
+
* schema?: import('zodex').SzType
|
|
756
863
|
* }} cfg
|
|
757
864
|
* @returns {import('jamilih').JamilihArray}
|
|
758
865
|
*/
|
|
759
866
|
const buildLegend = ({
|
|
760
867
|
className, splice, itemIndex, /* type, */ typeNamespace,
|
|
761
|
-
arrayItems, propName
|
|
868
|
+
arrayItems, propName, required, schema
|
|
762
869
|
}) => {
|
|
763
870
|
/**
|
|
764
871
|
* @callback ValidateLength
|
|
@@ -774,40 +881,81 @@ const arrayType = {
|
|
|
774
881
|
*/
|
|
775
882
|
const $validateLength = async function (avoidDialog) {
|
|
776
883
|
if (!sparse) {
|
|
777
|
-
return
|
|
884
|
+
return;
|
|
778
885
|
}
|
|
779
886
|
const inputsExceedingLength = arrayItems.$inputsExceedingLength();
|
|
780
887
|
const exceedsLength = inputsExceedingLength.length;
|
|
781
888
|
if (avoidDialog || !exceedsLength || !(/^\d+$/u).test(this.value)) {
|
|
782
|
-
return
|
|
889
|
+
return;
|
|
783
890
|
}
|
|
784
891
|
await dialogs.confirm({
|
|
785
892
|
message: 'You are attempting to add an (integer-based) ' +
|
|
786
893
|
'array item beyond the length. Click "Ok" to allow to ' +
|
|
787
894
|
'permit and extend the array length or "Cancel" otherwise.'
|
|
788
895
|
});
|
|
789
|
-
const arrLengthInput =
|
|
790
|
-
$
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
896
|
+
const arrLengthInput =
|
|
897
|
+
/** @type {HTMLInputElement & {$oldvalue: string}} */ (
|
|
898
|
+
$e(
|
|
899
|
+
/** @type {HTMLElement} */ (arrayItems.previousElementSibling),
|
|
900
|
+
'input'
|
|
901
|
+
)
|
|
902
|
+
);
|
|
795
903
|
const highest = /** @type {number} */ (inputsExceedingLength.map(
|
|
796
904
|
(i) => Number.parseInt(i.value)
|
|
797
905
|
).sort().at(-1));
|
|
798
906
|
arrLengthInput.value = String(highest + 1);
|
|
799
|
-
|
|
907
|
+
arrLengthInput.$oldvalue = String(highest + 1);
|
|
908
|
+
// Does have potential side effects calling `$inputsExceedingLength`
|
|
909
|
+
this.$validateLength(true);
|
|
800
910
|
};
|
|
801
911
|
if (mapProperties) {
|
|
802
912
|
const keyTypeSelection =
|
|
803
913
|
/** @type {import('../typeChoices.js').BuildTypeChoices} */ (
|
|
804
914
|
buildTypeChoices
|
|
805
915
|
)({
|
|
806
|
-
|
|
916
|
+
value: propName !== undefined && type === 'record'
|
|
917
|
+
? propName
|
|
918
|
+
: undefined,
|
|
919
|
+
setValue: propName !== undefined && type === 'record',
|
|
920
|
+
// Needed as false when map value supplied
|
|
921
|
+
autoTrigger: propName === undefined || type === 'record',
|
|
922
|
+
// eslint-disable-next-line object-shorthand -- TS
|
|
923
|
+
format: /** @type {import('../formats.js').AvailableFormat} */ (
|
|
924
|
+
format
|
|
925
|
+
),
|
|
926
|
+
schemaOriginal: schemaContent,
|
|
927
|
+
// Can also be a `Record`
|
|
928
|
+
schemaContent: /** @type {import('zodex').SzMap<any, any>} */ (
|
|
929
|
+
specificSchemaObject
|
|
930
|
+
)?.key,
|
|
807
931
|
typeNamespace: 'key-type-choices-only'
|
|
808
932
|
});
|
|
809
933
|
return ['legend', [
|
|
810
|
-
'
|
|
934
|
+
['span', {
|
|
935
|
+
class: 'mapKey',
|
|
936
|
+
title: (
|
|
937
|
+
type === 'map' &&
|
|
938
|
+
/** @type {import('zodex').SzMap<any, any>} */ (
|
|
939
|
+
specificSchemaObject
|
|
940
|
+
)?.key?.description
|
|
941
|
+
)
|
|
942
|
+
? '(map key)'
|
|
943
|
+
: type === 'record' &&
|
|
944
|
+
/** @type {import('zodex').SzRecord} */ (
|
|
945
|
+
specificSchemaObject
|
|
946
|
+
)?.key?.description
|
|
947
|
+
? '(record key)'
|
|
948
|
+
: undefined
|
|
949
|
+
}, [
|
|
950
|
+
type === 'map'
|
|
951
|
+
? /** @type {import('zodex').SzMap<any, any>} */ (
|
|
952
|
+
specificSchemaObject
|
|
953
|
+
)?.key?.description ?? 'Map key'
|
|
954
|
+
: /** @type {import('zodex').SzRecord} */ (
|
|
955
|
+
specificSchemaObject
|
|
956
|
+
)?.key?.description ?? 'Record key',
|
|
957
|
+
' '
|
|
958
|
+
]],
|
|
811
959
|
['span', {
|
|
812
960
|
dataset: {prop: 'true'},
|
|
813
961
|
className
|
|
@@ -816,7 +964,7 @@ const arrayType = {
|
|
|
816
964
|
nbsp.repeat(2),
|
|
817
965
|
|
|
818
966
|
['span', {
|
|
819
|
-
class: '
|
|
967
|
+
class: 'mapKeyHolder',
|
|
820
968
|
$on: {
|
|
821
969
|
change: [function () {
|
|
822
970
|
/**
|
|
@@ -858,10 +1006,18 @@ const arrayType = {
|
|
|
858
1006
|
return;
|
|
859
1007
|
}
|
|
860
1008
|
|
|
861
|
-
select.
|
|
862
|
-
|
|
1009
|
+
const control = select.hidden
|
|
1010
|
+
? /** @type {HTMLInputElement|HTMLTextAreaElement} */ ($e(
|
|
1011
|
+
/** @type {HTMLDivElement} */ (
|
|
1012
|
+
select.nextElementSibling
|
|
1013
|
+
), 'input,textarea'
|
|
1014
|
+
))
|
|
1015
|
+
: select;
|
|
1016
|
+
|
|
1017
|
+
control.setCustomValidity(
|
|
1018
|
+
`Duplicate ${type === 'map' ? 'Map' : 'Record'} key value`
|
|
863
1019
|
);
|
|
864
|
-
|
|
1020
|
+
control.reportValidity();
|
|
865
1021
|
});
|
|
866
1022
|
}
|
|
867
1023
|
}
|
|
@@ -869,24 +1025,85 @@ const arrayType = {
|
|
|
869
1025
|
]];
|
|
870
1026
|
}
|
|
871
1027
|
if (editableProperties) {
|
|
1028
|
+
// console.log('PROPNAME', propName, schema, specificSchemaObject);
|
|
1029
|
+
const description = /** @type {import('zodex').SzObject} */ (
|
|
1030
|
+
specificSchemaObject
|
|
1031
|
+
)?.properties?.[/** @type {string} */ (propName)]?.description;
|
|
1032
|
+
const optionalProperties = Object.entries(
|
|
1033
|
+
/** @type {import('zodex').SzObject} */ (
|
|
1034
|
+
specificSchemaObject
|
|
1035
|
+
)?.properties ?? {}
|
|
1036
|
+
).map(([prop, val]) => {
|
|
1037
|
+
if (!val.isOptional) {
|
|
1038
|
+
return null;
|
|
1039
|
+
}
|
|
1040
|
+
return prop;
|
|
1041
|
+
}).filter(Boolean);
|
|
1042
|
+
optionalPropertyId++;
|
|
1043
|
+
const initialValue = sparse
|
|
1044
|
+
? (
|
|
1045
|
+
splice === 'append'
|
|
1046
|
+
? ''
|
|
1047
|
+
: (propName !== undefined ? propName : itemIndex)
|
|
1048
|
+
)
|
|
1049
|
+
: propName || '';
|
|
872
1050
|
return /** @type {import('jamilih').JamilihArray} */ (['legend', [
|
|
873
1051
|
sparse
|
|
874
|
-
? 'Item'
|
|
875
|
-
: {'#':
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
1052
|
+
? elementDesc ?? 'Item'
|
|
1053
|
+
: {'#': required
|
|
1054
|
+
? [
|
|
1055
|
+
['b', {className, title: description ? propName : undefined}, [
|
|
1056
|
+
elementDesc ?? description ?? propName ?? ''
|
|
1057
|
+
]]
|
|
1058
|
+
]
|
|
1059
|
+
: [
|
|
1060
|
+
['span', {
|
|
1061
|
+
className: `${className}_propertyHolder${optionalPropertyId}`
|
|
1062
|
+
}, [
|
|
1063
|
+
...(specificSchemaObject && propName) // Optional but has name
|
|
1064
|
+
? [
|
|
1065
|
+
['b', [
|
|
1066
|
+
/** @type {import('zodex').SzObject} */
|
|
1067
|
+
(specificSchemaObject)?.properties?.[
|
|
1068
|
+
propName
|
|
1069
|
+
]
|
|
1070
|
+
? /** @type {import('zodex').SzObject} */ (
|
|
1071
|
+
specificSchemaObject
|
|
1072
|
+
)?.properties?.[
|
|
1073
|
+
propName
|
|
1074
|
+
]?.description ?? propName
|
|
1075
|
+
: /** @type {import('zodex').SzObject} */ (
|
|
1076
|
+
specificSchemaObject
|
|
1077
|
+
)?.catchall?.description ?? propName
|
|
1078
|
+
]]
|
|
1079
|
+
]
|
|
1080
|
+
: [
|
|
1081
|
+
'Property ',
|
|
1082
|
+
['span', {className}, [String(itemIndex)]],
|
|
1083
|
+
':'
|
|
1084
|
+
]
|
|
1085
|
+
]]
|
|
1086
|
+
]},
|
|
880
1087
|
nbsp.repeat(2),
|
|
1088
|
+
specificSchemaObject && !required
|
|
1089
|
+
? ['datalist', {
|
|
1090
|
+
id: `optionalProperties_${optionalPropertyId}`
|
|
1091
|
+
}, optionalProperties.map((optionalProperty) => {
|
|
1092
|
+
return ['option', {value: optionalProperty}];
|
|
1093
|
+
})]
|
|
1094
|
+
: '',
|
|
881
1095
|
['input', {
|
|
882
|
-
|
|
883
|
-
?
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
1096
|
+
list: specificSchemaObject && !required
|
|
1097
|
+
? `optionalProperties_${optionalPropertyId}`
|
|
1098
|
+
: undefined,
|
|
1099
|
+
style: {
|
|
1100
|
+
display: required && type !== 'arrayNonindexKeys'
|
|
1101
|
+
? 'none'
|
|
1102
|
+
: 'block'
|
|
1103
|
+
},
|
|
1104
|
+
disabled: required && type === 'arrayNonindexKeys',
|
|
1105
|
+
value: initialValue,
|
|
1106
|
+
dataset: {prop: true, object: true, optionalPropertyId},
|
|
890
1107
|
/*
|
|
891
1108
|
// Works but we do want to let the user input non-integer
|
|
892
1109
|
type: sparse ? 'number' : 'text',
|
|
@@ -905,7 +1122,7 @@ const arrayType = {
|
|
|
905
1122
|
try {
|
|
906
1123
|
try {
|
|
907
1124
|
await this.$validateLength();
|
|
908
|
-
} catch
|
|
1125
|
+
} catch {
|
|
909
1126
|
// Give chance for other validations if cancelled
|
|
910
1127
|
}
|
|
911
1128
|
// Don't give chance to validate positively if failed
|
|
@@ -916,7 +1133,7 @@ const arrayType = {
|
|
|
916
1133
|
// eslint-disable-next-line object-shorthand -- TS
|
|
917
1134
|
topRoot: /** @type {HTMLDivElement} */ (topRoot)
|
|
918
1135
|
}); // Needed
|
|
919
|
-
} catch
|
|
1136
|
+
} catch {}
|
|
920
1137
|
},
|
|
921
1138
|
|
|
922
1139
|
/**
|
|
@@ -943,12 +1160,12 @@ const arrayType = {
|
|
|
943
1160
|
* })[]}
|
|
944
1161
|
*/
|
|
945
1162
|
(/**
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
1163
|
+
* @type {HTMLInputElement & {
|
|
1164
|
+
* $arrayItems: HTMLDivElement & {
|
|
1165
|
+
* $getPropertyInputs: GetPropertyInputs
|
|
1166
|
+
* }
|
|
1167
|
+
* }}
|
|
1168
|
+
*/ (
|
|
952
1169
|
this
|
|
953
1170
|
).$arrayItems.$getPropertyInputs());
|
|
954
1171
|
if (inputs.length === 1) {
|
|
@@ -1051,6 +1268,34 @@ const arrayType = {
|
|
|
1051
1268
|
}
|
|
1052
1269
|
},
|
|
1053
1270
|
$on: {
|
|
1271
|
+
/**
|
|
1272
|
+
* @this {HTMLInputElement}
|
|
1273
|
+
*/
|
|
1274
|
+
input () {
|
|
1275
|
+
if (!specificSchemaObject && !schema) {
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
const propHolder = /** @type {HTMLElement} */ (
|
|
1279
|
+
$e(arrayItems, `.${className}_propertyHolder${
|
|
1280
|
+
this.dataset.optionalPropertyId
|
|
1281
|
+
}`)
|
|
1282
|
+
);
|
|
1283
|
+
DOM.removeChildren(propHolder);
|
|
1284
|
+
jml('b', [
|
|
1285
|
+
/** @type {import('zodex').SzObject} */
|
|
1286
|
+
(specificSchemaObject)?.properties?.[
|
|
1287
|
+
this.value
|
|
1288
|
+
]
|
|
1289
|
+
? /** @type {import('zodex').SzObject} */ (
|
|
1290
|
+
specificSchemaObject
|
|
1291
|
+
)?.properties?.[
|
|
1292
|
+
this.value
|
|
1293
|
+
]?.description ?? this.value
|
|
1294
|
+
: /** @type {import('zodex').SzObject} */ (
|
|
1295
|
+
specificSchemaObject
|
|
1296
|
+
)?.catchall?.description ?? this.value
|
|
1297
|
+
], propHolder);
|
|
1298
|
+
},
|
|
1054
1299
|
/**
|
|
1055
1300
|
* @param {Event} e
|
|
1056
1301
|
* @this {HTMLInputElement & {
|
|
@@ -1059,7 +1304,86 @@ const arrayType = {
|
|
|
1059
1304
|
* }}
|
|
1060
1305
|
*/
|
|
1061
1306
|
change (e) {
|
|
1062
|
-
|
|
1307
|
+
const neverProperty = /** @type {import('zodex').SzObject} */ (
|
|
1308
|
+
specificSchemaObject
|
|
1309
|
+
)?.properties?.[
|
|
1310
|
+
/** @type {string} */ (this.value)
|
|
1311
|
+
]?.type === 'never';
|
|
1312
|
+
|
|
1313
|
+
let invalid = false;
|
|
1314
|
+
if (neverProperty) {
|
|
1315
|
+
this.setCustomValidity('Never value');
|
|
1316
|
+
this.reportValidity();
|
|
1317
|
+
this.style.backgroundColor = 'pink';
|
|
1318
|
+
|
|
1319
|
+
invalid = true;
|
|
1320
|
+
} else if (this.list && !parentTypeObject.array) {
|
|
1321
|
+
const dataListValues = [
|
|
1322
|
+
...this.list.options
|
|
1323
|
+
].map(({value}) => value);
|
|
1324
|
+
|
|
1325
|
+
if (
|
|
1326
|
+
!dataListValues.includes(this.value) &&
|
|
1327
|
+
/** @type {import('zodex').SzObject} */ (
|
|
1328
|
+
specificSchemaObject
|
|
1329
|
+
).unknownKeys === 'strict'
|
|
1330
|
+
) {
|
|
1331
|
+
this.setCustomValidity('Bad value');
|
|
1332
|
+
this.reportValidity();
|
|
1333
|
+
this.style.backgroundColor = 'pink';
|
|
1334
|
+
invalid = true;
|
|
1335
|
+
} else {
|
|
1336
|
+
this.style.backgroundColor = 'revert-layer';
|
|
1337
|
+
this.setCustomValidity('');
|
|
1338
|
+
this.reportValidity();
|
|
1339
|
+
const {optionalPropertyId} = this.dataset;
|
|
1340
|
+
let placeholder = /** @type {HTMLElement} */ (
|
|
1341
|
+
$e(
|
|
1342
|
+
arrayItems,
|
|
1343
|
+
`.optionalProperties-placeholder${optionalPropertyId}`
|
|
1344
|
+
)
|
|
1345
|
+
);
|
|
1346
|
+
if (!placeholder) {
|
|
1347
|
+
placeholder = /** @type {HTMLElement} */ ($e(
|
|
1348
|
+
/** @type {HTMLElement} */
|
|
1349
|
+
(this.parentElement?.parentElement),
|
|
1350
|
+
`.property-placeholder`
|
|
1351
|
+
)?.previousElementSibling);
|
|
1352
|
+
// Remove extra select element too
|
|
1353
|
+
placeholder.previousElementSibling?.remove();
|
|
1354
|
+
}
|
|
1355
|
+
placeholder.replaceWith(
|
|
1356
|
+
jml('div', {
|
|
1357
|
+
className:
|
|
1358
|
+
`optionalProperties-placeholder${optionalPropertyId}`
|
|
1359
|
+
}, [
|
|
1360
|
+
...buildTypeChoicesForProperty({
|
|
1361
|
+
propName: this.value,
|
|
1362
|
+
schema:
|
|
1363
|
+
/** @type {import('zodex').SzObject} */
|
|
1364
|
+
(specificSchemaObject)?.properties?.[this.value] ??
|
|
1365
|
+
/** @type {import('zodex').SzObject} */ (
|
|
1366
|
+
specificSchemaObject
|
|
1367
|
+
)?.catchall ??
|
|
1368
|
+
{type: 'any'}
|
|
1369
|
+
})
|
|
1370
|
+
])
|
|
1371
|
+
);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
if (invalid) {
|
|
1376
|
+
const placeholder = /** @type {HTMLElement} */ (
|
|
1377
|
+
$e(
|
|
1378
|
+
arrayItems,
|
|
1379
|
+
`.optionalProperties-placeholder${optionalPropertyId}`
|
|
1380
|
+
)
|
|
1381
|
+
);
|
|
1382
|
+
DOM.removeChildren(placeholder);
|
|
1383
|
+
} else {
|
|
1384
|
+
// Should this be awaited or awaited after stopPropagation?
|
|
1385
|
+
this.$validate();
|
|
1386
|
+
}
|
|
1063
1387
|
// We don't want form `onchange` to run
|
|
1064
1388
|
// `$checkForKeyDuplicates` again
|
|
1065
1389
|
e.stopPropagation();
|
|
@@ -1070,13 +1394,34 @@ const arrayType = {
|
|
|
1070
1394
|
}]
|
|
1071
1395
|
]]);
|
|
1072
1396
|
}
|
|
1397
|
+
|
|
1398
|
+
const fileDesc = /** @type {import('zodex').SzEffect} */ (
|
|
1399
|
+
specificSchemaObject
|
|
1400
|
+
)?.inner?.description;
|
|
1401
|
+
|
|
1073
1402
|
return /** @type {import('jamilih').JamilihArray} */ (['legend', [
|
|
1074
|
-
|
|
1403
|
+
elementDesc
|
|
1404
|
+
? ''
|
|
1405
|
+
: fileDesc
|
|
1406
|
+
? `${fileDesc} `
|
|
1407
|
+
: schema?.description
|
|
1408
|
+
? `${schema?.description} `
|
|
1409
|
+
: type === 'tuple' && /** @type {import('zodex').SzTuple} */ (
|
|
1410
|
+
specificSchemaObject
|
|
1411
|
+
)?.rest?.description
|
|
1412
|
+
? `${/** @type {import('zodex').SzTuple} */ (
|
|
1413
|
+
specificSchemaObject
|
|
1414
|
+
)?.rest?.description} `
|
|
1415
|
+
: 'Item ',
|
|
1075
1416
|
['span', {
|
|
1076
1417
|
dataset: {prop: true, array: true},
|
|
1077
1418
|
className
|
|
1078
1419
|
}, [
|
|
1079
|
-
|
|
1420
|
+
elementDesc
|
|
1421
|
+
? `${elementDesc} ${itemIndex + 1}`
|
|
1422
|
+
: propName !== undefined
|
|
1423
|
+
? propName
|
|
1424
|
+
: String(itemIndex)
|
|
1080
1425
|
]]
|
|
1081
1426
|
]]);
|
|
1082
1427
|
};
|
|
@@ -1104,26 +1449,155 @@ const arrayType = {
|
|
|
1104
1449
|
}
|
|
1105
1450
|
};
|
|
1106
1451
|
|
|
1452
|
+
/**
|
|
1453
|
+
* @param {{
|
|
1454
|
+
* propName: string|undefined,
|
|
1455
|
+
* schema?: import('zodex').SzType,
|
|
1456
|
+
* schemaIdx?: number,
|
|
1457
|
+
* autoTrigger?: boolean
|
|
1458
|
+
* }} cfg
|
|
1459
|
+
*/
|
|
1460
|
+
const buildTypeChoicesForProperty = ({
|
|
1461
|
+
propName, schema, schemaIdx, autoTrigger
|
|
1462
|
+
}) => {
|
|
1463
|
+
return /** @type {import('../typeChoices.js').BuildTypeChoices} */ (
|
|
1464
|
+
buildTypeChoices
|
|
1465
|
+
)({
|
|
1466
|
+
autoTrigger,
|
|
1467
|
+
// resultType,
|
|
1468
|
+
// eslint-disable-next-line object-shorthand -- TS
|
|
1469
|
+
topRoot: /** @type {HTMLDivElement} */ (topRoot),
|
|
1470
|
+
// eslint-disable-next-line object-shorthand -- TS
|
|
1471
|
+
format: /** @type {import('../formats.js').AvailableFormat} */ (format),
|
|
1472
|
+
schemaOriginal: schemaContent,
|
|
1473
|
+
schemaIdx,
|
|
1474
|
+
schemaContent: schema || type === 'tuple'
|
|
1475
|
+
? (schema ?? /** @type {import('zodex').SzTuple} */ (
|
|
1476
|
+
specificSchemaObject
|
|
1477
|
+
).rest)
|
|
1478
|
+
: mapProperties
|
|
1479
|
+
// Can also be a `Record`
|
|
1480
|
+
? /** @type {import('zodex').SzMap<any, any>} */ (
|
|
1481
|
+
specificSchemaObject
|
|
1482
|
+
)?.value
|
|
1483
|
+
: type === 'set'
|
|
1484
|
+
? /** @type {import('zodex').SzSet} */ (
|
|
1485
|
+
specificSchemaObject
|
|
1486
|
+
)?.value
|
|
1487
|
+
: type === 'filelist'
|
|
1488
|
+
? /** @type {import('zodex').SzEffect} */ (
|
|
1489
|
+
specificSchemaObject
|
|
1490
|
+
)?.inner
|
|
1491
|
+
: type === 'array' || type === 'arrayNonindexKeys'
|
|
1492
|
+
? /** @type {import('zodex').SzArray} */ (
|
|
1493
|
+
specificSchemaObject
|
|
1494
|
+
)?.element
|
|
1495
|
+
: /** @type {import('zodex').SzObject} */ (
|
|
1496
|
+
specificSchemaObject
|
|
1497
|
+
)?.properties?.[/** @type {string} */ (propName)],
|
|
1498
|
+
state: parentTypeObject.filelist
|
|
1499
|
+
? 'filelistArray'
|
|
1500
|
+
: forcedState ?? type,
|
|
1501
|
+
// itemIndex,
|
|
1502
|
+
typeNamespace
|
|
1503
|
+
}).domArray;
|
|
1504
|
+
};
|
|
1505
|
+
|
|
1506
|
+
/**
|
|
1507
|
+
* @param {Integer} [offset]
|
|
1508
|
+
* @returns {boolean}
|
|
1509
|
+
*/
|
|
1510
|
+
function preventAdding (offset = 0) {
|
|
1511
|
+
switch (type) {
|
|
1512
|
+
case 'tuple':
|
|
1513
|
+
if (/** @type {import('zodex').SzTuple} */ (
|
|
1514
|
+
specificSchemaObject
|
|
1515
|
+
)?.rest?.type === 'never') {
|
|
1516
|
+
dialogs.alert(
|
|
1517
|
+
'Tuple has rest type "never", so one cannot add to it.'
|
|
1518
|
+
);
|
|
1519
|
+
return true;
|
|
1520
|
+
}
|
|
1521
|
+
if (
|
|
1522
|
+
/** @type {import('zodex').SzTuple} */ (
|
|
1523
|
+
specificSchemaObject
|
|
1524
|
+
)?.items?.[0]?.type === 'never'
|
|
1525
|
+
) {
|
|
1526
|
+
dialogs.alert(
|
|
1527
|
+
'Tuple has items type "never", so one cannot add to it.'
|
|
1528
|
+
);
|
|
1529
|
+
return true;
|
|
1530
|
+
}
|
|
1531
|
+
break;
|
|
1532
|
+
case 'set': {
|
|
1533
|
+
if (/** @type {import('zodex').SzSet} */ (
|
|
1534
|
+
specificSchemaObject
|
|
1535
|
+
)?.value?.type === 'never') {
|
|
1536
|
+
dialogs.alert('Set has type "never", so one cannot add to it.');
|
|
1537
|
+
return true;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
const {maxSize} = /** @type {import('zodex').SzSet} */ (
|
|
1541
|
+
specificSchemaObject
|
|
1542
|
+
) ?? {};
|
|
1543
|
+
if (maxSize !== undefined &&
|
|
1544
|
+
[...arrayItems.children].length + offset > maxSize
|
|
1545
|
+
) {
|
|
1546
|
+
dialogs.alert(`You cannot add beyond the \`maxSize\` of the Set`);
|
|
1547
|
+
return true;
|
|
1548
|
+
}
|
|
1549
|
+
break;
|
|
1550
|
+
} case 'array': case 'arrayNonindexKeys': {
|
|
1551
|
+
if (/** @type {import('zodex').SzArray} */ (
|
|
1552
|
+
specificSchemaObject
|
|
1553
|
+
)?.element?.type === 'never') {
|
|
1554
|
+
dialogs.alert('Array has type "never", so one cannot add to it.');
|
|
1555
|
+
return true;
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
const {maxLength} = /** @type {import('zodex').SzArray} */ (
|
|
1559
|
+
specificSchemaObject
|
|
1560
|
+
) ?? {};
|
|
1561
|
+
|
|
1562
|
+
if (maxLength !== undefined &&
|
|
1563
|
+
arrayItems.$getArrayLength() + offset > maxLength
|
|
1564
|
+
) {
|
|
1565
|
+
dialogs.alert(`You cannot add beyond the \`maxLength\` of the array`);
|
|
1566
|
+
return true;
|
|
1567
|
+
}
|
|
1568
|
+
break;
|
|
1569
|
+
} default:
|
|
1570
|
+
break;
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
return false;
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1107
1576
|
/**
|
|
1108
1577
|
* @callback AddArrayElement
|
|
1109
1578
|
* @param {{
|
|
1110
1579
|
* propName?: string,
|
|
1111
1580
|
* splice?: "append"|number,
|
|
1112
1581
|
* alwaysFocus?: true
|
|
1582
|
+
* required?: true
|
|
1583
|
+
* autoTrigger?: boolean,
|
|
1584
|
+
* schema?: import('zodex').SzType,
|
|
1585
|
+
* schemaIdx?: number
|
|
1113
1586
|
* }} cfg
|
|
1114
1587
|
* @returns {void}
|
|
1115
1588
|
*/
|
|
1116
1589
|
|
|
1117
1590
|
/**
|
|
1118
1591
|
* @type {AddArrayElement}
|
|
1119
|
-
* @this {
|
|
1592
|
+
* @this {HTMLElement & {
|
|
1120
1593
|
* $getArrayItems: GetArrayItems,
|
|
1121
1594
|
* $addArrayElement: AddArrayElement,
|
|
1122
1595
|
* $getMapKeySelects?: GetMapKeySelects
|
|
1123
1596
|
* }}
|
|
1124
1597
|
*/
|
|
1125
|
-
const $addArrayElement = function ({
|
|
1126
|
-
|
|
1598
|
+
const $addArrayElement = function ({
|
|
1599
|
+
propName, splice, alwaysFocus, required, schema, schemaIdx, autoTrigger
|
|
1600
|
+
}) {
|
|
1127
1601
|
const arrayItems = this.$getArrayItems();
|
|
1128
1602
|
if (sparse) {
|
|
1129
1603
|
if (propName) {
|
|
@@ -1150,180 +1624,230 @@ const arrayType = {
|
|
|
1150
1624
|
} else {
|
|
1151
1625
|
itemIndex++;
|
|
1152
1626
|
}
|
|
1153
|
-
const thisButton = this; // eslint-disable-line consistent-this
|
|
1627
|
+
const thisButton = this; // eslint-disable-line consistent-this -- Clarity
|
|
1154
1628
|
const className = `${type}Item`;
|
|
1155
|
-
const fieldset =
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
)
|
|
1179
|
-
}
|
|
1180
|
-
})
|
|
1181
|
-
);
|
|
1182
|
-
// console.log('values', values);
|
|
1183
|
-
|
|
1184
|
-
const dupeIndex = values.findLastIndex((value, idx) => {
|
|
1185
|
-
return values.some((val, index) => {
|
|
1186
|
-
return idx !== index && value === val;
|
|
1187
|
-
});
|
|
1188
|
-
});
|
|
1189
|
-
|
|
1190
|
-
if (dupeIndex === -1) {
|
|
1191
|
-
return;
|
|
1192
|
-
}
|
|
1193
|
-
const fieldsets = arrayItems.children;
|
|
1194
|
-
const controls = [...fieldsets].map((fieldset) => {
|
|
1195
|
-
const root = /** @type {HTMLDivElement} */ (
|
|
1196
|
-
$e(fieldset, 'div[data-type]')
|
|
1197
|
-
);
|
|
1198
|
-
/* istanbul ignore if -- Should err first? */
|
|
1199
|
-
if (!root) {
|
|
1200
|
-
return null;
|
|
1201
|
-
}
|
|
1202
|
-
return types.getFormControlForRoot(root);
|
|
1203
|
-
});
|
|
1204
|
-
|
|
1205
|
-
const control = controls[dupeIndex];
|
|
1206
|
-
/* istanbul ignore if -- Should exist */
|
|
1207
|
-
if (!control) {
|
|
1208
|
-
return;
|
|
1629
|
+
const fieldset = jml('fieldset',
|
|
1630
|
+
{
|
|
1631
|
+
dataset: required ? {required: 'required'} : {},
|
|
1632
|
+
$on: {
|
|
1633
|
+
change: [() => {
|
|
1634
|
+
if (type !== 'set') {
|
|
1635
|
+
return;
|
|
1636
|
+
}
|
|
1637
|
+
setTimeout(() => {
|
|
1638
|
+
const root = div;
|
|
1639
|
+
const values = /** @type {any[]} */ (
|
|
1640
|
+
parentTypeObject.getValue.call({
|
|
1641
|
+
...parentTypeObject,
|
|
1642
|
+
set: false
|
|
1643
|
+
}, {
|
|
1644
|
+
root,
|
|
1645
|
+
stateObj: {
|
|
1646
|
+
types,
|
|
1647
|
+
// eslint-disable-next-line object-shorthand -- TS
|
|
1648
|
+
formats:
|
|
1649
|
+
/** @type {import('../formats.js').default} */ (
|
|
1650
|
+
formats
|
|
1651
|
+
)
|
|
1209
1652
|
}
|
|
1653
|
+
})
|
|
1654
|
+
);
|
|
1655
|
+
// console.log('values', values);
|
|
1210
1656
|
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
control.reportValidity();
|
|
1657
|
+
const dupeIndex = values.findLastIndex((value, idx) => {
|
|
1658
|
+
return values.some((val, index) => {
|
|
1659
|
+
return idx !== index && value === val;
|
|
1215
1660
|
});
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
).pop()
|
|
1661
|
+
});
|
|
1662
|
+
|
|
1663
|
+
if (dupeIndex === -1) {
|
|
1664
|
+
return;
|
|
1665
|
+
}
|
|
1666
|
+
const fieldsets = arrayItems.children;
|
|
1667
|
+
const controls = [...fieldsets].map((fieldset) => {
|
|
1668
|
+
const root = /** @type {HTMLDivElement} */ (
|
|
1669
|
+
$e(fieldset, 'div[data-type]')
|
|
1226
1670
|
);
|
|
1671
|
+
/* istanbul ignore if -- Should err first? */
|
|
1672
|
+
if (!root) {
|
|
1673
|
+
return null;
|
|
1674
|
+
}
|
|
1675
|
+
return types.getFormControlForRoot(root);
|
|
1676
|
+
});
|
|
1677
|
+
|
|
1678
|
+
const control = controls[dupeIndex];
|
|
1679
|
+
/* istanbul ignore if -- Should exist */
|
|
1680
|
+
if (!control) {
|
|
1681
|
+
return;
|
|
1227
1682
|
}
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
}
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1683
|
+
|
|
1684
|
+
control.setCustomValidity(
|
|
1685
|
+
'Duplicate Set value'
|
|
1686
|
+
);
|
|
1687
|
+
control.reportValidity();
|
|
1688
|
+
});
|
|
1689
|
+
}, true]
|
|
1690
|
+
},
|
|
1691
|
+
$custom: {
|
|
1692
|
+
/** @type {GetPropertyInput} */
|
|
1693
|
+
$getPropertyInput () {
|
|
1694
|
+
return /** @type {HTMLInputElement} */ (
|
|
1695
|
+
DOM.filterChildElements(
|
|
1696
|
+
/**
|
|
1697
|
+
* @type {HTMLFieldSetElement & {
|
|
1698
|
+
* $getPropertyInput: GetPropertyInput
|
|
1699
|
+
* }}
|
|
1700
|
+
*/ (this),
|
|
1701
|
+
['legend', `input.propertyName-${typeNamespace}`]
|
|
1702
|
+
).pop()
|
|
1703
|
+
);
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
},
|
|
1707
|
+
[
|
|
1708
|
+
buildLegend({
|
|
1709
|
+
className,
|
|
1710
|
+
splice,
|
|
1711
|
+
itemIndex,
|
|
1712
|
+
// type,
|
|
1713
|
+
typeNamespace,
|
|
1714
|
+
arrayItems,
|
|
1715
|
+
propName,
|
|
1716
|
+
required,
|
|
1717
|
+
schema
|
|
1718
|
+
})
|
|
1719
|
+
],
|
|
1720
|
+
arrayItems);
|
|
1721
|
+
|
|
1243
1722
|
// We must ensure fieldset is built before passing it
|
|
1244
1723
|
jml({'#': [
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
}
|
|
1724
|
+
['span', {
|
|
1725
|
+
class: 'mapValue',
|
|
1726
|
+
title: type === 'map' &&
|
|
1727
|
+
/** @type {import('zodex').SzMap<any, any>} */ (
|
|
1728
|
+
specificSchemaObject
|
|
1729
|
+
)?.value?.description
|
|
1730
|
+
? '(map value)'
|
|
1731
|
+
: type === 'record' &&
|
|
1732
|
+
/** @type {import('zodex').SzRecord} */ (
|
|
1733
|
+
specificSchemaObject
|
|
1734
|
+
)?.value?.description
|
|
1735
|
+
? '(record value)'
|
|
1736
|
+
: undefined
|
|
1737
|
+
}, [
|
|
1738
|
+
type === 'map'
|
|
1739
|
+
? /** @type {import('zodex').SzMap<any, any>} */ (
|
|
1740
|
+
specificSchemaObject
|
|
1741
|
+
)?.value?.description ?? 'Map value'
|
|
1742
|
+
: type === 'record'
|
|
1743
|
+
? /** @type {import('zodex').SzRecord} */ (
|
|
1744
|
+
specificSchemaObject
|
|
1745
|
+
)?.value?.description ?? 'Record value'
|
|
1746
|
+
: '',
|
|
1747
|
+
' '
|
|
1748
|
+
]],
|
|
1749
|
+
...(specificSchemaObject && !propName && !parentTypeObject.array
|
|
1750
|
+
? [jml('span', {
|
|
1751
|
+
className: `optionalProperties-placeholder${optionalPropertyId}`
|
|
1752
|
+
})]
|
|
1753
|
+
: buildTypeChoicesForProperty({
|
|
1754
|
+
propName, schema, schemaIdx, autoTrigger
|
|
1755
|
+
})),
|
|
1756
|
+
['span', {className: 'property-placeholder'}],
|
|
1259
1757
|
nbsp.repeat(2),
|
|
1260
|
-
['button', {
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1758
|
+
['button', {
|
|
1759
|
+
disabled: type === 'tuple' && required &&
|
|
1760
|
+
// The last item of a tuple can have content after it, but earlier
|
|
1761
|
+
// items cannot
|
|
1762
|
+
/** @type {import('zodex').SzTuple} */
|
|
1763
|
+
(specificSchemaObject)?.items?.length - 1 > itemIndex,
|
|
1764
|
+
$on: {click (/** @type {Event} */ e) {
|
|
1765
|
+
e.preventDefault();
|
|
1766
|
+
// e.stopPropagation();
|
|
1767
|
+
|
|
1768
|
+
if (preventAdding(1)) {
|
|
1769
|
+
return;
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
/** @type {number | "append" | undefined} */
|
|
1773
|
+
let splice;
|
|
1774
|
+
if (sparse) {
|
|
1775
|
+
const prevInputVal =
|
|
1271
1776
|
/**
|
|
1272
|
-
* @type {
|
|
1273
|
-
* $
|
|
1777
|
+
* @type {HTMLInputElement & {
|
|
1778
|
+
* $parseInt: ParseInt
|
|
1274
1779
|
* }}
|
|
1275
1780
|
*/ (
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
fieldset.after(newArrayFieldset);
|
|
1288
|
-
if (sparse) {
|
|
1289
|
-
const newPrevInput = /** @type {HTMLInputElement} */ (
|
|
1290
|
-
newArrayFieldset.$getPropertyInput()
|
|
1291
|
-
);
|
|
1292
|
-
bringFocus(newPrevInput, true);
|
|
1293
|
-
} else {
|
|
1294
|
-
DOM.filterChildElements(
|
|
1295
|
-
arrayItems, [
|
|
1296
|
-
'fieldset', 'legend', '.' + className
|
|
1297
|
-
]
|
|
1298
|
-
).forEach((span, i) => {
|
|
1299
|
-
span.textContent = String(i + itemAdjust);
|
|
1781
|
+
/**
|
|
1782
|
+
* @type {HTMLFieldSetElement & {
|
|
1783
|
+
* $getPropertyInput: GetPropertyInput
|
|
1784
|
+
* }}
|
|
1785
|
+
*/ (
|
|
1786
|
+
fieldset
|
|
1787
|
+
).$getPropertyInput()).$parseInt();
|
|
1788
|
+
splice = prevInputVal === false ? 'append' : prevInputVal;
|
|
1789
|
+
}
|
|
1790
|
+
thisButton.$addArrayElement({
|
|
1791
|
+
splice, alwaysFocus: true, schema
|
|
1300
1792
|
});
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1793
|
+
const newArrayFieldset =
|
|
1794
|
+
/** @type {Element & {$getPropertyInput: GetPropertyInput}} */ (
|
|
1795
|
+
arrayItems.lastElementChild
|
|
1796
|
+
);
|
|
1797
|
+
fieldset.after(newArrayFieldset);
|
|
1798
|
+
if (sparse || (
|
|
1799
|
+
// Because schemas (with descriptions?) don't use className for
|
|
1800
|
+
// property count (and the else block will rewrite the
|
|
1801
|
+
// property name)
|
|
1802
|
+
(specificSchemaObject || schema) &&
|
|
1803
|
+
!parentTypeObject.array
|
|
1804
|
+
)) {
|
|
1805
|
+
const newPrevInput = /** @type {HTMLInputElement} */ (
|
|
1806
|
+
newArrayFieldset.$getPropertyInput()
|
|
1807
|
+
);
|
|
1808
|
+
bringFocus(newPrevInput, true);
|
|
1809
|
+
} else {
|
|
1810
|
+
DOM.filterChildElements(
|
|
1811
|
+
arrayItems, [
|
|
1812
|
+
'fieldset', 'legend', '.' + className
|
|
1813
|
+
]
|
|
1814
|
+
).forEach((span, i) => {
|
|
1815
|
+
span.textContent = elementDesc
|
|
1816
|
+
? `${elementDesc} ${i + itemAdjust + 1}`
|
|
1817
|
+
: String(i + itemAdjust);
|
|
1818
|
+
});
|
|
1819
|
+
}
|
|
1820
|
+
// Maybe not needed as addition (without renumbering)
|
|
1821
|
+
// wouldn't yet add type
|
|
1822
|
+
types.validateAllReferences({
|
|
1823
|
+
// eslint-disable-next-line object-shorthand -- TS
|
|
1824
|
+
topRoot: /** @type {HTMLDivElement} */ (topRoot)
|
|
1825
|
+
});
|
|
1826
|
+
arrayItems.$redrawMoveArrows();
|
|
1827
|
+
}}
|
|
1828
|
+
}, ['+']],
|
|
1310
1829
|
/**
|
|
1311
1830
|
* @type {import('jamilih').JamilihArray}
|
|
1312
1831
|
*/
|
|
1313
1832
|
([
|
|
1314
1833
|
'button',
|
|
1315
1834
|
{
|
|
1835
|
+
disabled: required,
|
|
1316
1836
|
$on: {
|
|
1317
1837
|
click (/** @type {Event} */ e) {
|
|
1318
1838
|
e.preventDefault();
|
|
1319
1839
|
// e.stopPropagation();
|
|
1320
1840
|
fieldset.remove();
|
|
1321
1841
|
decrementItemIndex(arrayItems);
|
|
1322
|
-
if (!sparse
|
|
1842
|
+
if (!sparse &&
|
|
1843
|
+
(!specificSchemaObject || parentTypeObject.array)
|
|
1844
|
+
) {
|
|
1323
1845
|
DOM.filterChildElements(arrayItems, [
|
|
1324
1846
|
'fieldset', 'legend', '.' + className
|
|
1325
1847
|
]).forEach((span, i) => {
|
|
1326
|
-
span.textContent =
|
|
1848
|
+
span.textContent = elementDesc
|
|
1849
|
+
? `${elementDesc} ${i + itemAdjust + 1}`
|
|
1850
|
+
: String(i + itemAdjust);
|
|
1327
1851
|
});
|
|
1328
1852
|
}
|
|
1329
1853
|
// Maybe not needed as removal would remove circular
|
|
@@ -1346,6 +1870,7 @@ const arrayType = {
|
|
|
1346
1870
|
class: `${type}Item-arrowHolder-${typeNamespace}`
|
|
1347
1871
|
}, []]
|
|
1348
1872
|
]}, fieldset);
|
|
1873
|
+
|
|
1349
1874
|
// Need to validate if adding more than one property (in
|
|
1350
1875
|
// case two have empty string)
|
|
1351
1876
|
if (editableProperties) {
|
|
@@ -1430,6 +1955,12 @@ const arrayType = {
|
|
|
1430
1955
|
}
|
|
1431
1956
|
},
|
|
1432
1957
|
$on: {click () {
|
|
1958
|
+
if (preventAdding(1)) {
|
|
1959
|
+
return;
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
// Todo: Should really check if all object properties have been used,
|
|
1963
|
+
// and stop and warn if so
|
|
1433
1964
|
/**
|
|
1434
1965
|
* @type {HTMLButtonElement & {
|
|
1435
1966
|
* $addArrayElement: AddArrayElement,
|
|
@@ -1453,10 +1984,11 @@ const arrayType = {
|
|
|
1453
1984
|
'Array length: ',
|
|
1454
1985
|
['input', {
|
|
1455
1986
|
type: 'number',
|
|
1456
|
-
value: (
|
|
1987
|
+
value: (objectValue && objectValue.length) || 0,
|
|
1457
1988
|
step: 1,
|
|
1458
1989
|
size: 4,
|
|
1459
|
-
|
|
1990
|
+
class: 'arrayLength',
|
|
1991
|
+
pattern: String.raw`\d`,
|
|
1460
1992
|
$on: {
|
|
1461
1993
|
/**
|
|
1462
1994
|
* @this {HTMLInputElement & {
|
|
@@ -1465,6 +1997,11 @@ const arrayType = {
|
|
|
1465
1997
|
* @returns {Promise<void>}
|
|
1466
1998
|
*/
|
|
1467
1999
|
async change () {
|
|
2000
|
+
if (preventAdding()) {
|
|
2001
|
+
this.value = this.$oldvalue ?? this.defaultValue;
|
|
2002
|
+
return;
|
|
2003
|
+
}
|
|
2004
|
+
|
|
1468
2005
|
const arrayItems = $e(
|
|
1469
2006
|
/** @type {HTMLElement} */ (
|
|
1470
2007
|
this.closest('.arrayContents')
|
|
@@ -1488,16 +2025,54 @@ const arrayType = {
|
|
|
1488
2025
|
const fieldset = /** @type {HTMLFieldSetElement} */ (
|
|
1489
2026
|
input.closest('fieldset')
|
|
1490
2027
|
);
|
|
1491
|
-
fieldset.
|
|
2028
|
+
if (!fieldset.matches('[data-required]')) {
|
|
2029
|
+
fieldset.remove();
|
|
2030
|
+
} else {
|
|
2031
|
+
// If not truncating the array, put the length back
|
|
2032
|
+
const arrayLengthInput =
|
|
2033
|
+
/**
|
|
2034
|
+
* @type {HTMLInputElement & {
|
|
2035
|
+
* $oldvalue: string
|
|
2036
|
+
* }}
|
|
2037
|
+
*/ (
|
|
2038
|
+
$e(div, '.arrayLength')
|
|
2039
|
+
);
|
|
2040
|
+
arrayLengthInput.value = String(Number.parseInt(
|
|
2041
|
+
arrayLengthInput.value
|
|
2042
|
+
) + 1);
|
|
2043
|
+
}
|
|
1492
2044
|
});
|
|
1493
2045
|
// Maybe not needed as removal would remove circular
|
|
1494
2046
|
types.validateAllReferences({
|
|
1495
2047
|
// eslint-disable-next-line object-shorthand -- TS
|
|
1496
2048
|
topRoot: /** @type {HTMLDivElement} */ (topRoot)
|
|
1497
2049
|
});
|
|
2050
|
+
} else {
|
|
2051
|
+
const element = /** @type {import('zodex').SzArray} */ (
|
|
2052
|
+
specificSchemaObject
|
|
2053
|
+
)?.element;
|
|
2054
|
+
if (!['void', 'undefined'].includes(
|
|
2055
|
+
element?.type
|
|
2056
|
+
) && (
|
|
2057
|
+
element?.type !== 'union' ||
|
|
2058
|
+
/** @type {import('zodex').SzUnion} */
|
|
2059
|
+
(element)?.options?.every((option) => {
|
|
2060
|
+
return !['void', 'undefined'].includes(option.type);
|
|
2061
|
+
})
|
|
2062
|
+
)) {
|
|
2063
|
+
const diff = Number.parseInt(this.value) -
|
|
2064
|
+
Number.parseInt(this.$oldvalue ?? this.defaultValue);
|
|
2065
|
+
for (let i = 0; i < diff; i++) {
|
|
2066
|
+
// Timeout needed by Cypress at least or will get
|
|
2067
|
+
// validation triggered which prevents moving forward
|
|
2068
|
+
setTimeout(() => {
|
|
2069
|
+
div.$addArrayElement({});
|
|
2070
|
+
});
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
1498
2073
|
}
|
|
1499
2074
|
this.$oldvalue = this.value;
|
|
1500
|
-
} catch
|
|
2075
|
+
} catch {
|
|
1501
2076
|
this.value = this.$oldvalue; // Revert
|
|
1502
2077
|
}
|
|
1503
2078
|
}
|
|
@@ -1507,47 +2082,50 @@ const arrayType = {
|
|
|
1507
2082
|
]]
|
|
1508
2083
|
: '';
|
|
1509
2084
|
|
|
1510
|
-
const arrayItems =
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
2085
|
+
const arrayItems =
|
|
2086
|
+
/** @type {HTMLDivElement & {$getArrayLength: GetArrayLength}} */ (
|
|
2087
|
+
jml('div', {
|
|
2088
|
+
class: 'arrayItems',
|
|
2089
|
+
$custom: {
|
|
2090
|
+
$swapGroup, $redrawMoveArrows, $getArrayLength,
|
|
2091
|
+
$inputsExceedingLength,
|
|
1515
2092
|
|
|
1516
|
-
/**
|
|
1517
|
-
* Only relevant for maps.
|
|
1518
|
-
* @type {GetMapKeySelects}
|
|
1519
|
-
* @this {HTMLDivElement}
|
|
1520
|
-
*/
|
|
1521
|
-
$getMapKeySelects () {
|
|
1522
|
-
const selects =
|
|
1523
2093
|
/**
|
|
1524
|
-
*
|
|
1525
|
-
*
|
|
1526
|
-
*
|
|
1527
|
-
*/
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
2094
|
+
* Only relevant for maps.
|
|
2095
|
+
* @type {GetMapKeySelects}
|
|
2096
|
+
* @this {HTMLDivElement}
|
|
2097
|
+
*/
|
|
2098
|
+
$getMapKeySelects () {
|
|
2099
|
+
const selects =
|
|
2100
|
+
/**
|
|
2101
|
+
* @type {(HTMLSelectElement & {
|
|
2102
|
+
* $getValue: import('../typeChoices.js').GetValue}
|
|
2103
|
+
* )[]}
|
|
2104
|
+
*/ (DOM.filterChildElements(
|
|
2105
|
+
this,
|
|
2106
|
+
[
|
|
2107
|
+
'fieldset',
|
|
2108
|
+
'legend:first-child',
|
|
2109
|
+
'span.mapKeyHolder',
|
|
2110
|
+
'select.typeChoices-key-type-choices-only'
|
|
2111
|
+
]
|
|
2112
|
+
));
|
|
2113
|
+
return selects;
|
|
2114
|
+
},
|
|
1538
2115
|
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
2116
|
+
/**
|
|
2117
|
+
* @type {GetPropertyInputs}
|
|
2118
|
+
* @this {HTMLDivElement}
|
|
2119
|
+
*/
|
|
2120
|
+
$getPropertyInputs () {
|
|
2121
|
+
return /** @type {HTMLInputElement[]} */ (DOM.filterChildElements(
|
|
2122
|
+
this,
|
|
2123
|
+
['fieldset', 'legend', `input.propertyName-${typeNamespace}`]
|
|
2124
|
+
));
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
})
|
|
2128
|
+
);
|
|
1551
2129
|
|
|
1552
2130
|
const minusButton = ['button', {$on: {click (/** @type {Event} */ e) {
|
|
1553
2131
|
e.preventDefault();
|
|
@@ -1576,7 +2154,7 @@ const arrayType = {
|
|
|
1576
2154
|
$e(/** @type {HTMLElement} */ (arrayContents), '.arrayItems')
|
|
1577
2155
|
);
|
|
1578
2156
|
const lastElement = arrayItems.lastElementChild;
|
|
1579
|
-
if (lastElement) {
|
|
2157
|
+
if (lastElement && !lastElement.matches('[data-required]')) {
|
|
1580
2158
|
lastElement.remove();
|
|
1581
2159
|
decrementItemIndex(arrayItems);
|
|
1582
2160
|
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
@@ -1592,15 +2170,25 @@ const arrayType = {
|
|
|
1592
2170
|
}}}, ['- Last item']],
|
|
1593
2171
|
// We could only add this when there was more than one
|
|
1594
2172
|
['button', {$on: {click () {
|
|
2173
|
+
const arrayContents = /** @type {HTMLElement} */ (
|
|
2174
|
+
/** @type {HTMLElement} */ (this).closest('.arrayContents')
|
|
2175
|
+
);
|
|
1595
2176
|
const arrayItems =
|
|
1596
2177
|
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
1597
2178
|
/** @type {HTMLDivElement & {$getPropertyInputs: GetPropertyInputs}} */ ($e(
|
|
1598
|
-
|
|
1599
|
-
/** @type {HTMLElement} */ (this).closest('.arrayContents')
|
|
1600
|
-
),
|
|
2179
|
+
arrayContents,
|
|
1601
2180
|
'.arrayItems'
|
|
1602
2181
|
));
|
|
1603
|
-
|
|
2182
|
+
|
|
2183
|
+
const optionalFieldsetItems = $$e(
|
|
2184
|
+
arrayContents,
|
|
2185
|
+
'.arrayItems > fieldset:not([data-required])'
|
|
2186
|
+
);
|
|
2187
|
+
|
|
2188
|
+
for (const optionalFieldsetItem of optionalFieldsetItems) {
|
|
2189
|
+
optionalFieldsetItem.remove();
|
|
2190
|
+
}
|
|
2191
|
+
|
|
1604
2192
|
if (sparse) {
|
|
1605
2193
|
decrementItemIndex(arrayItems);
|
|
1606
2194
|
} else {
|
|
@@ -1617,159 +2205,279 @@ const arrayType = {
|
|
|
1617
2205
|
|
|
1618
2206
|
const parentType = type;
|
|
1619
2207
|
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
2208
|
+
const div =
|
|
2209
|
+
/**
|
|
2210
|
+
* @type {HTMLDivElement & {
|
|
2211
|
+
* $addAndSetArrayElement:
|
|
2212
|
+
* import('../formats/structuredCloning.js').AddAndSetArrayElement,
|
|
2213
|
+
* $addArrayElement: AddArrayElement
|
|
2214
|
+
* $getArrayItems: GetArrayItems,
|
|
2215
|
+
* $getMapKeySelects?: GetMapKeySelects
|
|
2216
|
+
* }}
|
|
2217
|
+
*/ (
|
|
2218
|
+
jml('div', /** @type {import('jamilih').JamilihAttributes} */ ({
|
|
2219
|
+
dataset: {type},
|
|
2220
|
+
// is: 'array-or-object-editor',
|
|
2221
|
+
$custom: {
|
|
2222
|
+
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
2223
|
+
/** @type {import('../formats/structuredCloning.js').AddAndSetArrayElement} */
|
|
2224
|
+
$addAndSetArrayElement ({
|
|
2225
|
+
propName, type, value, bringIntoFocus, setAValue,
|
|
2226
|
+
schemaContent: schema, schemaIdx, mustBeOptional
|
|
2227
|
+
}) {
|
|
2228
|
+
if (parentType === 'map') {
|
|
2229
|
+
if (propName === '0') {
|
|
2230
|
+
this.$addArrayElement({
|
|
2231
|
+
propName,
|
|
2232
|
+
// At least needed when value supplied
|
|
2233
|
+
autoTrigger: false
|
|
2234
|
+
});
|
|
2235
|
+
const arrayItems = this.$getArrayItems();
|
|
2236
|
+
const keyTypeChoices = /**
|
|
2237
|
+
* @type {HTMLSelectElement & {
|
|
2238
|
+
* $setType: import('../typeChoices.js').SetType,
|
|
2239
|
+
* $getTypeRoot: import('../formatAndTypeChoices.js').
|
|
2240
|
+
* TypeRootGetter
|
|
2241
|
+
* }}
|
|
2242
|
+
*/ (DOM.filterChildElements(
|
|
2243
|
+
arrayItems,
|
|
2244
|
+
[
|
|
2245
|
+
'fieldset:last-of-type', 'legend',
|
|
2246
|
+
'.mapKeyHolder', 'select'
|
|
2247
|
+
]
|
|
2248
|
+
)[0]);
|
|
2249
|
+
keyTypeChoices.$setType({
|
|
2250
|
+
type, baseValue: value, bringIntoFocus,
|
|
2251
|
+
avoidReport: true
|
|
2252
|
+
});
|
|
1649
2253
|
|
|
1650
|
-
|
|
1651
|
-
|
|
2254
|
+
// The key may itself be a map, etc.
|
|
2255
|
+
return keyTypeChoices.$getTypeRoot();
|
|
2256
|
+
}
|
|
2257
|
+
} else if (parentType === 'record') {
|
|
2258
|
+
this.$addArrayElement({
|
|
2259
|
+
propName, autoTrigger: false,
|
|
2260
|
+
required: false
|
|
2261
|
+
});
|
|
2262
|
+
} else {
|
|
2263
|
+
// console.log('SCHEMA123', schema);
|
|
2264
|
+
this.$addArrayElement({
|
|
2265
|
+
propName, schema, autoTrigger: false,
|
|
2266
|
+
schemaIdx,
|
|
2267
|
+
required: !mustBeOptional && schema && !schema.isOptional &&
|
|
2268
|
+
schema.type !== 'never'
|
|
2269
|
+
});
|
|
1652
2270
|
}
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
typeObj.setValue
|
|
2271
|
+
const typeChoices = this.$getTypeChoices();
|
|
2272
|
+
typeChoices.$setType({
|
|
2273
|
+
type, baseValue: value, bringIntoFocus,
|
|
2274
|
+
avoidReport: true
|
|
2275
|
+
});
|
|
2276
|
+
const root = typeChoices.$getTypeRoot();
|
|
2277
|
+
// If run for all, causes problems with running `Error.cause`
|
|
2278
|
+
// type twice and is inefficient;
|
|
2279
|
+
// currently put behind `setAValue` as we need to set a value
|
|
2280
|
+
// from `errorsSpecialType` (and `filelistType`)
|
|
2281
|
+
if (setAValue) {
|
|
2282
|
+
const typeObj =
|
|
2283
|
+
/** @type {import('../types.js').TypeObject} */ (
|
|
2284
|
+
types.getTypeObject(type)
|
|
2285
|
+
);
|
|
2286
|
+
if (typeObj.setValue) {
|
|
2287
|
+
typeObj.setValue({root, value});
|
|
2288
|
+
}
|
|
1669
2289
|
}
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
2290
|
+
types.validate({
|
|
2291
|
+
type, root, topRoot,
|
|
2292
|
+
// We don't want focus when values auto-added
|
|
2293
|
+
avoidReport: true
|
|
2294
|
+
});
|
|
2295
|
+
return root;
|
|
2296
|
+
},
|
|
1674
2297
|
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
2298
|
+
/**
|
|
2299
|
+
* @typedef {() => Element} GetAddArrayElement
|
|
2300
|
+
*/
|
|
1678
2301
|
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
/**
|
|
1686
|
-
* @type {AddArrayElement}
|
|
1687
|
-
* @this {HTMLDivElement & {
|
|
1688
|
-
* $getAddArrayElement: GetAddArrayElement
|
|
1689
|
-
* }}
|
|
1690
|
-
*/
|
|
1691
|
-
// @ts-ignore TS is apparently getting wrong $addArrayElement
|
|
1692
|
-
$addArrayElement ({propName, splice, alwaysFocus}) {
|
|
1693
|
-
const addArrayElement = this.$getAddArrayElement();
|
|
2302
|
+
/** @type {GetAddArrayElement} */
|
|
2303
|
+
$getAddArrayElement () {
|
|
2304
|
+
const el = this.
|
|
2305
|
+
lastElementChild.firstElementChild.nextElementSibling;
|
|
2306
|
+
return sparse ? el.nextElementSibling : el;
|
|
2307
|
+
},
|
|
1694
2308
|
/**
|
|
1695
|
-
* @type {
|
|
1696
|
-
*
|
|
1697
|
-
* $
|
|
2309
|
+
* @type {AddArrayElement}
|
|
2310
|
+
* @this {HTMLDivElement & {
|
|
2311
|
+
* $getAddArrayElement: GetAddArrayElement
|
|
1698
2312
|
* }}
|
|
1699
2313
|
*/
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
2314
|
+
// @ts-expect-error TS is apparently getting wrong $addArrayElement
|
|
2315
|
+
$addArrayElement ({
|
|
2316
|
+
propName, splice, alwaysFocus, required, schema, schemaIdx,
|
|
2317
|
+
autoTrigger
|
|
2318
|
+
}) {
|
|
2319
|
+
const addArrayElement = this.$getAddArrayElement();
|
|
2320
|
+
/**
|
|
2321
|
+
* @type {HTMLButtonElement & {
|
|
2322
|
+
* $addArrayElement: AddArrayElement,
|
|
2323
|
+
* $getArrayItems: GetArrayItems
|
|
2324
|
+
* }}
|
|
2325
|
+
*/
|
|
2326
|
+
(addArrayElement).$addArrayElement({
|
|
2327
|
+
propName, splice, alwaysFocus, required, schema,
|
|
2328
|
+
schemaIdx,
|
|
2329
|
+
autoTrigger
|
|
2330
|
+
});
|
|
2331
|
+
},
|
|
2332
|
+
$getArrayItems () {
|
|
2333
|
+
const addArrayElement = this.$getAddArrayElement();
|
|
2334
|
+
return addArrayElement.previousElementSibling;
|
|
2335
|
+
},
|
|
2336
|
+
$getTypeChoices () {
|
|
2337
|
+
const arrayItems = this.$getArrayItems();
|
|
2338
|
+
return $e(
|
|
2339
|
+
arrayItems.lastElementChild,
|
|
2340
|
+
`fieldset > .typeChoices-${typeNamespace}` // Avoid keys
|
|
2341
|
+
);
|
|
2342
|
+
}
|
|
1705
2343
|
},
|
|
1706
|
-
$
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
if (![
|
|
1725
|
-
'checkbox', 'radio', 'file',
|
|
1726
|
-
'datetime-local',
|
|
1727
|
-
// For label radio clicks
|
|
1728
|
-
undefined
|
|
1729
|
-
].includes(target.type)) {
|
|
1730
|
-
e.preventDefault();
|
|
2344
|
+
$on: {
|
|
2345
|
+
click (ev) {
|
|
2346
|
+
const e = /** @type {Event} */ (ev);
|
|
2347
|
+
// eslint-disable-next-line prefer-destructuring -- TS
|
|
2348
|
+
const target = /** @type {HTMLInputElement} */ (e.target);
|
|
2349
|
+
|
|
2350
|
+
// We needed to stop preventing the default for the
|
|
2351
|
+
// invalid date checkbox; is this sufficient to prevent
|
|
2352
|
+
// other stray clicks apparently meant for the array
|
|
2353
|
+
// and object reference checking?
|
|
2354
|
+
if (![
|
|
2355
|
+
'checkbox', 'radio', 'file',
|
|
2356
|
+
'datetime-local',
|
|
2357
|
+
// For label radio clicks
|
|
2358
|
+
undefined
|
|
2359
|
+
].includes(target.type)) {
|
|
2360
|
+
e.preventDefault();
|
|
2361
|
+
}
|
|
1731
2362
|
}
|
|
1732
2363
|
}
|
|
1733
|
-
}
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
2364
|
+
}), /** @type {import('jamilih').JamilihChildren} */ ([
|
|
2365
|
+
[specificSchemaObject ? 'span' : 'b', {
|
|
2366
|
+
title: specificSchemaObject?.description ?? (DOM.initialCaps(
|
|
2367
|
+
/** @type {import('../types.js').AvailableType} */
|
|
2368
|
+
(type)
|
|
2369
|
+
).replace(/s$/u, ''))
|
|
2370
|
+
}, [
|
|
2371
|
+
specificSchemaObject
|
|
2372
|
+
? '—'
|
|
2373
|
+
: DOM.initialCaps(
|
|
2374
|
+
/** @type {import('../types.js').AvailableType} */
|
|
2375
|
+
(type)
|
|
2376
|
+
).replace(/s$/u, '')
|
|
2377
|
+
]],
|
|
2378
|
+
nbsp.repeat(2),
|
|
2379
|
+
type === 'filelist'
|
|
2380
|
+
? ['input', {
|
|
2381
|
+
name: typeNamespace + '-filelist',
|
|
2382
|
+
multiple: true,
|
|
2383
|
+
type: 'file',
|
|
2384
|
+
$on: {
|
|
2385
|
+
/**
|
|
2386
|
+
* @this {HTMLInputElement}
|
|
2387
|
+
*/
|
|
2388
|
+
change () {
|
|
2389
|
+
/* istanbul ignore if */
|
|
2390
|
+
if (!this.files) {
|
|
2391
|
+
return;
|
|
2392
|
+
}
|
|
2393
|
+
for (let i = 0; i < this.files.length; i++) {
|
|
2394
|
+
const file = this.files.item(i);
|
|
2395
|
+
|
|
2396
|
+
div.$addAndSetArrayElement({
|
|
2397
|
+
propName: String(i),
|
|
2398
|
+
type: 'file',
|
|
2399
|
+
value: file,
|
|
2400
|
+
bringIntoFocus: false,
|
|
2401
|
+
setAValue: true
|
|
2402
|
+
});
|
|
2403
|
+
}
|
|
1763
2404
|
}
|
|
1764
2405
|
}
|
|
1765
|
-
}
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
);
|
|
2406
|
+
}]
|
|
2407
|
+
: '',
|
|
2408
|
+
minusButton,
|
|
2409
|
+
arrayContents
|
|
2410
|
+
]))
|
|
2411
|
+
);
|
|
1772
2412
|
topRoot = topRoot || div;
|
|
2413
|
+
|
|
2414
|
+
if (!objectValue && specificSchemaObject) {
|
|
2415
|
+
switch (type) {
|
|
2416
|
+
case 'object': {
|
|
2417
|
+
// See comment referencing `arrayType.js` in `typeChoices.js`
|
|
2418
|
+
if (!schemaFallingBack) {
|
|
2419
|
+
for (const [prop, val] of
|
|
2420
|
+
Object.entries(
|
|
2421
|
+
/** @type {import('zodex').SzObject} */ (
|
|
2422
|
+
specificSchemaObject
|
|
2423
|
+
).properties ?? {}
|
|
2424
|
+
)
|
|
2425
|
+
) {
|
|
2426
|
+
if (!val.isOptional && val.type !== 'never') {
|
|
2427
|
+
div.$addArrayElement({propName: prop, required: true});
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
break;
|
|
2432
|
+
}
|
|
2433
|
+
case 'tuple': {
|
|
2434
|
+
// See comment referencing `arrayType.js` in `typeChoices.js`
|
|
2435
|
+
if (!schemaFallingBack) {
|
|
2436
|
+
const specificSchemaObj = /** @type {import('zodex').SzTuple} */ (
|
|
2437
|
+
specificSchemaObject
|
|
2438
|
+
);
|
|
2439
|
+
if (
|
|
2440
|
+
/** @type {import('zodex').SzTuple} */ (
|
|
2441
|
+
specificSchemaObject
|
|
2442
|
+
)?.items?.[0]?.type !== 'never'
|
|
2443
|
+
) {
|
|
2444
|
+
for (const schema of specificSchemaObj.items) {
|
|
2445
|
+
div.$addArrayElement({schema, required: true});
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
break;
|
|
2450
|
+
} case 'array': case 'arrayNonindexKeys': {
|
|
2451
|
+
const {minLength = 0} = /** @type {import('zodex').SzArray} */ (
|
|
2452
|
+
specificSchemaObject
|
|
2453
|
+
) ?? {};
|
|
2454
|
+
const arrayLengthInput =
|
|
2455
|
+
/** @type {HTMLInputElement & {$oldvalue: string}} */ (
|
|
2456
|
+
$e(div, '.arrayLength')
|
|
2457
|
+
);
|
|
2458
|
+
arrayLengthInput.value = String(minLength);
|
|
2459
|
+
arrayLengthInput.$oldvalue = String(minLength);
|
|
2460
|
+
|
|
2461
|
+
for (let i = 0; i < minLength; i++) {
|
|
2462
|
+
div.$addArrayElement({required: true});
|
|
2463
|
+
}
|
|
2464
|
+
break;
|
|
2465
|
+
} case 'set': {
|
|
2466
|
+
// See comment referencing `arrayType.js` in `typeChoices.js`
|
|
2467
|
+
if (!schemaFallingBack) {
|
|
2468
|
+
const {minSize = 0} = /** @type {import('zodex').SzSet} */ (
|
|
2469
|
+
specificSchemaObject
|
|
2470
|
+
) ?? {};
|
|
2471
|
+
for (let i = 0; i < minSize; i++) {
|
|
2472
|
+
div.$addArrayElement({required: true});
|
|
2473
|
+
}
|
|
2474
|
+
}
|
|
2475
|
+
break;
|
|
2476
|
+
} default:
|
|
2477
|
+
break;
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
|
|
1773
2481
|
return [div];
|
|
1774
2482
|
}
|
|
1775
2483
|
};
|