@es-joy/jsoe 0.0.1

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.
Files changed (43) hide show
  1. package/.editorconfig +15 -0
  2. package/.eslintignore +7 -0
  3. package/.eslintrc.cjs +120 -0
  4. package/CHANGES.md +5 -0
  5. package/LICENSE-MIT.txt +22 -0
  6. package/README.md +82 -0
  7. package/package.json +101 -0
  8. package/rollup.config.js +29 -0
  9. package/server.js +25 -0
  10. package/src/deepEqual.js +72 -0
  11. package/src/formats/indexedDBKey.js +66 -0
  12. package/src/formats/json.js +64 -0
  13. package/src/formats/schemaAndArbitrary.js +20 -0
  14. package/src/formats/schemaOnly.js +529 -0
  15. package/src/formats/structuredCloning.js +372 -0
  16. package/src/formats.js +229 -0
  17. package/src/fundamentalTypes/BooleanObjectType.js +55 -0
  18. package/src/fundamentalTypes/NumberObjectType.js +41 -0
  19. package/src/fundamentalTypes/StringObjectType.js +37 -0
  20. package/src/fundamentalTypes/arrayReferenceType.js +203 -0
  21. package/src/fundamentalTypes/arrayType.js +898 -0
  22. package/src/fundamentalTypes/bigintType.js +53 -0
  23. package/src/fundamentalTypes/dateType.js +129 -0
  24. package/src/fundamentalTypes/nullType.js +33 -0
  25. package/src/fundamentalTypes/numberType.js +56 -0
  26. package/src/fundamentalTypes/objectReferenceType.js +51 -0
  27. package/src/fundamentalTypes/objectType.js +30 -0
  28. package/src/fundamentalTypes/regexpType.js +95 -0
  29. package/src/fundamentalTypes/sparseUndefinedType.js +43 -0
  30. package/src/fundamentalTypes/stringType.js +31 -0
  31. package/src/fundamentalTypes/undefinedType.js +37 -0
  32. package/src/index.js +7 -0
  33. package/src/subTypes/blobHTMLType.js +160 -0
  34. package/src/subTypes/falseType.js +42 -0
  35. package/src/subTypes/trueType.js +42 -0
  36. package/src/superTypes/InfinitiesSuperType.js +49 -0
  37. package/src/superTypes/SpecialNumberSuperType.js +62 -0
  38. package/src/typeChoices.js +125 -0
  39. package/src/types.js +665 -0
  40. package/src/utils/dialogs.js +115 -0
  41. package/src/utils/jsonPointer.js +89 -0
  42. package/src/utils/templateUtils.js +106 -0
  43. package/src/utils/types.js +6 -0
