Add split button component with comprehensive functionality
- Create new SplitButtonComponent with primary action and dropdown menu - Support for three variants: filled, tonal, outlined - Three size options: small, medium, large - Icon support with configurable positioning - Configurable dropdown menu items with icons and labels - Full accessibility support with ARIA attributes and keyboard navigation - Interactive states: hover, active, disabled, loading - Responsive design using semantic design tokens - Complete demo application with multiple examples - Integration with dashboard navigation menu 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
@use '../../../../../shared-ui/src/styles/semantic/index' as *;
|
||||
|
||||
.demo-container {
|
||||
padding: $semantic-spacing-component-lg;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.demo-section {
|
||||
margin-bottom: $semantic-spacing-layout-section-lg;
|
||||
|
||||
h3 {
|
||||
font-family: map-get($semantic-typography-heading-h3, font-family);
|
||||
font-size: map-get($semantic-typography-heading-h3, font-size);
|
||||
font-weight: map-get($semantic-typography-heading-h3, font-weight);
|
||||
line-height: map-get($semantic-typography-heading-h3, line-height);
|
||||
color: $semantic-color-text-primary;
|
||||
margin-bottom: $semantic-spacing-component-md;
|
||||
border-bottom: $semantic-border-width-1 solid $semantic-color-border-secondary;
|
||||
padding-bottom: $semantic-spacing-component-sm;
|
||||
}
|
||||
}
|
||||
|
||||
.demo-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: $semantic-spacing-component-md;
|
||||
align-items: flex-start;
|
||||
margin-bottom: $semantic-spacing-component-lg;
|
||||
}
|
||||
|
||||
.demo-results {
|
||||
padding: $semantic-spacing-component-md;
|
||||
background: $semantic-color-surface-secondary;
|
||||
border: $semantic-border-width-1 solid $semantic-color-border-secondary;
|
||||
border-radius: $semantic-border-radius-md;
|
||||
|
||||
p {
|
||||
font-family: map-get($semantic-typography-body-medium, font-family);
|
||||
font-size: map-get($semantic-typography-body-medium, font-size);
|
||||
font-weight: map-get($semantic-typography-body-medium, font-weight);
|
||||
line-height: map-get($semantic-typography-body-medium, line-height);
|
||||
color: $semantic-color-text-primary;
|
||||
margin: $semantic-spacing-component-xs 0;
|
||||
|
||||
strong {
|
||||
font-weight: $semantic-typography-font-weight-semibold;
|
||||
color: $semantic-color-text-primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Responsive adjustments
|
||||
@media (max-width: 768px) {
|
||||
.demo-row {
|
||||
flex-direction: column;
|
||||
gap: $semantic-spacing-component-sm;
|
||||
}
|
||||
|
||||
.demo-container {
|
||||
padding: $semantic-spacing-component-md;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { faDownload, faSave, faShare, faEdit, faTrash, faFile, faFileText, faPrint } from '@fortawesome/free-solid-svg-icons';
|
||||
import { SplitButtonComponent, SplitButtonMenuItem } from '../../../../../ui-essentials/src/lib/components/buttons/split-button';
|
||||
|
||||
@Component({
|
||||
selector: 'ui-split-button-demo',
|
||||
standalone: true,
|
||||
imports: [CommonModule, SplitButtonComponent],
|
||||
template: `
|
||||
<div class="demo-container">
|
||||
<h2>Split Button Demo</h2>
|
||||
|
||||
<!-- Size Variants -->
|
||||
<section class="demo-section">
|
||||
<h3>Sizes</h3>
|
||||
<div class="demo-row">
|
||||
@for (size of sizes; track size) {
|
||||
<ui-split-button
|
||||
[size]="size"
|
||||
[menuItems]="basicMenuItems"
|
||||
(primaryClicked)="handlePrimaryClick('Primary clicked - ' + size)"
|
||||
(menuItemClicked)="handleMenuItemClick($event)">
|
||||
{{ size | titlecase }} Size
|
||||
</ui-split-button>
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Variant Styles -->
|
||||
<section class="demo-section">
|
||||
<h3>Variants</h3>
|
||||
<div class="demo-row">
|
||||
@for (variant of variants; track variant) {
|
||||
<ui-split-button
|
||||
[variant]="variant"
|
||||
[menuItems]="basicMenuItems"
|
||||
(primaryClicked)="handlePrimaryClick('Primary clicked - ' + variant)"
|
||||
(menuItemClicked)="handleMenuItemClick($event)">
|
||||
{{ variant | titlecase }}
|
||||
</ui-split-button>
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- With Icons -->
|
||||
<section class="demo-section">
|
||||
<h3>With Icons</h3>
|
||||
<div class="demo-row">
|
||||
<ui-split-button
|
||||
[icon]="faDownload"
|
||||
iconPosition="left"
|
||||
[menuItems]="exportMenuItems"
|
||||
(primaryClicked)="handlePrimaryClick('Download clicked')"
|
||||
(menuItemClicked)="handleMenuItemClick($event)">
|
||||
Download
|
||||
</ui-split-button>
|
||||
|
||||
<ui-split-button
|
||||
variant="tonal"
|
||||
[icon]="faSave"
|
||||
iconPosition="right"
|
||||
[menuItems]="saveMenuItems"
|
||||
(primaryClicked)="handlePrimaryClick('Save clicked')"
|
||||
(menuItemClicked)="handleMenuItemClick($event)">
|
||||
Save
|
||||
</ui-split-button>
|
||||
|
||||
<ui-split-button
|
||||
variant="outlined"
|
||||
[icon]="faShare"
|
||||
[menuItems]="shareMenuItems"
|
||||
(primaryClicked)="handlePrimaryClick('Share clicked')"
|
||||
(menuItemClicked)="handleMenuItemClick($event)">
|
||||
Share
|
||||
</ui-split-button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- States -->
|
||||
<section class="demo-section">
|
||||
<h3>States</h3>
|
||||
<div class="demo-row">
|
||||
<ui-split-button
|
||||
[disabled]="true"
|
||||
[menuItems]="basicMenuItems">
|
||||
Disabled
|
||||
</ui-split-button>
|
||||
|
||||
<ui-split-button
|
||||
[loading]="true"
|
||||
[menuItems]="basicMenuItems">
|
||||
Loading
|
||||
</ui-split-button>
|
||||
|
||||
<ui-split-button
|
||||
[fullWidth]=true
|
||||
variant="tonal"
|
||||
[menuItems]="basicMenuItems"
|
||||
(primaryClicked)="handlePrimaryClick('Full width clicked')"
|
||||
(menuItemClicked)="handleMenuItemClick($event)">
|
||||
Full Width
|
||||
</ui-split-button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Complex Examples -->
|
||||
<section class="demo-section">
|
||||
<h3>Complex Examples</h3>
|
||||
<div class="demo-row">
|
||||
<ui-split-button
|
||||
size="large"
|
||||
variant="filled"
|
||||
[icon]="faEdit"
|
||||
[menuItems]="editMenuItems"
|
||||
(primaryClicked)="handlePrimaryClick('Edit Document clicked')"
|
||||
(menuItemClicked)="handleMenuItemClick($event)">
|
||||
Edit Document
|
||||
</ui-split-button>
|
||||
|
||||
<ui-split-button
|
||||
size="medium"
|
||||
variant="outlined"
|
||||
[menuItems]="fileMenuItems"
|
||||
(primaryClicked)="handlePrimaryClick('New File clicked')"
|
||||
(menuItemClicked)="handleMenuItemClick($event)">
|
||||
New File
|
||||
</ui-split-button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Interactive Results -->
|
||||
<section class="demo-section">
|
||||
<h3>Interactive Results</h3>
|
||||
<div class="demo-results">
|
||||
<p><strong>Last Action:</strong> {{ lastAction || 'None' }}</p>
|
||||
<p><strong>Click Count:</strong> {{ clickCount }}</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
`,
|
||||
styleUrl: './split-button-demo.component.scss'
|
||||
})
|
||||
export class SplitButtonDemoComponent {
|
||||
sizes = ['small', 'medium', 'large'] as const;
|
||||
variants = ['filled', 'tonal', 'outlined'] as const;
|
||||
|
||||
clickCount = 0;
|
||||
lastAction = '';
|
||||
|
||||
// Icons
|
||||
faDownload = faDownload;
|
||||
faSave = faSave;
|
||||
faShare = faShare;
|
||||
faEdit = faEdit;
|
||||
faTrash = faTrash;
|
||||
faFile = faFile;
|
||||
faFileText = faFileText;
|
||||
faPrint = faPrint;
|
||||
|
||||
// Menu configurations
|
||||
basicMenuItems: SplitButtonMenuItem[] = [
|
||||
{ label: 'Option 1', value: 'opt1' },
|
||||
{ label: 'Option 2', value: 'opt2' },
|
||||
{ label: 'Option 3', value: 'opt3' },
|
||||
{ label: 'Disabled Option', value: 'disabled', disabled: true }
|
||||
];
|
||||
|
||||
exportMenuItems: SplitButtonMenuItem[] = [
|
||||
{ label: 'Download PDF', value: 'pdf', icon: this.faFile },
|
||||
{ label: 'Download CSV', value: 'csv', icon: this.faFileText },
|
||||
{ label: 'Download Excel', value: 'excel', icon: this.faFile },
|
||||
{ label: 'Print', value: 'print', icon: this.faPrint }
|
||||
];
|
||||
|
||||
saveMenuItems: SplitButtonMenuItem[] = [
|
||||
{ label: 'Save As...', value: 'save-as' },
|
||||
{ label: 'Save Copy', value: 'save-copy' },
|
||||
{ label: 'Save Template', value: 'save-template' }
|
||||
];
|
||||
|
||||
shareMenuItems: SplitButtonMenuItem[] = [
|
||||
{ label: 'Share Link', value: 'share-link' },
|
||||
{ label: 'Share via Email', value: 'share-email' },
|
||||
{ label: 'Export & Share', value: 'export-share' }
|
||||
];
|
||||
|
||||
editMenuItems: SplitButtonMenuItem[] = [
|
||||
{ label: 'Quick Edit', value: 'quick-edit', icon: this.faEdit },
|
||||
{ label: 'Advanced Edit', value: 'advanced-edit', icon: this.faEdit },
|
||||
{ label: 'Delete', value: 'delete', icon: this.faTrash }
|
||||
];
|
||||
|
||||
fileMenuItems: SplitButtonMenuItem[] = [
|
||||
{ label: 'New Text File', value: 'new-text', icon: this.faFileText },
|
||||
{ label: 'New Document', value: 'new-doc', icon: this.faFile },
|
||||
{ label: 'New from Template', value: 'new-template', icon: this.faFile }
|
||||
];
|
||||
|
||||
handlePrimaryClick(action: string): void {
|
||||
this.clickCount++;
|
||||
this.lastAction = action;
|
||||
console.log('Primary button clicked:', action);
|
||||
}
|
||||
|
||||
handleMenuItemClick(data: { event: Event; item: SplitButtonMenuItem }): void {
|
||||
this.clickCount++;
|
||||
this.lastAction = `Menu item: ${data.item.label} (value: ${data.item.value})`;
|
||||
console.log('Menu item clicked:', data);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user