feat(structure): adjust
This commit is contained in:
421
ts_web/elements/dees-button/dees-button.demo.ts
Normal file
421
ts_web/elements/dees-button/dees-button.demo.ts
Normal file
@@ -0,0 +1,421 @@
|
||||
import { html, css, cssManager, domtools } from '@design.estate/dees-element';
|
||||
import '@design.estate/dees-wcctools/demotools';
|
||||
import './dees-panel.js';
|
||||
import './dees-form.js';
|
||||
import './dees-form-submit.js';
|
||||
import './dees-input-text.js';
|
||||
import './dees-icon.js';
|
||||
import type { DeesButton } from './dees-button.js';
|
||||
|
||||
export const demoFunc = () => html`
|
||||
<style>
|
||||
${css`
|
||||
.demo-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
padding: 24px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
dees-panel {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
dees-panel:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.vertical-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.horizontal-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.demo-output {
|
||||
margin-top: 16px;
|
||||
padding: 12px;
|
||||
background: ${cssManager.bdTheme('hsl(210 40% 96.1%)', 'hsl(215 20.2% 16.8%)')};
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
font-family: monospace;
|
||||
color: ${cssManager.bdTheme('hsl(215.3 25% 8.8%)', 'hsl(210 40% 98%)')};
|
||||
}
|
||||
|
||||
.icon-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.code-snippet {
|
||||
background: ${cssManager.bdTheme('hsl(210 40% 96.1%)', 'hsl(215 20.2% 11.8%)')};
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
font-family: monospace;
|
||||
font-size: 13px;
|
||||
display: inline-block;
|
||||
margin: 4px 0;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
|
||||
<div class="demo-container">
|
||||
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
|
||||
// Log button clicks for demo purposes
|
||||
const buttons = elementArg.querySelectorAll('dees-button');
|
||||
buttons.forEach((button) => {
|
||||
button.addEventListener('clicked', () => {
|
||||
const type = button.getAttribute('type') || 'default';
|
||||
console.log(`Button variant clicked: ${type}`);
|
||||
});
|
||||
});
|
||||
}}>
|
||||
<dees-panel .title=${'1. Button Variants'} .subtitle=${'Different visual styles for various use cases'}>
|
||||
<div class="button-group">
|
||||
<dees-button type="default">Default</dees-button>
|
||||
<dees-button type="secondary">Secondary</dees-button>
|
||||
<dees-button type="destructive">Destructive</dees-button>
|
||||
<dees-button type="outline">Outline</dees-button>
|
||||
<dees-button type="ghost">Ghost</dees-button>
|
||||
<dees-button type="link">Link Button</dees-button>
|
||||
</div>
|
||||
</dees-panel>
|
||||
</dees-demowrapper>
|
||||
|
||||
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
|
||||
// Demonstrate size differences programmatically
|
||||
const buttons = elementArg.querySelectorAll('dees-button');
|
||||
buttons.forEach((button) => {
|
||||
button.addEventListener('clicked', () => {
|
||||
const size = button.getAttribute('size') || 'default';
|
||||
console.log(`Button size: ${size}`);
|
||||
});
|
||||
});
|
||||
}}>
|
||||
<dees-panel .title=${'2. Button Sizes'} .subtitle=${'Multiple sizes for different contexts and use cases'}>
|
||||
<div class="button-group">
|
||||
<dees-button size="sm">Small Button</dees-button>
|
||||
<dees-button size="default">Default Size</dees-button>
|
||||
<dees-button size="lg">Large Button</dees-button>
|
||||
<dees-button size="icon" type="outline" .text=${'🚀'}></dees-button>
|
||||
</div>
|
||||
|
||||
<div class="button-group" style="margin-top: 16px;">
|
||||
<dees-button size="sm" type="secondary">Small Secondary</dees-button>
|
||||
<dees-button size="default" type="destructive">Default Destructive</dees-button>
|
||||
<dees-button size="lg" type="outline">Large Outline</dees-button>
|
||||
</div>
|
||||
</dees-panel>
|
||||
</dees-demowrapper>
|
||||
|
||||
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
|
||||
// Track icon button clicks
|
||||
const iconButtons = elementArg.querySelectorAll('dees-button');
|
||||
iconButtons.forEach((button) => {
|
||||
button.addEventListener('clicked', () => {
|
||||
const hasIcon = button.querySelector('dees-icon');
|
||||
if (hasIcon) {
|
||||
const iconName = hasIcon.getAttribute('iconFA') || 'unknown';
|
||||
console.log(`Icon button clicked: ${iconName}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
}}>
|
||||
<dees-panel .title=${'3. Buttons with Icons'} .subtitle=${'Combining icons with text for enhanced visual communication'}>
|
||||
<div class="icon-row">
|
||||
<dees-button>
|
||||
<dees-icon iconFA="faPlus"></dees-icon>
|
||||
Add Item
|
||||
</dees-button>
|
||||
<dees-button type="destructive">
|
||||
<dees-icon iconFA="faTrash"></dees-icon>
|
||||
Delete
|
||||
</dees-button>
|
||||
<dees-button type="outline">
|
||||
<dees-icon iconFA="faDownload"></dees-icon>
|
||||
Download
|
||||
</dees-button>
|
||||
</div>
|
||||
|
||||
<div class="icon-row">
|
||||
<dees-button type="secondary" size="sm">
|
||||
<dees-icon iconFA="faCog"></dees-icon>
|
||||
Settings
|
||||
</dees-button>
|
||||
<dees-button type="ghost">
|
||||
<dees-icon iconFA="faChevronLeft"></dees-icon>
|
||||
Back
|
||||
</dees-button>
|
||||
<dees-button type="ghost">
|
||||
Next
|
||||
<dees-icon iconFA="faChevronRight"></dees-icon>
|
||||
</dees-button>
|
||||
</div>
|
||||
|
||||
<div class="icon-row">
|
||||
<dees-button size="icon" type="default">
|
||||
<dees-icon iconFA="faPlus"></dees-icon>
|
||||
</dees-button>
|
||||
<dees-button size="icon" type="secondary">
|
||||
<dees-icon iconFA="faCog"></dees-icon>
|
||||
</dees-button>
|
||||
<dees-button size="icon" type="outline">
|
||||
<dees-icon iconFA="faSearch"></dees-icon>
|
||||
</dees-button>
|
||||
<dees-button size="icon" type="ghost">
|
||||
<dees-icon iconFA="faEllipsisV"></dees-icon>
|
||||
</dees-button>
|
||||
<dees-button size="icon" type="destructive">
|
||||
<dees-icon iconFA="faTrash"></dees-icon>
|
||||
</dees-button>
|
||||
</div>
|
||||
</dees-panel>
|
||||
</dees-demowrapper>
|
||||
|
||||
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
|
||||
// Demonstrate status changes
|
||||
const pendingButton = elementArg.querySelector('dees-button[status="pending"]');
|
||||
const successButton = elementArg.querySelector('dees-button[status="success"]');
|
||||
const errorButton = elementArg.querySelector('dees-button[status="error"]');
|
||||
|
||||
// Simulate status changes
|
||||
if (pendingButton) {
|
||||
setTimeout(() => {
|
||||
console.log('Pending button is showing loading state');
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
if (successButton) {
|
||||
successButton.addEventListener('clicked', () => {
|
||||
console.log('Success state button clicked');
|
||||
});
|
||||
}
|
||||
|
||||
if (errorButton) {
|
||||
errorButton.addEventListener('clicked', () => {
|
||||
console.log('Error state button clicked');
|
||||
});
|
||||
}
|
||||
}}>
|
||||
<dees-panel .title=${'4. Button States'} .subtitle=${'Different states to indicate button status and loading conditions'}>
|
||||
<div class="button-group">
|
||||
<dees-button status="normal">Normal</dees-button>
|
||||
<dees-button status="pending">Processing...</dees-button>
|
||||
<dees-button status="success">Success!</dees-button>
|
||||
<dees-button status="error">Error!</dees-button>
|
||||
<dees-button disabled>Disabled</dees-button>
|
||||
</div>
|
||||
|
||||
<div class="button-group" style="margin-top: 16px;">
|
||||
<dees-button type="secondary" status="pending" size="sm">Small Loading</dees-button>
|
||||
<dees-button type="outline" status="pending">Default Loading</dees-button>
|
||||
<dees-button type="destructive" status="pending" size="lg">Large Loading</dees-button>
|
||||
</div>
|
||||
</dees-panel>
|
||||
</dees-demowrapper>
|
||||
|
||||
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
|
||||
// Set up click handlers with the output element
|
||||
const output = elementArg.querySelector('#click-output');
|
||||
|
||||
const clickMeBtn = elementArg.querySelector('dees-button:first-of-type');
|
||||
const dataBtn = elementArg.querySelector('dees-button[type="secondary"]');
|
||||
const asyncBtn = elementArg.querySelector('dees-button[type="destructive"]');
|
||||
|
||||
if (clickMeBtn && output) {
|
||||
clickMeBtn.addEventListener('clicked', () => {
|
||||
output.textContent = `Clicked: Default button at ${new Date().toLocaleTimeString()}`;
|
||||
});
|
||||
}
|
||||
|
||||
if (dataBtn && output) {
|
||||
dataBtn.addEventListener('clicked', (e: CustomEvent) => {
|
||||
output.textContent = `Clicked: Secondary button with data: ${e.detail.data}`;
|
||||
});
|
||||
}
|
||||
|
||||
if (asyncBtn && output) {
|
||||
asyncBtn.addEventListener('clicked', async () => {
|
||||
output.textContent = 'Processing...';
|
||||
await domtools.plugins.smartdelay.delayFor(2000);
|
||||
output.textContent = 'Action completed!';
|
||||
});
|
||||
}
|
||||
}}>
|
||||
<dees-panel .title=${'5. Event Handling'} .subtitle=${'Interactive examples with click event handling'}>
|
||||
<div class="button-group">
|
||||
<dees-button>Click Me</dees-button>
|
||||
<dees-button type="secondary" .eventDetailData=${'custom-data-123'}>
|
||||
Click with Data
|
||||
</dees-button>
|
||||
<dees-button type="destructive">Async Action</dees-button>
|
||||
</div>
|
||||
|
||||
<div id="click-output" class="demo-output">
|
||||
<em>Click a button to see the result...</em>
|
||||
</div>
|
||||
</dees-panel>
|
||||
</dees-demowrapper>
|
||||
|
||||
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
|
||||
// Set up form submission handling
|
||||
const form = elementArg.querySelector('dees-form');
|
||||
const output = elementArg.querySelector('#form-output');
|
||||
|
||||
if (form && output) {
|
||||
form.addEventListener('formData', (e: CustomEvent) => {
|
||||
output.innerHTML = '<strong>Form submitted with data:</strong><br>' +
|
||||
JSON.stringify(e.detail.data, null, 2);
|
||||
});
|
||||
}
|
||||
|
||||
// Track non-submit button clicks
|
||||
const draftBtn = elementArg.querySelector('dees-button[type="secondary"]');
|
||||
const cancelBtn = elementArg.querySelector('dees-button[type="ghost"]');
|
||||
|
||||
if (draftBtn) {
|
||||
draftBtn.addEventListener('clicked', () => {
|
||||
console.log('Save Draft clicked');
|
||||
});
|
||||
}
|
||||
|
||||
if (cancelBtn) {
|
||||
cancelBtn.addEventListener('clicked', () => {
|
||||
console.log('Cancel clicked');
|
||||
});
|
||||
}
|
||||
}}>
|
||||
<dees-panel .title=${'6. Form Integration'} .subtitle=${'Buttons working within forms with automatic spacing'}>
|
||||
<dees-form>
|
||||
<dees-input-text label="Name" key="name" required></dees-input-text>
|
||||
<dees-input-text label="Email" key="email" type="email" required></dees-input-text>
|
||||
<dees-input-text label="Message" key="message" isMultiline></dees-input-text>
|
||||
|
||||
<dees-button type="secondary">Save Draft</dees-button>
|
||||
<dees-button type="ghost">Cancel</dees-button>
|
||||
<dees-form-submit>Submit Form</dees-form-submit>
|
||||
</dees-form>
|
||||
|
||||
<div id="form-output" class="demo-output" style="white-space: pre-wrap;">
|
||||
<em>Submit the form to see the data...</em>
|
||||
</div>
|
||||
</dees-panel>
|
||||
</dees-demowrapper>
|
||||
|
||||
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
|
||||
// Log legacy type mappings
|
||||
const buttons = elementArg.querySelectorAll('dees-button');
|
||||
buttons.forEach((button) => {
|
||||
const type = button.getAttribute('type');
|
||||
if (type) {
|
||||
console.log(`Legacy type "${type}" is supported for backward compatibility`);
|
||||
}
|
||||
});
|
||||
}}>
|
||||
<dees-panel .title=${'7. Backward Compatibility'} .subtitle=${'Old button types are automatically mapped to new variants'}>
|
||||
<div class="button-group">
|
||||
<dees-button type="normal">Normal → Default</dees-button>
|
||||
<dees-button type="highlighted">Highlighted → Destructive</dees-button>
|
||||
<dees-button type="discreet">Discreet → Outline</dees-button>
|
||||
<dees-button type="big">Big → Large Size</dees-button>
|
||||
</div>
|
||||
|
||||
<p style="margin-top: 16px; font-size: 14px; color: ${cssManager.bdTheme('hsl(215.4 16.3% 56.9%)', 'hsl(215 20.2% 55.1%)')};">
|
||||
These legacy type values are maintained for backward compatibility but we recommend using the new variant system.
|
||||
</p>
|
||||
</dees-panel>
|
||||
</dees-demowrapper>
|
||||
|
||||
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
|
||||
// Track action group clicks
|
||||
const actionGroup = elementArg.querySelectorAll('.vertical-group')[0];
|
||||
const dangerGroup = elementArg.querySelectorAll('.vertical-group')[1];
|
||||
|
||||
if (actionGroup) {
|
||||
const buttons = actionGroup.querySelectorAll('dees-button');
|
||||
buttons.forEach((button, index) => {
|
||||
button.addEventListener('clicked', () => {
|
||||
const action = ['Save Changes', 'Discard', 'Help'][index];
|
||||
console.log(`Action group: ${action} clicked`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (dangerGroup) {
|
||||
const buttons = dangerGroup.querySelectorAll('dees-button');
|
||||
buttons.forEach((button, index) => {
|
||||
button.addEventListener('clicked', () => {
|
||||
const action = ['Delete Account', 'Archive Data', 'Not Available'][index];
|
||||
if (index !== 2) { // Skip disabled button
|
||||
console.log(`Danger zone: ${action} clicked`);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}}>
|
||||
<dees-panel .title=${'8. Advanced Examples'} .subtitle=${'Complex button configurations and real-world use cases'}>
|
||||
<div class="horizontal-group">
|
||||
<div class="vertical-group">
|
||||
<h4 style="margin: 0 0 8px 0; font-size: 14px; font-weight: 500;">Action Group</h4>
|
||||
<dees-button type="default" size="sm">
|
||||
<dees-icon iconFA="faSave"></dees-icon>
|
||||
Save Changes
|
||||
</dees-button>
|
||||
<dees-button type="secondary" size="sm">
|
||||
<dees-icon iconFA="faUndo"></dees-icon>
|
||||
Discard
|
||||
</dees-button>
|
||||
<dees-button type="ghost" size="sm">
|
||||
<dees-icon iconFA="faQuestionCircle"></dees-icon>
|
||||
Help
|
||||
</dees-button>
|
||||
</div>
|
||||
|
||||
<div class="vertical-group">
|
||||
<h4 style="margin: 0 0 8px 0; font-size: 14px; font-weight: 500;">Danger Zone</h4>
|
||||
<dees-button type="destructive" size="sm">
|
||||
<dees-icon iconFA="faTrash"></dees-icon>
|
||||
Delete Account
|
||||
</dees-button>
|
||||
<dees-button type="outline" size="sm">
|
||||
<dees-icon iconFA="faArchive"></dees-icon>
|
||||
Archive Data
|
||||
</dees-button>
|
||||
<dees-button type="ghost" size="sm" disabled>
|
||||
<dees-icon iconFA="faBan"></dees-icon>
|
||||
Not Available
|
||||
</dees-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 24px;">
|
||||
<h4 style="margin: 0 0 8px 0; font-size: 14px; font-weight: 500;">Code Example:</h4>
|
||||
<div class="code-snippet">
|
||||
<dees-button type="default" size="sm" @clicked="\${handleClick}"><br>
|
||||
<dees-icon iconFA="faSave"></dees-icon><br>
|
||||
Save Changes<br>
|
||||
</dees-button>
|
||||
</div>
|
||||
</div>
|
||||
</dees-panel>
|
||||
</dees-demowrapper>
|
||||
</div>
|
||||
`;
|
||||
391
ts_web/elements/dees-button/dees-button.ts
Normal file
391
ts_web/elements/dees-button/dees-button.ts
Normal file
@@ -0,0 +1,391 @@
|
||||
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';
|
||||
import { demoFunc } from './dees-button.demo.js';
|
||||
|
||||
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;
|
||||
}
|
||||
})
|
||||
accessor text: string;
|
||||
|
||||
@property()
|
||||
accessor eventDetailData: string;
|
||||
|
||||
@property({
|
||||
type: Boolean,
|
||||
reflect: true,
|
||||
})
|
||||
accessor disabled = false;
|
||||
|
||||
@property({
|
||||
type: Boolean
|
||||
})
|
||||
accessor isHidden = false;
|
||||
|
||||
@property({
|
||||
type: String
|
||||
})
|
||||
accessor type: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link' | 'normal' | 'highlighted' | 'discreet' | 'big' = 'default';
|
||||
|
||||
@property({
|
||||
type: String
|
||||
})
|
||||
accessor size: 'default' | 'sm' | 'lg' | 'icon' = 'default';
|
||||
|
||||
@property({
|
||||
type: String
|
||||
})
|
||||
accessor status: 'normal' | 'pending' | 'success' | 'error' = 'normal';
|
||||
|
||||
@property({
|
||||
type: Boolean,
|
||||
reflect: true
|
||||
})
|
||||
accessor 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`
|
||||
<div
|
||||
class="button ${this.isHidden ? 'hidden' : ''} ${actualType} size-${actualSize} ${this.status} ${this.disabled
|
||||
? 'disabled'
|
||||
: ''}"
|
||||
@click="${this.dispatchClick}"
|
||||
>
|
||||
${this.status === 'normal' ? html``: html`
|
||||
<dees-spinner
|
||||
.bnw=${true}
|
||||
status="${this.status}"
|
||||
size="${actualSize === 'sm' ? 14 : actualSize === 'lg' ? 18 : 16}"
|
||||
></dees-spinner>
|
||||
`}
|
||||
<div class="textbox">${this.text || html`<slot>Button</slot>`}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user