@ekzo-dev/bootstrap-addons 5.3.21 → 5.3.23
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/README.md +61 -147
- package/package.json +4 -12
- package/src/forms/duration-input/README.md +153 -0
- package/src/forms/duration-input/duration-input.html +1 -1
- package/src/forms/duration-input/duration-input.scss +1 -1
- package/src/forms/duration-input/duration-input.stories.ts +84 -39
- package/src/forms/select-dropdown/README.md +324 -0
- package/src/forms/select-dropdown/select-dropdown.stories.ts +137 -0
- package/src/index.ts +0 -1
- package/src/forms/json-input/index.ts +0 -1
- package/src/forms/json-input/json-input.html +0 -15
- package/src/forms/json-input/json-input.scss +0 -17
- package/src/forms/json-input/json-input.stories.ts +0 -59
- package/src/forms/json-input/json-input.ts +0 -270
- package/src/forms/select-dropdown/select.stories.ts +0 -89
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
import template from './json-input.html';
|
|
2
|
-
|
|
3
|
-
import './json-input.scss';
|
|
4
|
-
|
|
5
|
-
import type {
|
|
6
|
-
Content,
|
|
7
|
-
JSONContent,
|
|
8
|
-
JSONEditorPropsOptional,
|
|
9
|
-
JSONSchema,
|
|
10
|
-
JSONSchemaDefinitions,
|
|
11
|
-
MenuItem,
|
|
12
|
-
RenderValueComponentDescription,
|
|
13
|
-
RenderValueProps,
|
|
14
|
-
TextContent,
|
|
15
|
-
ValidationError,
|
|
16
|
-
ValidationSeverity,
|
|
17
|
-
Validator,
|
|
18
|
-
} from 'vanilla-jsoneditor';
|
|
19
|
-
|
|
20
|
-
import { ProxyObservable } from '@aurelia/runtime';
|
|
21
|
-
import { coerceBoolean } from '@ekzo-dev/toolkit';
|
|
22
|
-
import { JsonEditor } from '@ekzo-dev/vanilla-jsoneditor';
|
|
23
|
-
import { faUpRightAndDownLeftFromCenter } from '@fortawesome/free-solid-svg-icons/faUpRightAndDownLeftFromCenter';
|
|
24
|
-
import Ajv, { type AnySchema, type ErrorObject, type Options } from 'ajv';
|
|
25
|
-
import Ajv2019 from 'ajv/dist/2019';
|
|
26
|
-
import Ajv2020 from 'ajv/dist/2020';
|
|
27
|
-
import addFormats from 'ajv-formats';
|
|
28
|
-
import { bindable, BindingMode, customElement } from 'aurelia';
|
|
29
|
-
import { parsePath } from 'immutable-json-patch';
|
|
30
|
-
import { CompileOptions, compileSchema, JsonError, SchemaNode } from 'json-schema-library';
|
|
31
|
-
|
|
32
|
-
const patternMap: Record<string, string> = {
|
|
33
|
-
'^[A-Za-z_][-A-Za-z0-9._]*$': '^[A-Za-z_][\\-A-Za-z0-9._]*$',
|
|
34
|
-
'^[A-Za-z][-A-Za-z0-9.:_]*$': '^[A-Za-z][\\-A-Za-z0-9.:_]*$',
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
@customElement({
|
|
38
|
-
name: 'bs-json-input',
|
|
39
|
-
template,
|
|
40
|
-
dependencies: [JsonEditor],
|
|
41
|
-
})
|
|
42
|
-
export class BsJsonInput {
|
|
43
|
-
@bindable({ mode: BindingMode.twoWay })
|
|
44
|
-
value: unknown;
|
|
45
|
-
|
|
46
|
-
@bindable(coerceBoolean)
|
|
47
|
-
required: boolean = false;
|
|
48
|
-
|
|
49
|
-
@bindable(coerceBoolean)
|
|
50
|
-
disabled: boolean = false;
|
|
51
|
-
|
|
52
|
-
@bindable({ set: (v: unknown) => (v === '' || v === true || v === 'true' ? true : v) })
|
|
53
|
-
jsonSchema?: JSONSchema | SchemaNode | boolean;
|
|
54
|
-
|
|
55
|
-
@bindable()
|
|
56
|
-
validatorOptions?: CompileOptions;
|
|
57
|
-
|
|
58
|
-
@bindable()
|
|
59
|
-
jsonEditorOptions: JSONEditorPropsOptional = {};
|
|
60
|
-
|
|
61
|
-
valueCache?: unknown;
|
|
62
|
-
|
|
63
|
-
content?: JSONContent;
|
|
64
|
-
|
|
65
|
-
readonly input!: HTMLInputElement;
|
|
66
|
-
|
|
67
|
-
readonly editorElement!: HTMLElement;
|
|
68
|
-
|
|
69
|
-
readonly editorComponent!: JsonEditor;
|
|
70
|
-
|
|
71
|
-
binding() {
|
|
72
|
-
this.#setContent(this.value);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
valueChanged(value: unknown) {
|
|
76
|
-
this.#setContent(value);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
onRenderValue = (props: RenderValueProps): RenderValueComponentDescription[] => {
|
|
80
|
-
// let result: RenderValueComponentDescription[] | null;
|
|
81
|
-
// const { jsonSchema } = this;
|
|
82
|
-
const { editorModule } = this.editorComponent;
|
|
83
|
-
|
|
84
|
-
// TODO: this does not support bundled schemas and complex refs. So need to make own implementation later
|
|
85
|
-
// if (jsonSchema && typeof jsonSchema === 'object') {
|
|
86
|
-
// result = editorModule.renderJSONSchemaEnum(props, jsonSchema, this.#getSchemaDefinitions(jsonSchema));
|
|
87
|
-
// }
|
|
88
|
-
|
|
89
|
-
return editorModule!.renderValue(props);
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
onRenderMenu = (items: MenuItem[]): MenuItem[] | undefined => {
|
|
93
|
-
return [
|
|
94
|
-
...items,
|
|
95
|
-
{
|
|
96
|
-
type: 'space',
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
type: 'button',
|
|
100
|
-
onClick: () => {
|
|
101
|
-
if (document.fullscreenElement) {
|
|
102
|
-
void document.exitFullscreen();
|
|
103
|
-
} else {
|
|
104
|
-
void this.editorElement.requestFullscreen();
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
|
-
icon: faUpRightAndDownLeftFromCenter,
|
|
108
|
-
title: 'Toggle full screen',
|
|
109
|
-
},
|
|
110
|
-
];
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
onChange = (content: Content): void => {
|
|
114
|
-
const { json, text } = content as JSONContent & TextContent;
|
|
115
|
-
|
|
116
|
-
if (json !== undefined) {
|
|
117
|
-
this.valueCache = json;
|
|
118
|
-
} else {
|
|
119
|
-
const message = 'Please enter a valid JSON string';
|
|
120
|
-
|
|
121
|
-
try {
|
|
122
|
-
this.valueCache = text === '' ? '' : (this.jsonEditorOptions?.parser ?? JSON).parse(text);
|
|
123
|
-
|
|
124
|
-
if (this.input.validationMessage === message || text === '') {
|
|
125
|
-
this.input.setCustomValidity('');
|
|
126
|
-
}
|
|
127
|
-
} catch (e) {
|
|
128
|
-
if (e instanceof SyntaxError) {
|
|
129
|
-
this.input.setCustomValidity(message);
|
|
130
|
-
} else {
|
|
131
|
-
throw e;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
this.value = this.valueCache;
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
get inputRequired(): boolean {
|
|
140
|
-
return this.required && (this.value == null || this.value === '');
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
get schemaVersion(): string | undefined {
|
|
144
|
-
if (this.jsonSchema !== true) return undefined;
|
|
145
|
-
|
|
146
|
-
const value = this.value as JSONSchema;
|
|
147
|
-
|
|
148
|
-
return value == null || value.$schema == null ? '' : (value.$schema as string);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
get validator(): Validator | undefined {
|
|
152
|
-
const { schemaVersion, disabled, jsonSchema } = this;
|
|
153
|
-
const rawThis = ProxyObservable.getRaw<BsJsonInput>(this);
|
|
154
|
-
|
|
155
|
-
if (disabled) return;
|
|
156
|
-
|
|
157
|
-
if (jsonSchema && typeof jsonSchema === 'object') {
|
|
158
|
-
const schema = rawThis.#initJsonSchemaLibrary();
|
|
159
|
-
|
|
160
|
-
return (json: unknown): ValidationError[] => {
|
|
161
|
-
// do not validate empty documents
|
|
162
|
-
if (json === undefined || !schema) return [];
|
|
163
|
-
|
|
164
|
-
const { errors } = schema.validate(json);
|
|
165
|
-
|
|
166
|
-
return rawThis.#processErrors(errors, json);
|
|
167
|
-
};
|
|
168
|
-
} else if (schemaVersion != null) {
|
|
169
|
-
const ajv = rawThis.#initAjv(schemaVersion);
|
|
170
|
-
|
|
171
|
-
return (json: unknown): ValidationError[] => {
|
|
172
|
-
// do not validate empty documents
|
|
173
|
-
if (json === undefined) return [];
|
|
174
|
-
|
|
175
|
-
void ajv.validateSchema(json as AnySchema);
|
|
176
|
-
|
|
177
|
-
return rawThis.#processErrorsAjv(ajv.errors, json);
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
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): Ajv {
|
|
193
|
-
// some regexp's in 2019-09/2020-12 meta-schemas are not compatible with 'v' flag, so update them
|
|
194
|
-
const regExp = (pattern: string) => new RegExp(patternMap[pattern] ?? pattern, 'v');
|
|
195
|
-
|
|
196
|
-
regExp.code = 'regexp';
|
|
197
|
-
|
|
198
|
-
const options: Options = {
|
|
199
|
-
strict: false,
|
|
200
|
-
multipleOfPrecision: 2,
|
|
201
|
-
code: {
|
|
202
|
-
regExp,
|
|
203
|
-
},
|
|
204
|
-
allErrors: true,
|
|
205
|
-
};
|
|
206
|
-
let ajv: Ajv;
|
|
207
|
-
|
|
208
|
-
switch ($schema) {
|
|
209
|
-
case 'https://json-schema.org/draft/2019-09/schema':
|
|
210
|
-
ajv = new Ajv2019(options);
|
|
211
|
-
break;
|
|
212
|
-
|
|
213
|
-
case 'https://json-schema.org/draft/2020-12/schema':
|
|
214
|
-
ajv = new Ajv2020(options);
|
|
215
|
-
break;
|
|
216
|
-
|
|
217
|
-
default:
|
|
218
|
-
ajv = new Ajv(options);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
addFormats(ajv);
|
|
222
|
-
|
|
223
|
-
return ajv;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
#initJsonSchemaLibrary(): SchemaNode | undefined {
|
|
227
|
-
const { jsonSchema } = this;
|
|
228
|
-
|
|
229
|
-
// already a SchemaNode
|
|
230
|
-
if ((jsonSchema as SchemaNode).evaluationPath) {
|
|
231
|
-
return jsonSchema as SchemaNode;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
try {
|
|
235
|
-
return compileSchema(jsonSchema as JSONSchema, this.validatorOptions);
|
|
236
|
-
} catch (e) {
|
|
237
|
-
console.error('json-schema-library validator compilation error', e);
|
|
238
|
-
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
#getSchemaDefinitions(schema: JSONSchema): JSONSchemaDefinitions {
|
|
244
|
-
return (schema.$defs ?? schema.definitions) as JSONSchemaDefinitions;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
#processErrorsAjv(errors: ErrorObject[] | null | undefined, json: unknown): ValidationError[] {
|
|
248
|
-
const message = this.jsonSchema === true ? 'JSON is not a valid JSONSchema' : 'JSON does not match schema';
|
|
249
|
-
|
|
250
|
-
this.input.setCustomValidity(errors?.length ? message : '');
|
|
251
|
-
|
|
252
|
-
return (errors || []).map((error) => ({
|
|
253
|
-
path: parsePath(json, error.instancePath),
|
|
254
|
-
message: error.message || 'Unknown error',
|
|
255
|
-
severity: 'warning' as ValidationSeverity,
|
|
256
|
-
}));
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
#processErrors(errors: JsonError[] | null, json: unknown): ValidationError[] {
|
|
260
|
-
const message = this.jsonSchema === true ? 'JSON is not a valid JSONSchema' : 'JSON does not match schema';
|
|
261
|
-
|
|
262
|
-
this.input.setCustomValidity(errors?.length ? message : '');
|
|
263
|
-
|
|
264
|
-
return (errors || []).map((error) => ({
|
|
265
|
-
path: parsePath(json, error.data.pointer),
|
|
266
|
-
message: error.message || 'Unknown error',
|
|
267
|
-
severity: 'warning' as ValidationSeverity,
|
|
268
|
-
}));
|
|
269
|
-
}
|
|
270
|
-
}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
// // import { BsButton, BsOffcanvas } from '@ekzo-dev/bootstrap';
|
|
2
|
-
// // import { extractArgTypes, Meta, Story } from '@storybook/aurelia';
|
|
3
|
-
// //
|
|
4
|
-
// // import { selectControl } from '../../../../../.storybook/helpers';
|
|
5
|
-
// //
|
|
6
|
-
// // import { BsSelectDropdown } from '.';
|
|
7
|
-
// //
|
|
8
|
-
// // export default {
|
|
9
|
-
// // title: 'Ekzo / Bootstrap Addons / Forms / Select',
|
|
10
|
-
// // component: BsSelectDropdown,
|
|
11
|
-
// // parameters: {
|
|
12
|
-
// // actions: {
|
|
13
|
-
// // handles: ['change', 'input'],
|
|
14
|
-
// // },
|
|
15
|
-
// // },
|
|
16
|
-
// // args: {
|
|
17
|
-
// // label: 'Label',
|
|
18
|
-
// // floatingLabel: false,
|
|
19
|
-
// // valid: null,
|
|
20
|
-
// // },
|
|
21
|
-
// // argTypes: {
|
|
22
|
-
// // bsSize: {
|
|
23
|
-
// // ...extractArgTypes(BsSelectDropdown).bsSize,
|
|
24
|
-
// // ...selectControl(['', 'sm', 'lg']),
|
|
25
|
-
// // },
|
|
26
|
-
// // },
|
|
27
|
-
// // } as Meta;
|
|
28
|
-
// //
|
|
29
|
-
// // const Overview: Story = (args) => ({
|
|
30
|
-
// // props: args,
|
|
31
|
-
// // });
|
|
32
|
-
// //
|
|
33
|
-
// // Overview.args = {
|
|
34
|
-
// // options: [
|
|
35
|
-
// // { value: undefined, text: 'Select option' },
|
|
36
|
-
// // { value: '1', text: 'One', disabled: true },
|
|
37
|
-
// // { value: '2', text: 'Two' },
|
|
38
|
-
// // { value: '3', text: 'Three', group: 'Group' },
|
|
39
|
-
// // ],
|
|
40
|
-
// // value: '2',
|
|
41
|
-
// // };
|
|
42
|
-
// //
|
|
43
|
-
// // const Multiple: Story = (args) => ({
|
|
44
|
-
// // props: args,
|
|
45
|
-
// // });
|
|
46
|
-
// //
|
|
47
|
-
// // Multiple.args = {
|
|
48
|
-
// // multiple: true,
|
|
49
|
-
// // value: ['2', '3'],
|
|
50
|
-
// // options: [
|
|
51
|
-
// // { value: '1', text: 'One', disabled: true },
|
|
52
|
-
// // { value: '2', text: 'Two' },
|
|
53
|
-
// // { value: '3', text: 'Three', group: 'Group' },
|
|
54
|
-
// // ],
|
|
55
|
-
// // };
|
|
56
|
-
// //
|
|
57
|
-
// // const LargeOptions: Story = (args) => ({
|
|
58
|
-
// // props: args,
|
|
59
|
-
// // template:
|
|
60
|
-
// // '<bs-select value.bind="value" options.bind="options" label.bind="label" style="width: 400px; max-width: 100%"></bs-select>',
|
|
61
|
-
// // });
|
|
62
|
-
// //
|
|
63
|
-
// // LargeOptions.args = {
|
|
64
|
-
// // options: Array.from({ length: 1000 }).map((v, i) => ({
|
|
65
|
-
// // value: i.toString(),
|
|
66
|
-
// // text: `Option ${i} has long content which forces dropdown menu to scale larger that select box`,
|
|
67
|
-
// // })),
|
|
68
|
-
// // };
|
|
69
|
-
// //
|
|
70
|
-
// // const InModal: Story = (args) => ({
|
|
71
|
-
// // props: args,
|
|
72
|
-
// // template: `
|
|
73
|
-
// // <button bs-button click.trigger="offcanvas.toggle()">Open modal</button>
|
|
74
|
-
// // <bs-offcanvas component.ref="offcanvas">
|
|
75
|
-
// // <bs-select value.bind="value" options.bind="options" label.bind="label" style="width: 100%"></bs-select>
|
|
76
|
-
// // <div style="height: 2000px"></div>
|
|
77
|
-
// // </bs-offcanvas>`,
|
|
78
|
-
// // components: [BsOffcanvas, BsButton],
|
|
79
|
-
// // });
|
|
80
|
-
// //
|
|
81
|
-
// // InModal.args = {
|
|
82
|
-
// // options: Array.from({ length: 1000 }).map((v, i) => ({
|
|
83
|
-
// // value: i.toString(),
|
|
84
|
-
// // text: `Option ${i} has long content which forces dropdown menu to scale larger that select box`,
|
|
85
|
-
// // })),
|
|
86
|
-
// // };
|
|
87
|
-
// //
|
|
88
|
-
// // // eslint-disable-next-line
|
|
89
|
-
// // export { Overview, Multiple, LargeOptions, InModal };
|