@conectate/ct-lit
Version:
Super class wrapper for lit
193 lines (192 loc) • 6.71 kB
JavaScript
/**
* @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 : "";
}