Skip to content

Commit

Permalink
feature: nested component
Browse files Browse the repository at this point in the history
  • Loading branch information
KevinBLT committed Jan 8, 2024
1 parent 0499765 commit efa3375
Show file tree
Hide file tree
Showing 13 changed files with 106 additions and 20 deletions.
2 changes: 2 additions & 0 deletions packages/@hec.js/ui/example/nested-component/a.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<div>{{ text }}</div>
<c-b text="text"></c-b>
6 changes: 6 additions & 0 deletions packages/@hec.js/ui/example/nested-component/a.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { component, templateByName } from '../../lib/index.js';

component('c-a', { text: 'Hi' }, (props) => {

return templateByName('./a.html', props);
});
1 change: 1 addition & 0 deletions packages/@hec.js/ui/example/nested-component/b.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<div>{{ text }}</div>
6 changes: 6 additions & 0 deletions packages/@hec.js/ui/example/nested-component/b.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { component, templateByName } from '../../lib/index.js';

component('c-b', { text: '' }, (props) => {

return templateByName('./b.html', props);
});
15 changes: 15 additions & 0 deletions packages/@hec.js/ui/example/nested-component/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>hec.js :: Component</title>
<script type="module" async src="./a.js"></script>
<script type="module" async src="./b.js"></script>
</head>
<body>

<c-a></c-a>

</body>
</html>
64 changes: 55 additions & 9 deletions packages/@hec.js/ui/lib/src/component.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { signal } from "./signal.js";
import { f, nodeProps, prop, propsOf } from "./props.js";
import { isSignal, signal } from "./signal.js";

/**
* @template T
Expand All @@ -22,15 +23,23 @@ export function component(name, props, fn) {
/** @type { {[R in keyof T]: import("./signal.js").Signal<T[R]>} } */// @ts-ignore
#signals = Object.fromEntries(Object.entries(props).map(e => [e[0], signal(e[1])]));

/** @type { AbortController[] } */
#aborts = [];

/** @type {{ [key: string]: AbortController }} */
#parentSignalAborts = {};

connectedCallback() {
const shadow = this.attachShadow({ mode: 'open' }),
node = fn(this.#signals);
node = fn(this.#signals),
abort = new AbortController();

/** @param { Node } node */
const append = (node) => {
shadow.append(node);
this.dispatchEvent(new CustomEvent('::mount'));
nodeProps.set(this, propsOf(node));

shadow.append(node);

for (const k in this.#signals) {

if (!this.hasAttribute(k)) {
Expand All @@ -39,18 +48,36 @@ export function component(name, props, fn) {

this.#signals[k].subscribe({
next: v => this.setAttribute(k, v.toString())
});
}, { signal: abort.signal });
}

this.#aborts.push(abort);

this.dispatchEvent(new CustomEvent('::mount'));
}

node instanceof Promise ? node.then(append) : append(node);
}

disconnectedCallback() {
nodeProps.delete(this);

while (this.#aborts.length) {
this.#aborts.pop().abort();
}

for (const k in this.#parentSignalAborts) {
this.#parentSignalAborts[k].abort();
delete this.#parentSignalAborts[k];
}

this.dispatchEvent(new CustomEvent('::unmount'));
}

/** @param { string} key */
/**
* @param { string } key
* @returns { [ any, import("./signal.js").Signal<any> ] }
*/
#propSignalByLowerKey(key = '') {
key = key.toLowerCase();

Expand All @@ -60,7 +87,7 @@ export function component(name, props, fn) {
}
}

return [];
return [ null, null];
}