@@ -0,0 +1,372 @@
1
+ import Formats from '../formats.js';
2
+ import Types from '../types.js';
3
+ import {
4
+ Typeson, unescapeKeyPathComponent, structuredCloningThrowing
5
+ } from '../../node_modules/typeson-registry/dist/index.js';
6
+ import {
7
+ typesonPathToJSONPointer
8
+ } from '../utils/jsonPointer.js';
9
+
10
+ import * as json from './json.js';
11
+
12
+ /**
13
+ * @callback EncapsulateObserver
14
+ * @param {TypesonObserver} observerObj
15
+ * @returns {void}
16
+ */
17
+
18
+ /**
19
+ * @typedef {{
20
+ * typeNamespace: string,
21
+ * "readonly": boolean,
22
+ * format: string,
23
+ * schemaContent: string,
24
+ * getPossibleSchemasForPathAndType: (
25
+ * keypath: string,
26
+ * parentPath: string,
27
+ * arrayOrObjectPropertyName: string,
28
+ * valueType: string
29
+ * ) => StateObject
30
+ * }} StateObject
31
+ */
32
+
33
+ /**
34
+ * @param {StateObject} stateObj
35
+ * @returns {EncapsulateObserver}
36
+ */
37
+ const encapsulateObserver = (stateObj) => {
38
+ const {
39
+ typeNamespace, readonly, format, schemaContent,
40
+ getPossibleSchemasForPathAndType
41
+ } = stateObj;
42
+ const parents = {};
43
+ return (observerObj) => {
44
+ const {
45
+ type,
46
+ cyclic,
47
+ keypath,
48
+ value,
49
+ replaced,
50
+ cyclicKeypath,
51
+ endIterateIn,
52
+ endIterateOwn,
53
+ endIterateUnsetNumeric,
54
+ clone
55
+ } = observerObj;
56
+ // console.log('observerObj', observerObj);
57
+ if ('replaced' in observerObj) {
58
+ return;
59
+ }
60
+ if (cyclic === 'readonly' && !Array.isArray(observerObj.value)) {
61
+ return;
62
+ }
63
+ if (endIterateIn || endIterateOwn) {
64
+ return;
65
+ }
66
+ if (endIterateUnsetNumeric || (
67
+ clone === undefined && cyclicKeypath === undefined && Array.isArray(value)
68
+ )) {
69
+ return;
70
+ }
71
+
72
+ /* istanbul ignore if -- Not part of format */
73
+ if (type === 'sparseUndefined') { // We'll handle otherwise
74
+ return;
75
+ }
76
+
77
+ // console.log('observerObj', observerObj);
78
+
79
+ let newType;
80
+ let newValue = value;
81
+
82
+ /* schema: || format.startsWith('schema-') */
83
+ const state = format === 'structuredCloning'
84
+ ? 'arrayNonindexKeys'
85
+ // ? 'sparseArrays'
86
+ : 'array';
87
+ if (typeof cyclicKeypath === 'string') {
88
+ newValue = typesonPathToJSONPointer(cyclicKeypath);
89
+ newType = type === 'array' ? 'arrayReference' : 'objectReference';
90
+ newType = canonicalToAvailableType(
91
+ format, state, newType, value
92
+ ); // Todo (low): Add accurate state for second argument
93
+ } else {
94
+ try {
95
+ newType = canonicalToAvailableType(
96
+ format, state, type, value
97
+ ); // Todo (low): Add state for second argument
98
+ } catch (err) {
99
+ console.log('err', type, err);
100
+ stateObj.error = err;
101
+ return;
102
+ }
103
+ }
104
+
105
+ const li = keypath.lastIndexOf('.');
106
+ const arrayOrObjectPropertyName =
107
+ unescapeKeyPathComponent(keypath.slice(li + 1));
108
+ const parentPath = li === -1 ? '' : keypath.slice(0, li);
109
+
110
+ const hasChildren = [
111
+ 'array', 'object',
112
+ // 'sparseArrays',
113
+ 'arrayNonindexKeys'
114
+ ].includes(newType);
115
+
116
+ if (!stateObj.rootUI) {
117
+ stateObj.rootUI = Types.getUIForModeAndType({
118
+ readonly,
119
+ typeNamespace,
120
+ type: newType,
121
+ bringIntoFocus: false,
122
+ buildTypeChoices: Formats.buildTypeChoices,
123
+ format,
124
+ schemaContent,
125
+ schemaState: getPossibleSchemasForPathAndType,
126
+ /* schema:
127
+ &&
128
+ getPossibleSchemasForPathAndType({
129
+ keypath,
130
+ parentPath: '',
131
+ valueType: newType
132
+ }),
133
+ */
134
+ value: newValue,
135
+ hasValue: true,
136
+ // Not currently in use but may be convenient for a
137
+ // type wanting the serialized data
138
+ replaced
139
+ });
140
+ parents[''] = stateObj.rootUI;
141
+ return;
142
+ }
143
+
144
+ // Todo (low): If could be async, use async encapsulate method
145
+ // Todo (low): Handle `awaitingTypesonPromise` with place-holder
146
+ // Todo (low): Handle `resolvingTypesonPromise` to replace place-holder
147
+ setTimeout(() => {
148
+ const ui = parents[parentPath];
149
+ // These errors occur, e.g., if `replacing` not first added and then
150
+ // a converted object gets treated as the root UI (e.g., for `regexp`
151
+ // or `blobHTML` at root)
152
+ // If there isn't a problem in Typeson with transmitting the `readonly`
153
+ // status recursively down the object (should be no need to check
154
+ // for circulars there?), could change Typeson to report `readonly`
155
+ // for the nested items, in which case, we could block out `readonly`
156
+ // instead of doing this here
157
+ if (!ui || !ui.$addAndSetArrayElement) {
158
+ return;
159
+ }
160
+ const root = ui.$addAndSetArrayElement({
161
+ propName: arrayOrObjectPropertyName,
162
+ type: newType,
163
+ value: newValue,
164
+ bringIntoFocus: false,
165
+ schemaContent,
166
+ schemaState: getPossibleSchemasForPathAndType
167
+ /* schema:
168
+ && getPossibleSchemasForPathAndType({
169
+ keypath,
170
+ parentPath,
171
+ arrayOrObjectPropertyName,
172
+ valueType: newType
173
+ })
174
+ */
175
+ });
176
+ if (!readonly) {
177
+ Types.setValue({type: newType, root, value: newValue});
178
+ Types.validate({type: newType, root, topRoot: stateObj.rootUI});
179
+ }
180
+
181
+ if (hasChildren) {
182
+ parents[keypath] = root;
183
+ }
184
+ });
185
+ };
186
+ };
187
+
188
+ /**
189
+ * @param {string[]} originTypes
190
+ * @param {[originType: string, replacementType: string][]} replacements
191
+ * @returns {void}
192
+ */
193
+ const replaceTypes = (originTypes, replacements) => {
194
+ replacements.forEach(([originType, replacementType]) => {
195
+ originTypes.splice(originTypes.indexOf(originType), 1, replacementType);
196
+ });
197
+ };
198
+
199
+ /**
200
+ * @param {string} format
201
+ * @param {string} state
202
+ * @param {string} valType
203
+ * @param {ArbitraryValue} v
204
+ * @throws {Error}
205
+ * @returns {string}
206
+ */
207
+ const canonicalToAvailableType = (format, state, valType, v) => {
208
+ const frmt = Formats.availableFormats[format];
209
+ const {getTypesForState, convertFromTypeson, testInvalid} = frmt;
210
+ const allowableTypes = getTypesForState.call(frmt, state);
211
+ let ret;
212
+ console.log('format, state, valType, v', format, state, valType, v);
213
+ const isInvalid = (newValType) => {
214
+ console.log('newValType', newValType);
215
+ const err = new Error('Invalid');
216
+ err.newValType = newValType;
217
+ throw err;
218
+ };
219
+ if (convertFromTypeson) {
220
+ const newValType = convertFromTypeson(valType);
221
+ if (typeof newValType === 'string') {
222
+ if (testInvalid && testInvalid(newValType, v)) {
223
+ return isInvalid(newValType);
224
+ }
225
+ valType = newValType;
226
+ }
227
+ }
228
+ if (allowableTypes.some((allowableType) => {
229
+ if (allowableType === valType) {
230
+ ret = allowableType;
231
+ return true;
232
+ }
233
+ return false;
234
+ })) {
235
+ return ret;
236
+ }
237
+ console.log('ret', ret);
238
+ allowableTypes.some((allowableType) => {
239
+ const {
240
+ valueMatch, superType, childTypes
241
+ } = Types.availableTypes[allowableType];
242
+ if (
243
+ (superType && valueMatch &&
244
+ // Currently using for `true` and `false`
245
+ superType === valType && valueMatch(v)) ||
246
+ (childTypes && childTypes.includes(valType))
247
+ ) {
248
+ ret = allowableType;
249
+ return true;
250
+ }
251
+ return false;
252
+ });
253
+ console.log('ret2', ret);
254
+ if (ret === undefined) {
255
+ return isInvalid(valType);
256
+ }
257
+ return ret;
258
+ };
259
+
260
+ /**
261
+ * @callback FormatIterator
262
+ * @param {StructuredCloneValue} records
263
+ * @param {{
264
+ * format: string
265
+ * error: Error
266
+ * rootUI: Element
267
+ * }} stateObj
268
+ * @returns {Promise<Element>}
269
+ */
270
+
271
+ /**
272
+ * @type {FormatIterator}
273
+ */
274
+ export const iterate = (records, stateObj) => {
275
+ console.log('records', records);
276
+ if (!stateObj.format) {
277
+ stateObj.format = 'structuredCloning';
278
+ }
279
+ // Todo: Replace this with async typeson?
280
+ // eslint-disable-next-line promise/avoid-new
281
+ return new Promise((resolve, reject) => {
282
+ const typeson = new Typeson({
283
+ encapsulateObserver: encapsulateObserver(stateObj)
284
+ }).register(structuredCloningThrowing);
285
+ typeson.encapsulate(records);
286
+ // Todo (low): We might want to run async encapsulate for
287
+ // async types (and put this after Promise resolves)
288
+ if (stateObj.error) {
289
+ reject(stateObj.error);
290
+ } else {
291
+ resolve(stateObj.rootUI);
292
+ }
293
+ });
294
+ };
295
+
296
+ /**
297
+ * @param {string} state
298
+ * @returns {string[]}
299
+ */
300
+ export const getTypesForState = function (state) {
301
+ if (state && Types.contexts.structuredCloning[state]) {
302
+ const typesForFormat = this.getTypesForState() ||
303
+ /* istanbul ignore next -- types should be an array */
304
+ [];
305
+ Types.contexts.structuredCloning[state].forEach(({type, after}) => {
306
+ const precedingIdx = typesForFormat.indexOf(after);
307
+ typesForFormat.splice(precedingIdx + 1, 0, type);
308
+ });
309
+ return typesForFormat;
310
+ }
311
+ return this.types();
312
+ /*
313
+ // Todo (low): These need to specify their own inner contexts
314
+ if (['map', 'set'].includes(state)) {return;}
315
+ if ('int8array', 'uint8array', 'uint8clampedarray',
316
+ 'int16array', 'uint16array', 'int32array',
317
+ 'uint32array', 'float32array', 'float64array'
318
+ ).includes(state)) {return;}
319
+ */
320
+ };
321
+
322
+ /**
323
+ * @returns {string[]}
324
+ */
325
+ export const types = () => {
326
+ const jsonTypes = json.types();
327
+ replaceTypes(jsonTypes, [
328
+ [
329
+ 'array',
330
+ // 'sparseArrays',
331
+ 'arrayNonindexKeys'
332
+ ]
333
+ ]);
334
+ return [
335
+ // This type is only for throwing upon cloning errors:
336
+ // 'checkDataCloneException'
337
+ // This type might be supported by evaluable JS or config passed in:
338
+ // 'userObject'
339
+ ...jsonTypes,
340
+ 'undef', // Explicit undefined only
341
+ 'bigint',
342
+ 'SpecialNumber', // '`NaN`, `Infinity`, `-Infinity`'},
343
+ 'date',
344
+ 'regexp',
345
+ 'BooleanObject',
346
+ 'NumberObject',
347
+ 'StringObject',
348
+ 'blobHTML'
349
+ // Ok, but will need some work
350
+ // 'map', 'set',
351
+ // 'blob', 'file', 'filelist'
352
+ // 'arraybuffer', 'arraybufferview'
353
+ // 'dataview', 'imagedata', 'imagebitmap',
354
+ /*
355
+ // Typed Arrays
356
+ 'int8array',
357
+ 'uint8array',
358
+ 'uint8clampedarray',
359
+ 'int16array',
360
+ 'uint16array',
361
+ 'int32array',
362
+ 'uint32array',
363
+ 'float32array',
364
+ 'float64array',
365
+
366
+ // Intl (imperfect)
367
+ 'IntlCollator',
368
+ 'IntlDateTimeFormat',
369
+ 'IntlNumberFormat'
370
+ */
371
+ ];
372
+ };
package/src/formats.js ADDED
@@ -0,0 +1,229 @@
1
+ import Types from './types.js';
2
+ import {jml} from '../node_modules/jamilih/dist/jml-es.js';
3
+
4
+ import {$e, DOM} from './utils/templateUtils.js';
5
+ import dialogs from './utils/dialogs.js';
6
+
7
+ import * as indexedDBKey from './formats/indexedDBKey.js';
8
+ import * as json from './formats/json.js';
9
+ import * as structuredCloning from './formats/structuredCloning.js';
10
+
11
+ // Using methods ensure we have fresh copies
12
+ const Formats = {
13
+ availableFormats: {
14
+ indexedDBKey,
15
+ json,
16
+ // Todo (readme): these too? getTypesForState(state)
17
+ /* schema:
18
+ schemaAndArbitrary,
19
+ schemaOnly,
20
+ */
21
+ structuredCloning
22
+ }
23
+ };
24
+
25
+ /**
26
+ * An arbitrary Structured Clone, JSON, etc. value.
27
+ * @typedef {any} StructuredCloneValue
28
+ */
29
+
30
+ /**
31
+ * @callback BuildTypeChoices
32
+ * @param {object} cfg
33
+ * @param {string} cfg.format
34
+ * @param {string} cfg.typeNamespace
35
+ * @param {StructuredCloneValue} cfg.value
36
+ * @param {boolean} [cfg.setValue=false]
37
+ * @param {string} cfg.state
38
+ * @param {string} cfg.keySelectClass
39
+ * @param {boolean} cfg.requireObject
40
+ * @param {boolean} cfg.objectHasValue
41
+ * @param {RootElement} cfg.topRoot Always a `div` element?
42
+ * @param {string} cfg.schema Schema name
43
+ * @param {string} cfg.schemaContent Schema contents
44
+ * @returns {JamilihArray}
45
+ */
46
+
47
+ /**
48
+ * @type {BuildTypeChoices}
49
+ */
50
+ const buildTypeChoices = Formats.buildTypeChoices = ({
51
+ format,
52
+ typeNamespace,
53
+ value,
54
+ setValue = false,
55
+ state,
56
+ // itemIndex = 0,
57
+ keySelectClass,
58
+ requireObject,
59
+ objectHasValue,
60
+ topRoot,
61
+ schema,
62
+ schemaContent
63
+ }) => {
64
+ // console.log('format', format, 'state', state, 'path', typeNamespace);
65
+ const typeOptions = requireObject
66
+ ? [Types.getOptionForType('object')]
67
+ : Types.getTypeOptionsForFormatAndState(format, state);
68
+
69
+ let editUI;
70
+ const sel = jml('select', {
71
+ hidden: requireObject,
72
+ class: `typeChoices-${typeNamespace}${keySelectClass
73
+ ? ' ' + keySelectClass
74
+ : ''
75
+ }`,
76
+ // is: 'type-choices',
77
+ $custom: {
78
+ $setType ({type, baseValue, bringIntoFocus}) {
79
+ this.value = type;
80
+ this.$setStyles();
81
+ this.$addAndValidateEditUI({baseValue, bringIntoFocus});
82
+ },
83
+ $setTypeNoEditUI ({type}) {
84
+ this.value = type;
85
+ this.$setStyles();
86
+ },
87
+ $setStyles () {
88
+ const {value: type} = this;
89
+ this.dataset.type = type; // Used for styling
90
+ const parEl = this.parentElement;
91
+ if (parEl.nodeName.toLowerCase() === 'fieldset') {
92
+ parEl.dataset.type = type;
93
+ DOM.filterChildElements(parEl, 'legend').forEach((legend) => {
94
+ legend.dataset.type = type;
95
+ });
96
+ }
97
+ },
98
+ $getTypeRoot () {
99
+ const container = this.$getContainer();
100
+ /* istanbul ignore if -- How to replicate? */
101
+ if (!container) {
102
+ return false;
103
+ }
104
+ return $e(container, 'div[data-type]');
105
+ },
106
+ $addAndValidateEditUI ({baseValue, bringIntoFocus} = {}) {
107
+ const {value: type} = this;
108
+
109
+ const container = this.$getContainer();
110
+ DOM.removeChildren(container);
111
+
112
+ if (!type) { return; }
113
+ let topRoot = this.$getTopRoot();
114
+
115
+ // Todo (low): Try to avoid need for `baseValue`
116
+ // (needed by arrayNonindexKeys for setting an array
117
+ // length and avoiding errors); could set all
118
+ // values through here?
119
+ editUI = Types.getUIForModeAndType({
120
+ readonly: false,
121
+ typeNamespace,
122
+ type,
123
+ bringIntoFocus,
124
+ hasValue: type === 'arrayNonindexKeys' && baseValue,
125
+ value: baseValue,
126
+ buildTypeChoices,
127
+ format,
128
+ topRoot
129
+ });
130
+ this.$addEditUI({editUI});
131
+ this.$validate();
132
+ topRoot = this.$getTopRoot(); // May be existing now
133
+ // Needed; Array/object ref somewhere could now be valid or invalid
134
+ Types.validateAllReferences({topRoot});
135
+ },
136
+ $addTypeAndEditUI ({type, editUI}) {
137
+ this.$setTypeNoEditUI({type});
138
+ this.$addEditUI({editUI});
139
+ },
140
+ $addEditUI ({editUI}) {
141
+ const container = this.$getContainer();
142
+ jml(editUI, container);
143
+ },
144
+ $getContainer () {
145
+ return this.nextElementSibling;
146
+ },
147
+ $getTopRoot () {
148
+ return topRoot || this.$getTypeRoot();
149
+ },
150
+ $validate () {
151
+ const {value: type} = this;
152
+ const container = this.$getContainer();
153
+ if (!container.firstElementChild) {
154
+ return false;
155
+ }
156
+ const editUI = container.firstElementChild;
157
+ return Types.validate({
158
+ type, root: editUI, topRoot: this.$getTopRoot()
159
+ });
160
+ }
161
+ },
162
+ $on: {change (e) {
163
+ // We don't want form `onchange` to run `$checkForKeyDuplicates`
164
+ // again (through `addAndValidateEditUI`->`validateAllReferences`)
165
+ e.stopPropagation();
166
+ this.$addAndValidateEditUI();
167
+ this.$setStyles();
168
+ }}
169
+ }, [
170
+ ['option', {value: ''}, [
171
+ '(Choose a type)'
172
+ ]],
173
+ ...typeOptions.map(
174
+ ([optText, optAtts]) => [
175
+ 'option',
176
+ optAtts ||
177
+ /* istanbul ignore next -- Should always have atts */
178
+ {},
179
+ [optText]
180
+ ]
181
+ )
182
+ ]);
183
+ if (setValue || (requireObject && !objectHasValue)) {
184
+ setTimeout(async () => {
185
+ if (!setValue) { // if (requireObject && !objectHasValue) {
186
+ // Todo (low): We could auto-populate keypath if has
187
+ // keypath (and we probably also only want if
188
+ // not autoincrement)
189
+ value = {};
190
+ }
191
+ try {
192
+ const rootEditUI = await Formats.availableFormats[format].iterate(
193
+ value,
194
+ {
195
+ readonly: false,
196
+ typeNamespace,
197
+ schema,
198
+ schemaContent
199
+ }
200
+ );
201
+ const type = Types.getTypeForRoot(rootEditUI);
202
+ sel.$addTypeAndEditUI({type, editUI: rootEditUI});
203
+ } catch (err) {
204
+ /* istanbul ignore next -- At least some errors handled earlier */
205
+ dialogs.alert({
206
+ message: 'The object to be added had types not supported ' +
207
+ 'by the current format.'
208
+ });
209
+ /* istanbul ignore next -- How to trigger? */
210
+ console.log('err', err);
211
+ }
212
+ });
213
+ }
214
+ return [
215
+ sel,
216
+ ['div', {class: 'typeContainer'}]
217
+ ];
218
+ };
219
+
220
+ /* schema:
221
+ Formats.getTypeForFormatStateAndValue = ({format, state, value}) => {
222
+ const valType = new Typeson().register(
223
+ structuredCloningThrowing
224
+ ).rootTypeName(value);
225
+ return canonicalToAvailableType(format, state, valType, value);
226
+ };
227
+ */
228
+
229
+ export default Formats;
@@ -0,0 +1,55 @@
1
+ import {$e, $$e} from '../utils/templateUtils.js';
2
+
3
+ /**
4
+ * @type {TypeObject}
5
+ */
6
+ const BooleanObjectType = {
7
+ option: ['BooleanObject'],
8
+ stringRegex: /^Boolean\((.*)\)$/u,
9
+ toValue (s) {
10
+ return {
11
+ // eslint-disable-next-line no-new-wrappers, unicorn/new-for-builtins
12
+ value: new Boolean(s === 'true')
13
+ };
14
+ },
15
+ getInput ({root}) {
16
+ return $e(root, 'input');
17
+ },
18
+ getValue ({root}) {
19
+ return this.toValue(String(this.getInput({root}).checked)).value;
20
+ },
21
+ setValue ({root, value}) {
22
+ const inputs = $$e(root, 'input');
23
+ const input = inputs[value.valueOf() ? 0 : 1];
24
+ input.checked = true;
25
+ },
26
+ viewUI ({value}) {
27
+ return ['i', {dataset: {type: 'BooleanObject'}}, [`Boolean(${value})`]];
28
+ },
29
+ ct: 0,
30
+ editUI ({
31
+ // eslint-disable-next-line no-new-wrappers, unicorn/new-for-builtins
32
+ typeNamespace, value = new Boolean(true)
33
+ }) {
34
+ this.ct++;
35
+ return ['div', {dataset: {type: 'BooleanObject'}}, [
36
+ ['label', [
37
+ 'True',
38
+ ['input', {
39
+ type: 'radio', name: `${typeNamespace}-BooleanObject${this.ct}`,
40
+ value: 'true', checked: value.valueOf()
41
+ }]
42
+ ]],
43
+ ['label', [
44
+ 'False',
45
+ ['input', {
46
+ type: 'radio',
47
+ name: `${typeNamespace}-BooleanObject${this.ct}`,
48
+ value: 'false', checked: !value.valueOf()
49
+ }]
50
+ ]]
51
+ ]];
52
+ }
53
+ };
54
+
55
+ export default BooleanObjectType;
@@ -0,0 +1,41 @@
1
+ import {$e} from '../utils/templateUtils.js';
2
+ import Types from '../types.js';
3
+
4
+ /**
5
+ * @type {TypeObject}
6
+ */
7
+ const NumberObjectType = {
8
+ option: ['NumberObject'],
9
+ stringRegex: /^Number\((.*)\)$/u,
10
+ toValue (s) {
11
+ // eslint-disable-next-line no-new-wrappers, unicorn/new-for-builtins
12
+ return {value: new Number(s)};
13
+ },
14
+ validate ({root}) {
15
+ return Types.availableTypes.number.validate({root});
16
+ },
17
+ getInput ({root}) {
18
+ return $e(root, 'input');
19
+ },
20
+ getValue ({root}) {
21
+ return this.toValue(this.getInput({root}).value).value;
22
+ },
23
+ setValue ({root, value}) {
24
+ this.getInput({root}).value = String(value);
25
+ },
26
+ viewUI ({value}) {
27
+ return ['i', {dataset: {type: 'NumberObject'}}, [`Number(${value})`]];
28
+ },
29
+ editUI ({typeNamespace, value}) {
30
+ return ['div', {dataset: {type: 'NumberObject'}}, [
31
+ ['input', {
32
+ name: `${typeNamespace}-NumberObject`,
33
+ type: 'number',
34
+ value,
35
+ step: 'any'
36
+ }]
37
+ ]];
38
+ }
39
+ };
40
+
41
+ export default NumberObjectType;