Skip to content

Commit

Permalink
fix: Don't use shadowroot anymore, too nasty
Browse files Browse the repository at this point in the history
  • Loading branch information
KevinBLT committed Mar 10, 2024
1 parent d5a483d commit 98dd262
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 70 deletions.
9 changes: 7 additions & 2 deletions packages/@hec.js/ui/example/nested-component/a.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
<div>{{ text }}</div>

<div data-component="c-b" text="e.text" data-for="let e of list"></div>
<div data-component="c-c" person="e" data-for="let e of list"></div>
<div data-component="c-b" text="@e.text" data-for="let e of list">
<span>I would like to B you!</span>
</div>

<div data-component="c-c" person="@e" data-for="let e of list">
<span>Hi! Nice to C you!</span>
</div>
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
@@ -1 +1,2 @@
<slot></slot>
<div>Hi nested: {{ text }}</div>
2 changes: 1 addition & 1 deletion packages/@hec.js/ui/example/nested-component/c.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ import { component, templateByName } from '../../lib/index.js';
component('c-c', { person: { text: '' } }, (props) => {

return templateByName('./c.html', {
text: () => props.person().text
text: () => props.person()?.text
});
});
126 changes: 62 additions & 64 deletions packages/@hec.js/ui/lib/src/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,39 @@ import { isSignal, signal } from "./signal.js";

export const componentSelector = '[data-component], [data-view], [data-page]';

// TODO: Simplify this file?

/**
* @param { string } name
* @param { Element } root
*/
const updateComponents = (name, root = document.body, exec = useComponent) => {
const selector = componentSelector.replaceAll(']', `="${ name }"]`);

if (root.matches(selector)) {
exec(root, name);
}

for (const node of root.querySelectorAll(selector)) {
exec(node, name);
}
}

