@json-layout/core 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/compile/index.js +12 -0
- package/src/compile/skeleton-node.js +8 -8
- package/src/state/index.js +46 -4
- package/src/utils/doc-options.js +5 -0
- package/types/compile/index.d.ts +2 -0
- package/types/compile/index.d.ts.map +1 -1
- package/types/state/index.d.ts +13 -0
- package/types/state/index.d.ts.map +1 -1
- package/types/utils/doc-options.d.ts.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@json-layout/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Compilation and state management utilities for JSON Layout.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
},
|
|
60
60
|
"homepage": "https://github.com/json-layout/json-layout#readme",
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@json-layout/vocabulary": "^0.
|
|
62
|
+
"@json-layout/vocabulary": "^0.12.0",
|
|
63
63
|
"@types/markdown-it": "^13.0.1",
|
|
64
64
|
"ajv": "^8.12.0",
|
|
65
65
|
"ajv-errors": "^3.0.0",
|
package/src/compile/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import addFormats from 'ajv-formats'
|
|
|
6
6
|
import ajvErrors from 'ajv-errors'
|
|
7
7
|
import ajvLocalizeModule from 'ajv-i18n'
|
|
8
8
|
import MarkdownIt from 'markdown-it'
|
|
9
|
+
import { produce } from 'immer'
|
|
9
10
|
import i18n from '../i18n/index.js'
|
|
10
11
|
import { makeSkeletonTree } from './skeleton-tree.js'
|
|
11
12
|
import { resolveRefs } from './utils/resolve-refs.js'
|
|
@@ -29,6 +30,17 @@ const ajvLocalize = /** @type {typeof ajvLocalizeModule.default} */ (ajvLocalize
|
|
|
29
30
|
|
|
30
31
|
// const exprEvalParser = new ExprEvalParser()
|
|
31
32
|
|
|
33
|
+
// use Immer for efficient updating with immutability and no-op detection
|
|
34
|
+
/** @type {(draft: PartialCompileOptions, newOptions: PartialCompileOptions) => PartialCompileOptions} */
|
|
35
|
+
export const produceCompileOptions = produce((draft, newOptions) => {
|
|
36
|
+
for (const key of ['ajv', 'ajvOptions', 'code', 'markdown', 'markdownItOptions', 'locale', 'messages', 'optionsKeys']) {
|
|
37
|
+
// @ts-ignore
|
|
38
|
+
if (key in newOptions) draft[key] = newOptions[key]
|
|
39
|
+
// @ts-ignore
|
|
40
|
+
else delete draft[key]
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
|
|
32
44
|
/**
|
|
33
45
|
* @param {PartialCompileOptions} partialOptions
|
|
34
46
|
* @returns {CompileOptions}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// import Debug from 'debug'
|
|
2
|
-
import { normalizeLayoutFragment, isSwitchStruct, isGetItemsExpression, isGetItemsFetch, isItemsLayout } from '@json-layout/vocabulary'
|
|
2
|
+
import { normalizeLayoutFragment, isSwitchStruct, isGetItemsExpression, isGetItemsFetch, isItemsLayout, getSchemaFragmentType } from '@json-layout/vocabulary'
|
|
3
3
|
import { makeSkeletonTree } from './skeleton-tree.js'
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -27,8 +27,7 @@ export function makeSkeletonNode (
|
|
|
27
27
|
parentPointer,
|
|
28
28
|
required
|
|
29
29
|
) {
|
|
30
|
-
|
|
31
|
-
if (!schema.type && schema.properties) schema.type = 'object'
|
|
30
|
+
const { type, nullable } = getSchemaFragmentType(schema)
|
|
32
31
|
|
|
33
32
|
// improve on ajv error messages based on ajv-errors (https://ajv.js.org/packages/ajv-errors.html)
|
|
34
33
|
schema.errorMessage = schema.errorMessage ?? {}
|
|
@@ -49,8 +48,9 @@ export function makeSkeletonNode (
|
|
|
49
48
|
let defaultData
|
|
50
49
|
if ('default' in schema) defaultData = schema.default
|
|
51
50
|
else if (required) {
|
|
52
|
-
if (
|
|
53
|
-
if (
|
|
51
|
+
if (nullable) defaultData = null
|
|
52
|
+
else if (type === 'object') defaultData = {}
|
|
53
|
+
else if (type === 'array') defaultData = []
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
let pure = true
|
|
@@ -103,7 +103,7 @@ export function makeSkeletonNode (
|
|
|
103
103
|
|
|
104
104
|
/** @type {import('./types.js').SkeletonNode} */
|
|
105
105
|
const node = { key: key ?? '', pointer, parentPointer, pure, propertyKeys: [], roPropertyKeys: [] }
|
|
106
|
-
if (
|
|
106
|
+
if (type === 'object') {
|
|
107
107
|
if (schema.properties) {
|
|
108
108
|
node.children = node.children ?? []
|
|
109
109
|
for (const propertyKey of Object.keys(schema.properties)) {
|
|
@@ -166,7 +166,7 @@ export function makeSkeletonNode (
|
|
|
166
166
|
const childrenTrees = []
|
|
167
167
|
/** @type {string[]} */
|
|
168
168
|
for (let i = 0; i < schema.oneOf.length; i++) {
|
|
169
|
-
if (!schema.oneOf[i].type) schema.oneOf[i].type =
|
|
169
|
+
if (!schema.oneOf[i].type) schema.oneOf[i].type = type
|
|
170
170
|
const title = schema.oneOf[i].title ?? `option ${i}`
|
|
171
171
|
delete schema.oneOf[i].title
|
|
172
172
|
childrenTrees.push(makeSkeletonTree(
|
|
@@ -195,7 +195,7 @@ export function makeSkeletonNode (
|
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
-
if (
|
|
198
|
+
if (type === 'array' && schema.items) {
|
|
199
199
|
if (Array.isArray(schema.items)) {
|
|
200
200
|
node.children = schema.items.map((/** @type {any} */ itemSchema, /** @type {number} */ i) => {
|
|
201
201
|
return makeSkeletonNode(
|
package/src/state/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { produce } from 'immer'
|
|
|
5
5
|
import { evalExpression, producePatchedData } from './state-node.js'
|
|
6
6
|
import { createStateTree } from './state-tree.js'
|
|
7
7
|
import { Display } from './utils/display.js'
|
|
8
|
-
import { isFileLayout, isGetItemsExpression, isGetItemsFetch, isItemsLayout } from '@json-layout/vocabulary'
|
|
8
|
+
import { isFileLayout, isGetItemsExpression, isGetItemsFetch, isItemsLayout, editableCompNames } from '@json-layout/vocabulary'
|
|
9
9
|
import { shallowProduceArray } from './utils/immutable.js'
|
|
10
10
|
|
|
11
11
|
export { Display } from './utils/display.js'
|
|
@@ -74,6 +74,7 @@ function fillOptions (partialOptions, compiledLayout) {
|
|
|
74
74
|
removeAdditional: 'error',
|
|
75
75
|
autofocus: false,
|
|
76
76
|
readOnlyPropertiesMode: 'show',
|
|
77
|
+
debounceInputMs: 300,
|
|
77
78
|
...partialOptions,
|
|
78
79
|
messages
|
|
79
80
|
}
|
|
@@ -342,11 +343,12 @@ export class StatefulLayout {
|
|
|
342
343
|
}
|
|
343
344
|
|
|
344
345
|
/**
|
|
346
|
+
* @private
|
|
345
347
|
* @param {StateNode} node
|
|
346
348
|
* @param {unknown} data
|
|
347
349
|
* @param {number} [activateKey]
|
|
348
350
|
*/
|
|
349
|
-
|
|
351
|
+
applyInput (node, data, activateKey) {
|
|
350
352
|
logDataBinding('received input event from node', node, data)
|
|
351
353
|
|
|
352
354
|
const transformedData = node.layout.transformData && this.evalNodeExpression(node, node.layout.transformData, data)
|
|
@@ -370,7 +372,10 @@ export class StatefulLayout {
|
|
|
370
372
|
data = transformedData
|
|
371
373
|
}
|
|
372
374
|
|
|
373
|
-
if (
|
|
375
|
+
if (
|
|
376
|
+
(node.options.validateOn === 'input' || (node.options.validateOne === 'blur' && !editableCompNames.includes(node.layout.comp))) &&
|
|
377
|
+
!this.validationState.validatedChildren.includes(node.fullKey)
|
|
378
|
+
) {
|
|
374
379
|
this.validationState = { validatedChildren: this.validationState.validatedChildren.concat([node.fullKey]) }
|
|
375
380
|
}
|
|
376
381
|
if (activateKey !== undefined) {
|
|
@@ -385,17 +390,54 @@ export class StatefulLayout {
|
|
|
385
390
|
const parentNode = this._lastCreateStateTreeContext.nodes.find(p => p.fullKey === node.parentFullKey)
|
|
386
391
|
if (!parentNode) throw new Error(`parent with key "${node.parentFullKey}" not found`)
|
|
387
392
|
const newParentValue = producePatchedData(parentNode.data ?? (typeof node.key === 'number' ? [] : {}), node, data)
|
|
388
|
-
this.
|
|
393
|
+
this.applyInput(parentNode, newParentValue)
|
|
389
394
|
|
|
390
395
|
if (activateKey !== undefined) {
|
|
391
396
|
this.handleAutofocus()
|
|
392
397
|
}
|
|
393
398
|
}
|
|
394
399
|
|
|
400
|
+
/**
|
|
401
|
+
* @private
|
|
402
|
+
* @type {null | [StateNode, unknown, number | undefined, ReturnType<typeof setTimeout>]}
|
|
403
|
+
*/
|
|
404
|
+
debouncedInput = null
|
|
405
|
+
|
|
406
|
+
applyDebouncedInput () {
|
|
407
|
+
if (this.debouncedInput) {
|
|
408
|
+
clearTimeout(this.debouncedInput[3])
|
|
409
|
+
this.applyInput(this.debouncedInput[0], this.debouncedInput[1], this.debouncedInput[2])
|
|
410
|
+
this.debouncedInput = null
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* @param {StateNode} node
|
|
416
|
+
* @param {unknown} data
|
|
417
|
+
* @param {number} [activateKey]
|
|
418
|
+
*/
|
|
419
|
+
input (node, data, activateKey) {
|
|
420
|
+
// debounced data from the same node is cancelled if a new input is received
|
|
421
|
+
// debounced data from another node is applied immediately
|
|
422
|
+
if (this.debouncedInput) {
|
|
423
|
+
if (this.debouncedInput[0] === node) clearTimeout(this.debouncedInput[3])
|
|
424
|
+
else this.applyDebouncedInput()
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (editableCompNames.includes(node.layout.comp) && node.options.debounceInputMs) {
|
|
428
|
+
this.debouncedInput = [node, data, activateKey, setTimeout(() => this.applyDebouncedInput(), node.options.debounceInputMs)]
|
|
429
|
+
} else {
|
|
430
|
+
this.applyInput(node, data, activateKey)
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
395
434
|
/**
|
|
396
435
|
* @param {StateNode} node
|
|
397
436
|
*/
|
|
398
437
|
blur (node) {
|
|
438
|
+
// debounced data is applied immediately on blur
|
|
439
|
+
this.applyDebouncedInput()
|
|
440
|
+
|
|
399
441
|
logDataBinding('received blur event from node', node)
|
|
400
442
|
if (
|
|
401
443
|
(node.options.validateOn === 'input' || node.options.validateOn === 'blur') &&
|
package/src/utils/doc-options.js
CHANGED
|
@@ -123,5 +123,10 @@ export const runtimeOptions = [
|
|
|
123
123
|
hide: 'Hide the readOnly properties but keep them in the data.',
|
|
124
124
|
show: 'Show the readOnly properties.'
|
|
125
125
|
}
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
key: 'debounceInputMs',
|
|
129
|
+
description: 'The debounce time for the input event of editable fields.',
|
|
130
|
+
default: 300
|
|
126
131
|
}
|
|
127
132
|
]
|
package/types/compile/index.d.ts
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export function compile(_schema: object, partialOptions?: import("./types.js").PartialCompileOptions | undefined): CompiledLayout;
|
|
7
7
|
export { resolveRefs } from "./utils/resolve-refs.js";
|
|
8
|
+
/** @type {(draft: PartialCompileOptions, newOptions: PartialCompileOptions) => PartialCompileOptions} */
|
|
9
|
+
export const produceCompileOptions: (draft: PartialCompileOptions, newOptions: PartialCompileOptions) => PartialCompileOptions;
|
|
8
10
|
export type SkeletonTree = import('./types.js').SkeletonTree;
|
|
9
11
|
export type SkeletonNode = import('./types.js').SkeletonNode;
|
|
10
12
|
export type CompiledLayout = import('./types.js').CompiledLayout;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/compile/index.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/compile/index.js"],"names":[],"mappings":"AAkFA;;;;GAIG;AACH,iCAJW,MAAM,4EAEJ,cAAc,CA6E1B;;AAjID,yGAAyG;AACzG,4CADmB,qBAAqB,cAAc,qBAAqB,KAAK,qBAAqB,CAQnG;2BAxBW,OAAO,YAAY,EAAE,YAAY;2BACjC,OAAO,YAAY,EAAE,YAAY;6BACjC,OAAO,YAAY,EAAE,cAAc;6BACnC,OAAO,YAAY,EAAE,cAAc;oCACnC,OAAO,YAAY,EAAE,qBAAqB;iCAC1C,OAAO,YAAY,EAAE,kBAAkB"}
|
package/types/state/index.d.ts
CHANGED
|
@@ -167,6 +167,19 @@ export class StatefulLayout {
|
|
|
167
167
|
* @returns {any}
|
|
168
168
|
*/
|
|
169
169
|
private evalNodeExpression;
|
|
170
|
+
/**
|
|
171
|
+
* @private
|
|
172
|
+
* @param {StateNode} node
|
|
173
|
+
* @param {unknown} data
|
|
174
|
+
* @param {number} [activateKey]
|
|
175
|
+
*/
|
|
176
|
+
private applyInput;
|
|
177
|
+
/**
|
|
178
|
+
* @private
|
|
179
|
+
* @type {null | [StateNode, unknown, number | undefined, ReturnType<typeof setTimeout>]}
|
|
180
|
+
*/
|
|
181
|
+
private debouncedInput;
|
|
182
|
+
applyDebouncedInput(): void;
|
|
170
183
|
/**
|
|
171
184
|
* @param {StateNode} node
|
|
172
185
|
* @param {unknown} data
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/state/index.js"],"names":[],"mappings":";AAYA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,mEAAmE;AACnE,+BADkB,SAAS,GAAG,SAAS,8CACoC;AAE3E,oGAAoG;AACpG,iCADkB,SAAS,GAAG,SAAS,yHACkC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/state/index.js"],"names":[],"mappings":";AAYA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,mEAAmE;AACnE,+BADkB,SAAS,GAAG,SAAS,8CACoC;AAE3E,oGAAoG;AACpG,iCADkB,SAAS,GAAG,SAAS,yHACkC;AAmCzE;IAoHE;;;;;OAKG;IACH,4BALW,OAAO,aAAa,EAAE,cAAc,gBACpC,OAAO,aAAa,EAAE,YAAY,WAClC,QAAQ,qBAAqB,CAAC,SAC9B,OAAO,EAejB;IAtID;;;OAGG;IACH,iBAFU,OAAO,MAAM,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAEhD;IAEN;;;;OAIG;IACH,iCAAe;IACf,mEAAqD;IAErD;;;OAGG;IAEH,mBAAU;IACV,gDAA2C;IAE3C;;;OAGG;IACH,uBAFU,OAAO,aAAa,EAAE,YAAY,CAEhC;IAEZ;;;OAGG;IAEH,iBAAQ;IACR,uBAAuC;IAEvC;;;OAGG;IAEH,yBAAgB;IAQhB;;;OAGG;IACH,+DAOC;IAlBD;;OAEG;IACH,4DAEC;IAeD;;;OAGG;IAEH,iBAAQ;IAKR;;OAEG;IACH,6DAGC;IAVD;;OAEG;IACH,0DAAuC;IASvC;;;OAGG;IACH,cAAK;IAEL,uBAIC;IALD,oBAAiC;IAOjC;;;OAGG;IAEH,oCAA2B;IAE3B;;;OAGG;IACH,yBAAgB;IAChB;;;OAGG;IACH,iCAAwB;IAExB;;OAEG;IACH,OAFU,OAAO,EAAE,CAET;IAoVV;;OAEG;IACH,aAFU,OAAO,MAAM,EAAE,MAAM,CAAC,CAErB;IAhUX;;;OAGG;IACH,uBAGC;IAED;;OAEG;IACH,4BAOC;IAED;;OAEG;IACH,oBAiBC;IAED;;;OAGG;IACH,wBAgCC;IAED,iBAEC;IAED,wBAGC;IAED;;OAEG;IACH,qBAEC;IAED;;OAEG;IACH,uBAEC;IAED;;OAEG;IACH,8BAEC;IAED;;;;;;OAMG;IACH,2BAKC;IAED;;;;;OAKG;IACH,mBA+CC;IAED;;;OAGG;IACH,uBAAqB;IAErB,4BAMC;IAED;;;;OAIG;IACH,YAJW,SAAS,QACT,OAAO,0CAgBjB;IAED;;OAEG;IACH,WAFW,SAAS,QAanB;IAED;;OAEG;IACH,0BAFW,SAAS,QASnB;IAED;;;;;OAKG;IACH,uBAwDC;IAED;;;;OAIG;IACH,eAJW,SAAS,MACT,MAAM,GACJ,QAAQ,OAAO,yBAAyB,EAAE,WAAW,CAAC,CAMlE;IAOD;;;OAGG;IACH,mBAHW,SAAS,OACT,MAAM,QAWhB;IAED;;OAEG;IACH,qBAFW,SAAS,QAKnB;IAED,wBASC;CACF;wBAjjBY,OAAO,YAAY,EAAE,SAAS;wBAC9B,OAAO,YAAY,EAAE,SAAS;oCAC9B,OAAO,YAAY,EAAE,qBAAqB;mCAC1C,OAAO,YAAY,EAAE,oBAAoB;qCACzC,OAAO,YAAY,EAAE,sBAAsB;4BAC3C,OAAO,YAAY,EAAE,aAAa;2BAClC,OAAO,YAAY,EAAE,YAAY;8BACjC,OAAO,YAAY,EAAE,eAAe;yBACpC,OAAO,YAAY,EAAE,UAAU;0BAC/B,OAAO,YAAY,EAAE,WAAW;yBAChC,OAAO,YAAY,EAAE,UAAU;+BAC/B,OAAO,YAAY,EAAE,gBAAgB;2BACrC,OAAO,YAAY,EAAE,YAAY;2BACjC,OAAO,YAAY,EAAE,YAAY;yBACjC,OAAO,YAAY,EAAE,UAAU;8BAC/B,OAAO,YAAY,EAAE,eAAe;6BACpC,OAAO,YAAY,EAAE,cAAc;iCACnC,OAAO,YAAY,EAAE,kBAAkB;6BACvC,OAAO,YAAY,EAAE,cAAc;kCACnC,OAAO,YAAY,EAAE,mBAAmB;uBACxC,OAAO,YAAY,EAAE,QAAQ;+BAC7B,OAAO,YAAY,EAAE,gBAAgB;0BACrC,OAAO,YAAY,EAAE,WAAW;8BAChC,OAAO,YAAY,EAAE,eAAe;uBACpC,OAAO,YAAY,EAAE,QAAQ;4BAC7B,OAAO,YAAY,EAAE,aAAa;2BAClC,OAAO,YAAY,EAAE,YAAY;sBACjC,OAAO,YAAY,EAAE,OAAO;wBAlCjB,oBAAoB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doc-options.d.ts","sourceRoot":"","sources":["../../src/utils/doc-options.js"],"names":[],"mappings":"AAEA;;GAEG;AAEH,yBAAyB;AACzB,wCA8BC;AAED,yBAAyB;AACzB,
|
|
1
|
+
{"version":3,"file":"doc-options.d.ts","sourceRoot":"","sources":["../../src/utils/doc-options.js"],"names":[],"mappings":"AAEA;;GAEG;AAEH,yBAAyB;AACzB,wCA8BC;AAED,yBAAyB;AACzB,wCA2FC;yBAhIY;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,GAAG,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,MAAM,EAAE,GAAG,CAAC,CAAA;CAAC,EAAE"}
|