diff --git a/src/Component.ts b/src/Component.ts index 44bcf3f..a1971a4 100644 --- a/src/Component.ts +++ b/src/Component.ts @@ -77,6 +77,18 @@ export class Component extends ElementCompo return [...this.node.querySelectorAll(selectors)].map(e => new Component(e)); } + /** + * Traverse the component and its parents (heading toward the document root) until it finds a component that matches + * the specified {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_selectors CSS selector}. + * @param selectors + * @typeParam T Component element type + */ + public closest(selectors: string): Component | null { + const element = this.node.closest(selectors); + if (element == null) return null; + return new Component(element); + } + /** * Set style property * @param name Property name @@ -113,6 +125,10 @@ export class Component extends ElementCompo return this; } + public override clone(deep = true) { + return new Component(this.node.cloneNode(deep) as T); + } + public override on(type: K, listener: (ev: HTMLElementEventMap[K], component: this) => any): typeof this; public override on(type: K, listener: (ev: HTMLElementEventMap[K], component: this) => any, options: AddEventListenerOptions): typeof this; public override on(type: K, listener: (ev: HTMLElementEventMap[K], component: this) => any, useCapture: boolean): typeof this; diff --git a/src/DocumentComponent.ts b/src/DocumentComponent.ts index 3b96ba8..bbd60ba 100644 --- a/src/DocumentComponent.ts +++ b/src/DocumentComponent.ts @@ -44,4 +44,10 @@ export class DocumentComponent extends NodeComponent { component.slot(idPrefix + index, doc.node); return doc; } + + public override clone(deep = true) { + const doc = new DocumentComponent(); + doc.node.append(this.node.cloneNode(deep)); + return doc; + } } diff --git a/src/ElementComponent.ts b/src/ElementComponent.ts index 5aa0fa9..f69d90d 100644 --- a/src/ElementComponent.ts +++ b/src/ElementComponent.ts @@ -58,6 +58,25 @@ export abstract class ElementComponent extends NodeComponent< return this; } + /** + * Insert components in the children list of this `ElementComponent`’s parent, just before this `ElementComponent`. + * @param components Components to insert + */ + public before(...components: NodeComponent[]) { + this.node.before(...components.map(c => c.node)); + return this; + } + + /** + * Insert components in the children list of this `ElementComponent`’s parent, just after this `ElementComponent`. + * @param components Components to insert + */ + public after(...components: NodeComponent[]) { + this.node.after(...components.map(c => c.node)); + return this; + } + + /** * Add classes */ @@ -162,6 +181,23 @@ export abstract class ElementComponent extends NodeComponent< return this.node[name]; } + /** + * Check whether the component is visible. + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/checkVisibility Element: checkVisibility() - MDN} + */ + public isVisible() { + return this.node.checkVisibility(); + } + + /** + * Get a {@link !DOMRect} object providing information about the size of a component and its position relative to + * the {@link https://developer.mozilla.org/en-US/docs/Glossary/Viewport viewport}. + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect Element: getBoundingClientRect() - MDN} + */ + public boundingClientRect() { + return this.node.getBoundingClientRect(); + } + /** * Remove the element */ diff --git a/src/NodeComponent.ts b/src/NodeComponent.ts index 31988ce..b40b763 100644 --- a/src/NodeComponent.ts +++ b/src/NodeComponent.ts @@ -58,6 +58,14 @@ export abstract class NodeComponent { return this; } + /** + * Clone this component. Event listeners are not cloned. + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Node/cloneNode Node: cloneNode() - MDN} + * @param [deep] Whether to clone the whole subtree. + * @returns A duplicate of this component. + */ + public abstract clone(deep?: boolean): NodeComponent; + /** * Add event listener * @param type A case-sensitive string representing the event type to listen for. diff --git a/src/SvgComponent.ts b/src/SvgComponent.ts index 188a8e7..2665ee5 100644 --- a/src/SvgComponent.ts +++ b/src/SvgComponent.ts @@ -30,4 +30,8 @@ export class SvgComponent extends ElementComponent { public static from(svg: string) { return new SvgComponent(document.createRange().createContextualFragment(svg).children[0] as SVGSVGElement); } + + public override clone(deep = true) { + return new SvgComponent(this.node.cloneNode(deep) as SVGSVGElement); + } } diff --git a/src/TextComponent.ts b/src/TextComponent.ts index 2292c95..7b61e27 100644 --- a/src/TextComponent.ts +++ b/src/TextComponent.ts @@ -37,6 +37,13 @@ export class TextComponent extends NodeComponent { throw new DOMException(`NodeComponent.append: Cannot add children to a ${this.constructor.name}`); } + /** + * Clone this text component. + */ + public override clone() { + return new TextComponent(this.node.cloneNode() as Text); + } + /** * Get the text content */