Skip to content

Commit 6bde8c3

Browse files
committed
feat: introduce split-button directive with Material Design 3 support
- Replace loading-indicator component with split-button directive in documentation and playground. - Update installation and setup instructions to reflect the new split-button directive. - Implement split-button variants: text, filled, tonal, outlined, and elevated. - Add SCSS theming support for split-button, including mixins for customization. - Enhance playground component to demonstrate split-button functionality and variants. - Update public API to export split-button directive instead of loading-indicator component. - Modify index.html and styles.scss for split-button demo and theming.
1 parent 867a4e1 commit 6bde8c3

20 files changed

Lines changed: 1226 additions & 670 deletions

.github/workflows/deploy-demo.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,16 @@ jobs:
3434
with:
3535
node-version: '20'
3636

37-
- name: Remove local loading-indicator reference and install from npm
37+
- name: Remove local split-button reference and install from npm
3838
run: |
39-
npm remove @softwarity/loading-indicator
40-
npm i @softwarity/loading-indicator
39+
npm remove @softwarity/split-button
40+
npm i @softwarity/split-button
4141
4242
- name: Remove path mapping for demo
4343
run: sed -i '/"paths"/,/}/d' projects/demo/tsconfig.app.json
4444

4545
- name: Build demo for GitHub Pages
46-
run: npm run build:demo -- --base-href /loading-indicator/
46+
run: npm run build:demo -- --base-href /split-button/
4747

4848
- name: Setup Pages
4949
uses: actions/configure-pages@v4

.github/workflows/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Build softwarity/loading-indicator
1+
name: Build softwarity/split-button
22
run-name: ${{ github.actor }} run build on ${{ github.repository }}
33
on:
44
push:
@@ -19,7 +19,7 @@ jobs:
1919
uses: actions/upload-artifact@v4
2020
with:
2121
name: coverage-report
22-
path: coverage/loading-indicator/
22+
path: coverage/split-button/
2323
retention-days: 30
2424

2525

.github/workflows/tag.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Publish softwarity/loading-indicator to NPM
1+
name: Publish softwarity/split-button to NPM
22
run-name: ${{ github.actor }} run build on ${{ github.repository }}
33
on:
44
push:

README.md

Lines changed: 119 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -4,82 +4,112 @@
44
</a>
55
</p>
66

7-
# @softwarity/loading-indicator
7+
# @softwarity/split-button
88

99
<p align="center">
10-
<a href="https://www.npmjs.com/package/@softwarity/loading-indicator">
11-
<img src="https://img.shields.io/npm/v/@softwarity/loading-indicator?color=blue&label=npm" alt="npm version">
10+
<a href="https://www.npmjs.com/package/@softwarity/split-button">
11+
<img src="https://img.shields.io/npm/v/@softwarity/split-button?color=blue&label=npm" alt="npm version">
1212
</a>
13-
<a href="https://github.com/softwarity/loading-indicator/blob/main/LICENSE">
13+
<a href="https://github.com/softwarity/split-button/blob/main/LICENSE">
1414
<img src="https://img.shields.io/badge/license-MIT-blue" alt="license">
1515
</a>
16-
<a href="https://github.com/softwarity/loading-indicator/actions/workflows/main.yml">
17-
<img src="https://github.com/softwarity/loading-indicator/actions/workflows/main.yml/badge.svg" alt="build status">
16+
<a href="https://github.com/softwarity/split-button/actions/workflows/main.yml">
17+
<img src="https://github.com/softwarity/split-button/actions/workflows/main.yml/badge.svg" alt="build status">
1818
</a>
1919
</p>
2020