new MutationObserver((mutations) => {

for (const mutation of mutations) {

for (const node of mutation.addedNodes) {
const matchingType = node instanceof HTMLElement || node instanceof SVGElement;

if (matchingType && !activeComponents.has(node) && node.matches(componentSelector)) {
useComponent(node, node.dataset.component ?? node.dataset.view ?? node.dataset.page);
} else if (matchingType && activeComponents.has(node)) {
node.dispatchEvent(new CustomEvent('::mount'));
for (const key of Object.keys(registeredComponents)) {
updateComponents(key);
}
}

for (const node of mutation.removedNodes) {
if (activeComponents.has(node)) {
node.dispatchEvent(new CustomEvent('::unmount'));
for (const key of Object.keys(registeredComponents)) {
if (node instanceof Element) {
updateComponents(key, node, (node) => node.dispatchEvent(new CustomEvent('::unmount')));
}
}
}

Expand All @@ -33,7 +49,7 @@ new MutationObserver((mutations) => {
/**
* @template T
* @typedef { (
* props: {[R in keyof T]: import("./signal.js").Signal<T[R]>},
* props: {[R in keyof T]?: import("./signal.js").Signal<T[R]>},
* self: Component
* ) => Element | Node | Promise<Element | Node> } ComponentConstructor
*/
Expand All @@ -51,11 +67,12 @@ export const activeComponents = new WeakMap();
*/
export function useComponent(node, component) {

if (registeredComponents[component]) {
registeredComponents[component].use(node);
activeComponents.set(node, registeredComponents[component].component);
if (activeComponents.has(node) || !registeredComponents[component]) {
return;
}


registeredComponents[component].use(node);
activeComponents.set(node, registeredComponents[component].component);
}

/**
Expand All @@ -73,16 +90,15 @@ class Component {
Object.assign(this, options);
}

/** @type { {[R in keyof T]: import("./signal.js").Signal<T[R]>} } */
signals;
/** @type { {[R in keyof T]?: import("./signal.js").Signal<T[R]>} } */
signals = {};

/** @type { Element } */
node;

/** @type {{ [key: string]: any }} */
props;

#ready = false;
#lazy = null;

/**
Expand All @@ -109,17 +125,13 @@ class Component {
async insert(fn) {
this.#lazy ??= this.node.hasAttribute('data-lazy');

if (this.#lazy && !this.#ready) {
this.#ready = true;
if (this.#lazy) {
this.emit('::load', null, true);
this.node.removeAttribute('data-lazy');
await notifyVisible(this.node);
} else if (this.#ready) {
return this.emit('::mount');
} else {
await Promise.resolve();
this.emit('::load', null, true);
this.#ready = true;
await Promise.resolve();
}

const template = fn(this.signals, this);
Expand All @@ -128,19 +140,22 @@ class Component {
const append = (node) => {
setPropsOf(this, propsOf(node));

const slot = node instanceof Element && node.querySelector('slot');
const children = Array.from(this.node.children);

if (slot) {
const placeholder = document.createComment('children');
this.node.append(node);

slot.before(placeholder);
slot.replaceWith(...this.node.childNodes);
}
if (children.length) {
const slot = this.node.querySelector('slot'),
childrenStart = document.createComment('children/'),
childrenEnd = document.createComment('/children');

if (slot) {
slot.replaceWith(childrenStart, ...children, childrenEnd);
} else {
this.node.append(childrenStart, ...children);
}
}

this.node.append(node);

this.emit('::loaded', null, true);

for (const k in this.props) {
const attr = this.node.getAttribute(k) ?? '';

Expand All @@ -155,50 +170,36 @@ class Component {
}
}

this.emit('::loaded', null, true);
this.node.dispatchEvent(new CustomEvent('::mount'));
}

append(template instanceof Promise ? await template : template);
}

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

for (const k in this.signals) {
if (k.toLowerCase() == key) {
return [ this.props[k], this.signals[k] ];
}
}

return [null, null];
}

/**
* @param { string } name
* @param { string } _
* @param { string } value
*/
attributeChange(name, _, value) {
const [ p, signal ] = this.propSignalByLowerKey(name);

if (value?.startsWith('@')) { // @ts-ignore
const property = Object.entries(this.props).filter(e => e[0].toLowerCase() == name)[0];
if (value?.startsWith('@')) {
const parent = this.node.parentNode,
key = value.slice(1),
parentProp = prop(propsOf(parent), key) ?? prop(propsOf(this.node), key);

if (isSignal(parentProp)) {
this.signals[name] = parentProp;
} else {
signal(parentProp);
this.signals[property[0]] ??= signal(parentProp);
this.signals[property[0]](parentProp);
}

} else {
// @ts-ignore
signal(typeof p == 'number' ? parseFloat(value) : value);
this.signals[property[0]] ??= signal(value);
this.signals[property[0]](typeof property[1] == 'number' ? parseFloat(value) : value);
}
}
};
Expand Down Expand Up @@ -230,13 +231,14 @@ export function component(name, props, fn) {

use(node) {

this.component = new Component({
signals: Object.fromEntries(Object.entries(props).map(e => [e[0], signal(e[1])])),
props, node
}),
this.component = new Component({ props, node });

this.component.insert(fn);
for (const key of Object.keys(props)) {
this.component.attributeChange(key, null, node.getAttribute(key) ?? props[key]);
}

this.component.insert(fn);

this.observer.observe(node, {
attributes: true,
attributeOldValue: true,
Expand All @@ -245,11 +247,7 @@ export function component(name, props, fn) {
}
};

const selector = componentSelector.replaceAll(']', `="${ name }"]`);

for (const node of document.querySelectorAll(selector)) {
useComponent(node, name);
}
updateComponents(name, document.body);
}

export const view = component;
Expand Down
6 changes: 5 additions & 1 deletion packages/@hec.js/ui/lib/src/props.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ export function prop(props, key) {
return props;
}

if (!props) {
return null;
}

for (const p of chain) {

if (['state'].includes(p) && props?.state) {
Expand Down Expand Up @@ -100,7 +104,7 @@ export function prop(props, key) {
export function hasProp(props, key) {
const chain = key.split('.');

if (!key) {
if (!key || !props) {
return false;
}

Expand Down
2 changes: 0 additions & 2 deletions packages/@hec.js/ui/lib/src/template.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,6 @@ export const executeNodeAttributesTemplate = (node, props) => {

});

} else if (node.matches(componentSelector) && hasProp(props, attribute)) {
node.setAttribute(attributeName, `@${attribute}`);
}
}
}
Expand Down

0 comments on commit 98dd262

Please sign in to comment.