UNPKG

@conectate/ct-lit

Version:
193 lines (192 loc) 6.71 kB
/** * @license * Copyright (c) Conectate Team. All rights reserved. * This code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ import { LitElement } from "lit"; export { css, html, svg, unsafeCSS } from "lit"; export { state as internalProperty, property, query, queryAll, queryAssignedNodes, queryAsync, state } from "lit/decorators.js"; export { unsafeHTML } from "lit/directives/unsafe-html.js"; export { until } from "lit/directives/until.js"; export { LitElement }; /** * Class decorator factory that defines the decorated class as a custom element. * * ``` * @customElement('my-element') * class MyElement { * render() { * return html``; * } * } * ``` * @category Decorator * @param tagName The name of the custom element to define. */ export const customElement = (tagName) => (classOrDescriptor) => typeof classOrDescriptor === "function" ? legacyCustomElement(tagName, classOrDescriptor) : standardCustomElement(tagName, classOrDescriptor); /** * Legacy implementation of the customElement decorator for older browsers * @param tagName The name of the custom element to define * @param clazz The class to register as a custom element * @returns The class passed in * @internal */ const legacyCustomElement = (tagName, clazz) => { if (!window.customElements.get(tagName)) { window.customElements.define(tagName, clazz); } else { console.warn(tagName, "already defined"); } // Cast as any because TS doesn't recognize the return type as being a // subtype of the decorated class when clazz is typed as // `Constructor<HTMLElement>` for some reason. // `Constructor<HTMLElement>` is helpful to make sure the decorator is // applied to elements however. // tslint:disable-next-line:no-any return clazz; }; /** * Standard implementation of the customElement decorator for modern browsers * @param tagName The name of the custom element to define * @param descriptor The class descriptor * @returns The modified descriptor * @internal */ const standardCustomElement = (tagName, descriptor) => { const { kind, elements } = descriptor; return { kind, elements, // This callback is called once the class is otherwise fully defined finisher(clazz) { if (!window.customElements.get(tagName)) { window.customElements.define(tagName, clazz); } else { console.warn(tagName, "already defined"); } } }; }; /** * CtLit - A wrapper class for LitElement with additional utility methods * * This class extends LitElement and provides convenient utility methods * for common operations such as element selection, event dispatching, * and scrolling animations. */ export class CtLit extends LitElement { connectedCallback() { if (location.host.includes("usac.edu.gt") && !location.host.includes("medicina")) return; super.connectedCallback(); } /** * Returns the first element that is a descendant of node that matches selectors. * @param name * @returns {HTMLElement | Element | undefined | null} */ $$(name) { return this.renderRoot.querySelector(name); } /** * Returns all element descendants of node that match selectors. * @param name * @returns {NodeListOf<HTMLElement | Element> | undefined} */ $$$(name) { return this.renderRoot.querySelectorAll(name); } /** * Map all IDs for shadowRoot and save in `this.$` like a polymer element. * You should add in the first line of `firstUpdated()` * @deprecated */ mapIDs() { console.warn("mapIDs() is deprecated, use `@query` decorator from lit/decorators instead"); // @ts-ignore this.$ = {}; let nodeList = this.renderRoot.querySelectorAll("[id]"); for (let i = 0; nodeList != null && i < nodeList.length; i++) { // @ts-ignore this.$[nodeList[i].id] = nodeList[i]; } } /** * Clone all `native` types of object in a new object reference * @param ob Original Object */ deepClone(ob) { return window.structuredClone != null ? structuredClone(ob) : JSON.parse(JSON.stringify(ob)); } /** * Fire a event with name and value * @param name * @param value */ fire(name, value) { this.dispatchEvent(new CustomEvent(name, { detail: value })); } /** * * @param scrollTargetY pixels to scroll. Ej: const ticketsBlockPositionY = this.$.contact.getBoundingClientRect().top + window.scrollTarget.scrollTop; * @param time Time to scroll * @param easing * @param target scrollTarget Element */ scrollToY(scrollTargetY = 0, time = 600, easing = "easeInOutCubic", target = window.scrollTarget) { let currentTime = 0; const animationTime = time / 1000; // easing equations from https://github.com/danro/easing-js/blob/master/easing.js const easingEquations = { easeOutSine: (pos) => Math.sin(pos * (Math.PI / 2)), easeInOutSine: (pos) => -0.5 * (Math.cos(Math.PI * pos) - 1), easeInOutQuint: (pos) => { if ((pos /= 0.5) < 1) { return 0.5 * Math.pow(pos, 5); } return 0.5 * (Math.pow(pos - 2, 5) + 2); }, easeInOutCubic: function (pos) { if ((pos /= 0.5) < 1) return 0.5 * Math.pow(pos, 3); return 0.5 * (Math.pow(pos - 2, 3) + 2); } }; // add animation loop function tick() { currentTime += 1 / 60; const p = currentTime / animationTime; const t = easingEquations[easing](p); const scrollTop = target.pageYOffset || target.scrollTop || 0; const newPosition = scrollTop + (scrollTargetY - scrollTop) * t; if (p < 1) { window.requestAnimationFrame(tick); target.scrollTop = newPosition; } } tick(); } } /** * Conditional template rendering helper * * @param condition Boolean condition to evaluate * @param template The template to render if the condition is true * @returns The template if the condition is true, empty string otherwise * * @example * ```ts * render() { * return html` * ${If(this.isVisible, html`<div>Conditional content</div>`)} * `; * } * ``` */ export function If(condition, template) { return condition ? template : ""; }