import { LitElement, html, css } from 'lit'; import { property, state } from 'lit/decorators.js'; export class Accordion extends LitElement { @property({ type: Boolean }) expanded = false; @property({ type: Boolean }) disabled = false; @property({ type: Boolean }) disableGutters = false; @property({ type: String }) variant: 'elevation' | 'outlined' = 'elevation'; @property({ type: Boolean }) square = false; @property({ type: String }) TransitionComponent = 'collapse'; @property({ type: String }) TransitionProps = ''; static styles = css` :host { display: block; font-family: "Roboto", "Helvetica", "Arial", sans-serif; } .accordion { position: relative; transition: margin 150ms cubic-bezier(0.4, 0, 0.2, 1); overflow-anchor: none; } .accordion:not(:last-child) { border-bottom: 1px solid rgba(0, 0, 0, 0.12); } .accordion.variant-elevation { box-shadow: 0px 2px 1px -1px rgba(0, 0, 0, 0.2), 0px 1px 1px 0px rgba(0, 0, 0, 0.14), 0px 1px 3px 0px rgba(0, 0, 0, 0.12); margin: 8px 0; } .accordion.variant-elevation:first-child { margin-top: 0; } .accordion.variant-elevation:last-child { margin-bottom: 0; } .accordion.variant-outlined { border: 1px solid rgba(0, 0, 0, 0.12); margin: 0; } .accordion.variant-outlined:not(:first-child) { border-top: 0; } .accordion.square { border-radius: 0; } .accordion:not(.square) { border-radius: 4px; } .accordion.variant-outlined:not(.square):first-child { border-top-left-radius: 4px; border-top-right-radius: 4px; } .accordion.variant-outlined:not(.square):last-child { border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; } .accordion.expanded { margin: 16px 0; } .accordion.expanded.variant-outlined { margin: 0; } .accordion.disabled { background-color: rgba(0, 0, 0, 0.12); } .accordion.disabled .accordion-summary { opacity: 0.38; } /* Remove gutters */ .accordion.disable-gutters { margin: 0; } .accordion.disable-gutters.variant-elevation { box-shadow: none; } `; private _handleToggle() { if (this.disabled) return; const oldExpanded = this.expanded; this.expanded = !this.expanded; this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true, detail: { expanded: this.expanded, previousExpanded: oldExpanded } })); } render() { const classes = [ 'accordion', `variant-${this.variant}`, this.expanded ? 'expanded' : '', this.disabled ? 'disabled' : '', this.disableGutters ? 'disable-gutters' : '', this.square ? 'square' : '' ].filter(Boolean).join(' '); return html`
`; } } export class AccordionSummary extends LitElement { @property({ type: String }) expandIcon = 'expand_more'; @property({ type: Boolean }) disabled = false; @property({ type: String }) iconButtonProps = ''; static styles = css` :host { display: block; } .accordion-summary { display: flex; align-items: center; padding: 0 16px; min-height: 48px; cursor: pointer; transition: min-height 150ms cubic-bezier(0.4, 0, 0.2, 1), background-color 150ms cubic-bezier(0.4, 0, 0.2, 1); border: none; background: none; width: 100%; text-align: left; outline: none; position: relative; } .accordion-summary:hover { background-color: rgba(0, 0, 0, 0.04); } .accordion-summary:focus { background-color: rgba(0, 0, 0, 0.12); } .accordion-summary.disabled { cursor: default; pointer-events: none; } .summary-content { display: flex; align-items: center; flex: 1; margin: 12px 0; } .expand-icon { display: flex; align-items: center; justify-content: center; width: 48px; height: 48px; margin-left: auto; transition: transform 150ms cubic-bezier(0.4, 0, 0.2, 1); color: rgba(0, 0, 0, 0.54); } .expand-icon.expanded { transform: rotate(180deg); } .expand-icon svg { width: 24px; height: 24px; fill: currentColor; } /* Focus ring */ .accordion-summary::after { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; border-radius: inherit; pointer-events: none; transition: box-shadow 150ms cubic-bezier(0.4, 0, 0.2, 1); } .accordion-summary:focus-visible::after { box-shadow: 0 0 0 2px rgba(25, 118, 210, 0.2); } `; private _renderExpandIcon() { // Default expand_more icon const expandMorePath = 'M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z'; return html` `; } private _handleClick(event: Event) { if (this.disabled) { event.preventDefault(); event.stopPropagation(); return; } // Let the event bubble up to the Accordion component this.dispatchEvent(new CustomEvent('summary-click', { bubbles: true, composed: true, detail: { originalEvent: event } })); } render() { const accordion = this.closest('mui-accordion') as Accordion; const isExpanded = accordion?.expanded || false; return html` `; } } export class AccordionDetails extends LitElement { @property({ type: Boolean }) disablePadding = false; static styles = css` :host { display: block; } .accordion-details { padding: 8px 16px 16px; border-top: 1px solid rgba(0, 0, 0, 0.12); } .accordion-details.disable-padding { padding: 0; } /* Animation for content reveal */ :host([hidden]) { display: none !important; } .accordion-details { animation: accordion-expand 150ms cubic-bezier(0.4, 0, 0.2, 1); } @keyframes accordion-expand { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: translateY(0); } } `; render() { const classes = [ 'accordion-details', this.disablePadding ? 'disable-padding' : '' ].filter(Boolean).join(' '); return html`
`; } } export class AccordionActions extends LitElement { @property({ type: Boolean }) disableSpacing = false; static styles = css` :host { display: block; } .accordion-actions { display: flex; align-items: center; padding: 8px 16px; justify-content: flex-end; gap: 8px; } .accordion-actions.disable-spacing { padding: 0; } `; render() { const classes = [ 'accordion-actions', this.disableSpacing ? 'disable-spacing' : '' ].filter(Boolean).join(' '); return html`
`; } }