[ini]
This commit is contained in:
commit
999c2981a6
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
/.geany
|
||||||
|
/build/
|
||||||
24
misc/example-1.frm.json
Normal file
24
misc/example-1.frm.json
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"type": "group",
|
||||||
|
"members": {
|
||||||
|
"active": {
|
||||||
|
"type": "checkbox",
|
||||||
|
"label": "aktiv"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "text",
|
||||||
|
"label": "Name"
|
||||||
|
},
|
||||||
|
"ranks": {
|
||||||
|
"type": "list",
|
||||||
|
"element": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"label": "Rang"
|
||||||
|
},
|
||||||
|
"color": {
|
||||||
|
"type": "color",
|
||||||
|
"label": "Farbe"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
source/logic/base.ts
Normal file
1
source/logic/base.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
type int = number;
|
||||||
75
source/logic/form.ts
Normal file
75
source/logic/form.ts
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
namespace formgen
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
type type_action = {
|
||||||
|
label : string;
|
||||||
|
target : string;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export class class_form<type_value>
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
private input : formgen.input.interface_input<type_value>;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
private actions : Array<type_action>;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public constructor(
|
||||||
|
input : formgen.input.interface_input<type_value>,
|
||||||
|
actions : Array<type_action>
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this.input = input;
|
||||||
|
this.actions = actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public async setup(
|
||||||
|
element_target : Element
|
||||||
|
) : Promise<void>
|
||||||
|
{
|
||||||
|
const element_form : Element = document.createElement("form");
|
||||||
|
element_form.classList.add("formgen-form");
|
||||||
|
// element_form.setAttribute("method", "POST");
|
||||||
|
// input
|
||||||
|
{
|
||||||
|
const element_input : Element = document.createElement("div");
|
||||||
|
element_input.classList.add("formgen-form-input");
|
||||||
|
await this.input.setup(element_input);
|
||||||
|
element_form.appendChild(element_input);
|
||||||
|
}
|
||||||
|
// actions
|
||||||
|
{
|
||||||
|
const element_actions : Element = document.createElement("div");
|
||||||
|
element_actions.classList.add("formgen-form-actions");
|
||||||
|
for (const action of this.actions)
|
||||||
|
{
|
||||||
|
const element_action : Element = document.createElement("button");
|
||||||
|
element_action.classList.add("formgen-form-button");
|
||||||
|
element_action.setAttribute("formaction", action.target);
|
||||||
|
element_action.textContent = action.label;
|
||||||
|
element_actions.appendChild(element_action);
|
||||||
|
}
|
||||||
|
element_form.appendChild(element_actions);
|
||||||
|
}
|
||||||
|
element_target.appendChild(element_form);
|
||||||
|
return Promise.resolve<void>(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
19
source/logic/helpers/call.ts
Normal file
19
source/logic/helpers/call.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
namespace formgen.helpers.call
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function convey(
|
||||||
|
x : unknown,
|
||||||
|
fs : Array<Function>
|
||||||
|
) : unknown
|
||||||
|
{
|
||||||
|
let y : unknown = x;
|
||||||
|
for (const f of fs)
|
||||||
|
{
|
||||||
|
y = f(y);
|
||||||
|
}
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
17
source/logic/helpers/file.ts
Normal file
17
source/logic/helpers/file.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
namespace formgen.helpers.file
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo handle errors
|
||||||
|
*/
|
||||||
|
export function read_text(
|
||||||
|
path : string
|
||||||
|
) : Promise<string>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
fetch(path)
|
||||||
|
.then(x => x.text())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
14
source/logic/helpers/json.ts
Normal file
14
source/logic/helpers/json.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
namespace formgen.helpers.json
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo handle errors
|
||||||
|
*/
|
||||||
|
export function decode(
|
||||||
|
json : string
|
||||||
|
) : unknown
|
||||||
|
{
|
||||||
|
return JSON.parse(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
16
source/logic/helpers/list.ts
Normal file
16
source/logic/helpers/list.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
namespace formgen.helpers.list
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function transform<type_element_from, type_element_to>(
|
||||||
|
list : Array<type_element_from>,
|
||||||
|
function_ : ((type_element_from) => type_element_to)
|
||||||
|
) : Array<type_element_to>
|
||||||
|
{
|
||||||
|
return list.map(
|
||||||
|
(element) => function_(element)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
42
source/logic/helpers/map.ts
Normal file
42
source/logic/helpers/map.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
namespace formgen.helpers.map
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function read<type_value>(
|
||||||
|
map : Record<string, type_value>,
|
||||||
|
key : string,
|
||||||
|
fallback : type_value
|
||||||
|
) : type_value
|
||||||
|
{
|
||||||
|
return ((key in map) ? map[key] : fallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function transform<type_value_from, type_value_to>(
|
||||||
|
map : Record<string, type_value_from>,
|
||||||
|
function_ : ((string, type_value_from) => type_value_to)
|
||||||
|
) : Record<string, type_value_to>
|
||||||
|
{
|
||||||
|
return Object.fromEntries(
|
||||||
|
Object.entries(map)
|
||||||
|
.map(([key, value]) => ([key, function_(key, value)]))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function to_pairs<type_value>(
|
||||||
|
map : Record<string, type_value>,
|
||||||
|
) : Array<{key : string; value : type_value;}>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
Object.entries(map)
|
||||||
|
.map(([key, value]) => ({"key": key, "value": value}))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
37
source/logic/helpers/string.ts
Normal file
37
source/logic/helpers/string.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
namespace formgen.helpers.string
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
let _index : int = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function coin(
|
||||||
|
template : string,
|
||||||
|
arguments_ : Record<string,string>
|
||||||
|
) : string
|
||||||
|
{
|
||||||
|
let result = template;
|
||||||
|
for (const [key, value] of Object.entries(arguments_))
|
||||||
|
{
|
||||||
|
result = result.replace(
|
||||||
|
new RegExp("{{" + key + "}}", "g"),
|
||||||
|
value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function generate(
|
||||||
|
) : string
|
||||||
|
{
|
||||||
|
_index += 1;
|
||||||
|
return _index.toFixed(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
94
source/logic/input/_factory.ts
Normal file
94
source/logic/input/_factory.ts
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
namespace formgen.input
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function from_raw(
|
||||||
|
raw : any
|
||||||
|
) : interface_input<unknown>
|
||||||
|
{
|
||||||
|
switch (raw.type)
|
||||||
|
{
|
||||||
|
case "checkbox":
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
new class_input_checkbox(
|
||||||
|
{
|
||||||
|
"label": formgen.helpers.map.read<(null | string)>(raw, "label", null),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "number":
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
new class_input_number(
|
||||||
|
{
|
||||||
|
"label": formgen.helpers.map.read<(null | string)>(raw, "label", null),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "text":
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
new class_input_text(
|
||||||
|
{
|
||||||
|
"label": formgen.helpers.map.read<(null | string)>(raw, "label", null),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "color":
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
new class_input_color(
|
||||||
|
{
|
||||||
|
"label": formgen.helpers.map.read<(null | string)>(raw, "label", null),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "list":
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
new class_input_list(
|
||||||
|
() => from_raw(raw.element),
|
||||||
|
{
|
||||||
|
"label": formgen.helpers.map.read<(null | string)>(raw, "label", null),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "group":
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
new class_input_group(
|
||||||
|
formgen.helpers.list.transform(
|
||||||
|
formgen.helpers.map.to_pairs(raw.members),
|
||||||
|
(pair) => ({
|
||||||
|
"name": pair.key,
|
||||||
|
"input": from_raw(pair.value),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
{
|
||||||
|
"label": formgen.helpers.map.read<(null | string)>(raw, "label", null),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
throw (new Error("unhandled type: " + raw.type));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
33
source/logic/input/_interface.ts
Normal file
33
source/logic/input/_interface.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
namespace formgen.input
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export interface interface_input<type_value>
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
setup(
|
||||||
|
target : Element
|
||||||
|
) : Promise<void>
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
write(
|
||||||
|
value : type_value
|
||||||
|
) : Promise<void>
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
read(
|
||||||
|
) : Promise<type_value>
|
||||||
|
;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
113
source/logic/input/checkbox.ts
Normal file
113
source/logic/input/checkbox.ts
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
namespace formgen.input
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export class class_input_checkbox implements interface_input<boolean>
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
private additional_classes : Array<string>;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
private label : (null | string);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
private element_input : (null | Element);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public constructor(
|
||||||
|
{
|
||||||
|
"additional_classes": additional_classes = [],
|
||||||
|
"label": label = null,
|
||||||
|
}
|
||||||
|
:
|
||||||
|
{
|
||||||
|
additional_classes ?: Array<string>,
|
||||||
|
label ?: (null | string);
|
||||||
|
}
|
||||||
|
=
|
||||||
|
{
|
||||||
|
}
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this.additional_classes = ["formgen-input-checkbox"].concat(additional_classes);
|
||||||
|
this.label = label;
|
||||||
|
this.element_input = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [implementation]
|
||||||
|
*/
|
||||||
|
public setup(
|
||||||
|
target : Element
|
||||||
|
) : Promise<void>
|
||||||
|
{
|
||||||
|
const id : string = formgen.helpers.string.generate();
|
||||||
|
|
||||||
|
const element_container : Element = document.createElement("div");
|
||||||
|
element_container.classList.add("formgen-input");
|
||||||
|
for (const class_ of this.additional_classes)
|
||||||
|
{
|
||||||
|
element_container.classList.add(class_);
|
||||||
|
}
|
||||||
|
// label
|
||||||
|
{
|
||||||
|
if (this.label === null)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const element_label : Element = document.createElement("label");
|
||||||
|
element_label.setAttribute("for", id);
|
||||||
|
element_label.textContent = this.label;
|
||||||
|
element_container.appendChild(element_label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// input
|
||||||
|
{
|
||||||
|
const element_input : Element = document.createElement("input");
|
||||||
|
element_input.setAttribute("type", "checkbox");
|
||||||
|
element_input.setAttribute("id", id);
|
||||||
|
element_container.appendChild(element_input);
|
||||||
|
this.element_input = element_input;
|
||||||
|
}
|
||||||
|
target.appendChild(element_container);
|
||||||
|
return Promise.resolve<void>(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [implementation]
|
||||||
|
*/
|
||||||
|
public read(
|
||||||
|
) : Promise<boolean>
|
||||||
|
{
|
||||||
|
const value : boolean = (this.element_input as HTMLInputElement).checked;
|
||||||
|
return Promise.resolve<boolean>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [implementation]
|
||||||
|
*/
|
||||||
|
public write(
|
||||||
|
value : boolean
|
||||||
|
) : Promise<void>
|
||||||
|
{
|
||||||
|
(this.element_input as HTMLInputElement).checked = value;
|
||||||
|
return Promise.resolve<void>(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
40
source/logic/input/color.ts
Normal file
40
source/logic/input/color.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
namespace formgen.input
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo dedicated color type
|
||||||
|
*/
|
||||||
|
export class class_input_color extends class_input_simple<string>
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public constructor(
|
||||||
|
{
|
||||||
|
"additional_classes": additional_classes = [],
|
||||||
|
"label": label = null,
|
||||||
|
}
|
||||||
|
:
|
||||||
|
{
|
||||||
|
additional_classes ?: Array<string>,
|
||||||
|
label ?: (null | string);
|
||||||
|
}
|
||||||
|
=
|
||||||
|
{
|
||||||
|
}
|
||||||
|
)
|
||||||
|
{
|
||||||
|
super(
|
||||||
|
"color",
|
||||||
|
(value) => value,
|
||||||
|
(raw) => raw,
|
||||||
|
{
|
||||||
|
"label": label,
|
||||||
|
"additional_classes": ["formgen-input-color"].concat(additional_classes),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
98
source/logic/input/group.ts
Normal file
98
source/logic/input/group.ts
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
namespace formgen.input
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
type type_field<type_value> = {
|
||||||
|
name : string;
|
||||||
|
input : interface_input<type_value>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export class class_input_group implements interface_input<Record<string, any>>
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
private fields : Array<type_field<any>>;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
private label : (null | string);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public constructor(
|
||||||
|
fields : Array<type_field<any>>,
|
||||||
|
{
|
||||||
|
"label": label = null,
|
||||||
|
}
|
||||||
|
:
|
||||||
|
{
|
||||||
|
label ?: (null | string);
|
||||||
|
}
|
||||||
|
=
|
||||||
|
{
|
||||||
|
}
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this.fields = fields;
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [implementation]
|
||||||
|
*/
|
||||||
|
public async setup(
|
||||||
|
element_target : Element
|
||||||
|
) : Promise<void>
|
||||||
|
{
|
||||||
|
const element_container : Element = document.createElement("div");
|
||||||
|
element_container.classList.add("formgen-input");
|
||||||
|
element_container.classList.add("formgen-input-group");
|
||||||
|
for (const field of this.fields)
|
||||||
|
{
|
||||||
|
const element_field : Element = document.createElement("div");
|
||||||
|
element_field.classList.add("formgen-input-group-field");
|
||||||
|
element_field.setAttribute("rel", field.name);
|
||||||
|
await field.input.setup(element_field);
|
||||||
|
element_container.appendChild(element_field);
|
||||||
|
}
|
||||||
|
element_target.appendChild(element_container);
|
||||||
|
return Promise.resolve<void>(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [implementation]
|
||||||
|
*/
|
||||||
|
public async read(
|
||||||
|
) : Promise<Record<string, any>>
|
||||||
|
{
|
||||||
|
let result : Record<string, any> = {};
|
||||||
|
for (const field of this.fields)
|
||||||
|
{
|
||||||
|
result[field.name] = await field.input.read();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [implementation]
|
||||||
|
*/
|
||||||
|
public write(
|
||||||
|
value : Record<string, any>
|
||||||
|
) : Promise<void>
|
||||||
|
{
|
||||||
|
return Promise.reject(new Error("not implemented"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
191
source/logic/input/list.ts
Normal file
191
source/logic/input/list.ts
Normal file
|
|
@ -0,0 +1,191 @@
|
||||||
|
namespace formgen.input
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
type type_entry<type_element> = {
|
||||||
|
id : string;
|
||||||
|
input : interface_input<type_element>;
|
||||||
|
element : Element;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export class class_input_list<type_element> implements interface_input<Array<type_element>>
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
private element_input_factory : (() => interface_input<type_element>);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
private label : (null | string);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
private element_entries : (null | Element);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
private entries : Array<type_entry<type_element>>;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public constructor(
|
||||||
|
element_input_factory : (() => interface_input<type_element>),
|
||||||
|
{
|
||||||
|
"label": label = null,
|
||||||
|
}
|
||||||
|
:
|
||||||
|
{
|
||||||
|
label ?: (null | string);
|
||||||
|
}
|
||||||
|
=
|
||||||
|
{
|
||||||
|
}
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this.element_input_factory = element_input_factory;
|
||||||
|
this.label = label;
|
||||||
|
this.element_entries = null;
|
||||||
|
this.entries = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
private remove(
|
||||||
|
id : string
|
||||||
|
) : void
|
||||||
|
{
|
||||||
|
const index : int = this.entries.findIndex(entry => (entry.id === id));
|
||||||
|
this.entries[index].element.remove();
|
||||||
|
this.entries.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
private async add(
|
||||||
|
) : Promise<void>
|
||||||
|
{
|
||||||
|
const id : string = formgen.helpers.string.generate();
|
||||||
|
const input : interface_input<type_element> = this.element_input_factory();
|
||||||
|
const element_entry : Element = document.createElement("div");
|
||||||
|
element_entry.classList.add("formgen-input-list-element");
|
||||||
|
element_entry.setAttribute("rel", id);
|
||||||
|
// remover
|
||||||
|
{
|
||||||
|
const element_remover : Element = document.createElement("button");
|
||||||
|
element_remover.classList.add("formgen-input-list-remover");
|
||||||
|
element_remover.textContent = "-";
|
||||||
|
element_remover.addEventListener(
|
||||||
|
"click",
|
||||||
|
(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
this.remove(id);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
element_entry.appendChild(element_remover);
|
||||||
|
}
|
||||||
|
// input
|
||||||
|
{
|
||||||
|
await input.setup(element_entry);
|
||||||
|
}
|
||||||
|
this.entries.push(
|
||||||
|
{
|
||||||
|
"id": id,
|
||||||
|
"input": input,
|
||||||
|
"element": element_entry,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.element_entries.appendChild(element_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [implementation]
|
||||||
|
*/
|
||||||
|
public async setup(
|
||||||
|
element_target : Element
|
||||||
|
) : Promise<void>
|
||||||
|
{
|
||||||
|
const element_container : Element = document.createElement("div");
|
||||||
|
{
|
||||||
|
// label
|
||||||
|
{
|
||||||
|
if (this.label === null)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const element_label : Element = document.createElement("label");
|
||||||
|
element_label.textContent = this.label;
|
||||||
|
element_container.appendChild(element_label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// entries
|
||||||
|
{
|
||||||
|
const element_entries : Element = document.createElement("div");
|
||||||
|
element_entries.classList.add("formgen-input-list-entries");
|
||||||
|
element_container.appendChild(element_entries);
|
||||||
|
this.element_entries = element_entries;
|
||||||
|
}
|
||||||
|
// adder
|
||||||
|
{
|
||||||
|
const element_adder : Element = document.createElement("div");
|
||||||
|
element_adder.classList.add("formgen-input-list-adder");
|
||||||
|
{
|
||||||
|
const element_adder_button : Element = document.createElement("button");
|
||||||
|
element_adder_button.textContent = "+";
|
||||||
|
element_adder.appendChild(element_adder_button);
|
||||||
|
element_adder.addEventListener(
|
||||||
|
"click",
|
||||||
|
(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
this.add();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
element_container.appendChild(element_adder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
element_target.appendChild(element_container);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [implementation]
|
||||||
|
*/
|
||||||
|
public async read(
|
||||||
|
) : Promise<Array<type_element>>
|
||||||
|
{
|
||||||
|
let result : Array<type_element> = [];
|
||||||
|
for (const entry of this.entries)
|
||||||
|
{
|
||||||
|
result.push(await entry.input.read());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [implementation]
|
||||||
|
*/
|
||||||
|
public write(
|
||||||
|
value : Array<type_element>
|
||||||
|
) : Promise<void>
|
||||||
|
{
|
||||||
|
return Promise.reject(new Error("not implemented"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
39
source/logic/input/number.ts
Normal file
39
source/logic/input/number.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
namespace formgen.input
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export class class_input_number extends class_input_simple<number>
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public constructor(
|
||||||
|
{
|
||||||
|
"additional_classes": additional_classes = [],
|
||||||
|
"label": label = null,
|
||||||
|
}
|
||||||
|
:
|
||||||
|
{
|
||||||
|
additional_classes ?: Array<string>,
|
||||||
|
label ?: (null | string);
|
||||||
|
}
|
||||||
|
=
|
||||||
|
{
|
||||||
|
}
|
||||||
|
)
|
||||||
|
{
|
||||||
|
super(
|
||||||
|
"number",
|
||||||
|
(value) => value.toFixed(),
|
||||||
|
(raw) => parseInt(raw),
|
||||||
|
{
|
||||||
|
"label": label,
|
||||||
|
"additional_classes": ["formgen-input-number"].concat(additional_classes),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
136
source/logic/input/simple.ts
Normal file
136
source/logic/input/simple.ts
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
namespace formgen.input
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export class class_input_simple<type_value> implements interface_input<type_value>
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
private type : string;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
private value_encode : ((type_value) => string);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
private value_decode : ((string) => type_value);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
private additional_classes : Array<string>;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
private label : (null | string);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
private element_input : (null | Element);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public constructor(
|
||||||
|
type : string,
|
||||||
|
value_encode : ((type_value) => string),
|
||||||
|
value_decode : ((string) => type_value),
|
||||||
|
{
|
||||||
|
"additional_classes": additional_classes = [],
|
||||||
|
"label": label = null,
|
||||||
|
}
|
||||||
|
:
|
||||||
|
{
|
||||||
|
additional_classes ?: Array<string>,
|
||||||
|
label ?: (null | string);
|
||||||
|
}
|
||||||
|
=
|
||||||
|
{
|
||||||
|
}
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
this.value_encode = value_encode;
|
||||||
|
this.value_decode = value_decode;
|
||||||
|
this.additional_classes = additional_classes;
|
||||||
|
this.label = label;
|
||||||
|
this.element_input = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [implementation]
|
||||||
|
*/
|
||||||
|
public setup(
|
||||||
|
element_target : Element
|
||||||
|
) : Promise<void>
|
||||||
|
{
|
||||||
|
const id : string = formgen.helpers.string.generate();
|
||||||
|
|
||||||
|
const element_container : Element = document.createElement("div");
|
||||||
|
element_container.classList.add("formgen-input");
|
||||||
|
for (const class_ of this.additional_classes)
|
||||||
|
{
|
||||||
|
element_container.classList.add(class_);
|
||||||
|
}
|
||||||
|
// label
|
||||||
|
{
|
||||||
|
if (this.label === null)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const element_label : Element = document.createElement("label");
|
||||||
|
element_label.setAttribute("for", id);
|
||||||
|
element_label.textContent = this.label;
|
||||||
|
element_container.appendChild(element_label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// input
|
||||||
|
{
|
||||||
|
const element_input : Element = document.createElement("input");
|
||||||
|
element_input.setAttribute("type", this.type);
|
||||||
|
element_input.setAttribute("id", id);
|
||||||
|
element_container.appendChild(element_input);
|
||||||
|
this.element_input = element_input;
|
||||||
|
}
|
||||||
|
element_target.appendChild(element_container);
|
||||||
|
return Promise.resolve<void>(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [implementation]
|
||||||
|
*/
|
||||||
|
public read(
|
||||||
|
) : Promise<type_value>
|
||||||
|
{
|
||||||
|
const raw : string = (this.element_input as HTMLInputElement).value;
|
||||||
|
const value : type_value = this.value_decode(raw);
|
||||||
|
return Promise.resolve<type_value>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [implementation]
|
||||||
|
*/
|
||||||
|
public write(
|
||||||
|
value : type_value
|
||||||
|
) : Promise<void>
|
||||||
|
{
|
||||||
|
const raw : string = this.value_encode(value);
|
||||||
|
(this.element_input as HTMLInputElement).value = raw;
|
||||||
|
return Promise.resolve<void>(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
39
source/logic/input/text.ts
Normal file
39
source/logic/input/text.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
namespace formgen.input
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export class class_input_text extends class_input_simple<string>
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public constructor(
|
||||||
|
{
|
||||||
|
"additional_classes": additional_classes = [],
|
||||||
|
"label": label = null,
|
||||||
|
}
|
||||||
|
:
|
||||||
|
{
|
||||||
|
additional_classes ?: Array<string>,
|
||||||
|
label ?: (null | string);
|
||||||
|
}
|
||||||
|
=
|
||||||
|
{
|
||||||
|
}
|
||||||
|
)
|
||||||
|
{
|
||||||
|
super(
|
||||||
|
"text",
|
||||||
|
(value) => value,
|
||||||
|
(raw) => raw,
|
||||||
|
{
|
||||||
|
"label": label,
|
||||||
|
"additional_classes": ["formgen-input-text"].concat(additional_classes),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
84
source/logic/main.ts
Normal file
84
source/logic/main.ts
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
namespace formgen
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
let _input : (null | formgen.input.interface_input<unknown>) = null;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
async function render(
|
||||||
|
raw : string,
|
||||||
|
element_target : Element
|
||||||
|
) : Promise<void>
|
||||||
|
{
|
||||||
|
element_target.innerHTML = "";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const description : any = formgen.helpers.json.decode(raw);
|
||||||
|
const input : formgen.input.interface_input<unknown> = formgen.input.from_raw(description);
|
||||||
|
/*
|
||||||
|
const form : formgen.class_form<unknown> = new formgen.class_form<unknown>(
|
||||||
|
input,
|
||||||
|
[
|
||||||
|
{"target": "/", "label": "abschicken"},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
await form.setup(element_target)
|
||||||
|
*/
|
||||||
|
await input.setup(element_target);
|
||||||
|
_input = input;
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
function main(
|
||||||
|
) : void
|
||||||
|
{
|
||||||
|
document.querySelector("#render").addEventListener(
|
||||||
|
"click",
|
||||||
|
() => {
|
||||||
|
render(
|
||||||
|
(document.querySelector("#input") as HTMLInputElement).value,
|
||||||
|
document.querySelector("#output")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
document.querySelector("#read").addEventListener(
|
||||||
|
"click",
|
||||||
|
async () => {
|
||||||
|
if (_input === null)
|
||||||
|
{
|
||||||
|
console.warn("no input present");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const value = await _input.read();
|
||||||
|
console.info(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function entry(
|
||||||
|
) : void
|
||||||
|
{
|
||||||
|
document.addEventListener(
|
||||||
|
"DOMContentLoaded",
|
||||||
|
() => {
|
||||||
|
main();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
20
source/structure.html
Normal file
20
source/structure.html
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<script type="text/javascript" src="logic.js"></script>
|
||||||
|
<script type="text/javascript">formgen.entry();</script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="style.css"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<textarea id="input">
|
||||||
|
</textarea>
|
||||||
|
<hr/>
|
||||||
|
<button id="render">render</button>
|
||||||
|
<hr/>
|
||||||
|
<div id="output">
|
||||||
|
</div>
|
||||||
|
<hr/>
|
||||||
|
<button id="read">lesen</button>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
50
source/style.css
Normal file
50
source/style.css
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
html
|
||||||
|
{
|
||||||
|
background-color: #000000;
|
||||||
|
color: #FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
body
|
||||||
|
{
|
||||||
|
max-width: 960px;
|
||||||
|
margin: auto;
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
background-color: #202020;
|
||||||
|
color: #D0D0D0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#input
|
||||||
|
{
|
||||||
|
width: 100%;
|
||||||
|
min-height: 240px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#render
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#output
|
||||||
|
{
|
||||||
|
width: 100%;
|
||||||
|
min-height: 240px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formgen-input > label
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
font-size: 80%;
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formgen-input-group-field
|
||||||
|
{
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formgen-form-button
|
||||||
|
{
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
35
tools/build
Executable file
35
tools/build
Executable file
|
|
@ -0,0 +1,35 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
## consts
|
||||||
|
|
||||||
|
dir_source=source
|
||||||
|
dir_build=build
|
||||||
|
cmd_tsc=tsc
|
||||||
|
|
||||||
|
|
||||||
|
## exec
|
||||||
|
|
||||||
|
mkdir -p ${dir_build}
|
||||||
|
${cmd_tsc} \
|
||||||
|
--lib es2020,dom \
|
||||||
|
${dir_source}/logic/base.ts \
|
||||||
|
${dir_source}/logic/helpers/call.ts \
|
||||||
|
${dir_source}/logic/helpers/file.ts \
|
||||||
|
${dir_source}/logic/helpers/string.ts \
|
||||||
|
${dir_source}/logic/helpers/json.ts \
|
||||||
|
${dir_source}/logic/helpers/list.ts \
|
||||||
|
${dir_source}/logic/helpers/map.ts \
|
||||||
|
${dir_source}/logic/input/_interface.ts \
|
||||||
|
${dir_source}/logic/input/simple.ts \
|
||||||
|
${dir_source}/logic/input/text.ts \
|
||||||
|
${dir_source}/logic/input/number.ts \
|
||||||
|
${dir_source}/logic/input/color.ts \
|
||||||
|
${dir_source}/logic/input/checkbox.ts \
|
||||||
|
${dir_source}/logic/input/list.ts \
|
||||||
|
${dir_source}/logic/input/group.ts \
|
||||||
|
${dir_source}/logic/input/_factory.ts \
|
||||||
|
${dir_source}/logic/form.ts \
|
||||||
|
${dir_source}/logic/main.ts \
|
||||||
|
--outFile ${dir_build}/logic.js
|
||||||
|
cp ${dir_source}/structure.html ${dir_build}/index.html -u -v
|
||||||
|
cp ${dir_source}/style.css ${dir_build}/style.css -u -v
|
||||||
Loading…
Reference in a new issue