feat(dees-button,dees-statsgrid): add unified icon property and icon-position support to dees-button; add partition and disk tile types to dees-statsgrid

This commit is contained in:
2026-01-13 20:45:50 +00:00
parent 1982c40337
commit 5dd0367df0
22 changed files with 639 additions and 38 deletions

View File

@@ -142,54 +142,95 @@ export const demoFunc = () => html`
<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>
<dees-icon icon="fa:plus"></dees-icon>
Add Item
</dees-button>
<dees-button type="destructive">
<dees-icon iconFA="faTrash"></dees-icon>
<dees-icon icon="fa:trash"></dees-icon>
Delete
</dees-button>
<dees-button type="outline">
<dees-icon iconFA="faDownload"></dees-icon>
<dees-icon icon="lucide:Download"></dees-icon>
Download
</dees-button>
</div>
<div class="icon-row">
<dees-button type="secondary" size="sm">
<dees-icon iconFA="faCog"></dees-icon>
<dees-icon icon="fa:gear"></dees-icon>
Settings
</dees-button>
<dees-button type="ghost">
<dees-icon iconFA="faChevronLeft"></dees-icon>
<dees-icon icon="fa:caretLeft"></dees-icon>
Back
</dees-button>
<dees-button type="ghost">
Next
<dees-icon iconFA="faChevronRight"></dees-icon>
<dees-icon icon="fa:caretRight"></dees-icon>
</dees-button>
</div>
<div class="icon-row">
<dees-button size="icon" type="default">
<dees-icon iconFA="faPlus"></dees-icon>
<dees-icon icon="fa:plus"></dees-icon>
</dees-button>
<dees-button size="icon" type="secondary">
<dees-icon iconFA="faCog"></dees-icon>
<dees-icon icon="fa:gear"></dees-icon>
</dees-button>
<dees-button size="icon" type="outline">
<dees-icon iconFA="faSearch"></dees-icon>
<dees-icon icon="lucide:Search"></dees-icon>
</dees-button>
<dees-button size="icon" type="ghost">
<dees-icon iconFA="faEllipsisV"></dees-icon>
<dees-icon icon="lucide:MoreVertical"></dees-icon>
</dees-button>
<dees-button size="icon" type="destructive">
<dees-icon iconFA="faTrash"></dees-icon>
<dees-icon icon="fa:trash"></dees-icon>
</dees-button>
</div>
</dees-panel>
</dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// Track icon property button clicks
const buttons = elementArg.querySelectorAll('dees-button');
buttons.forEach((button) => {
button.addEventListener('clicked', () => {
const icon = button.getAttribute('icon') || 'none';
const position = button.getAttribute('iconPosition') || 'left';
console.log(`Icon property button: icon=${icon}, position=${position}`);
});
});
}}>
<dees-panel .title=${'4. Icons via Property'} .subtitle=${'Simplified icon syntax using the icon property'}>
<div class="icon-row">
<dees-button icon="fa:plus">Add Item</dees-button>
<dees-button type="destructive" icon="fa:trash">Delete</dees-button>
<dees-button type="outline" icon="lucide:Download">Download</dees-button>
</div>
<div class="icon-row">
<dees-button type="secondary" size="sm" icon="fa:gear">Settings</dees-button>
<dees-button type="ghost" icon="fa:caretLeft">Back</dees-button>
<dees-button type="ghost" icon="fa:caretRight" iconPosition="right">Next</dees-button>
</div>
<div class="icon-row">
<dees-button size="icon" type="default" icon="fa:plus"></dees-button>
<dees-button size="icon" type="secondary" icon="lucide:Settings"></dees-button>
<dees-button size="icon" type="outline" icon="lucide:Search"></dees-button>
<dees-button size="icon" type="ghost" icon="lucide:MoreVertical"></dees-button>
<dees-button size="icon" type="destructive" icon="fa:trash"></dees-button>
</div>
<div style="margin-top: 16px;">
<div class="code-snippet">
&lt;dees-button icon="fa:plus"&gt;Add Item&lt;/dees-button&gt;<br>
&lt;dees-button icon="fa:caretRight" iconPosition="right"&gt;Next&lt;/dees-button&gt;
</div>
</div>
</dees-panel>
</dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// Demonstrate status changes
const pendingButton = elementArg.querySelector('dees-button[status="pending"]');
@@ -215,7 +256,7 @@ export const demoFunc = () => html`
});
}
}}>
<dees-panel .title=${'4. Button States'} .subtitle=${'Different states to indicate button status and loading conditions'}>
<dees-panel .title=${'5. 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>
@@ -260,7 +301,7 @@ export const demoFunc = () => html`
});
}
}}>
<dees-panel .title=${'5. Event Handling'} .subtitle=${'Interactive examples with click event handling'}>
<dees-panel .title=${'6. 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'}>
@@ -303,7 +344,7 @@ export const demoFunc = () => html`
});
}
}}>
<dees-panel .title=${'6. Form Integration'} .subtitle=${'Buttons working within forms with automatic spacing'}>
<dees-panel .title=${'7. 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>
@@ -330,7 +371,7 @@ export const demoFunc = () => html`
}
});
}}>
<dees-panel .title=${'7. Backward Compatibility'} .subtitle=${'Old button types are automatically mapped to new variants'}>
<dees-panel .title=${'8. 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>
@@ -371,36 +412,36 @@ export const demoFunc = () => html`
});
}
}}>
<dees-panel .title=${'8. Advanced Examples'} .subtitle=${'Complex button configurations and real-world use cases'}>
<dees-panel .title=${'9. 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>
<dees-icon icon="lucide:Save"></dees-icon>
Save Changes
</dees-button>
<dees-button type="secondary" size="sm">
<dees-icon iconFA="faUndo"></dees-icon>
<dees-icon icon="lucide:Undo2"></dees-icon>
Discard
</dees-button>
<dees-button type="ghost" size="sm">
<dees-icon iconFA="faQuestionCircle"></dees-icon>
<dees-icon icon="lucide:HelpCircle"></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>
<dees-icon icon="fa:trash"></dees-icon>
Delete Account
</dees-button>
<dees-button type="outline" size="sm">
<dees-icon iconFA="faArchive"></dees-icon>
<dees-icon icon="lucide:Archive"></dees-icon>
Archive Data
</dees-button>
<dees-button type="ghost" size="sm" disabled>
<dees-icon iconFA="faBan"></dees-icon>
<dees-icon icon="lucide:Ban"></dees-icon>
Not Available
</dees-button>
</div>
@@ -409,8 +450,7 @@ export const demoFunc = () => html`
<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">
&lt;dees-button type="default" size="sm" @clicked="\${handleClick}"&gt;<br>
&nbsp;&nbsp;&lt;dees-icon iconFA="faSave"&gt;&lt;/dees-icon&gt;<br>
&lt;dees-button type="default" size="sm" icon="lucide:Save" @clicked="\${handleClick}"&gt;<br>
&nbsp;&nbsp;Save Changes<br>
&lt;/dees-button&gt;
</div>

View File

@@ -68,6 +68,12 @@ export class DeesButton extends DeesElement {
})
accessor insideForm: boolean = false;
@property({ type: String, reflect: true })
accessor icon: string;
@property({ type: String, reflect: true })
accessor iconPosition: 'left' | 'right' = 'left';
constructor() {
super();
}
@@ -340,9 +346,62 @@ export class DeesButton extends DeesElement {
height: 18px;
}
/* Text alignment */
.textbox {
display: flex;
align-items: center;
}
`,
];
/**
* Extracts icon and text from light DOM and sets properties
*/
private extractLightDom(): void {
const iconElement = this.querySelector('dees-icon') as any;
// Get all text content from light DOM
const textContent = Array.from(this.childNodes)
.filter(node => node.nodeType === Node.TEXT_NODE)
.map(node => node.textContent?.trim())
.filter(Boolean)
.join(' ');
if (textContent && !this.text) {
this.text = textContent;
}
if (iconElement) {
// Get icon value
const iconValue = iconElement.icon || iconElement.getAttribute('icon') ||
(iconElement.iconFA ? `fa:${iconElement.iconFA}` : null);
if (iconValue) {
// Determine position based on DOM order
const children = Array.from(this.childNodes);
const iconIndex = children.indexOf(iconElement);
const textNodes = children.filter(node =>
node.nodeType === Node.TEXT_NODE && node.textContent?.trim()
);
if (textNodes.length > 0) {
const firstTextIndex = children.indexOf(textNodes[0]);
this.iconPosition = iconIndex < firstTextIndex ? 'left' : 'right';
}
// Set the icon property
this.icon = iconValue;
}
// Remove the light DOM icon element
iconElement.remove();
}
// Clear all remaining light DOM
this.innerHTML = '';
}
public render(): TemplateResult {
// Map old types to new types for backward compatibility
const typeMap: {[key: string]: string} = {
@@ -351,10 +410,20 @@ export class DeesButton extends DeesElement {
'discreet': 'outline',
'big': 'default' // Will use size instead
};
const actualType = typeMap[this.type] || this.type;
const actualSize = this.type === 'big' ? 'lg' : this.size;
const leftIcon = this.iconPosition === 'left' && this.icon
? html`<dees-icon .icon=${this.icon}></dees-icon>`
: '';
const rightIcon = this.iconPosition === 'right' && this.icon
? html`<dees-icon .icon=${this.icon}></dees-icon>`
: '';
// For icon-only buttons, hide the textbox
const isIconOnly = actualSize === 'icon' && this.icon;
return html`
<div
class="button ${this.isHidden ? 'hidden' : ''} ${actualType} size-${actualSize} ${this.status} ${this.disabled
@@ -363,13 +432,15 @@ export class DeesButton extends DeesElement {
@click="${this.dispatchClick}"
>
${this.status === 'normal' ? html``: html`
<dees-spinner
.bnw=${true}
<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>
${leftIcon}
${isIconOnly ? '' : html`<div class="textbox">${this.text || 'Button'}</div>`}
${rightIcon}
</div>
`;
}
@@ -390,6 +461,7 @@ export class DeesButton extends DeesElement {
}
public async firstUpdated() {
// Don't set default text here as it interferes with slotted content
// Extract light DOM content (icon + text) and set as properties
this.extractLightDom();
}
}