import { LitElement, html, unsafeCSS } from 'lit'; import { property } from 'lit/decorators.js'; import { theme } from '../styles/theme.js'; export class ToggleButton extends LitElement { @property({ type: Boolean }) selected = false; @property({ type: Boolean }) disabled = false; @property({ type: String }) value = ''; @property({ type: String }) color: 'standard' | 'primary' | 'secondary' | 'error' | 'warning' | 'info' | 'success' = 'standard'; @property({ type: String }) size: 'small' | 'medium' | 'large' = 'medium'; @property({ type: Boolean }) fullWidth = false; static styles = unsafeCSS` :host { display: inline-block; } :host([fullWidth]) { display: block; width: 100%; } .toggle-button { display: inline-flex; align-items: center; justify-content: center; position: relative; box-sizing: border-box; background-color: transparent; outline: 0; border: 1px solid rgba(0, 0, 0, 0.12); margin: 0; border-radius: 4px; padding: 0; cursor: pointer; user-select: none; vertical-align: middle; text-decoration: none; font-family: "Roboto", "Helvetica", "Arial", sans-serif; font-weight: 500; line-height: 1.75; letter-spacing: 0.02857em; text-transform: uppercase; transition: color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, border-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; color: rgba(0, 0, 0, 0.87); width: 100%; } .toggle-button:hover { background-color: rgba(0, 0, 0, 0.04); } .toggle-button:disabled { color: rgba(0, 0, 0, 0.26); border-color: rgba(0, 0, 0, 0.12); cursor: default; pointer-events: none; } /* Size variants */ .toggle-button.small { padding: 4px 8px; font-size: 0.8125rem; min-height: 32px; } .toggle-button.medium { padding: 6px 12px; font-size: 0.875rem; min-height: 36px; } .toggle-button.large { padding: 8px 16px; font-size: 0.9375rem; min-height: 42px; } /* Selected state - standard */ .toggle-button.selected.standard { background-color: rgba(0, 0, 0, 0.08); border-color: rgba(0, 0, 0, 0.12); } .toggle-button.selected.standard:hover { background-color: rgba(0, 0, 0, 0.12); } /* Selected state - primary */ .toggle-button.selected.primary { background-color: rgba(25, 118, 210, 0.12); border-color: ${theme.palette.primary.main}; color: ${theme.palette.primary.main}; } .toggle-button.selected.primary:hover { background-color: rgba(25, 118, 210, 0.16); } /* Selected state - secondary */ .toggle-button.selected.secondary { background-color: rgba(156, 39, 176, 0.12); border-color: ${theme.palette.secondary.main}; color: ${theme.palette.secondary.main}; } .toggle-button.selected.secondary:hover { background-color: rgba(156, 39, 176, 0.16); } /* Selected state - error */ .toggle-button.selected.error { background-color: rgba(211, 47, 47, 0.12); border-color: ${theme.palette.error.main}; color: ${theme.palette.error.main}; } .toggle-button.selected.error:hover { background-color: rgba(211, 47, 47, 0.16); } /* Selected state - warning */ .toggle-button.selected.warning { background-color: rgba(237, 108, 2, 0.12); border-color: ${theme.palette.warning.main}; color: ${theme.palette.warning.main}; } .toggle-button.selected.warning:hover { background-color: rgba(237, 108, 2, 0.16); } /* Selected state - info */ .toggle-button.selected.info { background-color: rgba(2, 136, 209, 0.12); border-color: ${theme.palette.info.main}; color: ${theme.palette.info.main}; } .toggle-button.selected.info:hover { background-color: rgba(2, 136, 209, 0.16); } /* Selected state - success */ .toggle-button.selected.success { background-color: rgba(46, 125, 50, 0.12); border-color: ${theme.palette.success.main}; color: ${theme.palette.success.main}; } .toggle-button.selected.success:hover { background-color: rgba(46, 125, 50, 0.16); } /* Content */ .content { display: flex; align-items: center; justify-content: center; gap: 8px; } `; private handleClick() { if (this.disabled) return; this.dispatchEvent(new CustomEvent('toggle', { detail: { value: this.value, selected: !this.selected }, bubbles: true, composed: true })); } render() { const classes = [ 'toggle-button', this.selected ? 'selected' : '', this.color, this.size ].filter(Boolean).join(' '); return html` `; } } export class ToggleButtonGroup extends LitElement { @property({ type: String }) value = ''; @property({ type: Array }) values: string[] = []; @property({ type: String }) color: 'standard' | 'primary' | 'secondary' | 'error' | 'warning' | 'info' | 'success' = 'standard'; @property({ type: String }) size: 'small' | 'medium' | 'large' = 'medium'; @property({ type: String }) orientation: 'horizontal' | 'vertical' = 'horizontal'; @property({ type: Boolean }) exclusive = true; @property({ type: Boolean }) disabled = false; @property({ type: Boolean }) fullWidth = false; static styles = unsafeCSS` :host { display: inline-flex; } :host([fullWidth]) { display: flex; width: 100%; } .toggle-button-group { display: flex; } .toggle-button-group.horizontal { flex-direction: row; } .toggle-button-group.vertical { flex-direction: column; } .toggle-button-group.fullWidth { width: 100%; } /* Remove borders between buttons */ ::slotted(mui-toggle-button:not(:first-child)) { margin-left: -1px !important; } .toggle-button-group.vertical ::slotted(mui-toggle-button:not(:first-child)) { margin-left: 0 !important; margin-top: -1px !important; } /* Border radius for first and last buttons */ .toggle-button-group.horizontal ::slotted(mui-toggle-button:first-child) { border-top-right-radius: 0 !important; border-bottom-right-radius: 0 !important; } .toggle-button-group.horizontal ::slotted(mui-toggle-button:last-child) { border-top-left-radius: 0 !important; border-bottom-left-radius: 0 !important; } .toggle-button-group.horizontal ::slotted(mui-toggle-button:not(:first-child):not(:last-child)) { border-radius: 0 !important; } .toggle-button-group.vertical ::slotted(mui-toggle-button:first-child) { border-bottom-left-radius: 0 !important; border-bottom-right-radius: 0 !important; } .toggle-button-group.vertical ::slotted(mui-toggle-button:last-child) { border-top-left-radius: 0 !important; border-top-right-radius: 0 !important; } .toggle-button-group.vertical ::slotted(mui-toggle-button:not(:first-child):not(:last-child)) { border-radius: 0 !important; } /* Full width buttons */ .toggle-button-group.fullWidth ::slotted(mui-toggle-button) { flex: 1 !important; } /* Ensure selected buttons appear above others */ ::slotted(mui-toggle-button[selected]) { z-index: 1 !important; position: relative !important; } `; connectedCallback() { super.connectedCallback(); this.addEventListener('toggle', this.handleToggle); this.updateChildButtons(); } disconnectedCallback() { super.disconnectedCallback(); this.removeEventListener('toggle', this.handleToggle); } updated(changedProperties: Map) { super.updated(changedProperties); if (changedProperties.has('value') || changedProperties.has('values') || changedProperties.has('color') || changedProperties.has('size') || changedProperties.has('disabled')) { this.updateChildButtons(); } } private updateChildButtons() { const buttons = this.querySelectorAll('mui-toggle-button') as NodeListOf; buttons.forEach(button => { // Set group properties button.color = this.color; button.size = this.size; button.disabled = this.disabled || button.disabled; button.fullWidth = this.fullWidth; // Set selected state if (this.exclusive) { button.selected = button.value === this.value; } else { button.selected = this.values.includes(button.value); } }); } private handleToggle = (e: CustomEvent) => { e.stopPropagation(); const { value, selected } = e.detail; if (this.exclusive) { // Single selection mode if (selected) { this.value = value; } else { // Allow deselection in exclusive mode this.value = ''; } this.dispatchEvent(new CustomEvent('change', { detail: { value: this.value }, bubbles: true, composed: true })); } else { // Multiple selection mode if (selected) { if (!this.values.includes(value)) { this.values = [...this.values, value]; } } else { this.values = this.values.filter(v => v !== value); } this.dispatchEvent(new CustomEvent('change', { detail: { values: this.values }, bubbles: true, composed: true })); } }; render() { const classes = [ 'toggle-button-group', this.orientation, this.fullWidth ? 'fullWidth' : '' ].filter(Boolean).join(' '); return html`
`; } }