element helper

This commit is contained in:
Robin Appelman 2025-10-06 21:54:21 +02:00
commit 0c4bbbe3e4

View file

@ -27,70 +27,122 @@ const nextElementId = () => {
}
};
/**
* @typedef ElementSpec
* @type {object}
* @property {string} tag
* @property {Object.<string, string>} props
* @property {Object.<string, string>} attributes
* @property {Object.<string, any>} data
* @property {Object.<string, Function>} events
* @property {string[]} clases
* @property {(ElementSpec|string)[]} children
*/
/**
*
*
* @param {string|ElementSpec} spec
* @returns {HTMLElement|Text}
*/
const el = (spec) => {
if (typeof spec === "string") {
return document.createTextNode(spec);
}
let element = document.createElement(spec.tag);
for (const [key, value] of Object.entries(spec.props || {})) {
element[key] = value;
}
for (const [key, value] of Object.entries(spec.attributes || {})) {
element.setAttribute(key, value);
}
for (const [key, value] of Object.entries(spec.data || {})) {
element.dataset[key] = value;
}
for (const [key, value] of Object.entries(spec.events || {})) {
element.addEventListener(key, value);
}
for (const c of spec.classes || []) {
element.classList.add(c);
}
for (const child of spec.children || []) {
element.appendChild(el(child));
}
return element;
}
const addElementControls = (row) => {
{
let sizeCol = document.createElement('td');
sizeCol.classList.add('size');
let sizeInput = document.createElement('input');
sizeInput.addEventListener('input', render);
sizeInput.type = "number";
sizeInput.value = defaultFontSize.toString();
sizeCol.appendChild(document.createTextNode("Size"))
sizeCol.appendChild(sizeInput);
row.appendChild(sizeCol);
}
row.appendChild(el({
tag: 'td',
classes: ['size'],
children: ['Size', {
tag: 'input',
events: {input: render},
props: {
type: 'number',
value: defaultFontSize.toString(),
}
}]
}));
{
let posCol = document.createElement('td');
posCol.classList.add('pos');
let posInputX = document.createElement('input');
posInputX.addEventListener('input', render);
posInputX.type = "number";
posInputX.value = "0";
posInputX.classList.add('pos_x');
let posInputY = document.createElement('input');
posInputY.addEventListener('input', render);
posInputY.type = "number";
posInputY.value = "0";
posInputY.classList.add('pos_y');
posCol.appendChild(document.createTextNode("Position"))
posCol.appendChild(posInputX);
posCol.appendChild(posInputY);
row.appendChild(posCol);
}
row.appendChild(el({
tag: 'td',
classes: ['pos'],
children: ['Position', {
tag: 'input',
events: {input: render},
classes: ['pos_x'],
props: {
type: 'number',
value: "0",
}
}, {
tag: 'input',
events: {input: render},
classes: ['pos_y'],
props: {
type: 'number',
value: "0",
}
}]
}));
{
let removeCol = document.createElement('td');
removeCol.classList.add('delete');
let removeInput = document.createElement('input');
removeInput.type = "button";
removeInput.value = "×";
removeInput.addEventListener('click', removeElement);
removeCol.appendChild(removeInput);
row.appendChild(removeCol);
}
row.appendChild(el({
tag: 'td',
classes: ['delete'],
children: [{
tag: 'input',
events: {click: removeElement},
props: {
type: 'button',
value: 'x',
}
}]
}));
}
const addTextElement = () => {
let id = nextElementId();
let row = document.createElement('tr');
row.dataset.elementId = id;
row.dataset.type = 'text';
let textCol = document.createElement('td');
textCol.classList.add('text');
textCol.colSpan = 2;
let textInput = document.createElement('input');
textInput.type = "text";
textInput.addEventListener('input', render);
textCol.appendChild(textInput);
row.appendChild(textCol);
let row = el({
tag: 'tr',
data: {elementId: id, type: 'text'},
children: [{
tag: 'td',
classes: ['text'],
props: {colSpan: 2},
children: [{
tag: 'input',
events: {input: render},
props: {
type: 'text',
focus: 'focused',
}
}]
}]
});
addElementControls(row);
elementsTableBody.appendChild(row);
textInput.focus();
};
const loadImage = (event) => {
@ -107,29 +159,36 @@ const loadImage = (event) => {
const addImageElement = () => {
let id = nextElementId();
let row = document.createElement('tr');
row.dataset.elementId = id;
row.dataset.type = 'image';
let imageCol = document.createElement('td');
imageCol.classList.add('image');
let imageInput = document.createElement('input');
imageInput.type = 'file';
let imageId = `image-input-${id}`;
imageInput.id = imageId;
imageInput.addEventListener('change', loadImage);
let imageLabel = document.createElement('label');
imageLabel.appendChild(document.createTextNode("Browse..."));
imageLabel.setAttribute('for', imageId);
let thumbCol = document.createElement('td');
thumbCol.classList.add('preview');
let imageThumb = document.createElement('img');
imageCol.appendChild(imageLabel);
imageCol.appendChild(imageInput);
thumbCol.appendChild(imageThumb);
row.appendChild(imageCol);
row.appendChild(thumbCol);
let row = el({
tag: 'tr',
data: {elementId: id, type: 'image'},
children: [{
tag: 'td',
classes: ['image'],
children: [{
tag: 'input',
events: {change: loadImage},
props: {
id: imageId,
type: 'file',
focus: 'focused',
}
},{
tag: 'label',
events: {change: loadImage},
children: "Browse...",
attributes: {
for: imageId,
}
}]
}, {
tag: 'td',
classes: ['preview'],
children: [{tag: 'img'}]
}]
});
addElementControls(row);
elementsTableBody.appendChild(row);