Skip to content

Commit 8879733

Browse files
gracetxgaoxelaintdamienwebdev
authored
feat(design): add switch component (#3143)
Co-authored-by: xelaint <[email protected]> Co-authored-by: Damien Retzinger <[email protected]>
1 parent eb5e20b commit 8879733

37 files changed

+659
-2
lines changed

apps/design-land/src/app/app-routing.module.ts

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export const appRoutes: Routes = [
3636
{ path: 'progress-bar', loadChildren: () => import('./progress-bar/progress-bar.module').then(m => m.DesignLandProgressBarModule) },
3737
{ path: 'quantity-field', loadChildren: () => import('./quantity-field/quantity-field.module').then(m => m.DesignLandQuantityFieldModule) },
3838
{ path: 'sidebar', loadChildren: () => import('./sidebar/sidebar.module').then(m => m.DesignLandSidebarModule) },
39+
{ path: 'switch', loadChildren: () => import('./switch/switch.module').then(m => m.DesignLandSwitchModule) },
3940
{ path: 'radio', loadChildren: () => import('./radio/radio.module').then(m => m.DesignLandRadioModule) },
4041
{ path: 'toast', loadChildren: () => import('./toast/toast.module').then(m => m.DesignLandToastModule) },
4142
{ path: 'tree', loadChildren: () => import('./tree/tree.module').then(m => m.DesignLandTreeModule) },

apps/design-land/src/app/app.component.ts

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { PROGRESS_BAR_EXAMPLES } from '@daffodil/design/progress-bar/examples';
2727
import { QUANTITY_FIELD_EXAMPLES } from '@daffodil/design/quantity-field/examples';
2828
import { RADIO_EXAMPLES } from '@daffodil/design/radio/examples';
2929
import { SIDEBAR_EXAMPLES } from '@daffodil/design/sidebar/examples';
30+
import { SWITCH_EXAMPLES } from '@daffodil/design/switch/examples';
3031
import { TABS_EXAMPLES } from '@daffodil/design/tabs/examples';
3132
import { TOAST_EXAMPLES } from '@daffodil/design/toast/examples';
3233
import { TREE_EXAMPLES } from '@daffodil/design/tree/examples';
@@ -67,6 +68,7 @@ export class DesignLandAppComponent {
6768
...IMAGE_EXAMPLES,
6869
...INPUT_EXAMPLES,
6970
...SIDEBAR_EXAMPLES,
71+
...SWITCH_EXAMPLES,
7072
...TOAST_EXAMPLES,
7173
...TREE_EXAMPLES,
7274
...TABS_EXAMPLES,

apps/design-land/src/app/app.module.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ import { DaffButtonModule } from '@daffodil/design/button';
1313
import { DaffLinkSetModule } from '@daffodil/design/link-set';
1414
import { DaffNavbarModule } from '@daffodil/design/navbar';
1515
import { DaffSidebarModule } from '@daffodil/design/sidebar';
16-
import { provideDaffToast } from '@daffodil/design/toast';
1716
import { DaffThemeSwitchButtonModule } from '@daffodil/theme-switch';
1817

1918
import { DesignLandAppRoutingModule } from './app-routing.module';
2019
import { DesignLandAppComponent } from './app.component';
2120
import { DesignLandNavModule } from './core/nav/nav.module';
2221
import { DesignLandTemplateModule } from './core/template/template.module';
22+
import { DesignLandSwitchModule } from './switch/switch.module';
2323

2424
@NgModule({
2525
declarations: [
@@ -41,11 +41,11 @@ import { DesignLandTemplateModule } from './core/template/template.module';
4141
FontAwesomeModule,
4242
DesignLandNavModule,
4343
DesignLandTemplateModule,
44+
DesignLandSwitchModule,
4445
],
4546
providers: [
4647
DAFF_THEME_INITIALIZER,
4748
provideHttpClient(withInterceptorsFromDi()),
48-
provideDaffToast(),
4949
],
5050
})
5151
export class AppModule { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { NgModule } from '@angular/core';
2+
import {
3+
Routes,
4+
RouterModule,
5+
} from '@angular/router';
6+
7+
import { DesignLandSwitchComponent } from './switch.component';
8+
9+
const routes: Routes = [
10+
{ path: '', component: DesignLandSwitchComponent },
11+
];
12+
13+
@NgModule({
14+
imports: [RouterModule.forChild(routes)],
15+
exports: [RouterModule],
16+
})
17+
export class DesignLandSwitchRoutingModule { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<h1>Switch</h1>
2+
3+
<design-land-example-viewer-container example="basic-switch"></design-land-example-viewer-container>
4+
<design-land-example-viewer-container example="disabled-switch"></design-land-example-viewer-container>
5+
<design-land-example-viewer-container example="loading-switch"></design-land-example-viewer-container>
6+
<design-land-example-viewer-container example="switch-error"></design-land-example-viewer-container>
7+
<design-land-example-viewer-container example="switch-label-positions"></design-land-example-viewer-container>

apps/design-land/src/app/switch/switch.component.spec.ts

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { Component } from '@angular/core';
2+
3+
@Component({
4+
selector: 'design-land-switch',
5+
templateUrl: './switch.component.html',
6+
})
7+
export class DesignLandSwitchComponent {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { CommonModule } from '@angular/common';
2+
import { NgModule } from '@angular/core';
3+
4+
import { DaffArticleModule } from '@daffodil/design/article';
5+
6+
import { DesignLandSwitchRoutingModule } from './switch-routing.module';
7+
import { DesignLandSwitchComponent } from './switch.component';
8+
import { DesignLandArticleEncapsulatedModule } from '../core/article-encapsulated/article-encapsulated.module';
9+
import { DesignLandExampleViewerModule } from '../core/code-preview/container/example-viewer.module';
10+
11+
@NgModule({
12+
declarations: [
13+
DesignLandSwitchComponent,
14+
],
15+
imports: [
16+
CommonModule,
17+
DaffArticleModule,
18+
DesignLandExampleViewerModule,
19+
DesignLandSwitchRoutingModule,
20+
DesignLandArticleEncapsulatedModule,
21+
],
22+
})
23+
export class DesignLandSwitchModule { }

apps/design-land/src/assets/nav.json

+7
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,13 @@
222222
"items": [],
223223
"data": {}
224224
},
225+
{
226+
"title": "Switch",
227+
"url": "switch",
228+
"id": "switch",
229+
"items": [],
230+
"data": {}
231+
},
225232
{
226233
"title": "Toast",
227234
"url": "toast",

libs/design/scss/global.scss

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
@use 'typography' as t;
1515
@use '@angular/cdk/overlay-prebuilt';
16+
@use '@angular/cdk/a11y-prebuilt';
1617
@use 'modern-normalize/modern-normalize';
1718
@forward './theming/theme-css-variables';
1819

libs/design/scss/theme.scss

+2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
@use '../notification/src/notification-theme' as notification;
4444
@use '../paginator/src/paginator-theme' as paginator;
4545
@use '../sidebar/src/sidebar-theme' as sidebar;
46+
@use '../switch/src/switch-theme' as switch;
4647
@use '../progress-bar/src/progress-bar-theme' as progress-bar;
4748
@use '../scss/state/skeleton/mixins' as skeleton;
4849
@use '../tabs/src/tabs-theme' as tabs;
@@ -94,6 +95,7 @@
9495
@include notification.daff-notification-theme($theme);
9596
@include paginator.daff-paginator-theme($theme);
9697
@include sidebar.daff-sidebar-theme($theme);
98+
@include switch.daff-switch-theme($theme);
9799
@include tabs.daff-tabs-theme($theme);
98100
@include tree.daff-tree-theme($theme);
99101
@include toast.daff-toast-theme($theme);

libs/design/switch/README.md

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Switch
2+
A switch allows users to toggle the state of a single setting.
3+
4+
## Overview
5+
Switches provide a way to toggle between two states with a visual indicator of the current state. A label can be positioned on the left, right, top, or bottom of the switch. The switch can be set to a disabled state, display a loading indicator, or show a custom error message.
6+
7+
## Usage
8+
9+
### Within a standalone component
10+
To use a switch in a standalone component, import it directly into your custom component:
11+
12+
```ts
13+
@Component({
14+
selector: 'custom-component',
15+
templateUrl: './custom-component.component.html',
16+
standalone: true,
17+
imports: [
18+
DAFF_SWITCH_COMPONENTS,
19+
],
20+
})
21+
export class CustomComponent {}
22+
```
23+
24+
## Examples
25+
26+
### Basic Switch
27+
A basic switch can be toggled by setting the `checked` property to `true` or `false`. By default, this is set to `false`.
28+
29+
<design-land-example-viewer-container example="basic-switch"></design-land-example-viewer-container>
30+
31+
### Disabled Switch
32+
A switch with the `disabled` property will be non-interactive.
33+
34+
<design-land-example-viewer-container example="disabled-switch"></design-land-example-viewer-container>
35+
36+
### Loading Switch
37+
A switch can display a loading state by setting `loading` to `true`. This will also disable the switch.
38+
39+
<design-land-example-viewer-container example="loading-switch"></design-land-example-viewer-container>
40+
41+
### Switch with Error
42+
An error message can be displayed by setting `error` to `true` and including a `daff-error-message` to show the message text.
43+
44+
<design-land-example-viewer-container example="switch-error"></design-land-example-viewer-container>
45+
46+
### Changing Label Position
47+
The label position can be changed by setting the `labelPosition` property. The default position is `left`.
48+
49+
Supported positions: `left | right | top | bottom`
50+
51+
<design-land-example-viewer-container example="switch-label-positions"></design-land-example-viewer-container>
52+
53+
54+
## Accessibility
55+
Switches follow the [ARIA Switch design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/switch/).
56+
57+
### Keyboard Interactions
58+
| Key | Action |
59+
| --- | ------ |
60+
| Space | Toggles the switch to the opposite state. |
61+
| Tab | Moves to next component on the page. |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"$schema": "../../../../node_modules/ng-packagr/ng-entrypoint.schema.json",
3+
"lib": {
4+
"entryFile": "src/index.ts",
5+
"styleIncludePaths": ["../../scss"]
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<daff-switch [checked]="checked">Default</daff-switch>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { CommonModule } from '@angular/common';
2+
import {
3+
ChangeDetectionStrategy,
4+
Component,
5+
} from '@angular/core';
6+
import {
7+
UntypedFormControl,
8+
ReactiveFormsModule,
9+
} from '@angular/forms';
10+
11+
import { DAFF_SWITCH_COMPONENTS } from '@daffodil/design/switch';
12+
13+
@Component({
14+
// eslint-disable-next-line @angular-eslint/component-selector
15+
selector: 'basic-switch',
16+
templateUrl: './basic-switch.component.html',
17+
changeDetection: ChangeDetectionStrategy.OnPush,
18+
standalone: true,
19+
imports: [
20+
DAFF_SWITCH_COMPONENTS,
21+
ReactiveFormsModule,
22+
],
23+
styles: [`
24+
:host {
25+
display: flex;
26+
flex-wrap: wrap;
27+
}
28+
`],
29+
})
30+
export class BasicSwitchComponent {
31+
checked = false;
32+
disabled = false;
33+
loading = true;
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<daff-switch [checked]="checked" disabled>Disabled</daff-switch>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import {
2+
ChangeDetectionStrategy,
3+
Component,
4+
} from '@angular/core';
5+
6+
import { DAFF_SWITCH_COMPONENTS } from '@daffodil/design/switch';
7+
8+
@Component({
9+
// eslint-disable-next-line @angular-eslint/component-selector
10+
selector: 'disabled-switch',
11+
templateUrl: './disabled-switch.component.html',
12+
changeDetection: ChangeDetectionStrategy.OnPush,
13+
standalone: true,
14+
imports: [
15+
DAFF_SWITCH_COMPONENTS,
16+
],
17+
styles: [`
18+
:host {
19+
display: flex;
20+
flex-wrap: wrap;
21+
}
22+
`],
23+
})
24+
export class DisabledSwitchComponent {
25+
checked = false;
26+
disabled = true;
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { BasicSwitchComponent } from './basic-switch/basic-switch.component';
2+
import { DisabledSwitchComponent } from './disabled-switch/disabled-switch.component';
3+
import { LoadingSwitchComponent } from './loading-switch/loading-switch.component';
4+
import { SwitchErrorComponent } from './switch-error/switch-error.component';
5+
import { SwitchLabelPositionsComponent } from './switch-label-positions/switch-label-positions.component';
6+
7+
export const SWITCH_EXAMPLES = [
8+
BasicSwitchComponent,
9+
DisabledSwitchComponent,
10+
LoadingSwitchComponent,
11+
SwitchErrorComponent,
12+
SwitchLabelPositionsComponent,
13+
];
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './public_api';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<daff-switch [checked]="checked" loading="true">Loading</daff-switch>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import {
2+
ChangeDetectionStrategy,
3+
Component,
4+
} from '@angular/core';
5+
6+
import { DAFF_SWITCH_COMPONENTS } from '@daffodil/design/switch';
7+
8+
@Component({
9+
// eslint-disable-next-line @angular-eslint/component-selector
10+
selector: 'loading-switch',
11+
templateUrl: './loading-switch.component.html',
12+
changeDetection: ChangeDetectionStrategy.OnPush,
13+
standalone: true,
14+
imports: [
15+
DAFF_SWITCH_COMPONENTS,
16+
],
17+
styles: [`
18+
:host {
19+
display: flex;
20+
flex-wrap: wrap;
21+
}
22+
`],
23+
})
24+
export class LoadingSwitchComponent {
25+
checked = false;
26+
loading = true;
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export { SWITCH_EXAMPLES } from './examples';
2+
3+
export { BasicSwitchComponent } from './basic-switch/basic-switch.component';
4+
export { DisabledSwitchComponent } from './disabled-switch/disabled-switch.component';
5+
export { LoadingSwitchComponent } from './loading-switch/loading-switch.component';
6+
export { SwitchErrorComponent } from './switch-error/switch-error.component';
7+
export { SwitchLabelPositionsComponent } from './switch-label-positions/switch-label-positions.component';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<daff-switch [checked]="checked" [error]="true">
2+
Notifications
3+
<daff-error-message>Error message</daff-error-message>
4+
</daff-switch>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import {
2+
ChangeDetectionStrategy,
3+
Component,
4+
} from '@angular/core';
5+
import {
6+
UntypedFormControl,
7+
ReactiveFormsModule,
8+
} from '@angular/forms';
9+
10+
11+
import { DAFF_SWITCH_COMPONENTS } from '@daffodil/design/switch';
12+
@Component({
13+
// eslint-disable-next-line @angular-eslint/component-selector
14+
selector: 'switch-error',
15+
templateUrl: './switch-error.component.html',
16+
changeDetection: ChangeDetectionStrategy.OnPush,
17+
standalone: true,
18+
imports: [
19+
DAFF_SWITCH_COMPONENTS,
20+
ReactiveFormsModule,
21+
],
22+
styles: [`
23+
:host {
24+
display: flex;
25+
flex-wrap: wrap;
26+
}
27+
`],
28+
})
29+
export class SwitchErrorComponent {
30+
checked = false;
31+
error = false;
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<daff-switch [labelPosition]="labelPositionControl.value" [formControl]="labelSwitchExample" ngDefaultControl>Label</daff-switch>
2+
3+
<select [formControl]="labelPositionControl">
4+
<option *ngFor="let option of options" [value]="option.value">{{ option.label }}</option>
5+
</select>

0 commit comments

Comments
 (0)