import {
DeesElement,
html,
property,
customElement,
cssManager,
css,
unsafeCSS,
type TemplateResult,
} from '@design.estate/dees-element';
// Import design tokens
import { colors, bdTheme } from './00colors.js';
import { spacing, radius, shadows, transitions } from './00tokens.js';
import { fontFamilies, typography } from './00fonts.js';
declare global {
interface HTMLElementTagNameMap {
'sio-button': SioButton;
}
}
@customElement('sio-button')
export class SioButton extends DeesElement {
public static demo = () => html`
Default
Primary
Delete
Outline
Ghost
Small
Large
Disabled
`;
@property({ type: String })
public text: string = '';
@property({ type: String })
public type: 'default' | 'primary' | 'destructive' | 'outline' | 'ghost' = 'default';
@property({ type: String })
public size: 'sm' | 'default' | 'lg' = 'default';
@property({ type: Boolean, reflect: true })
public disabled: boolean = false;
@property({ type: String })
public status: 'normal' | 'pending' | 'success' | 'error' = 'normal';
public static styles = [
cssManager.defaultStyles,
css`
:host {
display: inline-block;
font-family: ${unsafeCSS(fontFamilies.sans)};
}
:host([disabled]) {
pointer-events: none;
}
.button {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
white-space: nowrap;
border-radius: ${unsafeCSS(radius.md)};
font-weight: 500;
transition: ${unsafeCSS(transitions.all)};
cursor: pointer;
user-select: none;
outline: none;
border: 1px solid transparent;
gap: ${unsafeCSS(spacing[2])};
font-size: 0.875rem;
line-height: 1.5;
}
/* Size variants */
.button.size-sm {
height: 32px;
padding: 0 ${unsafeCSS(spacing[3])};
font-size: 13px;
}
.button.size-default {
height: 36px;
padding: 0 ${unsafeCSS(spacing[4])};
font-size: 14px;
}
.button.size-lg {
height: 44px;
padding: 0 ${unsafeCSS(spacing[6])};
font-size: 16px;
}
/* Type variants */
.button.default {
background: ${bdTheme('secondary')};
color: ${bdTheme('secondaryForeground')};
border-color: ${bdTheme('border')};
}
.button.default:hover:not(.disabled) {
background: ${bdTheme('secondary')};
opacity: 0.8;
}
.button.default:active:not(.disabled) {
transform: translateY(1px);
}
.button.primary {
background: ${bdTheme('primary')};
color: ${bdTheme('primaryForeground')};
}
.button.primary:hover:not(.disabled) {
opacity: 0.9;
}
.button.primary:active:not(.disabled) {
transform: translateY(1px);
}
.button.destructive {
background: ${bdTheme('destructive')};
color: ${bdTheme('destructiveForeground')};
}
.button.destructive:hover:not(.disabled) {
opacity: 0.9;
}
.button.destructive:active:not(.disabled) {
transform: translateY(1px);
}
.button.outline {
background: transparent;
color: ${bdTheme('foreground')};
border-color: ${bdTheme('border')};
}
.button.outline:hover:not(.disabled) {
background: ${bdTheme('accent')};
color: ${bdTheme('accentForeground')};
}
.button.outline:active:not(.disabled) {
transform: translateY(1px);
}
.button.ghost {
background: transparent;
color: ${bdTheme('foreground')};
}
.button.ghost:hover:not(.disabled) {
background: ${bdTheme('accent')};
color: ${bdTheme('accentForeground')};
}
.button.ghost:active:not(.disabled) {
transform: translateY(1px);
}
/* Status states */
.button.pending {
pointer-events: none;
opacity: 0.7;
}
.spinner {
position: absolute;
left: ${unsafeCSS(spacing[3])};
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.button.success {
background: ${bdTheme('success')};
color: ${bdTheme('successForeground')};
pointer-events: none;
}
.button.error {
background: ${bdTheme('destructive')};
color: ${bdTheme('destructiveForeground')};
pointer-events: none;
}
/* Disabled state */
.button.disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* Focus state */
.button:focus-visible {
outline: 2px solid ${bdTheme('ring')};
outline-offset: 2px;
}
`,
];
public render(): TemplateResult {
const buttonClasses = [
'button',
this.type === 'primary' ? 'primary' : this.type,
`size-${this.size}`,
this.disabled ? 'disabled' : '',
this.status,
].filter(Boolean).join(' ');
return html`
`;
}
private handleClick(event: MouseEvent) {
if (this.disabled || this.status !== 'normal') {
event.preventDefault();
event.stopPropagation();
return;
}
// Dispatch a custom event with any data
this.dispatchEvent(new CustomEvent('click', {
detail: { originalEvent: event },
bubbles: true,
composed: true
}));
}
}