import { LitElement } from 'lit'; import { customElement, property } from 'lit/decorators.js'; import { unsafeStatic, html } from 'lit/static-html.js'; import { nothing } from 'lit/html.js'; import { AlignX, Space } from './utils/CommonTypes'; import { Hideable } from './utils/CommonInterfaces'; import { spread } from './utils/LitHelper'; import { SpreadController } from './utils/SpreadController'; import { TailwindStylesController } from './utils/TailwindStylesController'; import { UniqueSlotController } from './utils/UniqueSlotController'; import { HIDEABLE_ELEMENTS } from './utils/CommonConstants'; import { assertTagNameIsAllowed } from './utils/AssertHelpers'; export const validStackElementTagNames = ['div', 'ol', 'ul'] as const; export type StackElementTagName = typeof validStackElementTagNames[number]; @customElement('mono-stack') export class MonoStackComp extends LitElement { private __spreadController: SpreadController = new SpreadController(this); private __stylesController: TailwindStylesController = new TailwindStylesController( this, ); private __uniqueSlotController: UniqueSlotController = new UniqueSlotController( this, ); @property({ type: String, reflect: true }) as: StackElementTagName = 'div'; @property({ type: String, reflect: true }) space: Space = 'none'; @property({ type: String, reflect: true, attribute: 'align-x' }) alignX: AlignX = 'stretch'; __hiddenIndexes: Set = new Set(); private __computeSpaceY() { switch (this.space) { case 'none': return 'space-y-0'; case '3xs': return 'space-y-0.5'; case '2xs': return 'space-y-1'; case 'xs': return 'space-y-2'; case 'sm': return 'space-y-3'; case 'md': return 'space-y-4'; case 'gutter': return 'space-y-5'; case 'lg': return 'space-y-6'; case 'xl': return 'space-y-8'; case '2xl': return 'space-y-16'; default: return null; } } private __computeAlignX() { switch (this.alignX) { case 'left': return 'items-start'; case 'center': return 'items-center'; case 'right': return 'items-end'; default: return 'items-stretch'; } } private __includesHideable() { return ( Array.from(this.children).findIndex((child, _index) => HIDEABLE_ELEMENTS.includes(child.localName), ) >= 0 ); } private __handleResize(_event: Event) { Array.from(this.children).forEach((child, index) => { if (!HIDEABLE_ELEMENTS.includes(child.localName)) { return; } const hideableElement = (child as unknown) as Hideable; if (hideableElement.isHidden()) { this.__hiddenIndexes.add(index); this.requestUpdate(); } else if (this.__hiddenIndexes.has(index)) { this.__hiddenIndexes.delete(index); this.requestUpdate(); } }); } // if a normal function, this isn't the right this private __onHideableEvent = (event: Event) => { this.__handleResize(event); }; private __onSlotChange(_event: Event) { Array.from(this.children).forEach((child) => { child.removeEventListener('mono-hideable', this.__onHideableEvent); child.addEventListener('mono-hideable', this.__onHideableEvent); }); } private __renderChildren() { return Array.from(this.children).map((_, index) => { if (this.__hiddenIndexes.has(index)) { return nothing; } return html`
{ this.__onSlotChange(event); this.__uniqueSlotController.onSlotChange(event); }} >
`; }); } /* eslint-disable lit/binding-positions,lit/no-invalid-html */ render() { assertTagNameIsAllowed( this.as, (validStackElementTagNames as unknown) as string[], ); const tag = unsafeStatic(this.as); const attributesToSpread = this.__spreadController.buildSpreadAttributesIgnoring( ['as', 'style', 'class', 'slot', 'space', 'align-x'], ); const styles = this.__stylesController.tw( 'flex flex-col', this.__computeSpaceY(), this.__computeAlignX(), ); return html` <${tag} ...=${spread(attributesToSpread)}> ${ this.__includesHideable() ? html`
${this.__renderChildren()}
` : html`
${this.__renderChildren()}
` } `; } /* eslint-enable lit/binding-positions,lit/no-invalid-html */ } declare global { interface HTMLElementTagNameMap { 'mono-stack': MonoStackComp; } }