badge.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  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. import { AriaDescriber, A11yModule } from '@angular/cdk/a11y';
  9. import { coerceBooleanProperty } from '@angular/cdk/coercion';
  10. import { Directive, ElementRef, Inject, Input, NgZone, Optional, Renderer2, isDevMode, NgModule } from '@angular/core';
  11. import { mixinDisabled, MatCommonModule } from '@angular/material/core';
  12. import { ANIMATION_MODULE_TYPE } from '@angular/platform-browser/animations';
  13. /**
  14. * @fileoverview added by tsickle
  15. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  16. */
  17. /** @type {?} */
  18. let nextId = 0;
  19. // Boilerplate for applying mixins to MatBadge.
  20. /**
  21. * \@docs-private
  22. */
  23. class MatBadgeBase {
  24. }
  25. /** @type {?} */
  26. const _MatBadgeMixinBase = mixinDisabled(MatBadgeBase);
  27. /**
  28. * Directive to display a text badge.
  29. */
  30. class MatBadge extends _MatBadgeMixinBase {
  31. /**
  32. * @param {?} _ngZone
  33. * @param {?} _elementRef
  34. * @param {?} _ariaDescriber
  35. * @param {?} _renderer
  36. * @param {?=} _animationMode
  37. */
  38. constructor(_ngZone, _elementRef, _ariaDescriber, _renderer, _animationMode) {
  39. super();
  40. this._ngZone = _ngZone;
  41. this._elementRef = _elementRef;
  42. this._ariaDescriber = _ariaDescriber;
  43. this._renderer = _renderer;
  44. this._animationMode = _animationMode;
  45. /**
  46. * Whether the badge has any content.
  47. */
  48. this._hasContent = false;
  49. this._color = 'primary';
  50. this._overlap = true;
  51. /**
  52. * Position the badge should reside.
  53. * Accepts any combination of 'above'|'below' and 'before'|'after'
  54. */
  55. this.position = 'above after';
  56. /**
  57. * Size of the badge. Can be 'small', 'medium', or 'large'.
  58. */
  59. this.size = 'medium';
  60. /**
  61. * Unique id for the badge
  62. */
  63. this._id = nextId++;
  64. if (isDevMode()) {
  65. /** @type {?} */
  66. const nativeElement = _elementRef.nativeElement;
  67. if (nativeElement.nodeType !== nativeElement.ELEMENT_NODE) {
  68. throw Error('matBadge must be attached to an element node.');
  69. }
  70. }
  71. }
  72. /**
  73. * The color of the badge. Can be `primary`, `accent`, or `warn`.
  74. * @return {?}
  75. */
  76. get color() { return this._color; }
  77. /**
  78. * @param {?} value
  79. * @return {?}
  80. */
  81. set color(value) {
  82. this._setColor(value);
  83. this._color = value;
  84. }
  85. /**
  86. * Whether the badge should overlap its contents or not
  87. * @return {?}
  88. */
  89. get overlap() { return this._overlap; }
  90. /**
  91. * @param {?} val
  92. * @return {?}
  93. */
  94. set overlap(val) {
  95. this._overlap = coerceBooleanProperty(val);
  96. }
  97. /**
  98. * Message used to describe the decorated element via aria-describedby
  99. * @return {?}
  100. */
  101. get description() { return this._description; }
  102. /**
  103. * @param {?} newDescription
  104. * @return {?}
  105. */
  106. set description(newDescription) {
  107. if (newDescription !== this._description) {
  108. /** @type {?} */
  109. const badgeElement = this._badgeElement;
  110. this._updateHostAriaDescription(newDescription, this._description);
  111. this._description = newDescription;
  112. if (badgeElement) {
  113. newDescription ? badgeElement.setAttribute('aria-label', newDescription) :
  114. badgeElement.removeAttribute('aria-label');
  115. }
  116. }
  117. }
  118. /**
  119. * Whether the badge is hidden.
  120. * @return {?}
  121. */
  122. get hidden() { return this._hidden; }
  123. /**
  124. * @param {?} val
  125. * @return {?}
  126. */
  127. set hidden(val) {
  128. this._hidden = coerceBooleanProperty(val);
  129. }
  130. /**
  131. * Whether the badge is above the host or not
  132. * @return {?}
  133. */
  134. isAbove() {
  135. return this.position.indexOf('below') === -1;
  136. }
  137. /**
  138. * Whether the badge is after the host or not
  139. * @return {?}
  140. */
  141. isAfter() {
  142. return this.position.indexOf('before') === -1;
  143. }
  144. /**
  145. * @param {?} changes
  146. * @return {?}
  147. */
  148. ngOnChanges(changes) {
  149. /** @type {?} */
  150. const contentChange = changes['content'];
  151. if (contentChange) {
  152. /** @type {?} */
  153. const value = contentChange.currentValue;
  154. this._hasContent = value != null && `${value}`.trim().length > 0;
  155. this._updateTextContent();
  156. }
  157. }
  158. /**
  159. * @return {?}
  160. */
  161. ngOnDestroy() {
  162. /** @type {?} */
  163. const badgeElement = this._badgeElement;
  164. if (badgeElement) {
  165. if (this.description) {
  166. this._ariaDescriber.removeDescription(badgeElement, this.description);
  167. }
  168. // When creating a badge through the Renderer, Angular will keep it in an index.
  169. // We have to destroy it ourselves, otherwise it'll be retained in memory.
  170. if (this._renderer.destroyNode) {
  171. this._renderer.destroyNode(badgeElement);
  172. }
  173. }
  174. }
  175. /**
  176. * Gets the element into which the badge's content is being rendered.
  177. * Undefined if the element hasn't been created (e.g. if the badge doesn't have content).
  178. * @return {?}
  179. */
  180. getBadgeElement() {
  181. return this._badgeElement;
  182. }
  183. /**
  184. * Injects a span element into the DOM with the content.
  185. * @private
  186. * @return {?}
  187. */
  188. _updateTextContent() {
  189. if (!this._badgeElement) {
  190. this._badgeElement = this._createBadgeElement();
  191. }
  192. else {
  193. this._badgeElement.textContent = this.content;
  194. }
  195. return this._badgeElement;
  196. }
  197. /**
  198. * Creates the badge element
  199. * @private
  200. * @return {?}
  201. */
  202. _createBadgeElement() {
  203. /** @type {?} */
  204. const badgeElement = this._renderer.createElement('span');
  205. /** @type {?} */
  206. const activeClass = 'mat-badge-active';
  207. /** @type {?} */
  208. const contentClass = 'mat-badge-content';
  209. // Clear any existing badges which may have persisted from a server-side render.
  210. this._clearExistingBadges(contentClass);
  211. badgeElement.setAttribute('id', `mat-badge-content-${this._id}`);
  212. badgeElement.classList.add(contentClass);
  213. badgeElement.textContent = this.content;
  214. if (this._animationMode === 'NoopAnimations') {
  215. badgeElement.classList.add('_mat-animation-noopable');
  216. }
  217. if (this.description) {
  218. badgeElement.setAttribute('aria-label', this.description);
  219. }
  220. this._elementRef.nativeElement.appendChild(badgeElement);
  221. // animate in after insertion
  222. if (typeof requestAnimationFrame === 'function' && this._animationMode !== 'NoopAnimations') {
  223. this._ngZone.runOutsideAngular((/**
  224. * @return {?}
  225. */
  226. () => {
  227. requestAnimationFrame((/**
  228. * @return {?}
  229. */
  230. () => {
  231. badgeElement.classList.add(activeClass);
  232. }));
  233. }));
  234. }
  235. else {
  236. badgeElement.classList.add(activeClass);
  237. }
  238. return badgeElement;
  239. }
  240. /**
  241. * Sets the aria-label property on the element
  242. * @private
  243. * @param {?} newDescription
  244. * @param {?} oldDescription
  245. * @return {?}
  246. */
  247. _updateHostAriaDescription(newDescription, oldDescription) {
  248. // ensure content available before setting label
  249. /** @type {?} */
  250. const content = this._updateTextContent();
  251. if (oldDescription) {
  252. this._ariaDescriber.removeDescription(content, oldDescription);
  253. }
  254. if (newDescription) {
  255. this._ariaDescriber.describe(content, newDescription);
  256. }
  257. }
  258. /**
  259. * Adds css theme class given the color to the component host
  260. * @private
  261. * @param {?} colorPalette
  262. * @return {?}
  263. */
  264. _setColor(colorPalette) {
  265. if (colorPalette !== this._color) {
  266. if (this._color) {
  267. this._elementRef.nativeElement.classList.remove(`mat-badge-${this._color}`);
  268. }
  269. if (colorPalette) {
  270. this._elementRef.nativeElement.classList.add(`mat-badge-${colorPalette}`);
  271. }
  272. }
  273. }
  274. /**
  275. * Clears any existing badges that might be left over from server-side rendering.
  276. * @private
  277. * @param {?} cssClass
  278. * @return {?}
  279. */
  280. _clearExistingBadges(cssClass) {
  281. /** @type {?} */
  282. const element = this._elementRef.nativeElement;
  283. /** @type {?} */
  284. let childCount = element.children.length;
  285. // Use a reverse while, because we'll be removing elements from the list as we're iterating.
  286. while (childCount--) {
  287. /** @type {?} */
  288. const currentChild = element.children[childCount];
  289. if (currentChild.classList.contains(cssClass)) {
  290. element.removeChild(currentChild);
  291. }
  292. }
  293. }
  294. }
  295. MatBadge.decorators = [
  296. { type: Directive, args: [{
  297. selector: '[matBadge]',
  298. inputs: ['disabled: matBadgeDisabled'],
  299. host: {
  300. 'class': 'mat-badge',
  301. '[class.mat-badge-overlap]': 'overlap',
  302. '[class.mat-badge-above]': 'isAbove()',
  303. '[class.mat-badge-below]': '!isAbove()',
  304. '[class.mat-badge-before]': '!isAfter()',
  305. '[class.mat-badge-after]': 'isAfter()',
  306. '[class.mat-badge-small]': 'size === "small"',
  307. '[class.mat-badge-medium]': 'size === "medium"',
  308. '[class.mat-badge-large]': 'size === "large"',
  309. '[class.mat-badge-hidden]': 'hidden || !_hasContent',
  310. '[class.mat-badge-disabled]': 'disabled',
  311. },
  312. },] },
  313. ];
  314. /** @nocollapse */
  315. MatBadge.ctorParameters = () => [
  316. { type: NgZone },
  317. { type: ElementRef },
  318. { type: AriaDescriber },
  319. { type: Renderer2 },
  320. { type: String, decorators: [{ type: Optional }, { type: Inject, args: [ANIMATION_MODULE_TYPE,] }] }
  321. ];
  322. MatBadge.propDecorators = {
  323. color: [{ type: Input, args: ['matBadgeColor',] }],
  324. overlap: [{ type: Input, args: ['matBadgeOverlap',] }],
  325. position: [{ type: Input, args: ['matBadgePosition',] }],
  326. content: [{ type: Input, args: ['matBadge',] }],
  327. description: [{ type: Input, args: ['matBadgeDescription',] }],
  328. size: [{ type: Input, args: ['matBadgeSize',] }],
  329. hidden: [{ type: Input, args: ['matBadgeHidden',] }]
  330. };
  331. /**
  332. * @fileoverview added by tsickle
  333. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  334. */
  335. class MatBadgeModule {
  336. }
  337. MatBadgeModule.decorators = [
  338. { type: NgModule, args: [{
  339. imports: [
  340. A11yModule,
  341. MatCommonModule
  342. ],
  343. exports: [MatBadge],
  344. declarations: [MatBadge],
  345. },] },
  346. ];
  347. /**
  348. * @fileoverview added by tsickle
  349. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  350. */
  351. /**
  352. * @fileoverview added by tsickle
  353. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  354. */
  355. export { MatBadgeModule, MatBadge };
  356. //# sourceMappingURL=badge.js.map