@json-layout/vocabulary 0.9.1 → 0.10.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@json-layout/vocabulary",
3
- "version": "0.9.1",
3
+ "version": "0.10.0",
4
4
  "description": "Main JSON Layout vocabulary as JSON schemas and Typescript types. Also contains some small utility functions to validate and normalize annotations.",
5
5
  "type": "module",
6
6
  "exports": {
package/src/normalize.js CHANGED
@@ -209,10 +209,11 @@ const getType = (schemaFragment) => {
209
209
  * @param {SchemaFragment} schemaFragment
210
210
  * @param {string} schemaPath
211
211
  * @param {(text: string) => string} markdown
212
+ * @param {string[]} optionsKeys
212
213
  * @param {'oneOf'} [arrayChild]
213
214
  * @returns {{normalized: CompObject, errors: string[]}}
214
215
  */
215
- function getCompObject (layoutKeyword, schemaFragment, schemaPath, markdown, arrayChild) {
216
+ function getCompObject (layoutKeyword, schemaFragment, schemaPath, markdown, optionsKeys, arrayChild) {
216
217
  /** @type {string[]} */
217
218
  const errors = []
218
219
  const key = schemaPath.slice(schemaPath.lastIndexOf('/') + 1)
@@ -243,14 +244,14 @@ function getCompObject (layoutKeyword, schemaFragment, schemaPath, markdown, arr
243
244
 
244
245
  // @ts-ignore
245
246
  if (compositeCompNames.includes(partial.comp)) {
246
- partial.title = partial.title ?? schemaFragment.title ?? null
247
+ if (!('title' in partial)) partial.title = schemaFragment.title ?? null
247
248
  partial.children = getChildren(getDefaultChildren(schemaFragment), partial.children)
248
249
  } else if (partial.comp === 'list') {
249
- partial.title = partial.title ?? schemaFragment.title ?? key
250
+ if (!('title' in partial)) partial.title = schemaFragment.title ?? key
250
251
  partial.listEditMode = partial.listEditMode ?? (schemaFragment.items.type === 'object' ? 'inline-single' : 'inline')
251
252
  partial.listActions = partial.listActions ?? ['add', 'edit', 'delete', 'duplicate', 'sort']
252
253
  } else {
253
- partial.label = partial.label ?? schemaFragment.title ?? key
254
+ if (!('label' in partial)) partial.label = schemaFragment.title ?? key
254
255
  }
255
256
 
256
257
  if (['select', 'autocomplete', 'combobox'].includes(partial.comp) && !partial.items) {
@@ -298,6 +299,20 @@ function getCompObject (layoutKeyword, schemaFragment, schemaPath, markdown, arr
298
299
  partial.defaultData = ''
299
300
  }
300
301
 
302
+ // options can be set directly in layout and normalized in layout.options
303
+ // TODO: fetch this list from the schema ?
304
+ for (const optionKey of optionsKeys) {
305
+ if (optionKey in partial) {
306
+ partial.options = partial.options ?? {}
307
+ partial.options[optionKey] = partial[optionKey]
308
+ delete partial[optionKey]
309
+ }
310
+ }
311
+ if (schemaFragment.readOnly) {
312
+ partial.options = partial.options ?? {}
313
+ if (!('readOnly' in partial.options)) partial.options.readOnly = true
314
+ }
315
+
301
316
  if (partial.getOptions !== undefined) partial.getOptions = normalizeExpression(partial.getOptions)
302
317
  if (partial.getDefaultData !== undefined) partial.getDefaultData = normalizeExpression(partial.getDefaultData)
303
318
  if (partial.getConstData !== undefined) partial.getConstData = normalizeExpression(partial.getConstData)
@@ -371,10 +386,11 @@ function getCompObject (layoutKeyword, schemaFragment, schemaPath, markdown, arr
371
386
  * @param {SchemaFragment} schemaFragment
372
387
  * @param {string} schemaPath
373
388
  * @param {(text: string) => string} markdown
389
+ * @param {string[]} optionsKeys
374
390
  * @param {'oneOf'} [arrayChild]
375
391
  * @returns {{normalized: NormalizedLayout, errors: string[]}}
376
392
  */
377
- function getNormalizedLayout (layoutKeyword, schemaFragment, schemaPath, markdown, arrayChild) {
393
+ function getNormalizedLayout (layoutKeyword, schemaFragment, schemaPath, markdown, optionsKeys, arrayChild) {
378
394
  if (isPartialSwitch(layoutKeyword)) {
379
395
  /** @type {CompObject[]} */
380
396
  const normalizedSwitchCases = []
@@ -385,13 +401,13 @@ function getNormalizedLayout (layoutKeyword, schemaFragment, schemaPath, markdow
385
401
  }
386
402
  for (let i = 0; i < switchCases.length; i++) {
387
403
  const switchCase = switchCases[i]
388
- const compObjectResult = getCompObject(switchCase, schemaFragment, schemaPath, markdown, arrayChild)
404
+ const compObjectResult = getCompObject(switchCase, schemaFragment, schemaPath, markdown, optionsKeys, arrayChild)
389
405
  normalizedSwitchCases.push(compObjectResult.normalized)
390
406
  for (const error of compObjectResult.errors) errors.push(`switch ${i} - ${error}`)
391
407
  }
392
408
  return { normalized: { switch: normalizedSwitchCases }, errors: [] }
393
409
  } else {
394
- return getCompObject(layoutKeyword, schemaFragment, schemaPath, markdown, arrayChild)
410
+ return getCompObject(layoutKeyword, schemaFragment, schemaPath, markdown, optionsKeys, arrayChild)
395
411
  }
396
412
  }
397
413
 
@@ -423,14 +439,18 @@ function lighterValidationErrors (errors) {
423
439
  return errors.map(e => e.message ?? e.keyword)
424
440
  }
425
441
 
442
+ const defaultOptionsKeys = ['readOnly', 'summary', 'titleDepth', 'density', 'removeAdditional', 'validateOn', 'initialValidation', 'defaultOn', 'readOnlyPropertiesMode']
443
+
426
444
  /**
427
445
  * @param {SchemaFragment} schemaFragment
428
446
  * @param {string} schemaPath
429
447
  * @param {(text: string) => string} markdown
448
+ * @param {string[]} [optionsKeys]
430
449
  * @param {'oneOf'} [arrayChild]
431
450
  * @returns {{layout: NormalizedLayout, errors: string[]}}
432
451
  */
433
- export function normalizeLayoutFragment (schemaFragment, schemaPath, markdown = (src) => src, arrayChild) {
452
+ export function normalizeLayoutFragment (schemaFragment, schemaPath, markdown = (src) => src, optionsKeys, arrayChild) {
453
+ optionsKeys = optionsKeys ? optionsKeys.concat(defaultOptionsKeys) : defaultOptionsKeys
434
454
  let layoutKeyword
435
455
  if (arrayChild === 'oneOf') {
436
456
  layoutKeyword = schemaFragment.oneOfLayout ?? {}
@@ -440,15 +460,15 @@ export function normalizeLayoutFragment (schemaFragment, schemaPath, markdown =
440
460
  if (!validateLayoutKeyword(layoutKeyword)) {
441
461
  console.error(`layout keyword validation errors at path ${schemaPath}`, layoutKeyword, validateLayoutKeyword.errors)
442
462
  return {
443
- layout: getNormalizedLayout({}, schemaFragment, schemaPath, markdown, arrayChild).normalized,
463
+ layout: getNormalizedLayout({}, schemaFragment, schemaPath, markdown, optionsKeys, arrayChild).normalized,
444
464
  errors: lighterValidationErrors(validateLayoutKeyword.errors)
445
465
  }
446
466
  }
447
- const normalizedLayout = getNormalizedLayout(layoutKeyword, schemaFragment, schemaPath, markdown, arrayChild)
467
+ const normalizedLayout = getNormalizedLayout(layoutKeyword, schemaFragment, schemaPath, markdown, optionsKeys, arrayChild)
448
468
  if (!validateNormalizedLayout(normalizedLayout.normalized)) {
449
469
  console.error(`normalized layout validation errors at path ${schemaPath}`, normalizedLayout, validateNormalizedLayout.errors)
450
470
  return {
451
- layout: getNormalizedLayout({}, schemaFragment, schemaPath, markdown, arrayChild).normalized,
471
+ layout: getNormalizedLayout({}, schemaFragment, schemaPath, markdown, optionsKeys, arrayChild).normalized,
452
472
  errors: lighterValidationErrors(validateNormalizedLayout.errors)
453
473
  }
454
474
  // throw new Error(`invalid layout at path ${schemaPath}`, { cause: validateNormalizedLayout.errors })
@@ -1033,7 +1033,65 @@ export default {
1033
1033
  "default",
1034
1034
  "comfortable",
1035
1035
  "compact"
1036
+ ],
1037
+ "default": "default"
1038
+ },
1039
+ "removeAdditional": {
1040
+ "description": "true is the same as 'unknown', false is the same as 'none'",
1041
+ "default": "error",
1042
+ "oneOf": [
1043
+ {
1044
+ "type": "boolean"
1045
+ },
1046
+ {
1047
+ "type": "string",
1048
+ "enum": [
1049
+ "unknown",
1050
+ "error",
1051
+ "none"
1052
+ ]
1053
+ }
1036
1054
  ]
1055
+ },
1056
+ "validateOn": {
1057
+ "type": "string",
1058
+ "enum": [
1059
+ "input",
1060
+ "blur",
1061
+ "submit"
1062
+ ],
1063
+ "default": "input"
1064
+ },
1065
+ "initialValidation": {
1066
+ "type": "string",
1067
+ "enum": [
1068
+ "never",
1069
+ "always",
1070
+ "withData"
1071
+ ],
1072
+ "default": "withData"
1073
+ },
1074
+ "defaultOn": {
1075
+ "type": "string",
1076
+ "enum": [
1077
+ "missing",
1078
+ "empty",
1079
+ "never"
1080
+ ],
1081
+ "default": "empty"
1082
+ },
1083
+ "autofocus": {
1084
+ "type": "boolean",
1085
+ "default": false
1086
+ },
1087
+ "readOnlyPropertiesMode": {
1088
+ "type": "string",
1089
+ "enum": [
1090
+ "remove",
1091
+ "hide",
1092
+ "show"
1093
+ ],
1094
+ "default": "show"
1037
1095
  }
1038
1096
  }
1039
1097
  }
@@ -487,7 +487,39 @@
487
487
  "readOnly": {"type": "boolean", "default": false},
488
488
  "summary": {"type": "boolean", "default": false},
489
489
  "titleDepth": {"type": "integer", "minimum": 1, "maximum": 6, "default": 2},
490
- "density": {"type": "string", "enum": ["default", "comfortable", "compact"]}
490
+ "density": {"type": "string", "enum": ["default", "comfortable", "compact"], "default": "default"},
491
+ "removeAdditional": {
492
+ "description": "true is the same as 'unknown', false is the same as 'none'",
493
+ "default": "error",
494
+ "oneOf": [
495
+ {"type": "boolean"},
496
+ {
497
+ "type": "string",
498
+ "enum": ["unknown", "error", "none"]
499
+ }
500
+ ]
501
+ },
502
+ "validateOn": {
503
+ "type": "string",
504
+ "enum": ["input", "blur", "submit"],
505
+ "default": "input"
506
+ },
507
+ "initialValidation": {
508
+ "type": "string",
509
+ "enum": ["never", "always", "withData"],
510
+ "default": "withData"
511
+ },
512
+ "defaultOn": {
513
+ "type": "string",
514
+ "enum": ["missing", "empty", "never"],
515
+ "default": "empty"
516
+ },
517
+ "autofocus": {"type": "boolean", "default": false},
518
+ "readOnlyPropertiesMode": {
519
+ "type": "string",
520
+ "enum": ["remove", "hide", "show"],
521
+ "default": "show"
522
+ }
491
523
  }
492
524
  }
493
525
  ]
@@ -51,6 +51,15 @@ export type StateNodeOptionsBase = StateNodeOptionsBaseLib & {
51
51
  summary?: boolean;
52
52
  titleDepth?: number;
53
53
  density?: "default" | "comfortable" | "compact";
54
+ /**
55
+ * true is the same as 'unknown', false is the same as 'none'
56
+ */
57
+ removeAdditional?: boolean | ("unknown" | "error" | "none");
58
+ validateOn?: "input" | "blur" | "submit";
59
+ initialValidation?: "never" | "always" | "withData";
60
+ defaultOn?: "missing" | "empty" | "never";
61
+ autofocus?: boolean;
62
+ readOnlyPropertiesMode?: "remove" | "hide" | "show";
54
63
  [k: string]: unknown;
55
64
  };
56
65
  export type Cols = number;