import { css, LitElement } from 'lit'; import { customElement, property, query, queryAll, state, } from 'lit/decorators.js'; import { html, nothing } from 'lit/html.js'; import { createUUID } from './utils/UniqueId'; import { MonoTextComp } from './MonoTextComp'; import { fromOptionalConverter, spread } from './utils/LitHelper'; import { SpreadController } from './utils/SpreadController'; import { visuallyHidden } from './utils/AccessiblityHelper'; import { InputTone, RoundedCorners } from './utils/CommonTypes'; import { TailwindStylesController } from './utils/TailwindStylesController'; export type ButtonGroupOption = { selected?: boolean; value: string; text: string; }; @customElement('mono-button-group') export class MonoButtonGroupComp extends LitElement { static styles = css` ::slotted(p:first-of-type) { margin: 0; } `; private __spreadController: SpreadController = new SpreadController(this); private __stylesController: TailwindStylesController = new TailwindStylesController( this, ); @property({ type: String, reflect: true }) value: string = ''; @property({ type: Array, reflect: true }) options: ButtonGroupOption[] = []; @property({ type: String, reflect: true }) id: string = createUUID(); @property({ type: String, reflect: true }) name: string = ''; @property({ type: String, reflect: true, converter: fromOptionalConverter }) error?: string; @property({ type: Boolean, reflect: true }) disabled: boolean = false; @property({ type: Boolean, reflect: true }) required: boolean = false; @property({ type: String, reflect: true }) tone: InputTone = 'highlight'; @property({ type: String, reflect: true }) corners: RoundedCorners = 'none'; @state() focusedValue: string = ''; @query('legend', true) __legendEl!: HTMLLabelElement; @queryAll('input') __inputsEl!: Array; @queryAll('label') __labelsEl!: Array; @query('[id*="-errors"]', true) __errorEl!: MonoTextComp; private __onInput(_event: Event, value: string) { this.value = value; } private __onChange(event: Event, value: string) { event.preventDefault(); event.stopPropagation(); this.value = value; const changeEvent = new CustomEvent('change', { detail: { value: this.value }, bubbles: true, composed: true, }); this.dispatchEvent(changeEvent); } private __onFocus(_event: FocusEvent, value: string) { this.focusedValue = value; } private __onBlur(_event: FocusEvent, value: string) { if (this.focusedValue === value) { this.focusedValue = ''; } } private __hasError() { return this.error && this.error.length > 0; } private __renderError(ariaDescribedBy: string) { if (this.__hasError()) { return html` ${this.error} `; } return nothing; } // eslint-disable-next-line class-methods-use-this private __computeButtonStyle(selected: boolean) { if (!selected) { return 'bg-neutral-3 text-neutral-5'; } switch (this.tone) { case 'primary': { return 'bg-primary text-white'; } case 'accent': { return 'bg-accent text-white'; } case 'dark': { return 'bg-neutral-5 text-white'; } case 'white': { return 'bg-white text-neutral-5'; } default: return 'bg-highlight-2 text-white'; } } private __computeFocusStyle() { switch (this.tone) { case 'primary': { return 'border-primary border-2'; } case 'accent': { return 'border-accent border-2'; } case 'dark': { return 'border-neutral-5 border-2'; } case 'white': { return 'border-white border-2'; } default: return 'border-highlight-2 border-2'; } } private __computeCornersForIndex(index: number) { if (this.corners === 'none') { return null; } if (index === 0) { return 'rounded-l'; } if (index === this.options.length - 1) { return 'rounded-r'; } return null; } private __focus(_event: MouseEvent) { const selectedIndex = Math.max( 0, this.options.map((option) => option.value).indexOf(this.value), ); if (this.__inputsEl.length >= selectedIndex) { this.__inputsEl[selectedIndex].focus(); } } render() { const ariaDescribedBy = this.__hasError() ? `${this.id}-errors` : ''; const attributesToSpread = this.__spreadController.buildSpreadAttributesIgnoring( [ 'as', 'style', 'class', 'slot', 'value', 'id', 'name', 'options', 'error', 'disabled', 'required', 'tone', 'corners', ], ); /* eslint-disable lit-a11y/click-events-have-key-events */ return html`
${this.options.map(({ selected, value, text }, index) => { const id = `${this.id}-option-${index}`; return html` `; })}
${this.__renderError(ariaDescribedBy)}
`; /* eslint-enable lit-a11y/click-events-have-key-events */ } } declare global { interface HTMLElementTagNameMap { 'mono-button-group': MonoButtonGroupComp; } }