mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(badge): add badge component (#7483)
This commit is contained in:
@@ -32,6 +32,7 @@
|
||||
/src/lib/tabs/** @andrewseguin
|
||||
/src/lib/toolbar/** @devversion
|
||||
/src/lib/tooltip/** @andrewseguin
|
||||
/src/lib/badge/** @amcdnl
|
||||
|
||||
# Angular Material core
|
||||
/src/lib/core/* @jelbourn
|
||||
@@ -129,6 +130,7 @@
|
||||
/src/demo-app/toolbar/** @devversion
|
||||
/src/demo-app/tooltip/** @andrewseguin
|
||||
/src/demo-app/typography/** @crisbeto
|
||||
/src/demo-app/badge/** @amcdnl
|
||||
|
||||
# E2E app
|
||||
/e2e/* @jelbourn
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
<div class="badge-demo">
|
||||
|
||||
<div class="badge-examples">
|
||||
<h3>Text</h3>
|
||||
<span [matBadge]="badgeContent" matBadgeOverlap="false" *ngIf="visible">
|
||||
Hello
|
||||
</span>
|
||||
|
||||
<span [matBadge]="11111" matBadgeOverlap="false">
|
||||
Hello
|
||||
</span>
|
||||
|
||||
<span matBadge="22" matBadgeOverlap="false" matBadgePosition="below after" matBadgeColor="accent">
|
||||
Hello
|
||||
</span>
|
||||
|
||||
<span matBadge="22" matBadgeOverlap="false" matBadgePosition="above before" matBadgeColor="warn">
|
||||
Hello
|
||||
</span>
|
||||
|
||||
<span matBadge="⚡️" matBadgeOverlap="false" matBadgePosition="below before">
|
||||
Hello
|
||||
</span>
|
||||
|
||||
<span [matBadge]="badgeContent" matBadgeDescription="I've got {{badgeContent}} problems">
|
||||
Aria
|
||||
</span>
|
||||
|
||||
<span [matBadge]="badgeContent" matBadgeHidden="true">
|
||||
Hidden
|
||||
</span>
|
||||
|
||||
<input type="text" [(ngModel)]="badgeContent" />
|
||||
<button (click)="visible = !visible">Toggle</button>
|
||||
</div>
|
||||
|
||||
<div class="badge-examples">
|
||||
<h3>Buttons</h3>
|
||||
<button mat-raised-button [matBadge]="badgeContent">
|
||||
<mat-icon color="primary">home</mat-icon>
|
||||
</button>
|
||||
|
||||
<button mat-raised-button matBadge="22" matBadgePosition="below after" color="primary" matBadgeColor="accent">
|
||||
<mat-icon color="accent">home</mat-icon>
|
||||
</button>
|
||||
|
||||
<button mat-raised-button matBadge="22" matBadgePosition="above before">
|
||||
<mat-icon color="primary">home</mat-icon>
|
||||
</button>
|
||||
|
||||
<button mat-raised-button matBadge="22" matBadgePosition="below before">
|
||||
<mat-icon color="primary">home</mat-icon>
|
||||
</button>
|
||||
|
||||
<button mat-raised-button>
|
||||
<mat-icon color="primary" matBadge="22" color="accent">home</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="badge-examples">
|
||||
<h3>Icons</h3>
|
||||
<mat-icon [matBadge]="badgeContent">
|
||||
home
|
||||
</mat-icon>
|
||||
|
||||
<mat-icon color="primary" matBadge="22" matBadgePosition="below after" matBadgeColor="accent">
|
||||
home
|
||||
</mat-icon>
|
||||
|
||||
<mat-icon color="primary" matBadge="22" matBadgePosition="above before" matBadgeColor="warn">
|
||||
home
|
||||
</mat-icon>
|
||||
|
||||
<mat-icon color="primary" matBadge="22" matBadgePosition="below before">
|
||||
home
|
||||
</mat-icon>
|
||||
</div>
|
||||
|
||||
<div class="badge-examples">
|
||||
<h3>Size</h3>
|
||||
<mat-icon [matBadge]="badgeContent" matBadgeSize="small">
|
||||
home
|
||||
</mat-icon>
|
||||
<mat-icon [matBadge]="badgeContent" matBadgeSize="large">
|
||||
home
|
||||
</mat-icon>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
.badge-examples {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: 'badge-demo',
|
||||
templateUrl: 'badge-demo.html',
|
||||
styleUrls: ['badge-demo.css'],
|
||||
})
|
||||
export class BadgeDemo {
|
||||
visible = true;
|
||||
badgeContent = '0';
|
||||
}
|
||||
@@ -51,6 +51,7 @@ export class DemoApp {
|
||||
dark = false;
|
||||
navItems = [
|
||||
{name: 'Autocomplete', route: '/autocomplete'},
|
||||
{name: 'Badge', route: '/badge'},
|
||||
{name: 'Bottom sheet', route: '/bottom-sheet'},
|
||||
{name: 'Button Toggle', route: '/button-toggle'},
|
||||
{name: 'Button', route: '/button'},
|
||||
|
||||
@@ -61,6 +61,7 @@ import {TypographyDemo} from '../typography/typography-demo';
|
||||
import {DemoApp, Home} from './demo-app';
|
||||
import {DEMO_APP_ROUTES} from './routes';
|
||||
import {TableDemoModule} from '../table/table-demo-module';
|
||||
import {BadgeDemo} from 'badge/badge-demo';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -78,6 +79,7 @@ import {TableDemoModule} from '../table/table-demo-module';
|
||||
BaselineDemo,
|
||||
ButtonDemo,
|
||||
ButtonToggleDemo,
|
||||
BadgeDemo,
|
||||
CardDemo,
|
||||
CheckboxDemo,
|
||||
ChipsDemo,
|
||||
|
||||
@@ -51,11 +51,13 @@ import {TypographyDemo} from '../typography/typography-demo';
|
||||
import {DemoApp, Home} from './demo-app';
|
||||
import {TableDemoPage} from '../table/table-demo-page';
|
||||
import {TABLE_DEMO_ROUTES} from '../table/routes';
|
||||
import {BadgeDemo} from 'badge/badge-demo';
|
||||
|
||||
export const DEMO_APP_ROUTES: Routes = [
|
||||
{path: '', component: DemoApp, children: [
|
||||
{path: '', component: Home},
|
||||
{path: 'autocomplete', component: AutocompleteDemo},
|
||||
{path: 'badge', component: BadgeDemo},
|
||||
{path: 'bottom-sheet', component: BottomSheetDemo},
|
||||
{path: 'baseline', component: BaselineDemo},
|
||||
{path: 'button', component: ButtonDemo},
|
||||
|
||||
@@ -17,6 +17,7 @@ import {CdkTableModule} from '@angular/cdk/table';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {
|
||||
MatAutocompleteModule,
|
||||
MatBadgeModule,
|
||||
MatBottomSheetModule,
|
||||
MatButtonModule,
|
||||
MatButtonToggleModule,
|
||||
@@ -58,6 +59,7 @@ import {
|
||||
@NgModule({
|
||||
exports: [
|
||||
MatAutocompleteModule,
|
||||
MatBadgeModule,
|
||||
MatBottomSheetModule,
|
||||
MatButtonModule,
|
||||
MatButtonToggleModule,
|
||||
|
||||
@@ -91,6 +91,7 @@ System.config({
|
||||
'@angular/material/tabs': 'dist/packages/material/tabs/index.js',
|
||||
'@angular/material/toolbar': 'dist/packages/material/toolbar/index.js',
|
||||
'@angular/material/tooltip': 'dist/packages/material/tooltip/index.js',
|
||||
'@angular/material/badge': 'dist/packages/material/badge/index.js',
|
||||
},
|
||||
packages: {
|
||||
// Thirdparty barrels.
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
// This contains all of the styles for the badge
|
||||
// rather than just the color/theme because of
|
||||
// no style sheet support for directives.
|
||||
@import '../core/theming/palette';
|
||||
@import '../core/theming/theming';
|
||||
@import '../core/typography/typography-utils';
|
||||
|
||||
$mat-badge-font-size: 12px;
|
||||
$mat-badge-font-weight: 600;
|
||||
$mat-badge-default-size: 22px !default;
|
||||
$mat-badge-small-size: $mat-badge-default-size - 6;
|
||||
$mat-badge-large-size: $mat-badge-default-size + 6;
|
||||
|
||||
// Mixin for building offset given different sizes
|
||||
@mixin _mat-badge-size($size) {
|
||||
.mat-badge-content {
|
||||
width: $size;
|
||||
height: $size;
|
||||
line-height: $size;
|
||||
}
|
||||
|
||||
&.mat-badge-above {
|
||||
.mat-badge-content {
|
||||
top: -$size / 2;
|
||||
}
|
||||
}
|
||||
|
||||
&.mat-badge-below {
|
||||
.mat-badge-content {
|
||||
bottom: -$size / 2;
|
||||
}
|
||||
}
|
||||
|
||||
&.mat-badge-before {
|
||||
margin-left: $size;
|
||||
|
||||
&[dir='rtl'] {
|
||||
margin-left: 0;
|
||||
margin-right: $size;
|
||||
}
|
||||
|
||||
.mat-badge-content {
|
||||
left: -$size;
|
||||
}
|
||||
}
|
||||
|
||||
&.mat-badge-after {
|
||||
margin-right: $size;
|
||||
|
||||
&[dir='rtl'] {
|
||||
margin-right: 0;
|
||||
margin-left: $size;
|
||||
}
|
||||
|
||||
.mat-badge-content {
|
||||
right: -$size;
|
||||
}
|
||||
}
|
||||
|
||||
&.mat-badge-overlap {
|
||||
&.mat-badge-before {
|
||||
margin-left: $size / 2;
|
||||
|
||||
&[dir='rtl'] {
|
||||
margin-left: 0;
|
||||
margin-right: $size / 2;
|
||||
}
|
||||
|
||||
.mat-badge-content {
|
||||
left: -$size / 2;
|
||||
}
|
||||
}
|
||||
|
||||
&.mat-badge-after {
|
||||
margin-right: $size / 2;
|
||||
|
||||
&[dir='rtl'] {
|
||||
margin-right: 0;
|
||||
margin-left: $size;
|
||||
}
|
||||
|
||||
.mat-badge-content {
|
||||
right: -$size / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin mat-badge-theme($theme) {
|
||||
$accent: map-get($theme, accent);
|
||||
$warn: map-get($theme, warn);
|
||||
$primary: map-get($theme, primary);
|
||||
|
||||
.mat-badge-content {
|
||||
color: mat-color($primary, default-contrast);
|
||||
background: mat-color($primary);
|
||||
}
|
||||
|
||||
&.mat-badge-accent {
|
||||
.mat-badge-content {
|
||||
background: mat-color($accent);
|
||||
color: mat-color($accent, default-contrast);
|
||||
}
|
||||
}
|
||||
|
||||
&.mat-badge-warn {
|
||||
.mat-badge-content {
|
||||
color: mat-color($warn, default-contrast);
|
||||
background: mat-color($warn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin mat-badge-typography($config) {
|
||||
.mat-badge-content {
|
||||
font-weight: $mat-badge-font-weight;
|
||||
font-size: $mat-badge-font-size;
|
||||
font-family: mat-font-family($config);
|
||||
}
|
||||
|
||||
.mat-badge-small .mat-badge-content {
|
||||
font-size: $mat-badge-font-size / 2;
|
||||
}
|
||||
|
||||
.mat-badge-large .mat-badge-content {
|
||||
font-size: $mat-badge-font-size * 2;
|
||||
}
|
||||
}
|
||||
|
||||
.mat-badge {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.mat-badge-hidden {
|
||||
.mat-badge-content {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.mat-badge-content {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
border-radius: 50%;
|
||||
transition: all 0.2 ease-in-out;
|
||||
transform: scale(0.6);
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
// The active class is added after the element is added
|
||||
// so it can animate scale to default
|
||||
.mat-badge-content.mat-badge-active {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.mat-badge-small {
|
||||
@include _mat-badge-size($mat-badge-small-size);
|
||||
}
|
||||
.mat-badge-medium {
|
||||
@include _mat-badge-size($mat-badge-default-size);
|
||||
}
|
||||
.mat-badge-large {
|
||||
@include _mat-badge-size($mat-badge-large-size);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {NgModule} from '@angular/core';
|
||||
import {MatCommonModule} from '@angular/material/core';
|
||||
import {A11yModule} from '@angular/cdk/a11y';
|
||||
import {MatBadge} from './badge';
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
MatCommonModule,
|
||||
A11yModule,
|
||||
],
|
||||
exports: [
|
||||
MatBadge,
|
||||
],
|
||||
declarations: [
|
||||
MatBadge,
|
||||
],
|
||||
})
|
||||
export class MatBadgeModule {}
|
||||
@@ -0,0 +1,53 @@
|
||||
Badges are small status descriptors for UI elements. A badge consists of a small circle,
|
||||
typically containing a number or other short set of characters, that appears in proximity to another object.
|
||||
|
||||
### Badge position
|
||||
By default, the badge will be placed `above after`. The direction can be changed by defining
|
||||
the attribute `matBadgePosition` follow by `above|below` and `before|after`.
|
||||
|
||||
```html
|
||||
<mat-icon matBadge="22" matBadgePosition="above after">home</mat-icon>
|
||||
```
|
||||
|
||||
The overlap of the badge in relation to its inner contents can also be defined
|
||||
using the `matBadgeOverlap` tag. Typically, you want the badge to overlap an icon and not
|
||||
a text phrase. By default it will overlap.
|
||||
|
||||
```html
|
||||
<h1 matBadge="11" matBadgeOverlap="false">
|
||||
Email
|
||||
</h1>
|
||||
```
|
||||
|
||||
### Badge sizing
|
||||
The badge has 3 sizes: `small`, `medium` and `large`. By default, the badge is set to `medium`.
|
||||
You can change the size by adding `matBadgeSize` to the host element.
|
||||
|
||||
```html
|
||||
<h1 matBadge="11" matBadgeSize="large">
|
||||
Email
|
||||
</h1>
|
||||
```
|
||||
|
||||
### Badge visibility
|
||||
The badge visibility can be toggled programmatically by defining `matBadgeHidden`.
|
||||
|
||||
```html
|
||||
<h1 matBadge="11" [matBadgeHidden]="!visible">
|
||||
Email
|
||||
</h1>
|
||||
```
|
||||
|
||||
### Theming
|
||||
Badges can be colored in terms of the current theme using the `matBadgeColor` property to set the
|
||||
background color to `primary`, `accent`, or `warn`.
|
||||
|
||||
```html
|
||||
<mat-icon matBadge="22" matBadgeColor="accent">
|
||||
home
|
||||
</mat-icon>
|
||||
```
|
||||
|
||||
### Accessibility
|
||||
Badges should be given a meaningful description via `matBadgeDescription`. This description will be
|
||||
applied, via `aria-describedby` to the element decorated by `matBadge`.
|
||||
@@ -0,0 +1,132 @@
|
||||
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||
import {Component, DebugElement} from '@angular/core';
|
||||
import {By} from '@angular/platform-browser';
|
||||
import {MatBadge, MatBadgeModule} from './index';
|
||||
import {ThemePalette} from '@angular/material/core';
|
||||
|
||||
describe('MatBadge', () => {
|
||||
let fixture: ComponentFixture<any>;
|
||||
let testComponent: BadgeWithTextContent;
|
||||
let badgeNativeElement;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [MatBadgeModule],
|
||||
declarations: [BadgeWithTextContent],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(BadgeWithTextContent);
|
||||
testComponent = fixture.debugElement.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
describe('MatBadge Text', () => {
|
||||
let badgeDebugElement: DebugElement;
|
||||
|
||||
beforeEach(() => {
|
||||
badgeDebugElement = fixture.debugElement.query(By.directive(MatBadge));
|
||||
badgeNativeElement = badgeDebugElement.nativeElement;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should update the badge based on attribute', () => {
|
||||
let badgeContentDebugElement = badgeNativeElement.querySelector('.mat-badge-content');
|
||||
|
||||
expect(badgeContentDebugElement.textContent).toContain('1');
|
||||
|
||||
testComponent.badgeContent = '22';
|
||||
fixture.detectChanges();
|
||||
|
||||
badgeContentDebugElement = badgeNativeElement.querySelector('.mat-badge-content');
|
||||
expect(badgeContentDebugElement.textContent).toContain('22');
|
||||
});
|
||||
|
||||
it('should apply class based on color attribute', () => {
|
||||
testComponent.badgeColor = 'primary';
|
||||
fixture.detectChanges();
|
||||
expect(badgeNativeElement.classList.contains('mat-badge-primary')).toBe(true);
|
||||
|
||||
testComponent.badgeColor = 'accent';
|
||||
fixture.detectChanges();
|
||||
expect(badgeNativeElement.classList.contains('mat-badge-accent')).toBe(true);
|
||||
|
||||
testComponent.badgeColor = 'warn';
|
||||
fixture.detectChanges();
|
||||
expect(badgeNativeElement.classList.contains('mat-badge-warn')).toBe(true);
|
||||
|
||||
testComponent.badgeColor = undefined;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(badgeNativeElement.classList).not.toContain('mat-badge-accent');
|
||||
});
|
||||
|
||||
it('should update the badge position on direction change', () => {
|
||||
expect(badgeNativeElement.classList.contains('mat-badge-above')).toBe(true);
|
||||
expect(badgeNativeElement.classList.contains('mat-badge-after')).toBe(true);
|
||||
|
||||
testComponent.badgeDirection = 'below before';
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(badgeNativeElement.classList.contains('mat-badge-below')).toBe(true);
|
||||
expect(badgeNativeElement.classList.contains('mat-badge-before')).toBe(true);
|
||||
});
|
||||
|
||||
it('should change visibility to hidden', () => {
|
||||
expect(badgeNativeElement.classList.contains('mat-badge-hidden')).toBe(false);
|
||||
|
||||
testComponent.badgeHidden = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(badgeNativeElement.classList.contains('mat-badge-hidden')).toBe(true);
|
||||
});
|
||||
|
||||
it('should change badge sizes', () => {
|
||||
expect(badgeNativeElement.classList.contains('mat-badge-medium')).toBe(true);
|
||||
|
||||
testComponent.badgeSize = 'small';
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(badgeNativeElement.classList.contains('mat-badge-small')).toBe(true);
|
||||
|
||||
testComponent.badgeSize = 'large';
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(badgeNativeElement.classList.contains('mat-badge-large')).toBe(true);
|
||||
});
|
||||
|
||||
it('should change badge overlap', () => {
|
||||
expect(badgeNativeElement.classList.contains('mat-badge-overlap')).toBe(false);
|
||||
|
||||
testComponent.badgeOverlap = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(badgeNativeElement.classList.contains('mat-badge-overlap')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
/** Test component that contains a MatBadge. */
|
||||
@Component({
|
||||
selector: 'test-app',
|
||||
template: `
|
||||
<span [matBadge]="badgeContent"
|
||||
[matBadgeColor]="badgeColor"
|
||||
[matBadgePosition]="badgeDirection"
|
||||
[matBadgeHidden]="badgeHidden"
|
||||
[matBadgeSize]="badgeSize"
|
||||
[matBadgeOverlap]="badgeOverlap">
|
||||
home
|
||||
</span>
|
||||
`
|
||||
})
|
||||
class BadgeWithTextContent {
|
||||
badgeColor: ThemePalette;
|
||||
badgeContent = '1';
|
||||
badgeDirection = 'above after';
|
||||
badgeHidden = false;
|
||||
badgeSize = 'medium';
|
||||
badgeOverlap = false;
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, Input, ElementRef, Inject, Optional, NgZone} from '@angular/core';
|
||||
import {coerceBooleanProperty} from '@angular/cdk/coercion';
|
||||
import {ThemePalette} from '@angular/material/core';
|
||||
import {AriaDescriber} from '@angular/cdk/a11y';
|
||||
import {DOCUMENT} from '@angular/common';
|
||||
|
||||
let nextId = 0;
|
||||
|
||||
export type MatBadgePosition = 'above after' | 'above before' | 'below before' | 'below after';
|
||||
export type MatBadgeSize = 'small' | 'medium' | 'large';
|
||||
|
||||
/** Directive to display a text badge. */
|
||||
@Directive({
|
||||
selector: '[matBadge]',
|
||||
host: {
|
||||
'class': 'mat-badge',
|
||||
'[class.mat-badge-overlap]': '_overlap',
|
||||
'[class.mat-badge-above]': 'isAbove()',
|
||||
'[class.mat-badge-below]': '!isAbove()',
|
||||
'[class.mat-badge-before]': '!isAfter()',
|
||||
'[class.mat-badge-after]': 'isAfter()',
|
||||
'[class.mat-badge-small]': 'size === "small"',
|
||||
'[class.mat-badge-medium]': 'size === "medium"',
|
||||
'[class.mat-badge-large]': 'size === "large"',
|
||||
'[class.mat-badge-hidden]': 'hidden',
|
||||
},
|
||||
})
|
||||
export class MatBadge {
|
||||
|
||||
/** The color of the badge. Can be `primary`, `accent`, or `warn`. */
|
||||
@Input('matBadgeColor')
|
||||
get color(): ThemePalette { return this._color; }
|
||||
set color(value: ThemePalette) {
|
||||
this._setColor(value);
|
||||
this._color = value;
|
||||
}
|
||||
private _color: ThemePalette = 'primary';
|
||||
|
||||
/** Whether the badge should overlap its contents or not */
|
||||
@Input('matBadgeOverlap')
|
||||
get overlap(): boolean { return this._overlap; }
|
||||
set overlap(val: boolean) {
|
||||
this._overlap = coerceBooleanProperty(val);
|
||||
}
|
||||
private _overlap: boolean = true;
|
||||
|
||||
/**
|
||||
* Position the badge should reside.
|
||||
* Accepts any combination of 'above'|'below' and 'before'|'after'
|
||||
*/
|
||||
@Input('matBadgePosition') position: MatBadgePosition = 'above after';
|
||||
|
||||
/** The content for the badge */
|
||||
@Input('matBadge')
|
||||
get content(): string { return this._content; }
|
||||
set content(val: string) {
|
||||
this._content = val;
|
||||
this._updateTextContent();
|
||||
}
|
||||
private _content: string;
|
||||
|
||||
/** Message used to describe the decorated element via aria-describedby */
|
||||
@Input('matBadgeDescription')
|
||||
get description(): string { return this._description; }
|
||||
set description(val: string) {
|
||||
if (this._description) {
|
||||
this._updateHostAriaDescription(val, this._description);
|
||||
}
|
||||
this._description = val;
|
||||
}
|
||||
private _description: string;
|
||||
|
||||
/** Size of the badge. Can be 'small', 'medium', or 'large'. */
|
||||
@Input('matBadgeSize') size: MatBadgeSize = 'medium';
|
||||
|
||||
/** Whether the badge is hidden. */
|
||||
@Input('matBadgeHidden')
|
||||
get hidden(): boolean { return this._hidden; }
|
||||
set hidden(val: boolean) {
|
||||
this._hidden = coerceBooleanProperty(val);
|
||||
}
|
||||
private _hidden: boolean;
|
||||
|
||||
/** Unique id for the badge */
|
||||
_id: number = nextId++;
|
||||
|
||||
private _badgeElement: HTMLElement;
|
||||
|
||||
constructor(
|
||||
@Optional() @Inject(DOCUMENT) private _document: any,
|
||||
private _ngZone: NgZone,
|
||||
private _elementRef: ElementRef,
|
||||
private _ariaDescriber: AriaDescriber) {}
|
||||
|
||||
/** Whether the badge is above the host or not */
|
||||
isAbove(): boolean {
|
||||
return this.position.indexOf('below') === -1;
|
||||
}
|
||||
|
||||
/** Whether the badge is after the host or not */
|
||||
isAfter(): boolean {
|
||||
return this.position.indexOf('before') === -1;
|
||||
}
|
||||
|
||||
/** Injects a span element into the DOM with the content. */
|
||||
private _updateTextContent(): HTMLSpanElement {
|
||||
if (!this._badgeElement) {
|
||||
this._badgeElement = this._createBadgeElement();
|
||||
} else {
|
||||
this._badgeElement.textContent = this.content;
|
||||
}
|
||||
return this._badgeElement;
|
||||
}
|
||||
|
||||
/** Creates the badge element */
|
||||
private _createBadgeElement(): HTMLElement {
|
||||
const badgeElement = this._document.createElement('span');
|
||||
badgeElement.setAttribute('id', `mat-badge-content-${this._id}`);
|
||||
badgeElement.classList.add('mat-badge-content');
|
||||
badgeElement.textContent = this.content;
|
||||
|
||||
if (this.description) {
|
||||
badgeElement.setAttribute('aria-label', this.description);
|
||||
}
|
||||
|
||||
this._elementRef.nativeElement.appendChild(badgeElement);
|
||||
|
||||
// animate in after insertion
|
||||
this._ngZone.runOutsideAngular(() => requestAnimationFrame(() => {
|
||||
// ensure content available
|
||||
if (badgeElement) {
|
||||
badgeElement.classList.add('mat-badge-active');
|
||||
}
|
||||
}));
|
||||
|
||||
return badgeElement;
|
||||
}
|
||||
|
||||
/** Sets the aria-label property on the element */
|
||||
private _updateHostAriaDescription(val: string, prevVal: string): void {
|
||||
// ensure content available before setting label
|
||||
const content = this._updateTextContent();
|
||||
this._ariaDescriber.removeDescription(content, prevVal);
|
||||
this._ariaDescriber.describe(content, val);
|
||||
}
|
||||
|
||||
/** Adds css theme class given the color to the component host */
|
||||
private _setColor(colorPalette: ThemePalette) {
|
||||
if (colorPalette !== this._color) {
|
||||
if (this._color) {
|
||||
this._elementRef.nativeElement.classList.remove(`mat-badge-${this._color}`);
|
||||
}
|
||||
if (colorPalette) {
|
||||
this._elementRef.nativeElement.classList.add(`mat-badge-${colorPalette}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
export * from './public-api';
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
export * from './badge-module';
|
||||
export * from './badge';
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"extends": "../tsconfig-build",
|
||||
"files": [
|
||||
"public-api.ts",
|
||||
"../typings.d.ts"
|
||||
],
|
||||
"angularCompilerOptions": {
|
||||
"annotateForClosureCompiler": true,
|
||||
"strictMetadataEmit": true,
|
||||
"flatModuleOutFile": "index.js",
|
||||
"flatModuleId": "@angular/material/badge",
|
||||
"skipTemplateCodegen": true
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Import all the theming functionality.
|
||||
@import '../core';
|
||||
@import '../../autocomplete/autocomplete-theme';
|
||||
@import '../../badge/badge-theme';
|
||||
@import '../../bottom-sheet/bottom-sheet-theme';
|
||||
@import '../../button/button-theme';
|
||||
@import '../../button-toggle/button-toggle-theme';
|
||||
@@ -37,6 +38,7 @@
|
||||
@mixin angular-material-theme($theme) {
|
||||
@include mat-core-theme($theme);
|
||||
@include mat-autocomplete-theme($theme);
|
||||
@include mat-badge-theme($theme);
|
||||
@include mat-bottom-sheet-theme($theme);
|
||||
@include mat-button-theme($theme);
|
||||
@include mat-button-toggle-theme($theme);
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
$config: mat-typography-config();
|
||||
}
|
||||
|
||||
@include mat-badge-typography($config);
|
||||
@include mat-base-typography($config);
|
||||
@include mat-autocomplete-typography($config);
|
||||
@include mat-bottom-sheet-typography($config);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
export * from './version';
|
||||
export * from '@angular/material/autocomplete';
|
||||
export * from '@angular/material/badge';
|
||||
export * from '@angular/material/bottom-sheet';
|
||||
export * from '@angular/material/button';
|
||||
export * from '@angular/material/button-toggle';
|
||||
|
||||
@@ -71,6 +71,7 @@ System.config({
|
||||
'@angular/cdk/testing': 'dist/packages/cdk/testing/index.js',
|
||||
|
||||
'@angular/material/autocomplete': 'dist/packages/material/autocomplete/index.js',
|
||||
'@angular/material/badge': 'dist/packages/material/badge/index.js',
|
||||
'@angular/material/bottom-sheet': 'dist/packages/material/bottom-sheet/index.js',
|
||||
'@angular/material/button': 'dist/packages/material/button/index.js',
|
||||
'@angular/material/button-toggle': 'dist/packages/material/button-toggle/index.js',
|
||||
|
||||
Reference in New Issue
Block a user