1
0
mirror of https://github.com/jaandrle/deka-dom-el synced 2025-04-01 19:55:53 +02:00
This commit is contained in:
Jan Andrle 2025-03-05 19:29:03 +01:00
parent 05413cb2bb
commit 17e40fdd9c
28 changed files with 215 additions and 98 deletions

View File

@ -177,6 +177,11 @@ var scope = {
pop() {
if (scopes.length === 1) return;
return scopes.pop();
},
isolate(fn) {
this.push({ prevent: true });
fn();
this.pop();
}
};
function append(...els) {

File diff suppressed because one or more lines are too long

5
dist/dde.js vendored
View File

@ -154,6 +154,11 @@ var scope = {
pop() {
if (scopes.length === 1) return;
return scopes.pop();
},
isolate(fn) {
this.push({ prevent: true });
fn();
this.pop();
}
};
function append(...els) {

8
dist/dde.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -232,11 +232,13 @@ export const scope: {
state: Scope[],
/** Adds new child scope. All attributes are inherited by default. */
push(scope: Partial<Scope>): ReturnType<Array<Scope>["push"]>,
push(scope?: Partial<Scope>): ReturnType<Array<Scope>["push"]>,
/** Adds root scope as a child of the current scope. */
pushRoot(): ReturnType<Array<Scope>["push"]>,
/** Removes last/current child scope. */
pop(): ReturnType<Array<Scope>["pop"]>,
/** Runs function in a new (isolated) scope */
isolate(fn: Function): void,
};
export function customElementRender<

View File

@ -232,11 +232,13 @@ export const scope: {
state: Scope[],
/** Adds new child scope. All attributes are inherited by default. */
push(scope: Partial<Scope>): ReturnType<Array<Scope>["push"]>,
push(scope?: Partial<Scope>): ReturnType<Array<Scope>["push"]>,
/** Adds root scope as a child of the current scope. */
pushRoot(): ReturnType<Array<Scope>["push"]>,
/** Removes last/current child scope. */
pop(): ReturnType<Array<Scope>["pop"]>,
/** Runs function in a new (isolated) scope */
isolate(fn: Function): void,
};
export function customElementRender<

View File

@ -175,6 +175,11 @@ var scope = {
pop() {
if (scopes.length === 1) return;
return scopes.pop();
},
isolate(fn) {
this.push({ prevent: true });
fn();
this.pop();
}
};
function append(...els) {

File diff suppressed because one or more lines are too long

4
dist/esm.d.min.ts vendored
View File

@ -232,11 +232,13 @@ export const scope: {
state: Scope[],
/** Adds new child scope. All attributes are inherited by default. */
push(scope: Partial<Scope>): ReturnType<Array<Scope>["push"]>,
push(scope?: Partial<Scope>): ReturnType<Array<Scope>["push"]>,
/** Adds root scope as a child of the current scope. */
pushRoot(): ReturnType<Array<Scope>["push"]>,
/** Removes last/current child scope. */
pop(): ReturnType<Array<Scope>["pop"]>,
/** Runs function in a new (isolated) scope */
isolate(fn: Function): void,
};
export function customElementRender<

4
dist/esm.d.ts vendored
View File

@ -232,11 +232,13 @@ export const scope: {
state: Scope[],
/** Adds new child scope. All attributes are inherited by default. */
push(scope: Partial<Scope>): ReturnType<Array<Scope>["push"]>,
push(scope?: Partial<Scope>): ReturnType<Array<Scope>["push"]>,
/** Adds root scope as a child of the current scope. */
pushRoot(): ReturnType<Array<Scope>["push"]>,
/** Removes last/current child scope. */
pop(): ReturnType<Array<Scope>["pop"]>,
/** Runs function in a new (isolated) scope */
isolate(fn: Function): void,
};
export function customElementRender<

5
dist/esm.js vendored
View File

@ -152,6 +152,11 @@ var scope = {
pop() {
if (scopes.length === 1) return;
return scopes.pop();
},
isolate(fn) {
this.push({ prevent: true });
fn();
this.pop();
}
};
function append(...els) {

2
dist/esm.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,14 @@
import { el } from "deka-dom-el";
// Create element with properties
const button = el("button", {
textContent: "Click me",
className: "primary",
disabled: true
});
// Shorter and more expressive
// than the native approach
// Add to DOM
document.body.append(button);

View File

@ -0,0 +1,11 @@
import { el } from "deka-dom-el";
// Chainable, natural nesting
// append() returns parent element
// making chains easy and intuitive
document.body.append(
el("div").append(
el("h1", "Title"),
el("p", "Paragraph")
)
);

View File

@ -0,0 +1,19 @@
// Create element with properties
const button = document.createElement('button');
button.textContent = "Click me";
button.className = "primary";
button.disabled = true;
// Or using Object.assign()
const button2 = Object.assign(
document.createElement('button'),
{
textContent: "Click me",
className: "primary",
disabled: true
}
);
// Add to DOM
document.body.appendChild(button);
document.body.appendChild(button2);

View File

@ -0,0 +1,15 @@
// Verbose, needs temp variables
const div = document.createElement('div');
const h1 = document.createElement('h1');
h1.textContent = 'Title';
div.appendChild(h1);
const p = document.createElement('p');
p.textContent = 'Paragraph';
div.appendChild(p);
// appendChild doesn't return parent
// so chaining is not possible
// Add to DOM
document.body.appendChild(div);

View File

@ -0,0 +1,8 @@
import { el, on } from "deka-dom-el";
// Third approach - append with on addon
el("button", {
textContent: "click me"
}).append(
on("click", (e) => console.log("Clicked!", e))
);

View File

@ -0,0 +1,7 @@
import { el } from "deka-dom-el";
// Using events with HTML attribute style
el("button", {
textContent: "click me",
"=onclick": "console.log(event)"
});

View File

@ -0,0 +1,8 @@
import { el, on } from "deka-dom-el";
// Using events as addons - chainable approach
el("button", {
textContent: "click me",
},
on("click", (e) => console.log("Clicked!", e))
);

View File

@ -0,0 +1,2 @@
// Standard DOM event listener approach
element.addEventListener('click', callback, options);

View File

@ -0,0 +1,7 @@
import { el } from "deka-dom-el";
// Using events with property assignment
el("button", {
textContent: "click me",
onclick: console.log
});

View File

@ -0,0 +1,14 @@
import { scope } from "deka-dom-el";
import { S } from "deka-dom-el/signals";
function customSignalLogic() {
// Create an isolated scope for a specific operation
scope.push(); // Start new scope
// These signals are in the new scope
const isolatedCount = S(0);
const isolatedDerived = S(() => isolatedCount.get() * 2);
// Clean up by returning to previous scope
scope.pop();
}

View File

@ -0,0 +1,45 @@
import { el, scope } from "deka-dom-el";
import { S } from "deka-dom-el/signals";
function CounterWithIsolatedTimer() {
const { host } = scope;
// Main component state
const count = S(0);
// Create a timer in an isolated scope
scope.isolate(() => {
// These subscriptions won't be tied to the component lifecycle
// They would continue to run even if the component was removed
const timer = S(0);
// Not recommended for real applications!
// Just demonstrating scope isolation
setInterval(() => {
timer.set(timer.get() + 1);
console.log(`Timer: ${timer.get()}`);
}, 1000);
});
// Normal component functionality within main scope
function increment() {
count.set(count.get() + 1);
}
return el("div").append(
el("p").append(
"Count: ",
el("#text", S(() => count.get()))
),
el("button", {
textContent: "Increment",
onclick: increment
}),
el("p", "An isolated timer runs in console")
);
}
// Usage
document.body.append(
el(CounterWithIsolatedTimer)
);

View File

@ -77,33 +77,11 @@ export function page({ pkg, info }){
el("div", { class: "comparison" }).append(
el("div").append(
el("h5", t`Native DOM API`),
el(code, { content: `// Create element with properties
const button = document.createElement('button');
button.textContent = "Click me";
button.className = "primary";
button.disabled = true;
// Or using Object.assign()
const button = Object.assign(
document.createElement('button'),
{
textContent: "Click me",
className: "primary",
disabled: true
}
);`, page_id })
el(code, { src: fileURL("./components/examples/elements/native-dom-create.js"), page_id })
),
el("div").append(
el("h5", t`DDE Approach`),
el(code, { content: `// Create element with properties
const button = el("button", {
textContent: "Click me",
className: "primary",
disabled: true
});
// Shorter and more expressive
// than the native approach`, page_id })
el(code, { src: fileURL("./components/examples/elements/dde-dom-create.js"), page_id })
)
)
),
@ -159,29 +137,11 @@ const button = el("button", {
el("div", { class: "comparison" }).append(
el("div", { class: "bad-practice" }).append(
el("h5", t`❌ Native DOM API`),
el(code, { content: `// Verbose, needs temp variables
const div = document.createElement('div');
const h1 = document.createElement('h1');
h1.textContent = 'Title';
div.appendChild(h1);
const p = document.createElement('p');
p.textContent = 'Paragraph';
div.appendChild(p);
// appendChild doesn't return parent
// so chaining is not possible`, page_id })
el(code, { src: fileURL("./components/examples/elements/native-dom-tree.js"), page_id })
),
el("div", { class: "good-practice" }).append(
el("h5", t`✅ DDE Approach`),
el(code, { content: `// Chainable, natural nesting
const div = el("div").append(
el("h1", "Title"),
el("p", "Paragraph")
);
// append() returns parent element
// making chains easy and intuitive`, page_id })
el(code, { src: fileURL("./components/examples/elements/dde-dom-tree.js"), page_id })
)
)
),

View File

@ -101,10 +101,7 @@ export function page({ pkg, info }){
el("div", { className: "tabs" }).append(
el("div", { className: "tab", "data-tab": "html-attr" }).append(
el("h4", t`HTML Attribute Style`),
el(code, { content: `el("button", {
textContent: "click me",
"=onclick": "console.log(event)"
})`, page_id }),
el(code, { src: fileURL("./components/examples/events/attribute-event.js"), page_id }),
el("p").append(...T`
Forces usage as an HTML attribute. Corresponds to
${el("code", `<button onclick="console.log(event)">click me</button>`)}. This can be particularly
@ -113,17 +110,12 @@ export function page({ pkg, info }){
),
el("div", { className: "tab", "data-tab": "property" }).append(
el("h4", t`Property Assignment`),
el(code, { content: `el("button", {
textContent: "click me",
onclick: console.log
})`, page_id }),
el(code, { src: fileURL("./components/examples/events/property-event.js"), page_id }),
el("p", t`Assigns the event handler directly to the element's property.`)
),
el("div", { className: "tab", "data-tab": "addon" }).append(
el("h4", t`Addon Approach`),
el(code, { content: `el("button", {
textContent: "click me"
}, on("click", console.log))`, page_id }),
el(code, { src: fileURL("./components/examples/events/chain-event.js"), page_id }),
el("p", t`Uses the addon pattern, see above.`)
)
),

View File

@ -165,19 +165,19 @@ function MyComponent() {
el("div", { className: "function-table" }).append(
el("h4", t`Manual Scope Control API`),
el("dl").append(
el("dt", t`scope.current`),
el("dd", t`Returns the currently active scope object.`),
el("dt", t`scope.isolate(callback)`),
el("dd", t`Executes the callback function within a temporary scope, then automatically restores the previous scope.
Safer than manual push/pop for most use cases.`),
el("dt", t`scope.push()`),
el("dd", t`Creates a new scope and makes it the current active scope. All signals and subscriptions
created after this call will be associated with this new scope.`),
el("dt", t`scope.pop()`),
el("dd", t`Restores the previous scope that was active before the matching push() call.`),
el("dt", t`scope.current()`),
el("dd", t`Returns the currently active scope object.`),
el("dt", t`scope.withScope(callback)`),
el("dd", t`Executes the callback function within a temporary scope, then automatically restores the previous scope.
Safer than manual push/pop for most use cases.`)
)
),
el("p").append(...T`
@ -188,29 +188,8 @@ function MyComponent() {
el("li", t`Creating detached reactive logic that shouldn't be tied to a component's lifecycle`),
el("li", t`Building utilities that work with signals but need scope isolation`)
),
el(code, { content: `// Inside a component
function SomeComponent() {
// Create an isolated scope for a specific operation
scope.push(); // Start new scope
// These signals are in the new scope
const isolatedCount = S(0);
const isolatedDerived = S(() => isolatedCount.get() * 2);
// Clean up by returning to previous scope
scope.pop();
// Alternative: Use withScope for automatic scope management
scope.withScope(() => {
// This code runs in an isolated scope
const tempSignal = S("temporary");
// No need for pop() - scope is restored automatically
});
// Rest of component remains in the original scope
return el("div");
}`, page_id }),
el(example, { src: fileURL("./components/examples/scopes/custom-scope.js"), page_id }),
el(example, { src: fileURL("./components/examples/scopes/with-scope.js"), page_id }),
el("div", { className: "warning" }).append(
el("p").append(...T`
${el("strong", "Be careful with manual scope control!")} Always ensure you have matching push() and pop() calls,

2
index.d.ts vendored
View File

@ -237,6 +237,8 @@ export const scope: {
pushRoot(): ReturnType<Array<Scope>["push"]>,
/** Removes last/current child scope. */
pop(): ReturnType<Array<Scope>["pop"]>,
/** Runs function in a new (isolated) scope */
isolate(fn: Function): void,
};
export function customElementRender<

View File

@ -72,6 +72,12 @@ export const scope= {
if(scopes.length===1) return;
return scopes.pop();
},
isolate(fn){
this.push({ prevent: true });
fn();
this.pop();
}
};
/**
* Chainable append function for elements