Skip to content

Commit c98d217

Browse files
amcdnljelbourn
authored andcommitted
feat(badge): add badge component (#7483)
1 parent 2f891e6 commit c98d217

21 files changed

+707
-0
lines changed

.github/CODEOWNERS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
/src/lib/tabs/** @andrewseguin
3333
/src/lib/toolbar/** @devversion
3434
/src/lib/tooltip/** @andrewseguin
35+
/src/lib/badge/** @amcdnl
3536

3637
# Angular Material core
3738
/src/lib/core/* @jelbourn
@@ -129,6 +130,7 @@
129130
/src/demo-app/toolbar/** @devversion
130131
/src/demo-app/tooltip/** @andrewseguin
131132
/src/demo-app/typography/** @crisbeto
133+
/src/demo-app/badge/** @amcdnl
132134

133135
# E2E app
134136
/e2e/* @jelbourn

src/demo-app/badge/badge-demo.html

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<div class="badge-demo">
2+
3+
<div class="badge-examples">
4+
<h3>Text</h3>
5+
<span [matBadge]="badgeContent" matBadgeOverlap="false" *ngIf="visible">
6+
Hello
7+
</span>
8+
9+
<span [matBadge]="11111" matBadgeOverlap="false">
10+
Hello
11+
</span>
12+
13+
<span matBadge="22" matBadgeOverlap="false" matBadgePosition="below after" matBadgeColor="accent">
14+
Hello
15+
</span>
16+
17+
<span matBadge="22" matBadgeOverlap="false" matBadgePosition="above before" matBadgeColor="warn">
18+
Hello
19+
</span>
20+
21+
<span matBadge="⚡️" matBadgeOverlap="false" matBadgePosition="below before">
22+
Hello
23+
</span>
24+
25+
<span [matBadge]="badgeContent" matBadgeDescription="I've got {{badgeContent}} problems">
26+
Aria
27+
</span>
28+
29+
<span [matBadge]="badgeContent" matBadgeHidden="true">
30+
Hidden
31+
</span>
32+
33+
<input type="text" [(ngModel)]="badgeContent" />
34+
<button (click)="visible = !visible">Toggle</button>
35+
</div>
36+
37+
<div class="badge-examples">
38+
<h3>Buttons</h3>
39+
<button mat-raised-button [matBadge]="badgeContent">
40+
<mat-icon color="primary">home</mat-icon>
41+
</button>
42+
43+
<button mat-raised-button matBadge="22" matBadgePosition="below after" color="primary" matBadgeColor="accent">
44+
<mat-icon color="accent">home</mat-icon>
45+
</button>
46+
47+
<button mat-raised-button matBadge="22" matBadgePosition="above before">
48+
<mat-icon color="primary">home</mat-icon>
49+
</button>
50+
51+
<button mat-raised-button matBadge="22" matBadgePosition="below before">
52+
<mat-icon color="primary">home</mat-icon>
53+
</button>
54+
55+
<button mat-raised-button>
56+
<mat-icon color="primary" matBadge="22" color="accent">home</mat-icon>
57+
</button>
58+
</div>
59+
60+
<div class="badge-examples">
61+
<h3>Icons</h3>
62+
<mat-icon [matBadge]="badgeContent">
63+
home
64+
</mat-icon>
65+
66+
<mat-icon color="primary" matBadge="22" matBadgePosition="below after" matBadgeColor="accent">
67+
home
68+
</mat-icon>
69+
70+
<mat-icon color="primary" matBadge="22" matBadgePosition="above before" matBadgeColor="warn">
71+
home
72+
</mat-icon>
73+
74+
<mat-icon color="primary" matBadge="22" matBadgePosition="below before">
75+
home
76+
</mat-icon>
77+
</div>
78+
79+
<div class="badge-examples">
80+
<h3>Size</h3>
81+
<mat-icon [matBadge]="badgeContent" matBadgeSize="small">
82+
home
83+
</mat-icon>
84+
<mat-icon [matBadge]="badgeContent" matBadgeSize="large">
85+
home
86+
</mat-icon>
87+
88+
</div>
89+
90+
</div>

src/demo-app/badge/badge-demo.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.badge-examples {
2+
margin-bottom: 25px;
3+
}

src/demo-app/badge/badge-demo.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {Component} from '@angular/core';
10+
11+
@Component({
12+
moduleId: module.id,
13+
selector: 'badge-demo',
14+
templateUrl: 'badge-demo.html',
15+
styleUrls: ['badge-demo.css'],
16+
})
17+
export class BadgeDemo {
18+
visible = true;
19+
badgeContent = '0';
20+
}

src/demo-app/demo-app/demo-app.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export class DemoApp {
5151
dark = false;
5252
navItems = [
5353
{name: 'Autocomplete', route: '/autocomplete'},
54+
{name: 'Badge', route: '/badge'},
5455
{name: 'Bottom sheet', route: '/bottom-sheet'},
5556
{name: 'Button Toggle', route: '/button-toggle'},
5657
{name: 'Button', route: '/button'},

src/demo-app/demo-app/demo-module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ import {TypographyDemo} from '../typography/typography-demo';
6161
import {DemoApp, Home} from './demo-app';
6262
import {DEMO_APP_ROUTES} from './routes';
6363
import {TableDemoModule} from '../table/table-demo-module';
64+
import {BadgeDemo} from 'badge/badge-demo';
6465

6566
@NgModule({
6667
imports: [
@@ -78,6 +79,7 @@ import {TableDemoModule} from '../table/table-demo-module';
7879
BaselineDemo,
7980
ButtonDemo,
8081
ButtonToggleDemo,
82+
BadgeDemo,
8183
CardDemo,
8284
CheckboxDemo,
8385
ChipsDemo,

src/demo-app/demo-app/routes.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,13 @@ import {TypographyDemo} from '../typography/typography-demo';
5151
import {DemoApp, Home} from './demo-app';
5252
import {TableDemoPage} from '../table/table-demo-page';
5353
import {TABLE_DEMO_ROUTES} from '../table/routes';
54+
import {BadgeDemo} from 'badge/badge-demo';
5455

5556
export const DEMO_APP_ROUTES: Routes = [
5657
{path: '', component: DemoApp, children: [
5758
{path: '', component: Home},
5859
{path: 'autocomplete', component: AutocompleteDemo},
60+
{path: 'badge', component: BadgeDemo},
5961
{path: 'bottom-sheet', component: BottomSheetDemo},
6062
{path: 'baseline', component: BaselineDemo},
6163
{path: 'button', component: ButtonDemo},

src/demo-app/demo-material-module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {CdkTableModule} from '@angular/cdk/table';
1717
import {NgModule} from '@angular/core';
1818
import {
1919
MatAutocompleteModule,
20+
MatBadgeModule,
2021
MatBottomSheetModule,
2122
MatButtonModule,
2223
MatButtonToggleModule,
@@ -58,6 +59,7 @@ import {
5859
@NgModule({
5960
exports: [
6061
MatAutocompleteModule,
62+
MatBadgeModule,
6163
MatBottomSheetModule,
6264
MatButtonModule,
6365
MatButtonToggleModule,

src/demo-app/system-config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ System.config({
9191
'@angular/material/tabs': 'dist/packages/material/tabs/index.js',
9292
'@angular/material/toolbar': 'dist/packages/material/toolbar/index.js',
9393
'@angular/material/tooltip': 'dist/packages/material/tooltip/index.js',
94+
'@angular/material/badge': 'dist/packages/material/badge/index.js',
9495
},
9596
packages: {
9697
// Thirdparty barrels.

src/lib/badge/_badge-theme.scss

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
// This contains all of the styles for the badge
2+
// rather than just the color/theme because of
3+
// no style sheet support for directives.
4+
@import '../core/theming/palette';
5+
@import '../core/theming/theming';
6+
@import '../core/typography/typography-utils';
7+
8+
$mat-badge-font-size: 12px;
9+
$mat-badge-font-weight: 600;
10+
$mat-badge-default-size: 22px !default;
11+
$mat-badge-small-size: $mat-badge-default-size - 6;
12+
$mat-badge-large-size: $mat-badge-default-size + 6;
13+
14+
// Mixin for building offset given different sizes
15+
@mixin _mat-badge-size($size) {
16+
.mat-badge-content {
17+
width: $size;
18+
height: $size;
19+
line-height: $size;
20+
}
21+
22+
&.mat-badge-above {
23+
.mat-badge-content {
24+
top: -$size / 2;
25+
}
26+
}
27+
28+
&.mat-badge-below {
29+
.mat-badge-content {
30+
bottom: -$size / 2;
31+
}
32+
}
33+
34+
&.mat-badge-before {
35+
margin-left: $size;
36+
37+
&[dir='rtl'] {
38+
margin-left: 0;
39+
margin-right: $size;
40+
}
41+
42+
.mat-badge-content {
43+
left: -$size;
44+
}
45+
}
46+
47+
&.mat-badge-after {
48+
margin-right: $size;
49+
50+
&[dir='rtl'] {
51+
margin-right: 0;
52+
margin-left: $size;
53+
}
54+
55+
.mat-badge-content {
56+
right: -$size;
57+
}
58+
}
59+
60+
&.mat-badge-overlap {
61+
&.mat-badge-before {
62+
margin-left: $size / 2;
63+
64+
&[dir='rtl'] {
65+
margin-left: 0;
66+
margin-right: $size / 2;
67+
}
68+
69+
.mat-badge-content {
70+
left: -$size / 2;
71+
}
72+
}
73+
74+
&.mat-badge-after {
75+
margin-right: $size / 2;
76+
77+
&[dir='rtl'] {
78+
margin-right: 0;
79+
margin-left: $size;
80+
}
81+
82+
.mat-badge-content {
83+
right: -$size / 2;
84+
}
85+
}
86+
}
87+
}
88+
89+
@mixin mat-badge-theme($theme) {
90+
$accent: map-get($theme, accent);
91+
$warn: map-get($theme, warn);
92+
$primary: map-get($theme, primary);
93+
94+
.mat-badge-content {
95+
color: mat-color($primary, default-contrast);
96+
background: mat-color($primary);
97+
}
98+
99+
&.mat-badge-accent {
100+
.mat-badge-content {
101+
background: mat-color($accent);
102+
color: mat-color($accent, default-contrast);
103+
}
104+
}
105+
106+
&.mat-badge-warn {
107+
.mat-badge-content {
108+
color: mat-color($warn, default-contrast);
109+
background: mat-color($warn);
110+
}
111+
}
112+
}
113+
114+
@mixin mat-badge-typography($config) {
115+
.mat-badge-content {
116+
font-weight: $mat-badge-font-weight;
117+
font-size: $mat-badge-font-size;
118+
font-family: mat-font-family($config);
119+
}
120+
121+
.mat-badge-small .mat-badge-content {
122+
font-size: $mat-badge-font-size / 2;
123+
}
124+
125+
.mat-badge-large .mat-badge-content {
126+
font-size: $mat-badge-font-size * 2;
127+
}
128+
}
129+
130+
.mat-badge {
131+
position: relative;
132+
}
133+
134+
.mat-badge-hidden {
135+
.mat-badge-content {
136+
display: none;
137+
}
138+
}
139+
140+
.mat-badge-content {
141+
position: absolute;
142+
text-align: center;
143+
display: inline-block;
144+
border-radius: 50%;
145+
transition: all 0.2 ease-in-out;
146+
transform: scale(0.6);
147+
overflow: hidden;
148+
white-space: nowrap;
149+
text-overflow: ellipsis;
150+
pointer-events: none;
151+
}
152+
153+
// The active class is added after the element is added
154+
// so it can animate scale to default
155+
.mat-badge-content.mat-badge-active {
156+
transform: scale(1);
157+
}
158+
159+
.mat-badge-small {
160+
@include _mat-badge-size($mat-badge-small-size);
161+
}
162+
.mat-badge-medium {
163+
@include _mat-badge-size($mat-badge-default-size);
164+
}
165+
.mat-badge-large {
166+
@include _mat-badge-size($mat-badge-large-size);
167+
}

src/lib/badge/badge-module.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {NgModule} from '@angular/core';
10+
import {MatCommonModule} from '@angular/material/core';
11+
import {A11yModule} from '@angular/cdk/a11y';
12+
import {MatBadge} from './badge';
13+
14+
15+
@NgModule({
16+
imports: [
17+
MatCommonModule,
18+
A11yModule,
19+
],
20+
exports: [
21+
MatBadge,
22+
],
23+
declarations: [
24+
MatBadge,
25+
],
26+
})
27+
export class MatBadgeModule {}

0 commit comments

Comments
 (0)