/**
Expand All @@ -69,9 +96,28 @@ export function component(name, props, fn) {
* @param { string } value
*/
attributeChangedCallback(name, _, value) {
const [ prop, signal ] = this.#propSignalByLowerKey(name);
const [ p, signal ] = this.#propSignalByLowerKey(name);

if (value.startsWith('@parent.')) { // @ts-ignore
const parent = this.parentNode.host || this.parentNode,
parentProps = propsOf(parent),
parentProp = prop(parentProps, value.slice(8));

if (typeof prop == 'number') {
if (isSignal(parentProp)) {
this.#parentSignalAborts[p]?.abort();
this.#parentSignalAborts[p] = new AbortController();

signal(f(parentProp));

parentProp.subscribe({ next: signal }, {
signal: this.#parentSignalAborts[p].signal
});

} else {
signal(parentProp);
}

} else if (typeof p == 'number') {
// @ts-ignore
signal(parseFloat(value));
} else {
Expand Down
2 changes: 1 addition & 1 deletion packages/@hec.js/ui/lib/src/plugins/data-bind.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { isSignal } from "../signal.js";
import { f, prop } from "../value.js";
import { f, prop } from "../props.js";

/** @type {{ [key: string]: (node: HTMLInputElement) => any }} */
const valueByType = {
Expand Down
2 changes: 1 addition & 1 deletion packages/@hec.js/ui/lib/src/plugins/data-for.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { isSignal } from "../signal.js";
import { templateByNode } from "../template.js";
import { f, prop } from "../value.js";
import { f, prop } from "../props.js";

const done = new WeakSet();

Expand Down
2 changes: 1 addition & 1 deletion packages/@hec.js/ui/lib/src/plugins/data-if.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { isSignal } from "../signal.js";
import { f, prop } from "../value.js";
import { f, prop } from "../props.js";

/**
* @type { import("../plugins.js").Plugin }
Expand Down
2 changes: 1 addition & 1 deletion packages/@hec.js/ui/lib/src/plugins/data-on.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { prop } from "../value.js";
import { prop } from "../props.js";

/** @type { import("../plugins.js").Plugin } */
export const dataOnPlugin = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import { isSignal, signal } from "./signal.js";

/**
* @template T
* @type { WeakMap<Node, { [key: string]: any }> }
*/
export const nodeProps = new WeakMap();

/** @param { Node } node */
export const propsOf = (node) => nodeProps.get(node);

/**
* @param { any | function(): any} v
* @returns { any }
Expand Down
2 changes: 1 addition & 1 deletion packages/@hec.js/ui/lib/src/signal.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { f } from "./value.js";
import { f } from "./props.js";

/**
* @template T
Expand Down
13 changes: 7 additions & 6 deletions packages/@hec.js/ui/lib/src/template.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ import { expression } from './expression.js';
import { pipes } from './pipes.js';
import { plugins } from './plugins.js';
import { isSignal } from './signal.js';
import { f, prop } from './value.js';

/** @type { WeakSet<Node> } */
const done = new WeakSet();
import { f, nodeProps, prop } from './props.js';

/** @type {{ [key: string]: Promise<HTMLTemplateElement> }} */
const templatesLoading = {}
Expand Down Expand Up @@ -66,7 +63,7 @@ export function templateByString(template, props = {}) {
*/
export function templateByNode(template, props = {}) {

if (done.has(template)) {
if (nodeProps.has(template)) {
return;
}

Expand Down Expand Up @@ -144,7 +141,7 @@ export function templateByNode(template, props = {}) {
const findExpression = (node) => {
const parentNode = node.parentNode;

done.add(node);
nodeProps.set(node, props);

if (node instanceof HTMLElement) {

Expand All @@ -161,9 +158,13 @@ export function templateByNode(template, props = {}) {
const attribute = node.getAttribute(attributeName);

if (attribute.includes('{{')) {

bindExpressions(attribute, (text) => {
node.setAttribute(attributeName, text.trim().replace(/ +/, ' '))
});

} else if (node.localName.includes('-') && props[attribute]) {
node.setAttribute(attributeName, `@parent.${attribute}`);
}
}
}
Expand Down

0 comments on commit efa3375

Please sign in to comment.