Skip to content

Commit

Permalink
Support inner morph (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
joeldrapper authored Apr 4, 2024
1 parent 5d8e976 commit bf208fb
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 54 deletions.
5 changes: 4 additions & 1 deletion dist/morphlex.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,8 @@ interface Options {
beforePropertyUpdated?: (node: Node, propertyName: PropertyKey, newValue: unknown) => boolean;
afterPropertyUpdated?: (node: Node, propertyName: PropertyKey, previousValue: unknown) => void;
}
export declare function morph(node: ChildNode, reference: ChildNode | string, options?: Options): void;
export declare function morph(node: ChildNode, reference: ChildNode, options?: Options): void;
export declare function morphInner(element: Element, reference: Element, options?: Options): void;
export declare function morphFromString(node: ChildNode, reference: string, options?: Options): void;
export declare function morphInnerFromString(element: Element, reference: string, options?: Options): void;
export {};
69 changes: 43 additions & 26 deletions dist/morphlex.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

78 changes: 51 additions & 27 deletions src/morphlex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,24 +49,36 @@ interface Options {
afterPropertyUpdated?: (node: Node, propertyName: PropertyKey, previousValue: unknown) => void;
}

export function morph(node: ChildNode, reference: ChildNode | string, options: Options = {}): void {
if (typeof reference === "string") {
const template = document.createElement("template");
template.innerHTML = reference.trim();
reference = template.content.firstChild as ChildNode;
if (!reference) {
throw new Error("[Morphlex] The string did not contain any nodes.");
}
}
export function morph(node: ChildNode, reference: ChildNode, options: Options = {}): void {
new Morph(options).morph([node, reference]);
}

if (isElement(node)) {
const originalAriaBusy = node.ariaBusy;
node.ariaBusy = "true";
new Morph(options).morph([node, reference]);
node.ariaBusy = originalAriaBusy;
} else {
new Morph(options).morph([node, reference]);
}
export function morphInner(element: Element, reference: Element, options: Options = {}): void {
new Morph(options).morphInner([element, reference]);
}

export function morphFromString(node: ChildNode, reference: string, options: Options = {}): void {
morph(node, parseChildNodeFromString(reference), options);
}

export function morphInnerFromString(element: Element, reference: string, options: Options = {}): void {
morphInner(element, parseElementFromString(reference), options);
}

function parseElementFromString(string: string): Element {
const node = parseChildNodeFromString(string);

if (isElement(node)) return node;
else throw new Error("[Morphlex] The string was not a valid HTML element.");
}

function parseChildNodeFromString(string: string): ChildNode {
const parser = new DOMParser();
const doc = parser.parseFromString(string, "text/html");
const firstChild = doc.body.firstChild;

if (doc.childNodes.length === 1) return firstChild as ChildNode;
else throw new Error("[Morphlex] The string was not a valid HTML node.");
}

class Morph {
Expand Down Expand Up @@ -107,17 +119,30 @@ class Morph {
}

morph(pair: NodeReferencePair<ChildNode>): void {
if (isParentNodePair(pair)) this.#buildMaps(pair);
this.#morphNode(pair);
this.#withAriaBusy(pair[0], () => {
if (isParentNodePair(pair)) this.#buildMaps(pair);
this.#morphNode(pair);
});
}

morphInner(pair: NodeReferencePair<Element>): void {
if (isMatchingElementPair(pair)) {
this.#buildMaps(pair);
this.#morphMatchingElementContent(pair);
} else {
throw new Error("[Morphlex] You can only do an inner morph with matching elements.");
}
this.#withAriaBusy(pair[0], () => {
if (isMatchingElementPair(pair)) {
this.#buildMaps(pair);
this.#morphMatchingElementContent(pair);
} else {
throw new Error("[Morphlex] You can only do an inner morph with matching elements.");
}
});
}

#withAriaBusy(node: Node, block: () => void): void {
if (isElement(node)) {
const originalAriaBusy = node.ariaBusy;
node.ariaBusy = "true";
block();
node.ariaBusy = originalAriaBusy;
} else block();
}

#buildMaps([node, reference]: NodeReferencePair<ParentNode>): void {
Expand Down Expand Up @@ -451,8 +476,7 @@ function isMatchingElementPair(pair: NodeReferencePair<Node>): pair is MatchingE
}

function isParentNodePair(pair: NodeReferencePair<Node>): pair is NodeReferencePair<ParentNode> {
const [a, b] = pair;
return isParentNode(a) && isParentNode(b);
return isParentNode(pair[0]) && isParentNode(pair[1]);
}

function isElement(node: Node): node is Element;
Expand Down

0 comments on commit bf208fb

Please sign in to comment.