UNPKG

@nexim/element

Version:

Utility functions and mixins for building high-performance web components with Lit.

8 lines (7 loc) 8.76 kB
{ "version": 3, "sources": ["../src/main.ts", "../src/mixin/light-dom.ts", "../src/mixin/logging.ts"], "sourcesContent": ["import { packageTracer } from '@alwatr/package-tracer';\n\n__dev_mode__: packageTracer.add(__package_name__, __package_version__);\n\nexport * from './mixin/light-dom.js';\nexport * from './mixin/logging.js';\n", "import type { Class } from '@alwatr/type-helper';\nimport type { CSSResultGroup, LitElement, ReactiveElement } from 'lit';\n\n/**\n * A mixin to enable light DOM rendering and style encapsulation for LitElement components.\n * @typeParam T - The base class to extend.\n *\n * @returns A class that extends the base class with light DOM functionality.\n *\n * @example\n * ```ts\n * import {LitElement, html, css} from 'lit';\n * import {LightDomMixin} from '@nexim/element';\n *\n * class MyLightDomElement extends LightDomMixin(LitElement) {\n * static styles = css`\n * p {\n * color: blue;\n * }\n * `;\n *\n * protected override render() {\n * return html`<p>Hello, light DOM!</p>`;\n * }\n * }\n * ```\n */\nexport function lightDomMixin<T extends Class<LitElement> = Class<LitElement>>(superClass: T): T {\n /**\n * The base class to extend.\n */\n class MixinClass extends superClass {\n /**\n * Flattens the CSSResultGroup into a single string of CSS text.\n * @param styles - The styles to flatten.\n * @returns A string of concatenated CSS text.\n */\n static flatCssText(styles?: CSSResultGroup): string {\n if (styles === undefined) return '';\n if ('cssText' in styles) return styles.cssText.trim();\n if (Array.isArray(styles)) {\n return styles\n .map((style) => ('cssText' in style ? style.cssText : MixinClass.flatCssText(style)))\n .join('\\n')\n .trim();\n }\n return '';\n }\n\n /**\n * Injects light DOM styles into the document head if not already present.\n *\n * @param tagName - The tag name of the custom element.\n * @param element - The element class containing the styles.\n */\n static lightDomStyles(tagName: string, element: typeof ReactiveElement): void {\n const className = `${tagName}-light-dom-style`;\n if (document.querySelector(`style.${className}`) !== null) return;\n\n const cssText = MixinClass.flatCssText(element.styles);\n if (cssText === '') return;\n\n const styleEl = document.createElement('style');\n styleEl.classList.add(className);\n styleEl.innerHTML = cssText;\n document.head.append(styleEl);\n }\n\n /**\n * Overrides the default render root to use the light DOM.\n */\n override createRenderRoot() {\n return this;\n }\n\n /**\n * Called when the element is added to the document's DOM.\n * Adds the light DOM styles to the document head.\n */\n override connectedCallback() {\n super.connectedCallback();\n MixinClass.lightDomStyles(this.tagName.toLowerCase(), this.constructor as typeof LitElement);\n }\n }\n\n return MixinClass;\n}\n", "import { type AlwatrLogger, createLogger } from '@alwatr/logger';\n\nimport type { Class } from '@alwatr/type-helper';\nimport type { LitElement, PropertyValues } from 'lit';\n\n/**\n * Global element index to uniquely identify each element instance\n */\nlet elementIndex = /* @__PURE__ */ 0;\n\n/**\n * Interface for elements that have a logger instance.\n * @noInheritDoc\n */\nexport interface LoggerMixinInterface extends LitElement {\n logger_: AlwatrLogger;\n}\n\n/**\n * Create a mixin class that extends the provided superclass and logs the lifecycle methods of the element.\n *\n * Hint: function super() must be called in the methods to logger work.\n * @typeParam T - The base class to extend.\n *\n * @returns A mixin class that extends the superclass and logs the lifecycle methods of the element.\n *\n * @example\n * ```ts\n * import {LitElement, html} from 'lit';\n * import {LoggerMixin} from '@nexim/element';\n *\n * class MyElement extends LoggerMixin(LitElement) {\n * protected override render() {\n * super.render(); // must call super method to logger work\n *\n * return html`<p>Hello, world!</p>`;\n * }\n * }\n * ```\n */\nexport function loggerMixin<T extends Class<LitElement> = Class<LitElement>>(superClass: T): Class<LoggerMixinInterface> & T {\n /**\n * The base class to extend.\n */\n return class MixinClass extends superClass {\n /**\n * Unique index for each element instance.\n */\n private elementIndex__: number = ++elementIndex;\n\n /**\n * Logger instance with a tag name and unique index.\n */\n protected logger_ = createLogger(`<${this.tagName.toLowerCase()}-${this.elementIndex__.toString()}>`);\n\n private firstUpdated__?: true;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n constructor(...args: any[]) {\n super(...args);\n this.logger_.logMethod?.('constructor');\n }\n\n override connectedCallback(): void {\n this.logger_.logMethod?.('connectedCallback');\n super.connectedCallback();\n }\n\n override disconnectedCallback(): void {\n this.logger_.logMethod?.('disconnectedCallback');\n super.disconnectedCallback();\n }\n\n // Override update to measure update time\n protected override update(changedProperties: PropertyValues): void {\n this.logger_.logMethodArgs?.('update', { changedProperties });\n this.logger_.time?.(this.firstUpdated__ ? 'update-time' : 'first-update-time');\n super.update(changedProperties);\n }\n\n // Override firstUpdated to end the first update time measurement\n protected override firstUpdated(changedProperties: PropertyValues): void {\n this.logger_.logMethodArgs?.('firstUpdated', { changedProperties });\n this.logger_.timeEnd?.('first-update-time');\n super.firstUpdated(changedProperties);\n }\n\n // Override updated to end the update time measurement\n protected override updated(changedProperties: PropertyValues): void {\n this.logger_.logMethodArgs?.('updated', { changedProperties });\n\n if (this.firstUpdated__) {\n this.logger_.timeEnd?.('update-time');\n }\n else {\n this.firstUpdated__ = true;\n }\n\n super.updated(changedProperties);\n }\n\n protected override render(): unknown {\n this.logger_.logMethod?.('render');\n return;\n }\n\n override dispatchEvent(event: Event): boolean {\n this.logger_.logMethodArgs?.('dispatchEvent', {\n type: event.type,\n detail: (event as Event & { detail?: unknown }).detail,\n });\n return super.dispatchEvent(event);\n }\n\n override remove(): void {\n this.logger_.logMethod?.('remove');\n super.remove();\n }\n } as unknown as Class<LoggerMixinInterface> & T; // TypeScript doesn't support protected mixin methods!\n}\n"], "mappings": ";;AAAA,OAAS,kBAAqB,yBC2BvB,SAAS,cAA+D,WAAkB,CAI/F,MAAM,mBAAmB,UAAW,CAMlC,OAAO,YAAY,OAAiC,CAClD,GAAI,SAAW,OAAW,MAAO,GACjC,GAAI,YAAa,OAAQ,OAAO,OAAO,QAAQ,KAAK,EACpD,GAAI,MAAM,QAAQ,MAAM,EAAG,CACzB,OAAO,OACJ,IAAK,OAAW,YAAa,MAAQ,MAAM,QAAU,WAAW,YAAY,KAAK,CAAE,EACnF,KAAK,IAAI,EACT,KAAK,CACV,CACA,MAAO,EACT,CAQA,OAAO,eAAe,QAAiB,QAAuC,CAC5E,MAAM,UAAY,GAAG,OAAO,mBAC5B,GAAI,SAAS,cAAc,SAAS,SAAS,EAAE,IAAM,KAAM,OAE3D,MAAM,QAAU,WAAW,YAAY,QAAQ,MAAM,EACrD,GAAI,UAAY,GAAI,OAEpB,MAAM,QAAU,SAAS,cAAc,OAAO,EAC9C,QAAQ,UAAU,IAAI,SAAS,EAC/B,QAAQ,UAAY,QACpB,SAAS,KAAK,OAAO,OAAO,CAC9B,CAKS,kBAAmB,CAC1B,OAAO,IACT,CAMS,mBAAoB,CAC3B,MAAM,kBAAkB,EACxB,WAAW,eAAe,KAAK,QAAQ,YAAY,EAAG,KAAK,WAAgC,CAC7F,CACF,CAEA,OAAO,UACT,CCtFA,OAA4B,iBAAoB,iBAQhD,IAAI,aAA+B,EAgC5B,SAAS,YAA6D,WAAgD,CAI3H,OAAO,MAAM,mBAAmB,UAAW,CAczC,eAAe,KAAa,CAC1B,MAAM,GAAG,IAAI,EAXf,KAAQ,eAAyB,EAAE,aAKnC,KAAU,QAAU,aAAa,IAAI,KAAK,QAAQ,YAAY,CAAC,IAAI,KAAK,eAAe,SAAS,CAAC,GAAG,EAOlG,KAAK,QAAQ,YAAY,aAAa,CACxC,CAES,mBAA0B,CACjC,KAAK,QAAQ,YAAY,mBAAmB,EAC5C,MAAM,kBAAkB,CAC1B,CAES,sBAA6B,CACpC,KAAK,QAAQ,YAAY,sBAAsB,EAC/C,MAAM,qBAAqB,CAC7B,CAGmB,OAAO,kBAAyC,CACjE,KAAK,QAAQ,gBAAgB,SAAU,CAAE,iBAAkB,CAAC,EAC5D,KAAK,QAAQ,OAAO,KAAK,eAAiB,cAAgB,mBAAmB,EAC7E,MAAM,OAAO,iBAAiB,CAChC,CAGmB,aAAa,kBAAyC,CACvE,KAAK,QAAQ,gBAAgB,eAAgB,CAAE,iBAAkB,CAAC,EAClE,KAAK,QAAQ,UAAU,mBAAmB,EAC1C,MAAM,aAAa,iBAAiB,CACtC,CAGmB,QAAQ,kBAAyC,CAClE,KAAK,QAAQ,gBAAgB,UAAW,CAAE,iBAAkB,CAAC,EAE7D,GAAI,KAAK,eAAgB,CACvB,KAAK,QAAQ,UAAU,aAAa,CACtC,KACK,CACH,KAAK,eAAiB,IACxB,CAEA,MAAM,QAAQ,iBAAiB,CACjC,CAEmB,QAAkB,CACnC,KAAK,QAAQ,YAAY,QAAQ,EACjC,MACF,CAES,cAAc,MAAuB,CAC5C,KAAK,QAAQ,gBAAgB,gBAAiB,CAC5C,KAAM,MAAM,KACZ,OAAS,MAAuC,MAClD,CAAC,EACD,OAAO,MAAM,cAAc,KAAK,CAClC,CAES,QAAe,CACtB,KAAK,QAAQ,YAAY,QAAQ,EACjC,MAAM,OAAO,CACf,CACF,CACF,CFrHA,aAAc,cAAc,IAAI,iBAAkB,OAAmB", "names": [] }