Skip to content

Commit 5515a10

Browse files
committed
fix: typings and api issues, some refactoring
1 parent 2b69a12 commit 5515a10

File tree

10 files changed

+191
-104
lines changed

10 files changed

+191
-104
lines changed

packages/engine/src/engine.ts

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1+
// import type { StandardSchemaV1 } from '@standard-schema/spec';
2+
13
import { Logger } from '@jsfe/engine/logger';
24
import { Signal } from 'signal-polyfill';
35

46
import type {
57
CommonWidgetOptions,
68
FeatureFlags,
79
FormFieldElement,
10+
GenericData,
811
ObjectWidgetOptions,
912
PathArray,
1013
} from './types/form.js';
11-
import type { UiSchema } from './types/index.js';
14+
import type { UiSchema, Widgets } from './types/index.js';
1215
import type { ReadonlyJSONSchema7 } from './types/index.js';
1316
import type { FieldPathFromSchema } from './types/paths.js';
1417

@@ -19,10 +22,11 @@ const log = new Logger();
1922

2023
export class JsonSchemaFormEngine<
2124
Schema extends ReadonlyJSONSchema7 | undefined = undefined,
25+
Ui extends UiSchema = UiSchema,
26+
Data extends GenericData = GenericData,
2227
Path extends string = Schema extends ReadonlyJSONSchema7
2328
? FieldPathFromSchema<Schema>
2429
: string,
25-
Data = Record<number | string, unknown>,
2630
> extends EventTarget {
2731
public readonly $data = new Signal.State({} as Data);
2832

@@ -42,8 +46,7 @@ export class JsonSchemaFormEngine<
4246
function recurse(field: CommonWidgetOptions | ObjectWidgetOptions) {
4347
fields[field.pathAsString] = field;
4448

45-
if ('children' in field)
46-
for (const child of field.children) recurse(child);
49+
if ('fields' in field) for (const child of field.fields) recurse(child);
4750
}
4851
recurse(this.rootField);
4952

@@ -55,17 +58,16 @@ export class JsonSchemaFormEngine<
5558
}
5659

