import { demoFunc } from './dees-button.demo.js'; import { customElement, html, DeesElement, property, type TemplateResult, cssManager, css, type CSSResult, unsafeCSS, } from '@design.estate/dees-element'; import * as domtools from '@design.estate/dees-domtools'; declare global { interface HTMLElementTagNameMap { 'dees-button': DeesButton; } } @customElement('dees-button') export class DeesButton extends DeesElement { public static demo = demoFunc; @property({ reflect: true, hasChanged() { return true; } }) public text: string; @property() public eventDetailData: string; @property({ type: Boolean, reflect: true, }) public disabled = false; @property({ type: Boolean }) public isHidden = false; @property({ type: String }) public type: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link' | 'normal' | 'highlighted' | 'discreet' | 'big' = 'default'; @property({ type: String }) public size: 'default' | 'sm' | 'lg' | 'icon' = 'default'; @property({ type: String }) public status: 'normal' | 'pending' | 'success' | 'error' = 'normal'; @property({ type: Boolean, reflect: true }) public insideForm: boolean = false; constructor() { super(); } public async connectedCallback() { await super.connectedCallback(); // Auto-detect if inside a form if (!this.insideForm && this.closest('dees-form')) { this.insideForm = true; } } public static styles = [ cssManager.defaultStyles, css` :host { display: inline-block; box-sizing: border-box; font-family: inherit; } :host([hidden]) { display: none; } /* Form spacing styles */ :host([inside-form]) { margin-bottom: 16px; } :host([inside-form]:last-child) { margin-bottom: 0; } dees-form[horizontal-layout] :host([inside-form]) { display: inline-block; margin-right: 16px; margin-bottom: 0; } dees-form[horizontal-layout] :host([inside-form]:last-child) { margin-right: 0; } .button { position: relative; display: inline-flex; align-items: center; justify-content: center; white-space: nowrap; border-radius: 6px; font-weight: 500; transition: all 0.15s ease; cursor: pointer; user-select: none; outline: none; letter-spacing: -0.01em; gap: 8px; } /* Size variants */ .button.size-default { height: 36px; padding: 0 16px; font-size: 14px; } .button.size-sm { height: 32px; padding: 0 12px; font-size: 13px; } .button.size-lg { height: 44px; padding: 0 24px; font-size: 16px; } .button.size-icon { height: 36px; width: 36px; padding: 0; } /* Default variant */ .button.default { background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(215 20.2% 11.8%)')}; color: ${cssManager.bdTheme('hsl(215.3 25% 8.8%)', 'hsl(210 40% 98%)')}; border: 1px solid ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(215 20.2% 16.8%)')}; box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); } .button.default:hover:not(.disabled) { background: ${cssManager.bdTheme('hsl(210 40% 96.1%)', 'hsl(215 20.2% 10.2%)')}; border-color: ${cssManager.bdTheme('hsl(214.3 31.8% 85%)', 'hsl(215 20.2% 20%)')}; } .button.default:active:not(.disabled) { background: ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(215 20.2% 9%)')}; } /* Destructive variant */ .button.destructive { background: hsl(0 84.2% 60.2%); color: hsl(0 0% 98%); border: 1px solid transparent; } .button.destructive:hover:not(.disabled) { background: hsl(0 84.2% 56.2%); } .button.destructive:active:not(.disabled) { background: hsl(0 84.2% 52.2%); } /* Outline variant */ .button.outline { background: transparent; color: ${cssManager.bdTheme('hsl(215.3 25% 8.8%)', 'hsl(210 40% 98%)')}; border: 1px solid ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(215 20.2% 21.8%)')}; } .button.outline:hover:not(.disabled) { background: ${cssManager.bdTheme('hsl(210 40% 96.1%)', 'hsl(215 20.2% 16.8%)')}; border-color: ${cssManager.bdTheme('hsl(214.3 31.8% 85%)', 'hsl(215 20.2% 26.8%)')}; } .button.outline:active:not(.disabled) { background: ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(215 20.2% 13.8%)')}; } /* Secondary variant */ .button.secondary { background: ${cssManager.bdTheme('hsl(210 40% 96.1%)', 'hsl(215 20.2% 16.8%)')}; color: ${cssManager.bdTheme('hsl(215.3 25% 8.8%)', 'hsl(210 40% 98%)')}; border: 1px solid transparent; } .button.secondary:hover:not(.disabled) { background: ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(215 20.2% 13.8%)')}; } .button.secondary:active:not(.disabled) { background: ${cssManager.bdTheme('hsl(214.3 31.8% 85%)', 'hsl(215 20.2% 11.8%)')}; } /* Ghost variant */ .button.ghost { background: transparent; color: ${cssManager.bdTheme('hsl(215.3 25% 8.8%)', 'hsl(210 40% 98%)')}; border: 1px solid transparent; } .button.ghost:hover:not(.disabled) { background: ${cssManager.bdTheme('hsl(210 40% 96.1%)', 'hsl(215 20.2% 16.8%)')}; } .button.ghost:active:not(.disabled) { background: ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(215 20.2% 13.8%)')}; } /* Link variant */ .button.link { background: transparent; color: ${cssManager.bdTheme('hsl(222.2 47.4% 51.2%)', 'hsl(213.1 93.9% 67.8%)')}; border: none; text-decoration: underline; text-decoration-color: transparent; height: auto; padding: 0; } .button.link:hover:not(.disabled) { text-decoration-color: currentColor; } /* Status states */ .button.pending, .button.success, .button.error { pointer-events: none; padding-left: 36px; /* Space for spinner */ } .button.size-sm.pending, .button.size-sm.success, .button.size-sm.error { padding-left: 32px; } .button.size-lg.pending, .button.size-lg.success, .button.size-lg.error { padding-left: 44px; } .button.pending { background: ${cssManager.bdTheme('hsl(222.2 47.4% 51.2%)', 'hsl(213.1 93.9% 67.8% / 0.2)')}; color: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(213.1 93.9% 67.8%)')}; border: 1px solid transparent; } .button.success { background: ${cssManager.bdTheme('hsl(142.1 76.2% 36.3%)', 'hsl(142.1 70.6% 45.3% / 0.2)')}; color: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(142.1 70.6% 45.3%)')}; border: 1px solid transparent; } .button.error { background: ${cssManager.bdTheme('hsl(0 84.2% 60.2%)', 'hsl(0 62.8% 70.6% / 0.2)')}; color: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(0 62.8% 70.6%)')}; border: 1px solid transparent; } /* Disabled state */ .button.disabled { opacity: 0.5; cursor: not-allowed; pointer-events: none; } /* Hidden state */ .button.hidden { display: none; } /* Focus state */ .button:focus-visible { outline: 2px solid ${cssManager.bdTheme('hsl(222.2 47.4% 51.2%)', 'hsl(213.1 93.9% 67.8%)')}; outline-offset: 2px; } /* Loading spinner */ dees-spinner { position: absolute; left: 10px; width: 16px; height: 16px; } .button.size-sm dees-spinner { left: 8px; width: 14px; height: 14px; } .button.size-lg dees-spinner { left: 14px; width: 18px; height: 18px; } /* Icon sizing within buttons */ .button dees-icon { width: 16px; height: 16px; flex-shrink: 0; } .button.size-sm dees-icon { width: 14px; height: 14px; } .button.size-lg dees-icon { width: 18px; height: 18px; } `, ]; public render(): TemplateResult { // Map old types to new types for backward compatibility const typeMap: {[key: string]: string} = { 'normal': 'default', 'highlighted': 'destructive', 'discreet': 'outline', 'big': 'default' // Will use size instead }; const actualType = typeMap[this.type] || this.type; const actualSize = this.type === 'big' ? 'lg' : this.size; return html`
${this.status === 'normal' ? html``: html` `}
${this.text || html`Button`}
`; } public async dispatchClick() { if (this.disabled) { return; } this.dispatchEvent( new CustomEvent('clicked', { detail: { data: this.eventDetailData, }, bubbles: true, }) ); } public async firstUpdated() { // Don't set default text here as it interferes with slotted content } }