@ekzo-dev/bootstrap-addons 5.2.5 → 5.2.7

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,7 +1,7 @@
1
1
  {
2
2
  "name": "@ekzo-dev/bootstrap-addons",
3
3
  "description": "Aurelia Bootstrap additional component",
4
- "version": "5.2.5",
4
+ "version": "5.2.7",
5
5
  "homepage": "https://github.com/ekzo-dev/aurelia-components/tree/main/packages/bootstrap-addons",
6
6
  "repository": {
7
7
  "type": "git",
@@ -10,7 +10,7 @@
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@ekzo-dev/bootstrap": "^5.2.11",
13
- "@ekzo-dev/vanilla-jsoneditor": "^0.23.6",
13
+ "@ekzo-dev/vanilla-jsoneditor": "^0.23.7",
14
14
  "@ekzo-dev/toolkit": "^1.2.4",
15
15
  "@fortawesome/free-solid-svg-icons": "^6.5.2",
16
16
  "@types/json-schema": "^7.0.14",
@@ -1,12 +1,14 @@
1
1
  <template>
2
2
  <input ref="input" required.bind="inputRequired" />
3
3
  <json-editor
4
- ref="editor"
5
- json.bind="value"
4
+ ref="editorElement"
5
+ component.ref="editorComponent"
6
+ content.to-view="content"
6
7
  validator.bind="validator"
7
8
  read-only.bind="disabled"
8
9
  on-render-value.one-time="onRenderValue"
9
10
  on-render-menu.one-time="onRenderMenu"
11
+ on-change.one-time="onChange"
10
12
  mode="text"
11
13
  ...$bindables="jsonEditorOptions"
12
14
  ></json-editor>
@@ -2,27 +2,31 @@ import template from './json-input.html';
2
2
 
3
3
  import './json-input.scss';
4
4
 
5
- import { coerceBoolean } from '@ekzo-dev/toolkit';
6
- import { JsonEditor } from '@ekzo-dev/vanilla-jsoneditor';
7
- import { faUpRightAndDownLeftFromCenter } from '@fortawesome/free-solid-svg-icons/faUpRightAndDownLeftFromCenter';
8
- import Ajv, { ErrorObject, Options } from 'ajv';
9
- import Ajv2019 from 'ajv/dist/2019';
10
- import Ajv2020 from 'ajv/dist/2020';
11
- import addFormats from 'ajv-formats';
12
- import { bindable, BindingMode, customElement } from 'aurelia';
13
- import { JSONValue, parsePath } from 'immutable-json-patch';
14
- import {
5
+ import type {
6
+ Content,
7
+ JSONContent,
15
8
  JSONEditorPropsOptional,
16
9
  JSONSchema,
17
10
  JSONSchemaDefinitions,
18
11
  MenuItem,
19
12
  RenderValueComponentDescription,
20
13
  RenderValueProps,
14
+ TextContent,
21
15
  ValidationError,
22
16
  ValidationSeverity,
23
17
  Validator,
24
18
  } from 'vanilla-jsoneditor';
25
19
 
20
+ import { coerceBoolean } from '@ekzo-dev/toolkit';
21
+ import { JsonEditor } from '@ekzo-dev/vanilla-jsoneditor';
22
+ import { faUpRightAndDownLeftFromCenter } from '@fortawesome/free-solid-svg-icons/faUpRightAndDownLeftFromCenter';
23
+ import Ajv, { ErrorObject, Options } from 'ajv';
24
+ import Ajv2019 from 'ajv/dist/2019';
25
+ import Ajv2020 from 'ajv/dist/2020';
26
+ import addFormats from 'ajv-formats';
27
+ import { bindable, BindingMode, customElement } from 'aurelia';
28
+ import { JSONValue, parsePath } from 'immutable-json-patch';
29
+
26
30
  @customElement({
27
31
  name: 'bs-json-input',
28
32
  template,
@@ -47,36 +51,34 @@ export class BsJsonInput {
47
51
  @bindable()
48
52
  jsonEditorOptions: JSONEditorPropsOptional = {};
49
53
 
50
- editorModule?: typeof import('vanilla-jsoneditor');
54
+ valueCache?: unknown;
51
55
 
52
- schemaVersion?: string;
56
+ content?: JSONContent;
53
57
 
54
58
  readonly input!: HTMLInputElement;
55
59
 
56
- readonly editor!: HTMLElement;
60
+ readonly editorElement!: HTMLElement;
61
+
62
+ readonly editorComponent!: JsonEditor;
57
63
 
58
- async binding() {
59
- this.schemaVersion = this.#getSchemaVersion(this.value);
60
- this.editorModule = await import('vanilla-jsoneditor');
64
+ binding() {
65
+ this.#setContent(this.value);
61
66
  }
62
67
 
63
68
  valueChanged(value: unknown) {
64
- this.schemaVersion = this.jsonSchema === true ? this.#getSchemaVersion(value) : undefined;
65
-
66
- if (value == null || value === '') {
67
- this.input.setCustomValidity('');
68
- }
69
+ this.#setContent(value);
69
70
  }
70
71
 
71
72
  onRenderValue = (props: RenderValueProps): RenderValueComponentDescription[] => {
72
73
  let result: RenderValueComponentDescription[] | null;
73
74
  const { jsonSchema } = this;
75
+ const { editorModule } = this.editorComponent;
74
76
 
75
77
  if (jsonSchema && typeof jsonSchema === 'object') {
76
- result = this.editorModule.renderJSONSchemaEnum(props, jsonSchema, this.#getSchemaDefinitions(jsonSchema));
78
+ result = editorModule.renderJSONSchemaEnum(props, jsonSchema, this.#getSchemaDefinitions(jsonSchema));
77
79
  }
78
80
 
79
- return result ?? this.editorModule.renderValue(props);
81
+ return result ?? editorModule.renderValue(props);
80
82
  };
81
83
 
82
84
  onRenderMenu = (items: MenuItem[]): MenuItem[] | undefined => {
@@ -91,7 +93,7 @@ export class BsJsonInput {
91
93
  if (document.fullscreenElement) {
92
94
  void document.exitFullscreen();
93
95
  } else {
94
- void this.editor.requestFullscreen();
96
+ void this.editorElement.requestFullscreen();
95
97
  }
96
98
  },
97
99
  icon: faUpRightAndDownLeftFromCenter,
@@ -100,36 +102,76 @@ export class BsJsonInput {
100
102
  ];
101
103
  };
102
104
 
105
+ onChange = (content: Content): void => {
106
+ const { json, text } = content as JSONContent & TextContent;
107
+
108
+ if (json !== undefined) {
109
+ this.valueCache = json;
110
+ } else {
111
+ const message = 'Please enter a valid JSON string';
112
+
113
+ try {
114
+ this.valueCache = text === '' ? '' : (this.jsonEditorOptions?.parser ?? JSON).parse(text);
115
+
116
+ if (this.input.validationMessage === message || text === '') {
117
+ this.input.setCustomValidity('');
118
+ }
119
+ } catch (e) {
120
+ if (e instanceof SyntaxError) {
121
+ this.input.setCustomValidity(message);
122
+ } else {
123
+ throw e;
124
+ }
125
+ }
126
+ }
127
+
128
+ this.value = this.valueCache;
129
+ };
130
+
103
131
  get inputRequired(): boolean {
104
132
  return this.required && (this.value == null || this.value === '');
105
133
  }
106
134
 
135
+ get schemaVersion(): string {
136
+ if (this.jsonSchema !== true) return undefined;
137
+
138
+ const { value } = this;
139
+
140
+ return value == null || value['$schema'] == null ? '' : (value['$schema'] as string);
141
+ }
142
+
107
143
  get validator(): Validator | undefined {
108
- const { schemaVersion, disabled } = this;
144
+ const { schemaVersion, disabled, ajvOptions, jsonSchema } = this;
109
145
  // use raw object because proxies don't work with private properties
110
146
  const rawThis = this['__raw__'] as BsJsonInput;
111
147
  // use jsonSchema from raw object to pass original (non-proxied) object to AJV
112
- const { jsonSchema } = rawThis;
148
+ const { jsonSchema: rawJsonSchema } = rawThis;
113
149
 
114
150
  if (jsonSchema && typeof jsonSchema === 'object' && !disabled) {
115
- const ajv = rawThis.#initAjv(jsonSchema.$schema as string);
151
+ const ajv = rawThis.#initAjv(jsonSchema.$schema as string, ajvOptions);
116
152
 
117
153
  addFormats(ajv);
118
- const validate = ajv.compile(jsonSchema);
154
+ const validate = ajv.compile(rawJsonSchema);
119
155
 
120
156
  if (validate.errors) {
121
157
  throw validate.errors[0];
122
158
  }
123
159
 
124
160
  return (json: unknown): ValidationError[] => {
161
+ // do not validate empty documents
162
+ if (json === undefined) return [];
163
+
125
164
  validate(json);
126
165
 
127
166
  return rawThis.#processErrors(validate.errors, json);
128
167
  };
129
168
  } else if (schemaVersion != null && !disabled) {
130
- const ajv = rawThis.#initAjv(schemaVersion);
169
+ const ajv = rawThis.#initAjv(schemaVersion, ajvOptions);
131
170
 
132
171
  return (json: unknown): ValidationError[] => {
172
+ // do not validate empty documents
173
+ if (json === undefined) return [];
174
+
133
175
  void ajv.validateSchema(json);
134
176
 
135
177
  return rawThis.#processErrors(ajv.errors, json);
@@ -137,11 +179,21 @@ export class BsJsonInput {
137
179
  }
138
180
  }
139
181
 
140
- #initAjv($schema: string): Ajv {
182
+ #setContent(value: unknown): void {
183
+ if (value !== this.valueCache) {
184
+ // reset validation state before assigning new content
185
+ this.input?.setCustomValidity('');
186
+
187
+ this.content = { json: value };
188
+ this.valueCache = value;
189
+ }
190
+ }
191
+
192
+ #initAjv($schema: string, ajvOptions: Options): Ajv {
141
193
  const options: Options = {
142
194
  strict: false,
143
195
  multipleOfPrecision: 2,
144
- ...this.ajvOptions,
196
+ ...ajvOptions,
145
197
  };
146
198
  let ajv: Ajv;
147
199
 
@@ -165,10 +217,6 @@ export class BsJsonInput {
165
217
  return (schema.$defs ?? schema.definitions) as JSONSchemaDefinitions;
166
218
  }
167
219
 
168
- #getSchemaVersion(value: unknown): string {
169
- return value === Object(value) && value['$schema'] ? (value['$schema'] as string) : '';
170
- }
171
-
172
220
  #processErrors(errors: ErrorObject[] | null, json: unknown): ValidationError[] {
173
221
  const message = this.jsonSchema === true ? 'JSON is not a valid JSONSchema' : 'JSON does not match schema';
174
222