21-
An Angular component that displays an expressive [Material 3 loading indicator](https://m3.material.io/components/loading-indicator/overview) with smooth morphing animation between organic shapes.
21+
An Angular directive that creates a [Material Design 3 split button](https://m3.material.io/components/buttons/overview) with a dropdown menu for secondary actions.
2222

23-
**[Live Demo](https://softwarity.github.io/loading-indicator/)** | **[Release Notes](RELEASE_NOTES.md)**
23+
**[Live Demo](https://softwarity.github.io/split-button/)** | **[Release Notes](RELEASE_NOTES.md)**
2424

2525
<p align="center">
26-
<a href="https://softwarity.github.io/loading-indicator/">
27-
<img src="projects/demo/src/assets/preview.png" alt="Loading Indicator Preview" width="400">
26+
<a href="https://softwarity.github.io/split-button/">
27+
<img src="projects/demo/src/assets/preview.png" alt="Split Button Preview" width="400">
2828
</a>
2929
</p>
3030

3131
## Features
3232

33-
- **Material 3 Expressive Design** - Smooth morphing animation between 7 organic shapes
34-
- **60fps Animation** - Uses requestAnimationFrame for butter-smooth performance
33+
- **Material Design 3 Compliant** - Follows M3 button specifications
34+
- **5 Button Variants** - Text, Filled, Tonal, Outlined, Elevated
3535
- **Responsive to Theme** - Automatically adapts to light/dark color schemes
36-
- **Customizable Size** - Adjustable diameter from small to large
37-
- **Optional Container** - Circular background for better visibility
36+
- **MatMenu Integration** - Works seamlessly with Angular Material's menu component
3837
- **Material 3 Ready** - Uses M3 design tokens for theming (`--mat-sys-*`)
39-
- **Standalone Component** - Easy to import in any Angular 21+ application
40-
- **Lightweight** - Pure SVG animation, no external dependencies
38+
- **Standalone Directive** - Easy to import in any Angular 21+ application
39+
- **Accessible** - Keyboard navigation and ARIA support
4140

4241
## Installation
4342

4443
```bash
45-
npm install @softwarity/loading-indicator
44+
npm install @softwarity/split-button
4645
```
4746

4847
### Peer Dependencies
4948

5049
| Package | Version |
5150
|---------|---------|
5251
| @angular/core | >= 21.0.0 |
52+
| @angular/material | >= 21.0.0 |
5353

5454
## Usage
5555

56-
Import the component in your standalone component:
56+
### 1. Include styles in your global stylesheet
57+
58+
In your `styles.scss`, import and include the split-button styles:
59+
60+
```scss
61+
@use '@softwarity/split-button/split-button-theme' as split-button;
62+
63+
// Include split-button base styles (required)
64+
@include split-button.styles();
65+
```
66+
67+
### 2. Import the directive in your component
5768

5869
```typescript
59-
import { LoadingIndicatorComponent } from '@softwarity/loading-indicator';
70+
import { SplitButtonDirective } from '@softwarity/split-button';
71+
import { MatMenuModule } from '@angular/material/menu';
6072

6173
@Component({
6274
selector: 'app-my-component',
63-
imports: [LoadingIndicatorComponent],
75+
imports: [SplitButtonDirective, MatMenuModule],
6476
template: `...`
6577
})
6678
export class MyComponent {}
6779
```
6880

69-
Add the `loading-indicator` component in your template:
81+
### 3. Add the `appSplitButton` directive to your button
7082

7183
```html
72-
<!-- Basic usage -->
73-
<loading-indicator />
84+
<!-- Text button (default) -->
85+
<button appSplitButton [appSplitButtonTrigger]="trigger" (click)="onSave()">
86+
Save
87+
</button>
88+
<span [matMenuTriggerFor]="menu" #trigger="matMenuTrigger"></span>
89+
<mat-menu #menu="matMenu">
90+
<button mat-menu-item (click)="onSaveAs()">Save As...</button>
91+
<button mat-menu-item (click)="onSaveDraft()">Save Draft</button>
92+
</mat-menu>
93+
94+
<!-- Filled variant -->
95+
<button appSplitButton="filled" [appSplitButtonTrigger]="trigger" (click)="onSubmit()">
96+
Submit
97+
</button>
7498

75-
<!-- With custom diameter -->
76-
<loading-indicator [diameter]="96" />
99+
<!-- Outlined variant -->
100+
<button appSplitButton="outlined" [appSplitButtonTrigger]="trigger" (click)="onAction()">
101+
Action
102+
</button>
77103

78-
<!-- With container background -->
79-
<loading-indicator withContainer />
104+
<!-- Tonal variant -->
105+
<button appSplitButton="tonal" [appSplitButtonTrigger]="trigger" (click)="onProcess()">
106+
Process
107+
</button>
80108

81-
<!-- Combined -->
82-
<loading-indicator [diameter]="64" withContainer />
109+
<!-- Elevated variant -->
110+
<button appSplitButton="elevated" [appSplitButtonTrigger]="trigger" (click)="onExport()">
111+
Export
112+
</button>
83113
```
84114

85115
## API
@@ -88,20 +118,31 @@ Add the `loading-indicator` component in your template:
88118

89119
| Input | Type | Default | Description |
90120
|-------|------|---------|-------------|
91-
| `diameter` | `number` | `48` | Size of the loading indicator in pixels |
92-
| `withContainer` | `boolean` | `false` | When true, displays a circular background container behind the shape |
121+
| `appSplitButton` | `'' \| 'filled' \| 'tonal' \| 'outlined' \| 'elevated'` | `''` | Button variant following Material Design 3 guidelines |
122+
| `appSplitButtonTrigger` | `MatMenuTrigger` | `undefined` | Reference to the MatMenuTrigger for the dropdown menu |
123+
| `disabled` | `boolean` | `false` | Whether the button is disabled |
124+
125+
### Button Variants
126+
127+
| Variant | Description |
128+
|---------|-------------|
129+
| Text (default) | Lowest emphasis, for less important actions |
130+
| Filled | High emphasis, for primary actions |
131+
| Tonal | Medium emphasis with a container color from the secondary palette |
132+
| Outlined | Medium emphasis with a border outline |
133+
| Elevated | Medium emphasis with a shadow elevation |
93134

94135
## Theming (Material 3)
95136

96-
The component provides a SCSS mixin to customize the colors. This approach follows Angular Material's theming pattern.
137+
The directive provides a SCSS mixin to customize the colors. This approach follows Angular Material's theming pattern.
97138

98139
### Setup
99140

100141
In your application's `styles.scss`, import the theme file and call the `overrides` mixin:
101142

102143
```scss
103144
@use '@angular/material' as mat;
104-
@use '@softwarity/loading-indicator/loading-indicator-theme' as loading-indicator;
145+
@use '@softwarity/split-button/split-button-theme' as split-button;
105146

106147
// Your Material 3 theme
107148
html {
@@ -112,8 +153,8 @@ html {
112153
)
113154
));
114155

115-
// Optional: customize loading indicator colors
116-
// @include loading-indicator.overrides();
156+
// Optional: customize split-button colors
157+
// @include split-button.overrides();
117158
}
118159
```
119160

@@ -123,70 +164,74 @@ The `overrides` mixin accepts a map of tokens to customize the appearance:
123164

124165
| Token | Default | Description |
125166
|-------|---------|-------------|
126-
| `background-color` | `var(--mat-sys-primary-container)` | Background color when withContainer is true |
127-
| `shape-color` | `var(--mat-sys-on-secondary-container)` | Color of the animated shape |
167+
| `text-label-color` | `var(--mat-sys-primary)` | Label color for text variant |
168+
| `filled-container-color` | `var(--mat-sys-primary)` | Container color for filled variant |
169+
| `filled-label-color` | `var(--mat-sys-on-primary)` | Label color for filled variant |
170+
| `outlined-outline-color` | `var(--mat-sys-outline)` | Border color for outlined variant |
171+
| `outlined-label-color` | `var(--mat-sys-primary)` | Label color for outlined variant |
172+
| `tonal-container-color` | `var(--mat-sys-secondary-container)` | Container color for tonal variant |
173+
| `tonal-label-color` | `var(--mat-sys-on-secondary-container)` | Label color for tonal variant |
174+
| `elevated-container-color` | `var(--mat-sys-surface-container-low)` | Container color for elevated variant |
175+
| `elevated-label-color` | `var(--mat-sys-primary)` | Label color for elevated variant |
128176

129177
### Examples
130178

131179
```scss
132-
// Customize colors with light/dark support
133-
@include loading-indicator.overrides((
134-
background-color: light-dark(#e8def8, #4a4458),
135-
shape-color: light-dark(#6750a4, #ccc2dc)
180+
// Customize filled button colors
181+
@include split-button.overrides((
182+
filled-container-color: light-dark(#6750a4, #d0bcff),
183+
filled-label-color: light-dark(#ffffff, #381e72)
136184
));
137185

138-
// Use Material 3 system colors
139-
@include loading-indicator.overrides((
140-
background-color: var(--mat-sys-tertiary-container),
141-
shape-color: var(--mat-sys-on-tertiary-container)
186+
// Use Material 3 system colors for tonal variant
187+
@include split-button.overrides((
188+
tonal-container-color: var(--mat-sys-tertiary-container),
189+
tonal-label-color: var(--mat-sys-on-tertiary-container)
142190
));
143191

144192
// Custom brand colors
145-
@include loading-indicator.overrides((
146-
shape-color: #ff5722
193+
@include split-button.overrides((
194+
filled-container-color: #ff5722,
195+
filled-label-color: #ffffff
147196
));
148197
```
149198

150199
## Examples
151200

152-
### Centered Overlay
153-
154-
```html
155-
<div class="loading-overlay">
156-
<loading-indicator [diameter]="64" withContainer />
157-
</div>
158-
```
159-
160-
### Inline Button Loading
201+
### Save Action with Alternatives
161202

162203
```html
163-
<button [disabled]="isLoading">
164-
@if (isLoading) {
165-
<loading-indicator [diameter]="24" />
166-
} @else {
167-
Submit
168-
}
204+
<button appSplitButton="filled" [appSplitButtonTrigger]="saveTrigger" (click)="onSave()">
205+
Save
169206
</button>
207+
<span [matMenuTriggerFor]="saveMenu" #saveTrigger="matMenuTrigger"></span>
208+
<mat-menu #saveMenu="matMenu">
209+
<button mat-menu-item (click)="onSaveAs()">Save As...</button>
210+
<button mat-menu-item (click)="onSaveDraft()">Save Draft</button>
211+
<button mat-menu-item (click)="onSaveAndClose()">Save & Close</button>
212+
</mat-menu>
170213
```
171214

172-
### Content Placeholder
215+
### Export with Format Options
173216

174217
```html
175-
@if (isLoading) {
176-
<loading-indicator [diameter]="48" />
177-
} @else {
178-
<app-content />
179-
}
218+
<button appSplitButton="outlined" [appSplitButtonTrigger]="exportTrigger" (click)="onExportPDF()">
219+
Export PDF
220+
</button>
221+
<span [matMenuTriggerFor]="exportMenu" #exportTrigger="matMenuTrigger"></span>
222+
<mat-menu #exportMenu="matMenu">
223+
<button mat-menu-item (click)="onExportCSV()">Export CSV</button>
224+
<button mat-menu-item (click)="onExportXLSX()">Export Excel</button>
225+
<button mat-menu-item (click)="onExportJSON()">Export JSON</button>
226+
</mat-menu>
180227
```
181228

182-
### With Deferred Loading
229+
### Disabled State
183230

184231
```html
185-
@defer (on idle) {
186-
<app-heavy-component />
187-
} @placeholder (minimum 500ms) {
188-
<loading-indicator [diameter]="64" withContainer />
189-
}
232+
<button appSplitButton="filled" [appSplitButtonTrigger]="trigger" [disabled]="true">
233+
Disabled
234+
</button>
190235
```
191236

192237
## License

angular.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
}
5656
}
5757
},
58-
"loading-indicator": {
58+
"split-button": {
5959
"projectType": "library",
6060
"root": "",
6161
"sourceRoot": "src",

karma.conf.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ module.exports = function (config) {
2525
suppressAll: true // removes the duplicated traces
2626
},
2727
coverageReporter: {
28-
dir: require('path').join(__dirname, './coverage/loading-indicator'),
28+
dir: require('path').join(__dirname, './coverage/split-button'),
2929
subdir: '.',
3030
reporters: [
3131
{ type: 'html' },

ng-package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"assets": [
88
{
99
"input": "src/lib",
10-
"glob": "_loading-indicator-theme.scss",
10+
"glob": "_split-button-theme.scss",
1111
"output": "."
1212
}
1313
]

0 commit comments

Comments
 (0)