5760
public constructor(
58-
public schema: Schema = {} as Schema,
59-
public ui: UiSchema = {} as UiSchema,
61+
public readonly schema: Schema = {} as Schema,
62+
public readonly ui: Ui = {} as Ui,
63+
/** Initial data */
6064
data: Data = {} as Data,
65+
public readonly widgets: Partial<Widgets> = {},
66+
public readonly name?: string,
6167
) {
6268
super();
6369

64-
this.$data.set(structuredClone(data));
65-
// HACK: Otherwise, first modified field won't propagate it's value indefinitely.
66-
queueMicrotask(() => {
67-
this.$data.set(structuredClone(data));
68-
});
70+
this.$data.set(data);
6971

7072
this.$rootField = new Signal.Computed(() =>
7173
this.traverse({
@@ -88,17 +90,17 @@ export class JsonSchemaFormEngine<
8890
// NOTE: Partially implemented
8991
_schemaPath?: PathArray,
9092
) {
91-
const updatedData = structuredClone(this.data) as Record<string, unknown>;
93+
const updatedData = { ...this.data };
9294
setSafe(updatedData, path, value);
9395

94-
this.$data.set(updatedData as Data);
96+
this.$data.set(updatedData);
9597

9698
this.dispatchEvent(new Event('change'));
97-
log.debug({ updatedData });
99+
log.debug({ updatedData: this.$data.get() });
98100
}
99101

100102
// TODO: Input/Change distinction.
101-
public handleFormEvent(event: InputEvent, coerce = false) {
103+
public handleFormEvent(event: Event | InputEvent /* coerce = true */) {
102104
const fieldElement = event.target as FormFieldElement;
103105

104106
const nameAttribute = fieldElement.getAttribute('name');
@@ -122,7 +124,7 @@ export class JsonSchemaFormEngine<
122124

123125
// TODO: Extract coercion to helper
124126
if (
125-
coerce &&
127+
// coerce &&
126128
field.schema.type === 'number' &&
127129
'valueAsNumber' in fieldElement
128130
)
@@ -144,7 +146,7 @@ export class JsonSchemaFormEngine<
144146
if (!formElement) throw new ReferenceError('Missing form element.');
145147

146148
// IDEA: Parametrize?
147-
// event.preventDefault();
149+
event.preventDefault();
148150

149151
let formData;
150152
try {

packages/engine/src/object.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ export function widgetObject({
2929
const error = 'Wrong object field';
3030
if (typeof schema.properties !== 'object') {
3131
return {
32-
children: [],
3332
classes: {},
33+
fields: [],
3434
form,
3535
helpText: error,
3636
html: {
@@ -50,7 +50,7 @@ export function widgetObject({
5050
};
5151
}
5252

53-
const children: CommonWidgetOptions[] = [];
53+
const fields: CommonWidgetOptions[] = [];
5454
for (const [propertyName, propertyValue] of Object.entries(
5555
schema.properties,
5656
)) {
@@ -65,9 +65,13 @@ export function widgetObject({
6565

6666
const required = schema.required?.includes(propertyName) ?? false;
6767

68-
const schemaPathAugmented: PathArray = [...schemaPath, propertyName];
68+
const schemaPathAugmented: PathArray = [
69+
...schemaPath,
70+
'properties',
71+
propertyName,
72+
];
6973

70-
children.push(
74+
fields.push(
7175
form.traverse({
7276
data: childData,
7377
form,
@@ -89,8 +93,8 @@ export function widgetObject({
8993
if (pathAsString === '') pathAsString = '__root';
9094

9195
const options: ObjectWidgetOptions = {
92-
children,
9396
classes: {},
97+
fields,
9498
form,
9599
helpText: description,
96100
html: {

packages/engine/src/primitive.ts

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
export function widgetPrimitive(
2121
parameters: WidgetTypeBaseParameters,
2222
): PrimitiveWidgetOptions {
23-
const { data, schema, uiSchema } = parameters;
23+
const { schema, uiSchema } = parameters;
2424
const options = createBaseOptions(parameters);
2525

2626
// Handle enum types first
@@ -34,20 +34,20 @@ export function widgetPrimitive(
3434

3535
// Handle date/time types
3636
if (schema.format && ['date', 'date-time', 'time'].includes(schema.format)) {
37-
return createDateWidget(options, schema, data);
37+
return createDateWidget(options, schema, uiSchema);
3838
}
3939

4040
// Handle primitive types
4141
switch (schema.type) {
4242
case 'boolean': {
43-
return createBooleanWidget(options, uiSchema);
43+
return createBooleanWidget(options, schema, uiSchema);
4444
}
4545
case 'integer':
4646
case 'number': {
47-
return createNumberWidget(options, schema, uiSchema, data);
47+
return createNumberWidget(options, schema, uiSchema);
4848
}
4949
case 'string': {
50-
return createStringWidget(options, schema, uiSchema, data);
50+
return createStringWidget(options, schema, uiSchema);
5151
}
5252
default: {
5353
return options;
@@ -94,34 +94,39 @@ function createBaseOptions({
9494
placeholder: uiSchema['ui:placeholder'],
9595
readonly: uiSchema['ui:readonly'] ?? false,
9696
required,
97+
value: baseValue,
9798
},
9899
label,
99100
level,
100101
path,
101102
pathAsString: dotPath,
102103
schema,
103-
value: baseValue,
104104
widget: null,
105105
};
106106
}
107107

108108
function createBooleanWidget(
109109
options: PrimitiveWidgetOptions,
110+
_schema: WidgetTypeBaseParameters['schema'],
110111
uiSchema: WidgetTypeBaseParameters['uiSchema'],
111112
): PrimitiveWidgetOptions<BooleanInputAttributes> {
112113
const booleanOptions: PrimitiveWidgetOptions<BooleanInputAttributes> = {
113114
...options,
115+
element: 'input',
114116
html: {
115117
...options.html,
116-
element: 'input',
117118
type: 'checkbox',
118-
value: options.value === undefined ? undefined : Boolean(options.value),
119+
value:
120+
options.html.value === undefined
121+
? undefined
122+
: Boolean(options.html.value),
119123
},
120124
};
121125

122126
switch (uiSchema['ui:widget']) {
123127
case 'Button': {
124-
booleanOptions.html.type = null;
128+
booleanOptions.element = undefined;
129+
booleanOptions.html.type = undefined;
125130
booleanOptions.widget = 'ButtonGroupBoolean';
126131
break;
127132
}
@@ -145,7 +150,7 @@ function createBooleanWidget(
145150
function createDateWidget(
146151
options: PrimitiveWidgetOptions,
147152
schema: WidgetTypeBaseParameters['schema'],
148-
data: WidgetTypeBaseParameters['data'],
153+
_uiSchema: WidgetTypeBaseParameters['uiSchema'],
149154
): PrimitiveWidgetOptions<DateInputAttributes> {
150155
const type =
151156
schema.format === 'date-time'
@@ -154,13 +159,15 @@ function createDateWidget(
154159

155160
const dateOptions: PrimitiveWidgetOptions<DateInputAttributes> = {
156161
...options,
162+
element: 'input',
157163
html: {
158164
...options.html,
159-
element: 'input',
160165
type,
161-
value: typeof data === 'string' ? data : undefined,
166+
value:
167+
options.html.value === undefined
168+
? undefined
169+
: String(options.html.value as string),
162170
},
163-
value: typeof options.value === 'string' ? options.value : undefined,
164171
widget: 'Date',
165172
};
166173

@@ -175,9 +182,9 @@ function createEnumWidget(
175182
const enumOptions: EnumWidgetOptions = {
176183
...options,
177184
enum: undefined,
185+
element: 'select',
178186
html: {
179187
...options.html,
180-
element: 'select',
181188
value: undefined,
182189
},
183190
type: undefined,
@@ -206,15 +213,17 @@ function createNumberWidget(
206213
options: PrimitiveWidgetOptions,
207214
schema: WidgetTypeBaseParameters['schema'],
208215
uiSchema: WidgetTypeBaseParameters['uiSchema'],
209-
data: WidgetTypeBaseParameters['data'],
210216
): PrimitiveWidgetOptions<NumberInputAttributes> {
211217
const numberOptions: PrimitiveWidgetOptions<NumberInputAttributes> = {
212218
...options,
219+
element: 'input',
213220
html: {
214221
...options.html,
215-
element: 'input',
216222
type: 'number',
217-
value: typeof data === 'number' ? data : undefined,
223+
value:
224+
options.html.value === undefined
225+
? undefined
226+
: Number(options.html.value),
218227
},
219228
};
220229

@@ -246,26 +255,28 @@ function createStringWidget(
246255
options: PrimitiveWidgetOptions,
247256
schema: WidgetTypeBaseParameters['schema'],
248257
uiSchema: WidgetTypeBaseParameters['uiSchema'],
249-
data: WidgetTypeBaseParameters['data'],
250258
): PrimitiveWidgetOptions<StringInputAttributes> {
251259
const stringOptions: PrimitiveWidgetOptions<StringInputAttributes> = {
252260
...options,
261+
element: 'input',
253262
html: {
254263
...options.html,
255-
element: 'input',
256264
type: 'text',
257-
value: typeof data === 'string' ? data : undefined,
265+
value:
266+
options.html.value === undefined
267+
? undefined
268+
: String(options.html.value as string),
258269
},
259270
};
260271

261272
if (uiSchema['ui:widget'] === 'Textarea') {
262-
stringOptions.html.element = 'textarea';
273+
stringOptions.element = 'textarea';
263274
stringOptions.widget = 'Textarea';
264275
} else if (uiSchema['ui:widget'] === 'ColorPicker') {
265-
stringOptions.html.element = 'input';
276+
stringOptions.element = 'input';
266277
stringOptions.widget = 'ColorPicker';
267278
} else {
268-
stringOptions.html.element = 'input';
279+
stringOptions.element = 'input';
269280
stringOptions.widget = 'Text';
270281
stringOptions.html.type = 'text';
271282
}
@@ -280,8 +291,8 @@ function createStringWidget(
280291
stringOptions.html.type = 'tel';
281292
}
282293

283-
stringOptions.html.minLength = schema.minLength;
284-
stringOptions.html.maxLength = schema.maxLength;
294+
stringOptions.html.minlength = schema.minLength;
295+
stringOptions.html.maxlength = schema.maxLength;
285296
stringOptions.html.pattern = schema.pattern;
286297

287298
return stringOptions;

0 commit comments

Comments
 (0)