diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz
index 0f7e442..7c3ad5f 100644
Binary files a/.yarn/install-state.gz and b/.yarn/install-state.gz differ
diff --git a/README.md b/README.md
index 25b2d31..d4a2a93 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ This is a Web Component for generating input forms from [OGraf/GDD](https://ogra
npm install ograf-form
```
-Or you can use a CDN: `https://cdn.jsdelivr.net/npm/ograf-form/dist/main.js);`
+Or you can use a CDN: `https://cdn.jsdelivr.net/npm/ograf-form);`
### Example usage
@@ -26,7 +26,7 @@ Or you can use a CDN: `https://cdn.jsdelivr.net/npm/ograf-form/dist/main.js);`
@@ -42,15 +42,15 @@ Or you can use a CDN: `https://cdn.jsdelivr.net/npm/ograf-form/dist/main.js);`
// Listen to changes:
const form = document.getElementById("ograf-form");
const dataDiv = document.getElementById("data");
- form.addEventListener("onChange", (e) => {
- console.log("Caught onChange event", e.detail.data);
- // The onChange event is fired when a user changes a value in the form
+ form.addEventListener("change", (e) => {
+ console.log("Caught change event", e.target.value);
+ // The change event is fired when a user changes a value in the form
// It does NOT fire on each key stroke.
// This is a good time to update our data object:
- dataDiv.innerHTML = JSON.stringify(e.detail.data, null, 2);
+ dataDiv.innerHTML = JSON.stringify(e.target.value, null, 2);
});
- form.addEventListener("onKeyUp", (e) => {
- console.log("Caught onKeyUp event", e.detail.data);
+ form.addEventListener("keyup", (e) => {
+ console.log("Caught keyup event", e.target.value);
});
@@ -75,3 +75,8 @@ yarn release
```
+
+## TODO
+
+- Support `patternProperties`, `additionalProperties`, `unevaluatedProperties`
+- Support tuples (`prefixItems` / `items` as array) & `additionalItems`
diff --git a/examples/html.html b/examples/html.html
index 471be0a..8b453a8 100644
--- a/examples/html.html
+++ b/examples/html.html
@@ -1,11 +1,11 @@
-
-
+ src="https://cdn.jsdelivr.net/npm/ograf-form"
+ > -->
+
{
- console.log("Caught onChange event", e.detail.data);
- // The onChange event is fired when a user changes a value in the form
+ form.addEventListener("change", (e) => {
+ console.log("Caught change event", e.target.value);
+ // The change event is fired when a user changes a value in the form
// It does NOT fire on each key stroke.
// This is a good time to update our data object:
- dataDiv.innerHTML = JSON.stringify(e.detail.data, null, 2);
+ dataDiv.innerHTML = JSON.stringify(e.target.value, null, 2);
});
- form.addEventListener("onKeyUp", (e) => {
- console.log("Caught onKeyUp event", e.detail.data);
+ form.addEventListener("keyup", (e) => {
+ console.log("Caught keyup event", e.target.value);
});
diff --git a/examples/javascript.html b/examples/javascript.html
index 6e1ba42..a860b5d 100644
--- a/examples/javascript.html
+++ b/examples/javascript.html
@@ -9,7 +9,8 @@
const dataDiv = document.getElementById("data");
// Load the library, either from CDN or locally:
- const OgrafForm = await import("https://cdn.jsdelivr.net/npm/ograf-form/dist/main.js");
+ // const OgrafForm = await import("https://cdn.jsdelivr.net/npm/ograf-form");
+ const OgrafForm = await import("/dist/main.js");
// const OgrafForm = await import("ograf-form");
// Define a OGraf/GDD JSON-schema:
@@ -30,20 +31,20 @@
// Initialize the Form:
const form = new OgrafForm.SuperFlyTvOgrafDataForm();
- form.addEventListener("onChange", (e) => {
- console.log("Caught onChange event", JSON.stringify(e.detail));
- // The onChange event is fired when a user changes a value in the form
+ form.addEventListener("change", (e) => {
+ console.log("Caught change event", e.target.value, e.detail);
+ // The change event is fired when a user changes a value in the form
// It does NOT fire on each key stroke.
// This is a good time to update our data object:
- dataDiv.innerHTML = JSON.stringify(e.detail.data, null, 2);
+ dataDiv.innerHTML = JSON.stringify(e.target.value, null, 2);
});
- form.addEventListener("onKeyUp", (e) => {
- console.log("Caught onKey event", JSON.stringify(e.detail));
- // The onKeyUp event is fired on each key stroke in the form.
- // e.detail.data contains the current data.
+ form.addEventListener("keyup", (e) => {
+ console.log("Caught keyup event", e.target.value, e.detail);
+ // The keyup event is fired on each key stroke in the form.
+ // e.target.value contains the current data.
});
form.schema = exampleSchema;
- form.data = data;
+ form.value = data;
dataDiv.innerHTML = JSON.stringify(data, null, 2);
diff --git a/examples/react.jsx b/examples/react.jsx
index a348377..e18bf60 100644
--- a/examples/react.jsx
+++ b/examples/react.jsx
@@ -19,17 +19,17 @@ export function OGrafForm({ schema, onDataChangeCallback }) {
// Set up listener for when the data has changed in the form:
React.useLayoutEffect(() => {
if (formRef.current) {
- const listener = (e) => onDataChange(e.detail.data);
- formRef.current.addEventListener("onChange", listener);
- return () => formRef.current.removeEventListener("onChange", listener);
+ const listener = (e) => onDataChange(e.target.value);
+ formRef.current.addEventListener("change", listener);
+ return () => formRef.current.removeEventListener("change", listener);
}
});
// (Optional) Set up listener for whenever user is editing (ie every key)
// React.useLayoutEffect(() => {
// if (formRef.current) {
- // const listener = (e) => onDataChange(e.detail.data);
- // formRef.current.addEventListener("onKeyUp", listener);
- // return () => formRef.current.removeEventListener("onKeyUp", listener);
+ // const listener = (e) => onDataChange(e.target.value);
+ // formRef.current.addEventListener("keyup", listener);
+ // return () => formRef.current.removeEventListener("keyup", listener);
// }
// });
@@ -38,7 +38,7 @@ export function OGrafForm({ schema, onDataChangeCallback }) {
);
diff --git a/lib/components/gdd-elements/gdd-elements/color-rrggbb.ts b/lib/components/gdd-elements/gdd-elements/color-rrggbb.ts
index 4bc9084..d16ffb7 100644
--- a/lib/components/gdd-elements/gdd-elements/color-rrggbb.ts
+++ b/lib/components/gdd-elements/gdd-elements/color-rrggbb.ts
@@ -12,11 +12,11 @@ export class GDDColorRRGGBB extends GDDInputBase {
this.elInput.onchange = (e) => {
if (!e.target) return;
- this.emitOnChange((e.target as any).value);
+ this.emitChangeEvent((e.target as any).value);
};
}
- this.elInput.value = this.data || "";
+ this.elInput.value = this.value || "";
this._renderStyle();
return initialRender;
}
diff --git a/lib/components/gdd-elements/gdd-elements/color-rrggbbaa.ts b/lib/components/gdd-elements/gdd-elements/color-rrggbbaa.ts
index b337147..48f4042 100644
--- a/lib/components/gdd-elements/gdd-elements/color-rrggbbaa.ts
+++ b/lib/components/gdd-elements/gdd-elements/color-rrggbbaa.ts
@@ -24,24 +24,30 @@ export class GDDColorRRGGBBAA extends GDDColorRRGGBB {
this.elContentAlpha.onchange = (e) => {
if (!e.target) return;
if (!(e.target instanceof HTMLInputElement)) return;
+ e.stopPropagation();
const int = this._parseInput(e.target.value);
if (int === null) {
// invalid input, revert
e.target.value = `${this._alpha}` || "";
} else {
- if (this.data !== int) {
+ if (this.value !== int) {
this._alpha = int;
- this.emitOnChange(this.getRRGGBBAA(this._rrggbb, this._alpha));
+ this.emitChangeEvent(this.getRRGGBBAA(this._rrggbb, this._alpha));
}
e.target.value = `${int}`;
}
};
this.elContentAlpha.onkeydown = (e) => {
+ if (e.key === "Enter") {
+ e.preventDefault();
+ // Prevent form submission
+ }
if (!e.target) return;
- this.emitOnKeyDown(
+
+ this.emitKeyDownEvent(
e,
(e.target as any).value,
(e.target as any).value
@@ -50,11 +56,12 @@ export class GDDColorRRGGBBAA extends GDDColorRRGGBB {
this.elContentAlpha.onkeyup = (e) => {
if (!e.target) return;
if (!(e.target instanceof HTMLInputElement)) return;
+ e.stopPropagation();
const int = this._parseInput(e.target.value);
if (int === null) return;
- this.emitOnKeyUp(
+ this.emitKeyUpEvent(
e,
e.target.value,
this.getRRGGBBAA(this._rrggbb, int)
@@ -66,13 +73,13 @@ export class GDDColorRRGGBBAA extends GDDColorRRGGBB {
this._rrggbb = (e.target as any).value.replace("#", "");
- this.emitOnChange(this.getRRGGBBAA(this._rrggbb, this._alpha));
+ this.emitChangeEvent(this.getRRGGBBAA(this._rrggbb, this._alpha));
};
}
if (!this.elContentAlpha) return initialRender;
- const rrggbbaa = this.data || "#00000000";
+ const rrggbbaa = this.value || "#00000000";
this._rrggbb = rrggbbaa.slice(1, 7);
this._alpha = parseInt(rrggbbaa.slice(7, 9), 16) / 255;
diff --git a/lib/components/gdd-elements/gdd-elements/file-path.ts b/lib/components/gdd-elements/gdd-elements/file-path.ts
index 387e1b7..bafd927 100644
--- a/lib/components/gdd-elements/gdd-elements/file-path.ts
+++ b/lib/components/gdd-elements/gdd-elements/file-path.ts
@@ -15,24 +15,9 @@ export class GDDFilePath extends GDDInputBase {
this.elInput.accept =
schema.gddOptions.extensions.map((e) => `.${e}`).join(",") || "*";
this.elInput.name = this.path;
-
- // this.elInput.style.fontFamily = "monospace";
-
- this.elInput.onchange = (e) => {
- if (!e.target) return;
- this.emitOnChange((e.target as any).value);
- };
- this.elInput.onkeydown = (e) => {
- if (!e.target) return;
- this.emitOnKeyDown(e, (e.target as any).value, (e.target as any).value);
- };
- this.elInput.onkeyup = (e) => {
- if (!e.target) return;
- this.emitOnKeyUp(e, (e.target as any).value, (e.target as any).value);
- };
}
- this.elInput.value = this.data || "";
+ this.elInput.value = this.value || "";
this._renderStyle();
return initialRender;
diff --git a/lib/components/gdd-elements/gdd-elements/multiline-text.ts b/lib/components/gdd-elements/gdd-elements/multiline-text.ts
index 959b5ef..0af8f9f 100644
--- a/lib/components/gdd-elements/gdd-elements/multiline-text.ts
+++ b/lib/components/gdd-elements/gdd-elements/multiline-text.ts
@@ -12,23 +12,26 @@ export class GDDMultiLineText extends GDDElementBase {
this.elInput.onchange = (e) => {
if (!e.target) return;
- this.emitOnChange((e.target as any).value);
+ if (!(e.target instanceof HTMLTextAreaElement)) return;
+ this.emitChangeEvent(e.target.value);
this._renderStyle();
};
this.elInput.onkeydown = (e) => {
if (!e.target) return;
- this.emitOnKeyDown(e, (e.target as any).value, (e.target as any).value);
+ if (!(e.target instanceof HTMLTextAreaElement)) return;
+ this.emitKeyDownEvent(e, e.target.value, e.target.value);
};
this.elInput.onkeyup = (e) => {
if (!e.target) return;
- this.emitOnKeyUp(e, (e.target as any).value, (e.target as any).value);
+ if (!(e.target instanceof HTMLTextAreaElement)) return;
+ this.emitKeyUpEvent(e, e.target.value, e.target.value);
this._renderStyle();
};
}
this.elInput.name = this.path;
- this.elInput.value = this.data || "";
+ this.elInput.value = this.value || "";
this._renderStyle();
return initialRender;
diff --git a/lib/components/gdd-elements/gdd-elements/percentage.ts b/lib/components/gdd-elements/gdd-elements/percentage.ts
index 29f6d7b..5d8bbd6 100644
--- a/lib/components/gdd-elements/gdd-elements/percentage.ts
+++ b/lib/components/gdd-elements/gdd-elements/percentage.ts
@@ -20,6 +20,10 @@ export class GDDPercentage extends GDDInputNumberBase {
this.elInput.type = "text";
const orgOnKeyDown = this.elInput.onkeydown;
this.elInput.onkeydown = (e) => {
+ if (e.key === "Enter") {
+ e.preventDefault();
+ // Prevent form submission
+ }
if (!this.elInput) return;
if (e.key === "ArrowUp" || e.key === "ArrowDown") {
@@ -75,7 +79,7 @@ export class GDDPercentage extends GDDInputNumberBase {
e.preventDefault();
if (this._isChanged) {
- // because onChange is not emitted if we've only modified using arrow keys:
+ // because change is not emitted if we've only modified using arrow keys:
this.elInput?.onchange?.(e);
}
this._isChanged = false;
diff --git a/lib/components/gdd-elements/gdd-elements/select.ts b/lib/components/gdd-elements/gdd-elements/select.ts
index 3c6c57c..07630e8 100644
--- a/lib/components/gdd-elements/gdd-elements/select.ts
+++ b/lib/components/gdd-elements/gdd-elements/select.ts
@@ -28,7 +28,7 @@ export class GDDSelect extends GDDElementBase {
const option = this._options[(e.target as any).value];
if (option) {
- this.emitOnChange(option.value);
+ this.emitChangeEvent(option.value);
}
};
}
@@ -69,7 +69,7 @@ export class GDDSelect extends GDDElementBase {
if (!(elOption instanceof HTMLOptionElement)) continue;
const option = this._options[elOption.getAttribute("value") || ""];
if (!option) continue;
- elOption.selected = isEqual(this.data, option.value);
+ elOption.selected = isEqual(this.value, option.value);
}
this._renderStyle();
diff --git a/lib/components/gdd-elements/index.ts b/lib/components/gdd-elements/index.ts
index 72a8f98..69ea5f1 100644
--- a/lib/components/gdd-elements/index.ts
+++ b/lib/components/gdd-elements/index.ts
@@ -1,67 +1,16 @@
-import { GDDElementBase } from "./lib/_base.js";
-import { GDDArray } from "./json-elements/array.js";
-import { GDDBoolean } from "./json-elements/boolean.js";
-import { GDDInteger } from "./json-elements/integer.js";
-import { GDDObject } from "./json-elements/object.js";
-import { GDDString } from "./json-elements/string.js";
-import { GDDMultiLineText } from "./gdd-elements/multiline-text.js";
-import { GDDFilePath } from "./gdd-elements/file-path.js";
-import { GDDSelect } from "./gdd-elements/select.js";
-import { GDDColorRRGGBB } from "./gdd-elements/color-rrggbb.js";
-import { GDDColorRRGGBBAA } from "./gdd-elements/color-rrggbbaa.js";
-import { GDDPercentage } from "./gdd-elements/percentage.js";
-import { assertNever } from "../../lib/lib.js";
-import type { GDDSchema, GDDTypeName } from "../../lib/types.js";
-import { GDDNumber } from "./json-elements/number.js";
+export { getBasicType } from "./lib/lib.js";
-export {
- GDDElementBase,
- GDDObject,
- GDDArray,
- GDDString,
- GDDInteger,
- GDDBoolean,
- GDDNumber,
-};
+export { GDDElementBase } from "./lib/_base.js";
+export { GDDArray } from "./json-elements/array.js";
+export { GDDBoolean } from "./json-elements/boolean.js";
+export { GDDInteger } from "./json-elements/integer.js";
+export { GDDObject } from "./json-elements/object.js";
+export { GDDString } from "./json-elements/string.js";
+export { GDDNumber } from "./json-elements/number.js";
-export function getGDDElement(schema: GDDSchema, path: string): GDDElementBase {
- const basicType = getBasicType(schema.type);
-
- const gddType = schema.gddType + "/";
-
- let element: GDDElementBase;
-
- if (basicType === "string" && gddType.startsWith("multi-line("))
- element = new GDDMultiLineText();
- else if (basicType === "string" && gddType.startsWith("file-path/"))
- element = new GDDFilePath();
- else if (
- (basicType === "string" ||
- basicType === "integer" ||
- basicType === "number") &&
- gddType.startsWith("select/")
- )
- element = new GDDSelect();
- else if (basicType === "string" && gddType.startsWith("color-rrggbb/"))
- element = new GDDColorRRGGBB();
- else if (basicType === "string" && gddType.startsWith("color-rrggbbaa/"))
- element = new GDDColorRRGGBBAA();
- else if (basicType === "number" && gddType.startsWith("percentage/"))
- element = new GDDPercentage();
- else if (basicType === "object") element = new GDDObject();
- else if (basicType === "array") element = new GDDArray();
- else if (basicType === "string") element = new GDDString();
- else if (basicType === "integer") element = new GDDInteger();
- else if (basicType === "boolean") element = new GDDBoolean();
- else if (basicType === "number") element = new GDDNumber();
- else {
- assertNever(basicType);
- throw new Error(`Unsupported GDD type: ${basicType}`);
- }
-
- element.path = path;
- return element;
-}
-export function getBasicType(schemaType: GDDSchema["type"]): GDDTypeName {
- return Array.isArray(schemaType) ? schemaType[0] : schemaType;
-}
+export { GDDColorRRGGBB } from "./gdd-elements/color-rrggbb.js";
+export { GDDColorRRGGBBAA } from "./gdd-elements/color-rrggbbaa.js";
+export { GDDFilePath } from "./gdd-elements/file-path.js";
+export { GDDMultiLineText } from "./gdd-elements/multiline-text.js";
+export { GDDPercentage } from "./gdd-elements/percentage.js";
+export { GDDSelect } from "./gdd-elements/select.js";
diff --git a/lib/components/gdd-elements/json-elements/_inputBase.ts b/lib/components/gdd-elements/json-elements/_inputBase.ts
index 09f5026..ab03614 100644
--- a/lib/components/gdd-elements/json-elements/_inputBase.ts
+++ b/lib/components/gdd-elements/json-elements/_inputBase.ts
@@ -10,11 +10,28 @@ export abstract class GDDInputBase extends GDDElementBase {
this.elInput = document.createElement("input");
this.appendChild(this.elInput);
+ this.elInput.name = this.path;
+ this.elInput.onchange = (e) => {
+ if (!e.target) return;
+ if (!(e.target instanceof HTMLInputElement)) return;
+ e.stopPropagation();
+ this.emitChangeEvent(e.target.value);
+ };
this.elInput.onkeydown = (e) => {
if (e.key === "Enter") {
e.preventDefault();
// Prevent form submission
}
+ if (!e.target) return;
+ if (!(e.target instanceof HTMLInputElement)) return;
+ e.stopPropagation();
+ this.emitKeyDownEvent(e, e.target.value, e.target.value);
+ };
+ this.elInput.onkeyup = (e) => {
+ if (!e.target) return;
+ if (!(e.target instanceof HTMLInputElement)) return;
+ e.stopPropagation();
+ this.emitKeyUpEvent(e, e.target.value, e.target.value);
};
}
@@ -26,4 +43,12 @@ export abstract class GDDInputBase extends GDDElementBase {
}
return initialRender;
}
+ destroy(): void {
+ super.destroy();
+ if (this.elInput) {
+ this.elInput.onchange = null;
+ this.elInput.onkeydown = null;
+ this.elInput.onkeyup = null;
+ }
+ }
}
diff --git a/lib/components/gdd-elements/json-elements/_inputNumberBase.ts b/lib/components/gdd-elements/json-elements/_inputNumberBase.ts
index fece6ab..201de59 100644
--- a/lib/components/gdd-elements/json-elements/_inputNumberBase.ts
+++ b/lib/components/gdd-elements/json-elements/_inputNumberBase.ts
@@ -13,38 +13,43 @@ export abstract class GDDInputNumberBase extends GDDInputBase {
this.elInput.onchange = (e) => {
if (!e.target) return;
if (!(e.target instanceof HTMLInputElement)) return;
+ e.stopPropagation();
const input = e.target.value;
const value = this._parseInput(input);
if (value === null) {
// invalid input, revert
- e.target.value = this._stringifyValue(this.data);
+ e.target.value = this._stringifyValue(this.value);
} else {
- if (this.data !== value) {
- this.data = value;
- this.emitOnChange(this.data);
+ if (this.value !== value) {
+ this.value = value;
+ this.emitChangeEvent(this.value);
}
e.target.value = this._stringifyValue(value);
}
};
this.elInput.onkeydown = (e) => {
+ if (e.key === "Enter") {
+ e.preventDefault();
+ // Prevent form submission
+ }
if (!e.target) return;
- if (!(e.target instanceof HTMLInputElement)) return;
+ if (!(e.target instanceof HTMLInputElement)) return;
+ e.stopPropagation();
const input = e.target.value;
-
- this.emitOnKeyDown(e, input, this._parseInput(input));
+ this.emitKeyDownEvent(e, input, this._parseInput(input));
};
this.elInput.onkeyup = (e) => {
if (!e.target) return;
if (!(e.target instanceof HTMLInputElement)) return;
+ e.stopPropagation();
const input = e.target.value;
-
- this.emitOnKeyUp(e, input, this._parseInput(input));
+ this.emitKeyUpEvent(e, input, this._parseInput(input));
};
}
- this.elInput.value = this._stringifyValue(this.data);
+ this.elInput.value = this._stringifyValue(this.value);
return initialRender;
}
diff --git a/lib/components/gdd-elements/json-elements/array.ts b/lib/components/gdd-elements/json-elements/array.ts
index b3189e4..78127c0 100644
--- a/lib/components/gdd-elements/json-elements/array.ts
+++ b/lib/components/gdd-elements/json-elements/array.ts
@@ -1,7 +1,6 @@
import { getDefaultDataFromSchema } from "../../../lib/default-data.js";
-import { last } from "../../../lib/lib.js";
-import { GDDElementBase, isCustomEvent } from "../lib/_base.js";
-import { getGDDElement } from "../index.js";
+import { nthInIterable } from "../../../lib/lib.js";
+import { GDDElementBase, isCustomEventKey } from "../lib/_base.js";
import { renderContentError } from "../lib/lib.js";
export class GDDArray extends GDDElementBase {
@@ -14,14 +13,20 @@ export class GDDArray extends GDDElementBase {
if (!this.elContent) {
initialRender = true;
if (this.schema.items.type === "object" && this.schema.items.properties) {
- this.elContent = new GDDTable();
+ this.elContent = new GDDTable({
+ path: this.path,
+ getGDDElement: this.getGDDElement,
+ });
} else {
- this.elContent = new GDDSimpleArray();
+ this.elContent = new GDDSimpleArray({
+ path: this.path,
+ getGDDElement: this.getGDDElement,
+ });
}
this.appendChild(this.elContent);
}
this.elContent.update({
- data: this.data,
+ value: this.value,
schema: this.schema,
renderOptions: this.renderOptions,
});
@@ -34,24 +39,28 @@ export class GDDSimpleArray extends GDDElementBase {
itemsContainer: HTMLDivElement;
items: {
container: HTMLDivElement;
- element: GDDElementBase;
+ element: GDDArrayProperty;
removeButton: HTMLButtonElement;
+ index: number;
}[];
} | null = null;
- private addItem() {
+ private addItem(index = -1): number | undefined {
if (!this.schema?.items) return;
const schemaItems = this.schema.items;
const defaultData = getDefaultDataFromSchema(schemaItems);
- this.data.push(defaultData);
+ if (index < 0) index = this.value.length - index;
+
+ this.value.splice(index, 0, defaultData);
this.render();
- this.emitOnChange(this.data);
+ this.emitChangeEvent(this.value);
+ return index;
}
private removeItem(e: Event, index: number) {
- this.data.splice(index, 1);
- this.emitOnKeyUp(e, "", this.data);
- this.emitOnChange(this.data);
+ this.value.splice(index, 1);
+ this.emitKeyUpEvent(e, "", this.value);
+ this.emitChangeEvent(this.value);
this.render();
}
@@ -61,7 +70,7 @@ export class GDDSimpleArray extends GDDElementBase {
if (!this.schema.items) return false;
const schemaItems = this.schema.items;
- if (!this.data) this.data = [];
+ if (!this.value) this.value = [];
if (!this.content) {
initialRender = true;
@@ -80,91 +89,107 @@ export class GDDSimpleArray extends GDDElementBase {
this.appendChild(this.content.addButton);
}
- for (const item of this.content.items.splice(this.data.length)) {
+ // Remove extra items
+ for (const item of this.content.items.splice(this.value.length)) {
+ item.element.destroy();
this.content.itemsContainer.removeChild(item.container);
}
- for (let i = 0; i < this.data.length; i++) {
+ for (let i = 0; i < this.value.length; i++) {
+ const itemPath = `${this.path}[${i}]`;
if (!this.content.items[i]) {
const container = document.createElement("div");
container.className = "gdd-simple-array-item";
const removeButton = document.createElement("button");
- removeButton.tabIndex = -1;
+ const element = new GDDArrayProperty({
+ path: itemPath,
+ getGDDElement: this.getGDDElement,
+ });
- removeButton.onclick = (e: Event) => {
- e.preventDefault();
- this.removeItem(e, i);
+ const contentItem = {
+ container,
+ removeButton,
+ element,
+ index: i,
};
+ this.content.items.push(contentItem);
- const element = new GDDArrayProperty();
- element.path = `${this.path}[${i}]`;
-
- element.addEventListener("onChange", (e: Event) => {
+ contentItem.element.addListener("change", (e: Event) => {
if (!(e instanceof CustomEvent)) return;
e.stopPropagation();
- this.data[i] = e.detail.data;
- this.emitOnChange(this.data);
+ this.value[contentItem.index] = e.detail.value;
+
+ this.emitChangeEvent(this.value);
});
- element.addEventListener("onKeyUp", (e: Event) => {
- if (!isCustomEvent(e)) return;
+ contentItem.element.addListener("keydown", (e: Event) => {
+ if (!isCustomEventKey(e)) return;
+ keyDownValueStr = e.detail.valueStr;
+ });
+ contentItem.element.addListener("keyup", (e: Event) => {
+ if (!isCustomEventKey(e)) return;
if (e.detail.key === "Enter") {
// Add another item
- this.addItem();
- last(
- this.content?.itemsContainer.querySelectorAll(
- "input, textarea"
- )
- )?.focus();
- // } else if (e.detail.key === "Backspace" && e.detail.inputStr === "") {
- // // Remove the item
- // this.removeItem(e, i);
-
- // last(
- // this.content?.itemsContainer.querySelectorAll(
- // "input, textarea"
- // )
- // )?.focus();
- } else {
e.stopPropagation();
- this.data[i] = e.detail.data;
- this.emitOnKeyUp(e, "", this.data);
- }
- });
- element.addEventListener("onKeyDown", (e: Event) => {
- if (!isCustomEvent(e)) return;
- if (e.detail.key === "Backspace" && e.detail.inputStr === "") {
+ const newIndex = this.addItem(contentItem.index + 1);
+
+ if (newIndex !== undefined) {
+ nthInIterable(
+ this.content?.itemsContainer.querySelectorAll(
+ "input, textarea"
+ ),
+ newIndex
+ )?.focus();
+ }
+ } else if (
+ e.detail.key === "Backspace" &&
+ e.detail.valueStr === "" &&
+ keyDownValueStr === ""
+ ) {
+ e.preventDefault();
+ e.stopPropagation();
// Remove the item
- this.removeItem(e, i);
- last(
+ this.removeItem(e, contentItem.index);
+
+ nthInIterable(
this.content?.itemsContainer.querySelectorAll(
"input, textarea"
- )
+ ),
+ Math.max(0, contentItem.index - 1)
)?.focus();
+ } else {
+ e.stopPropagation();
+ this.value[contentItem.index] = e.detail.value;
+ this.emitKeyUpEvent(e, "", this.value);
}
});
+ removeButton.tabIndex = -1;
+ removeButton.onclick = (e: Event) => {
+ e.preventDefault();
+ this.removeItem(e, contentItem.index);
+ };
+
+ let keyDownValueStr = `${this.value[contentItem.index]}`;
+
container.appendChild(removeButton);
container.appendChild(element);
this.content.itemsContainer.appendChild(container);
-
- this.content.items.push({
- container,
- removeButton,
- element,
- });
}
const item = this.content.items[i];
+ item.element.path = itemPath;
+
+ item.index = i;
item.removeButton.textContent = this.renderOptions.dictionary.removeItem;
item.removeButton.title =
this.renderOptions.dictionary.removeItemDescription;
item.element.update({
- data: this.data[i],
+ value: this.value[i],
schema: schemaItems,
renderOptions: this.renderOptions,
});
@@ -212,7 +237,7 @@ export class GDDTable extends GDDElementBase {
if (!this.schema.items) return initialRender;
const schemaItems = this.schema.items;
- if (!this.data) this.data = [];
+ if (!this.value) this.value = [];
if (!this.content) {
initialRender = true;
@@ -227,23 +252,23 @@ export class GDDTable extends GDDElementBase {
this.content.elAddButton.onclick = (e) => {
e.preventDefault();
- this.data.push(getDefaultDataFromSchema(schemaItems));
+ this.value.push(getDefaultDataFromSchema(schemaItems));
this.render();
- this.emitOnChange(this.data);
+ this.emitChangeEvent(this.value);
};
this.appendChild(this.content.elAddButton);
}
this._renderTopRow(this.content.elTrTop);
- for (const elLine of this.content.rows.splice(this.data.length)) {
+ for (const elLine of this.content.rows.splice(this.value.length)) {
// for (const row of elLine.rows) {
// row.element.removeEventListener()
// }
this.content.elTable.removeChild(elLine.tr);
}
- for (let i = 0; i < this.data.length; i++) {
+ for (let i = 0; i < this.value.length; i++) {
if (!this.content.rows[i]) {
const tr = document.createElement("tr");
const removeButton = document.createElement("button");
@@ -257,9 +282,9 @@ export class GDDTable extends GDDElementBase {
tdEnd.onclick = (e: Event) => {
e.preventDefault();
- this.data.splice(i, 1);
- this.emitOnKeyUp(e, "", this.data);
- this.emitOnChange(this.data);
+ this.value.splice(i, 1);
+ this.emitKeyUpEvent(e, "", this.value);
+ this.emitChangeEvent(this.value);
this.render();
};
@@ -280,19 +305,21 @@ export class GDDTable extends GDDElementBase {
const properties = Object.entries(schemaItems.properties);
for (const field of contentRow.fields.splice(properties.length)) {
+ field.element.destroy();
contentRow.tr.removeChild(field.td);
- // field.element.removeEventListener()
}
for (let j = 0; j < properties.length; j++) {
const key = properties[j][0];
const schema = properties[j][1];
- const data = this.data[i][key];
+ const value = this.value[i][key];
if (!contentRow.fields[j]) {
const td = document.createElement("td");
- const el = new GDDArrayProperty();
- el.path = `${this.path}[${i}].${key}`;
+ const el = new GDDArrayProperty({
+ path: `${this.path}[${i}].${key}`,
+ getGDDElement: this.getGDDElement,
+ });
contentRow.fields.push({
td,
element: el,
@@ -300,25 +327,25 @@ export class GDDTable extends GDDElementBase {
td.appendChild(el);
contentRow.tr.appendChild(td);
- el.addEventListener("onChange", (e: Event) => {
+ el.addListener("change", (e: Event) => {
if (!(e instanceof CustomEvent)) return;
e.stopPropagation();
- this.data[i][key] = e.detail.data;
+ this.value[i][key] = e.detail.value;
- this.emitOnChange(this.data);
+ this.emitChangeEvent(this.value);
});
- el.addEventListener("onKeyUp", (e: Event) => {
+ el.addListener("keyup", (e: Event) => {
if (!(e instanceof CustomEvent)) return;
e.stopPropagation();
- this.data[i][key] = e.detail.data;
- this.emitOnKeyUp(e, "", this.data);
+ this.value[i][key] = e.detail.value;
+ this.emitKeyUpEvent(e, "", this.value);
});
}
const field = contentRow.fields[j];
field.element.update({
schema: schema,
- data: data,
+ value: value,
renderOptions: this.renderOptions,
});
}
@@ -329,12 +356,6 @@ export class GDDTable extends GDDElementBase {
contentRow.removeButton.textContent =
this.renderOptions.dictionary.removeRow;
-
- // this.content.rows[i].element.update({
- // schema: schemaItems,
- // data: rowData,
- // renderOptions: this.renderOptions,
- // });
}
this.content.elAddButton.textContent = this.renderOptions.dictionary.addRow;
@@ -342,6 +363,16 @@ export class GDDTable extends GDDElementBase {
this._renderStyle();
return initialRender;
}
+ destroy(): void {
+ super.destroy();
+ if (this.content) {
+ for (const row of this.content.rows) {
+ for (const field of row.fields) {
+ field.element.destroy();
+ }
+ }
+ }
+ }
private _renderStyle() {
if (this.renderOptions.formStyle === "default") {
} else {
@@ -353,7 +384,7 @@ export class GDDTable extends GDDElementBase {
elParent.innerHTML = "";
- if (this.data.length === 0) return;
+ if (this.value.length === 0) return;
const elTh = document.createElement("th");
elParent.appendChild(elTh);
@@ -372,56 +403,65 @@ export class GDDTable extends GDDElementBase {
}
export class GDDArrayProperty extends GDDElementBase {
private content: {
- content: GDDElementBase;
+ element: GDDElementBase;
contentError: HTMLDivElement;
} | null = null;
- constructor() {
- super();
- }
connectedCallback(): void {}
+ destroy() {
+ super.destroy();
+ if (this.content) {
+ this.content.element.destroy();
+ }
+ }
+
render(): boolean {
let initialRender = false;
if (!this.schema) return initialRender;
if (!this.content) {
+ const element = this.getGDDElement({
+ schema: this.schema,
+ path: this.path,
+ getGDDElement: this.getGDDElement,
+ });
this.content = {
- content: getGDDElement(this.schema, this.path),
+ element,
contentError: document.createElement("div"),
};
// Listen to events to update validation error:
- this.content.content.addEventListener("onChange", (e: any) => {
- if (isCustomEvent(e) && this.content && this.schema) {
+ this.content.element.addListener("change", (e: any) => {
+ if (isCustomEventKey(e) && this.content && this.schema) {
renderContentError(
this.content.contentError,
this.schema,
- e.detail.data
+ e.detail.value
);
}
});
- this.content.content.addEventListener("onKeyUp", (e: any) => {
- if (isCustomEvent(e) && this.content && this.schema) {
+ this.content.element.addListener("keyup", (e: any) => {
+ if (isCustomEventKey(e) && this.content && this.schema) {
renderContentError(
this.content.contentError,
this.schema,
- e.detail.data
+ e.detail.value
);
}
});
- this.appendChild(this.content.content);
+ this.appendChild(this.content.element);
}
- this.content.content.update({
+ this.content.element.update({
schema: this.schema,
- data: this.data,
+ value: this.value,
renderOptions: this.renderOptions,
});
// this.content.label.textContent = this.schema.title || "";
// this.content.label.title = this.schema.description || "";
- renderContentError(this.content.contentError, this.schema, this.data);
+ renderContentError(this.content.contentError, this.schema, this.value);
this._renderStyle();
return initialRender;
diff --git a/lib/components/gdd-elements/json-elements/boolean.ts b/lib/components/gdd-elements/json-elements/boolean.ts
index 3edcfd3..ba49f5d 100644
--- a/lib/components/gdd-elements/json-elements/boolean.ts
+++ b/lib/components/gdd-elements/json-elements/boolean.ts
@@ -12,18 +12,19 @@ export class GDDBoolean extends GDDInputBase {
this.elInput.onchange = (e) => {
if (!e.target) return;
if (!(e.target instanceof HTMLInputElement)) return;
+ e.stopPropagation();
const checked = Boolean(e.target.checked);
- if (this.data !== checked) {
- this.data = checked;
- this.emitOnKeyUp(e, "", checked);
- this.emitOnChange(this.data);
+ if (this.value !== checked) {
+ this.value = checked;
+ this.emitKeyUpEvent(e, "", checked);
+ this.emitChangeEvent(this.value);
}
- e.target.checked = Boolean(this.data);
+ e.target.checked = Boolean(this.value);
};
}
- this.elInput.checked = Boolean(this.data);
+ this.elInput.checked = Boolean(this.value);
this._renderStyle();
return initialRender;
diff --git a/lib/components/gdd-elements/json-elements/object.ts b/lib/components/gdd-elements/json-elements/object.ts
index 2462047..e4605a9 100644
--- a/lib/components/gdd-elements/json-elements/object.ts
+++ b/lib/components/gdd-elements/json-elements/object.ts
@@ -1,16 +1,11 @@
-import { GDDElementBase, isCustomEvent } from "../lib/_base.js";
-import { getBasicType, getGDDElement } from "../index.js";
-import { renderContentError } from "../lib/lib.js";
+import { GDDElementBase, isCustomEventKey } from "../lib/_base.js";
+import { getBasicType, renderContentError } from "../lib/lib.js";
export class GDDObject extends GDDElementBase {
private content: Record = {};
- constructor() {
- super();
- }
-
render(): boolean {
- if (!this.data) this.data = {};
+ if (!this.value) this.value = {};
const properties = Object.entries(this.schema?.properties ?? {}).sort(
(a, b) => {
@@ -27,39 +22,44 @@ export class GDDObject extends GDDElementBase {
for (const [key, schema] of properties) {
existingKeys.add(key);
if (!this.content[key]) {
- const el = new GDDObjectProperty();
- el.path = `${this.path}.${key}`;
+ const el = new GDDObjectProperty({
+ path: `${this.path}.${key}`,
+ getGDDElement: this.getGDDElement,
+ });
this.content[key] = el;
this.appendChild(el);
- el.addEventListener("onChange", (e: Event) => {
+ el.addListener("change", (e: Event) => {
if (!(e instanceof CustomEvent)) return;
e.stopPropagation();
- this.data[key] = e.detail.data;
+ this.value[key] = e.detail.value;
- this.emitOnChange(this.data);
+ this.emitChangeEvent(this.value);
});
- el.addEventListener("onKeyUp", (e: Event) => {
+ el.addListener("keyup", (e: Event) => {
if (!(e instanceof CustomEvent)) return;
e.stopPropagation();
- this.data[key] = e.detail.data;
- this.emitOnKeyUp(e, "