From 19b17b49eb7f255215054dcf045686a1f2a0dfb0 Mon Sep 17 00:00:00 2001 From: siddharth Date: Thu, 21 Sep 2023 14:17:47 +0530 Subject: [PATCH 01/16] tabs module added --- documentation.json | 26797 ++++++++++++---- package.json | 4 +- ui/package.json | 2 +- ui/src/components/card/BUILD.bazel | 66 + ui/src/components/card/README.md | 1 + ui/src/components/card/_card-theme.scss | 133 + ui/src/components/card/card-header.html | 8 + ui/src/components/card/card-title-group.html | 12 + ui/src/components/card/card.html | 1 + ui/src/components/card/card.md | 96 + ui/src/components/card/card.scss | 223 + ui/src/components/card/card.spec.ts | 55 + ui/src/components/card/card.ts | 236 + ui/src/components/card/index.ts | 9 + ui/src/components/card/migration.md | 2 + ui/src/components/card/module.ts | 51 + ui/src/components/card/public-api.ts | 10 + .../core/common-behaviors/common-module.ts | 153 + .../core/common-behaviors/constructor.ts | 2 + .../common-behaviors/disable-ripple.spec.ts | 37 + .../core/common-behaviors/disable-ripple.ts | 40 + .../components/core/common-behaviors/index.ts | 2 + ui/src/components/core/public-api.ts | 1 + .../components/core/ripple/_ripple-theme.scss | 29 + ui/src/components/core/ripple/_ripple.scss | 43 + ui/src/components/core/ripple/index.ts | 22 + .../core/ripple/ripple-event-manager.ts | 81 + ui/src/components/core/ripple/ripple-ref.ts | 58 + .../components/core/ripple/ripple-renderer.ts | 448 + ui/src/components/core/ripple/ripple.md | 181 + ui/src/components/core/ripple/ripple.spec.ts | 907 + ui/src/components/core/ripple/ripple.ts | 214 + ui/src/components/core/style/_elevation.scss | 105 + .../components/core/style/_layout-common.scss | 8 + ui/src/components/core/style/_private.scss | 30 + .../components/core/theming/_all-theme.scss | 3 + ui/src/components/core/version.ts | 12 + ui/src/components/index.ts | 2 + ui/src/components/tabs/BUILD.bazel | 124 + ui/src/components/tabs/README.md | 1 + ui/src/components/tabs/_tabs-common.scss | 514 + ui/src/components/tabs/_tabs-theme.scss | 48 + ui/src/components/tabs/index.ts | 9 + ui/src/components/tabs/ink-bar.ts | 217 + ui/src/components/tabs/module.ts | 60 + .../components/tabs/paginated-tab-header.ts | 658 + ui/src/components/tabs/public-api.ts | 21 + ui/src/components/tabs/tab-body.html | 6 + ui/src/components/tabs/tab-body.scss | 45 + ui/src/components/tabs/tab-body.spec.ts | 218 + ui/src/components/tabs/tab-body.ts | 271 + ui/src/components/tabs/tab-config.ts | 45 + ui/src/components/tabs/tab-content.ts | 25 + ui/src/components/tabs/tab-group.html | 72 + ui/src/components/tabs/tab-group.scss | 56 + ui/src/components/tabs/tab-group.spec.ts | 1409 + ui/src/components/tabs/tab-group.ts | 592 + ui/src/components/tabs/tab-header.html | 47 + ui/src/components/tabs/tab-header.scss | 29 + ui/src/components/tabs/tab-header.spec.ts | 771 + ui/src/components/tabs/tab-header.ts | 102 + ui/src/components/tabs/tab-label-wrapper.ts | 52 + ui/src/components/tabs/tab-label.ts | 45 + .../components/tabs/tab-nav-bar/tab-link.html | 14 + .../components/tabs/tab-nav-bar/tab-link.scss | 24 + .../tabs/tab-nav-bar/tab-nav-bar.html | 39 + .../tabs/tab-nav-bar/tab-nav-bar.scss | 17 + .../tabs/tab-nav-bar/tab-nav-bar.spec.ts | 576 + .../tabs/tab-nav-bar/tab-nav-bar.ts | 450 + ui/src/components/tabs/tab.html | 4 + ui/src/components/tabs/tab.ts | 201 + ui/src/components/tabs/tabs-animations.ts | 66 + ui/src/components/tabs/tabs.md | 124 + ui/src/stories/button/button.stories.mdx | 4 +- ui/src/stories/card/card.component.ts | 29 + ui/src/stories/card/card.stories.mdx | 54 + ui/src/stories/tabs/tabs.component.ts | 15 + ui/src/stories/tabs/tabs.stories.mdx | 52 + 78 files changed, 30068 insertions(+), 7122 deletions(-) create mode 100644 ui/src/components/card/BUILD.bazel create mode 100644 ui/src/components/card/README.md create mode 100644 ui/src/components/card/_card-theme.scss create mode 100644 ui/src/components/card/card-header.html create mode 100644 ui/src/components/card/card-title-group.html create mode 100644 ui/src/components/card/card.html create mode 100644 ui/src/components/card/card.md create mode 100644 ui/src/components/card/card.scss create mode 100644 ui/src/components/card/card.spec.ts create mode 100644 ui/src/components/card/card.ts create mode 100644 ui/src/components/card/index.ts create mode 100644 ui/src/components/card/migration.md create mode 100644 ui/src/components/card/module.ts create mode 100644 ui/src/components/card/public-api.ts create mode 100644 ui/src/components/core/common-behaviors/common-module.ts create mode 100644 ui/src/components/core/common-behaviors/disable-ripple.spec.ts create mode 100644 ui/src/components/core/common-behaviors/disable-ripple.ts create mode 100644 ui/src/components/core/ripple/_ripple-theme.scss create mode 100644 ui/src/components/core/ripple/_ripple.scss create mode 100644 ui/src/components/core/ripple/index.ts create mode 100644 ui/src/components/core/ripple/ripple-event-manager.ts create mode 100644 ui/src/components/core/ripple/ripple-ref.ts create mode 100644 ui/src/components/core/ripple/ripple-renderer.ts create mode 100644 ui/src/components/core/ripple/ripple.md create mode 100644 ui/src/components/core/ripple/ripple.spec.ts create mode 100644 ui/src/components/core/ripple/ripple.ts create mode 100644 ui/src/components/core/style/_elevation.scss create mode 100644 ui/src/components/core/style/_layout-common.scss create mode 100644 ui/src/components/core/style/_private.scss create mode 100644 ui/src/components/core/version.ts create mode 100644 ui/src/components/tabs/BUILD.bazel create mode 100644 ui/src/components/tabs/README.md create mode 100644 ui/src/components/tabs/_tabs-common.scss create mode 100644 ui/src/components/tabs/_tabs-theme.scss create mode 100644 ui/src/components/tabs/index.ts create mode 100644 ui/src/components/tabs/ink-bar.ts create mode 100644 ui/src/components/tabs/module.ts create mode 100644 ui/src/components/tabs/paginated-tab-header.ts create mode 100644 ui/src/components/tabs/public-api.ts create mode 100644 ui/src/components/tabs/tab-body.html create mode 100644 ui/src/components/tabs/tab-body.scss create mode 100644 ui/src/components/tabs/tab-body.spec.ts create mode 100644 ui/src/components/tabs/tab-body.ts create mode 100644 ui/src/components/tabs/tab-config.ts create mode 100644 ui/src/components/tabs/tab-content.ts create mode 100644 ui/src/components/tabs/tab-group.html create mode 100644 ui/src/components/tabs/tab-group.scss create mode 100644 ui/src/components/tabs/tab-group.spec.ts create mode 100644 ui/src/components/tabs/tab-group.ts create mode 100644 ui/src/components/tabs/tab-header.html create mode 100644 ui/src/components/tabs/tab-header.scss create mode 100644 ui/src/components/tabs/tab-header.spec.ts create mode 100644 ui/src/components/tabs/tab-header.ts create mode 100644 ui/src/components/tabs/tab-label-wrapper.ts create mode 100644 ui/src/components/tabs/tab-label.ts create mode 100644 ui/src/components/tabs/tab-nav-bar/tab-link.html create mode 100644 ui/src/components/tabs/tab-nav-bar/tab-link.scss create mode 100644 ui/src/components/tabs/tab-nav-bar/tab-nav-bar.html create mode 100644 ui/src/components/tabs/tab-nav-bar/tab-nav-bar.scss create mode 100644 ui/src/components/tabs/tab-nav-bar/tab-nav-bar.spec.ts create mode 100644 ui/src/components/tabs/tab-nav-bar/tab-nav-bar.ts create mode 100644 ui/src/components/tabs/tab.html create mode 100644 ui/src/components/tabs/tab.ts create mode 100644 ui/src/components/tabs/tabs-animations.ts create mode 100644 ui/src/components/tabs/tabs.md create mode 100644 ui/src/stories/card/card.component.ts create mode 100644 ui/src/stories/card/card.stories.mdx create mode 100644 ui/src/stories/tabs/tabs.component.ts create mode 100644 ui/src/stories/tabs/tabs.stories.mdx diff --git a/documentation.json b/documentation.json index 709dc0cc6..e33526550 100644 --- a/documentation.json +++ b/documentation.json @@ -2,7 +2,7 @@ "pipes": [ { "name": "FilterPipe", - "id": "pipe-FilterPipe-d3ef6e7c035e16cc2171352a106b766bba9aa62ce5701813a7e9860112a83514488d53a08f150d7c5ae5db897b20faebe3f125bd471069e6692d06cc4dc179f9", + "id": "pipe-FilterPipe-ed59eb64603921d1d2b336e28b8083821b6c9b807b4855cc30f75e5349d30d9fc181d9c0f584973f505f08ace7beb831b361ff5271d38fb7f6bcef5e49b31eaf", "file": "ui/src/components/select/filter.pipe.ts", "type": "pipe", "deprecated": false, @@ -71,18 +71,49 @@ } ], "ngname": "filterOptions", - "sourceCode": "import { Pipe, PipeTransform } from '@angular/core';\r\n@Pipe({\r\n name: 'filterOptions',\r\n})\r\nexport class FilterPipe implements PipeTransform {\r\n transform(items: any[], searchText: string, field: string): any[] {\r\n if (!items) {\r\n return [];\r\n }\r\n\r\n if (!searchText) {\r\n return items;\r\n }\r\n\r\n return items.filter((it) => {\r\n let results;\r\n // Support both array and the json object\r\n if (it[field]) {\r\n results = it[field].toLowerCase().includes(searchText.toLowerCase());\r\n } else {\r\n results = it.toLowerCase().includes(searchText.toLowerCase());\r\n }\r\n return results;\r\n });\r\n }\r\n}\r\n" + "sourceCode": "import { Pipe, PipeTransform } from '@angular/core';\n@Pipe({\n name: 'filterOptions',\n})\nexport class FilterPipe implements PipeTransform {\n transform(items: any[], searchText: string, field: string): any[] {\n if (!items) {\n return [];\n }\n\n if (!searchText) {\n return items;\n }\n\n return items.filter((it) => {\n let results;\n // Support both array and the json object\n if (it[field]) {\n results = it[field].toLowerCase().includes(searchText.toLowerCase());\n } else {\n results = it.toLowerCase().includes(searchText.toLowerCase());\n }\n return results;\n });\n }\n}\n" } ], "interfaces": [ + { + "name": "_MatInkBarPositioner", + "id": "interface-_MatInkBarPositioner-c66d837f89288a5041dd998f81c628222ec5bd8f3b85b99a3149dccf83c6ed34eb0e869a24f2fdf2b1cf37be4a3dea32982dece8f2645d7c3bbabd39803ef7d0", + "file": "ui/src/components/tabs/ink-bar.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "interface", + "sourceCode": "import {BooleanInput, coerceBooleanProperty} from '@angular/cdk/coercion';\r\nimport {ElementRef, InjectionToken, OnDestroy, OnInit, QueryList} from '@angular/core';\r\nimport { isDevMode } from '@angular/core';\r\n\r\n\r\n/**\r\n * Item inside a tab header relative to which the ink bar can be aligned.\r\n * @docs-private\r\n */\r\nexport interface MatInkBarItem extends OnInit, OnDestroy {\r\n elementRef: ElementRef;\r\n activateInkBar(previousIndicatorClientRect?: ClientRect): void;\r\n deactivateInkBar(): void;\r\n fitInkBarToContent: boolean;\r\n}\r\n\r\n/** Class that is applied when a tab indicator is active. */\r\nconst ACTIVE_CLASS = 'mdc-tab-indicator--active';\r\n\r\n/** Class that is applied when the tab indicator should not transition. */\r\nconst NO_TRANSITION_CLASS = 'mdc-tab-indicator--no-transition';\r\n\r\n/**\r\n * Abstraction around the MDC tab indicator that acts as the tab header's ink bar.\r\n * @docs-private\r\n */\r\nexport class MatInkBar {\r\n /** Item to which the ink bar is aligned currently. */\r\n private _currentItem: MatInkBarItem | undefined;\r\n\r\n constructor(private _items: QueryList) {}\r\n\r\n /** Hides the ink bar. */\r\n hide() {\r\n this._items.forEach(item => item.deactivateInkBar());\r\n }\r\n\r\n /** Aligns the ink bar to a DOM node. */\r\n alignToElement(element: HTMLElement) {\r\n const correspondingItem = this._items.find(item => item.elementRef.nativeElement === element);\r\n const currentItem = this._currentItem;\r\n\r\n if (correspondingItem === currentItem) {\r\n return;\r\n }\r\n\r\n currentItem?.deactivateInkBar();\r\n\r\n if (correspondingItem) {\r\n const clientRect = currentItem?.elementRef.nativeElement.getBoundingClientRect?.();\r\n\r\n // The ink bar won't animate unless we give it the `ClientRect` of the previous item.\r\n correspondingItem.activateInkBar(clientRect);\r\n this._currentItem = correspondingItem;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Mixin that can be used to apply the `MatInkBarItem` behavior to a class.\r\n * Base on MDC's `MDCSlidingTabIndicatorFoundation`:\r\n * https://github.com/material-components/material-components-web/blob/c0a11ef0d000a098fd0c372be8f12d6a99302855/packages/mdc-tab-indicator/sliding-foundation.ts\r\n * @docs-private\r\n */\r\nexport function mixinInkBarItem<\r\n T extends new (...args: any[]) => {elementRef: ElementRef},\r\n>(base: T): T & (new (...args: any[]) => MatInkBarItem) {\r\n return class extends base {\r\n constructor(...args: any[]) {\r\n super(...args);\r\n }\r\n\r\n private _inkBarElement: HTMLElement | null;\r\n private _inkBarContentElement: HTMLElement | null;\r\n private _fitToContent = false;\r\n\r\n /** Whether the ink bar should fit to the entire tab or just its content. */\r\n get fitInkBarToContent(): boolean {\r\n return this._fitToContent;\r\n }\r\n set fitInkBarToContent(v: BooleanInput) {\r\n const newValue = coerceBooleanProperty(v);\r\n\r\n if (this._fitToContent !== newValue) {\r\n this._fitToContent = newValue;\r\n\r\n if (this._inkBarElement) {\r\n this._appendInkBarElement();\r\n }\r\n }\r\n }\r\n\r\n /** Aligns the ink bar to the current item. */\r\n activateInkBar(previousIndicatorClientRect?: ClientRect) {\r\n const element = this.elementRef.nativeElement;\r\n\r\n // Early exit if no indicator is present to handle cases where an indicator\r\n // may be activated without a prior indicator state\r\n if (\r\n !previousIndicatorClientRect ||\r\n !element.getBoundingClientRect ||\r\n !this._inkBarContentElement\r\n ) {\r\n element.classList.add(ACTIVE_CLASS);\r\n return;\r\n }\r\n\r\n // This animation uses the FLIP approach. You can read more about it at the link below:\r\n // https://aerotwist.com/blog/flip-your-animations/\r\n\r\n // Calculate the dimensions based on the dimensions of the previous indicator\r\n const currentClientRect = element.getBoundingClientRect();\r\n const widthDelta = previousIndicatorClientRect.width / currentClientRect.width;\r\n const xPosition = previousIndicatorClientRect.left - currentClientRect.left;\r\n element.classList.add(NO_TRANSITION_CLASS);\r\n this._inkBarContentElement.style.setProperty(\r\n 'transform',\r\n `translateX(${xPosition}px) scaleX(${widthDelta})`,\r\n );\r\n\r\n // Force repaint before updating classes and transform to ensure the transform properly takes effect\r\n element.getBoundingClientRect();\r\n\r\n element.classList.remove(NO_TRANSITION_CLASS);\r\n element.classList.add(ACTIVE_CLASS);\r\n this._inkBarContentElement.style.setProperty('transform', '');\r\n }\r\n\r\n /** Removes the ink bar from the current item. */\r\n deactivateInkBar() {\r\n this.elementRef.nativeElement.classList.remove(ACTIVE_CLASS);\r\n }\r\n\r\n /** Initializes the foundation. */\r\n ngOnInit() {\r\n this._createInkBarElement();\r\n }\r\n\r\n /** Destroys the foundation. */\r\n ngOnDestroy() {\r\n this._inkBarElement?.remove();\r\n this._inkBarElement = this._inkBarContentElement = null!;\r\n }\r\n\r\n /** Creates and appends the ink bar element. */\r\n private _createInkBarElement() {\r\n const documentNode = this.elementRef.nativeElement.ownerDocument || document;\r\n this._inkBarElement = documentNode.createElement('span');\r\n this._inkBarContentElement = documentNode.createElement('span');\r\n\r\n this._inkBarElement.className = 'mdc-tab-indicator';\r\n this._inkBarContentElement.className =\r\n 'mdc-tab-indicator__content mdc-tab-indicator__content--underline';\r\n\r\n this._inkBarElement.appendChild(this._inkBarContentElement);\r\n this._appendInkBarElement();\r\n }\r\n\r\n /**\r\n * Appends the ink bar to the tab host element or content, depending on whether\r\n * the ink bar should fit to content.\r\n */\r\n private _appendInkBarElement() {\r\n if (!this._inkBarElement && (typeof isDevMode === 'undefined' || isDevMode)) {\r\n throw Error('Ink bar element has not been created and cannot be appended');\r\n }\r\n\r\n const parentElement = this._fitToContent\r\n ? this.elementRef.nativeElement.querySelector('.mdc-tab__content')\r\n : this.elementRef.nativeElement;\r\n\r\n if (!parentElement && (typeof isDevMode === 'undefined' || isDevMode)) {\r\n throw Error('Missing element to host the ink bar');\r\n }\r\n\r\n parentElement!.appendChild(this._inkBarElement!);\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Interface for a MatInkBar positioner method, defining the positioning and width of the ink\r\n * bar in a set of tabs.\r\n */\r\nexport interface _MatInkBarPositioner {\r\n (element: HTMLElement): {left: string; width: string};\r\n}\r\n\r\n/**\r\n * The default positioner function for the MatInkBar.\r\n * @docs-private\r\n */\r\nexport function _MAT_INK_BAR_POSITIONER_FACTORY(): _MatInkBarPositioner {\r\n const method = (element: HTMLElement) => ({\r\n left: element ? (element.offsetLeft || 0) + 'px' : '0',\r\n width: element ? (element.offsetWidth || 0) + 'px' : '0',\r\n });\r\n\r\n return method;\r\n}\r\n\r\n/** Injection token for the MatInkBar's Positioner. */\r\nexport const _MAT_INK_BAR_POSITIONER = new InjectionToken<_MatInkBarPositioner>(\r\n 'MatInkBarPositioner',\r\n {\r\n providedIn: 'root',\r\n factory: _MAT_INK_BAR_POSITIONER_FACTORY,\r\n },\r\n);\r\n", + "properties": [ + { + "id": "call-declaration-c66d837f89288a5041dd998f81c628222ec5bd8f3b85b99a3149dccf83c6ed34eb0e869a24f2fdf2b1cf37be4a3dea32982dece8f2645d7c3bbabd39803ef7d0", + "args": [ + { + "name": "element", + "type": "HTMLElement", + "deprecated": false, + "deprecationMessage": "" + } + ], + "returnType": "literal type", + "line": 193, + "deprecated": false, + "deprecationMessage": "" + } + ], + "indexSignatures": [], + "kind": 173, + "description": "

Interface for a MatInkBar positioner method, defining the positioning and width of the ink\nbar in a set of tabs.

\n", + "rawdescription": "\n\nInterface for a MatInkBar positioner method, defining the positioning and width of the ink\nbar in a set of tabs.\n", + "methods": [] + }, { "name": "ArrowViewStateTransition", - "id": "interface-ArrowViewStateTransition-4717641713d241cab001db987fc7a8caedcbcd3599b5591ef6fe987c139bb6fcd548281b22693638199ab3eaf8ef83533cc472e375db98aa3385053dd3292c6e", + "id": "interface-ArrowViewStateTransition-98f8434c04f2234c1bbe192cbd5e37fe09bd24e7c4c8d6d5d3b1681a4d80407c1152007cc5b70aa2c90fff616681dd1b677f0b53cfc0fa6194657b59d014969b", "file": "ui/src/components/sort/sort-header.ts", "deprecated": false, "deprecationMessage": "", "type": "interface", - "sourceCode": "import { coerceBooleanProperty } from '@angular/cdk/coercion';\r\nimport {\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n Input,\r\n OnDestroy,\r\n OnInit,\r\n Optional,\r\n ViewEncapsulation,\r\n Inject,\r\n ElementRef,\r\n IterableDiffers,\r\n NgZone,\r\n} from '@angular/core';\r\nimport { CanDisable, CanDisableCtor, mixinDisabled } from '../core';\r\nimport { merge, Subscription } from 'rxjs';\r\nimport { OuiSort, OuiSortable } from './sort';\r\nimport { ouiSortAnimations } from './sort-animations';\r\nimport { SortDirection } from './sort-direction';\r\nimport { getSortHeaderNotContainedWithinSortError } from './sort-errors';\r\nimport { OuiSortHeaderIntl } from './sort-header-intl';\r\nimport { FocusMonitor } from '@angular/cdk/a11y';\r\n\r\n// Boilerplate for applying mixins to the sort header.\r\n/** @docs-private */\r\nexport class OuiSortHeaderBase {}\r\nexport const _OuiSortHeaderMixinBase: CanDisableCtor &\r\n typeof OuiSortHeaderBase = mixinDisabled(OuiSortHeaderBase);\r\n\r\n/**\r\n * Valid positions for the arrow to be in for its opacity and translation. If the state is a\r\n * sort direction, the position of the arrow will be above/below and opacity 0. If the state is\r\n * hint, the arrow will be in the center with a slight opacity. Active state means the arrow will\r\n * be fully opaque in the center.\r\n *\r\n * @docs-private\r\n */\r\nexport type ArrowViewState = SortDirection | 'hint' | 'active';\r\n\r\n/**\r\n * States describing the arrow's animated position (animating fromState to toState).\r\n * If the fromState is not defined, there will be no animated transition to the toState.\r\n *\r\n * @docs-private\r\n */\r\nexport interface ArrowViewStateTransition {\r\n fromState?: ArrowViewState;\r\n toState: ArrowViewState;\r\n}\r\n\r\n/** Column definition associated with a `OuiSortHeader`. */\r\ninterface OuiSortHeaderColumnDef {\r\n name: string;\r\n}\r\n\r\n/**\r\n * Applies sorting behavior (click to change sort) and styles to an element, including an\r\n * arrow to display the current sort direction.\r\n *\r\n * Must be provided with an id and contained within a parent OuiSort directive.\r\n *\r\n * If used on header cells in a CdkTable, it will automatically default its id from its containing\r\n * column definition.\r\n */\r\n@Component({\r\n // eslint-disable-next-line\r\n selector: '[oui-sort-header]',\r\n exportAs: 'ouiSortHeader',\r\n templateUrl: 'sort-header.html',\r\n styleUrls: ['sort-header.scss'],\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n '(click)': '_handleClick()',\r\n '(mouseenter)': '_setIndicatorHintVisible(true)',\r\n '(longpress)': '_setIndicatorHintVisible(true)',\r\n '(mouseleave)': '_setIndicatorHintVisible(false)',\r\n '[attr.aria-sort]': '_getAriaSortAttribute()',\r\n '[class.oui-sort-header-disabled]': '_isDisabled()',\r\n },\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['disabled'],\r\n animations: [\r\n ouiSortAnimations.indicator,\r\n ouiSortAnimations.leftPointer,\r\n ouiSortAnimations.rightPointer,\r\n ouiSortAnimations.arrowOpacity,\r\n ouiSortAnimations.arrowPosition,\r\n ouiSortAnimations.allowChildren,\r\n ],\r\n})\r\nexport class OuiSortHeader\r\n extends _OuiSortHeaderMixinBase\r\n implements CanDisable, OuiSortable, OnDestroy, OnInit\r\n{\r\n private _rerenderSubscription: Subscription;\r\n\r\n /**\r\n * Flag set to true when the indicator should be displayed while the sort is not active. Used to\r\n * provide an affordance that the header is sortable by showing on focus and hover.\r\n */\r\n _showIndicatorHint = false;\r\n\r\n /**\r\n * The view transition state of the arrow (translation/ opacity) - indicates its `from` and `to`\r\n * position through the animation. If animations are currently disabled, the fromState is removed\r\n * so that there is no animation displayed.\r\n */\r\n _viewState: ArrowViewStateTransition;\r\n\r\n /** The direction the arrow should be facing according to the current state. */\r\n _arrowDirection: SortDirection = '';\r\n\r\n /**\r\n * Whether the view state animation should show the transition between the `from` and `to` states.\r\n */\r\n _disableViewStateAnimation = false;\r\n\r\n /**\r\n * ID of this sort header. If used within the context of a CdkColumnDef, this will default to\r\n * the column's name.\r\n */\r\n // eslint-disable-next-line @angular-eslint/no-input-rename\r\n @Input('oui-sort-header') id: string;\r\n\r\n /** Sets the position of the arrow that displays when sorted. */\r\n @Input() arrowPosition: 'before' | 'after' = 'after';\r\n\r\n /** Overrides the sort start value of the containing OuiSort for this OuiSortable. */\r\n @Input() start: 'asc' | 'desc';\r\n\r\n // To set browser tooltip\r\n title: string;\r\n\r\n /** Overrides the disable clear value of the containing OuiSort for this OuiSortable. */\r\n @Input()\r\n get disableClear(): boolean {\r\n return this._disableClear;\r\n }\r\n set disableClear(v) {\r\n this._disableClear = coerceBooleanProperty(v);\r\n }\r\n private _disableClear: boolean;\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n constructor(\r\n public _intl: OuiSortHeaderIntl,\r\n changeDetectorRef: ChangeDetectorRef,\r\n protected elementRef: ElementRef,\r\n protected _differs: IterableDiffers,\r\n private _focusMonitor: FocusMonitor,\r\n private _ngZone: NgZone,\r\n @Optional() public _sort: OuiSort,\r\n @Inject('OUI_SORT_HEADER_COLUMN_DEF')\r\n @Optional()\r\n public _columnDef: OuiSortHeaderColumnDef,\r\n private _elementRef: ElementRef\r\n ) {\r\n // Note that we use a string token for the `_columnDef`, because the value is provided both by\r\n // `once-ui/table` and `cdk/table` and we can't have the CDK depending on once-ui,\r\n // and we want to avoid having the sort header depending on the CDK table because\r\n // of this single reference.\r\n super();\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this.elementRef, true)\r\n .subscribe(() => this._ngZone.run(() => {}));\r\n\r\n if (!_sort) {\r\n throw getSortHeaderNotContainedWithinSortError();\r\n }\r\n\r\n this._rerenderSubscription = merge(\r\n _sort.sortChange,\r\n _sort._stateChanges,\r\n _intl.changes\r\n ).subscribe(() => {\r\n if (this._isSorted()) {\r\n this._updateArrowDirection();\r\n }\r\n\r\n // If this header was recently active and now no longer sorted, animate away the arrow.\r\n if (\r\n !this._isSorted() &&\r\n this._viewState &&\r\n this._viewState.toState === 'active'\r\n ) {\r\n this._disableViewStateAnimation = false;\r\n this._setAnimationTransitionState({\r\n fromState: 'active',\r\n toState: this._arrowDirection,\r\n });\r\n }\r\n\r\n changeDetectorRef.markForCheck();\r\n });\r\n }\r\n\r\n ngOnInit() {\r\n const columnHeading: string = this._elementRef.nativeElement.innerText;\r\n this.title = 'Sort by ' + columnHeading;\r\n if (!this.id && this._columnDef) {\r\n this.id = this._columnDef.name;\r\n }\r\n\r\n // Initialize the direction of the arrow and set the view state to be immediately that state.\r\n this._updateArrowDirection();\r\n this._setAnimationTransitionState({\r\n toState: this._isSorted() ? 'active' : this._arrowDirection,\r\n });\r\n\r\n this._sort.register(this);\r\n }\r\n\r\n ngOnDestroy() {\r\n this._sort.deregister(this);\r\n this._rerenderSubscription.unsubscribe();\r\n this._focusMonitor.stopMonitoring(this.elementRef);\r\n this._monitorSubscription.unsubscribe();\r\n }\r\n\r\n /**\r\n * Sets the \"hint\" state such that the arrow will be semi-transparently displayed as a hint to the\r\n * user showing what the active sort will become. If set to false, the arrow will fade away.\r\n */\r\n _setIndicatorHintVisible(visible: boolean) {\r\n // No-op if the sort header is disabled - should not make the hint visible.\r\n if (this._isDisabled() && visible) {\r\n return;\r\n }\r\n\r\n this._showIndicatorHint = visible;\r\n\r\n if (!this._isSorted()) {\r\n this._updateArrowDirection();\r\n if (this._showIndicatorHint) {\r\n this._setAnimationTransitionState({\r\n fromState: this._arrowDirection,\r\n toState: 'hint',\r\n });\r\n } else {\r\n this._setAnimationTransitionState({\r\n fromState: 'hint',\r\n toState: this._arrowDirection,\r\n });\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Sets the animation transition view state for the arrow's position and opacity. If the\r\n * `disableViewStateAnimation` flag is set to true, the `fromState` will be ignored so that\r\n * no animation appears.\r\n */\r\n _setAnimationTransitionState(viewState: ArrowViewStateTransition) {\r\n this._viewState = viewState;\r\n\r\n // If the animation for arrow position state (opacity/translation) should be disabled,\r\n // remove the fromState so that it jumps right to the toState.\r\n if (this._disableViewStateAnimation) {\r\n this._viewState = { toState: viewState.toState };\r\n }\r\n }\r\n\r\n /** Triggers the sort on this sort header and removes the indicator hint. */\r\n _handleClick() {\r\n if (this._isDisabled()) {\r\n return;\r\n }\r\n\r\n this._sort.sort(this);\r\n\r\n // Do not show the animation if the header was already shown in the right position.\r\n if (\r\n this._viewState.toState === 'hint' ||\r\n this._viewState.toState === 'active'\r\n ) {\r\n this._disableViewStateAnimation = true;\r\n }\r\n\r\n // If the arrow is now sorted, animate the arrow into place. Otherwise, animate it away into\r\n // the direction it is facing.\r\n const viewState: ArrowViewStateTransition = this._isSorted()\r\n ? { fromState: this._arrowDirection, toState: 'active' }\r\n : { fromState: 'active', toState: this._arrowDirection };\r\n this._setAnimationTransitionState(viewState);\r\n\r\n this._showIndicatorHint = false;\r\n }\r\n\r\n /** Whether this OuiSortHeader is currently sorted in either ascending or descending order. */\r\n _isSorted() {\r\n return (\r\n this._sort.active === this.id &&\r\n (this._sort.direction === 'asc' || this._sort.direction === 'desc')\r\n );\r\n }\r\n\r\n /** Returns the animation state for the arrow direction (indicator and pointers). */\r\n _getArrowDirectionState() {\r\n return `${this._isSorted() ? 'active-' : ''}${this._arrowDirection}`;\r\n }\r\n\r\n /** Returns the arrow position state (opacity, translation). */\r\n _getArrowViewState() {\r\n const fromState = this._viewState.fromState;\r\n return (fromState ? `${fromState}-to-` : '') + this._viewState.toState;\r\n }\r\n\r\n /**\r\n * Updates the direction the arrow should be pointing. If it is not sorted, the arrow should be\r\n * facing the start direction. Otherwise if it is sorted, the arrow should point in the currently\r\n * active sorted direction. The reason this is updated through a function is because the direction\r\n * should only be changed at specific times - when deactivated but the hint is displayed and when\r\n * the sort is active and the direction changes. Otherwise the arrow's direction should linger\r\n * in cases such as the sort becoming deactivated but we want to animate the arrow away while\r\n * preserving its direction, even though the next sort direction is actually different and should\r\n * only be changed once the arrow displays again (hint or activation).\r\n */\r\n _updateArrowDirection() {\r\n this._arrowDirection = this._isSorted()\r\n ? this._sort.direction\r\n : this.start || this._sort.start;\r\n }\r\n\r\n _isDisabled() {\r\n return this._sort.disabled || this.disabled;\r\n }\r\n\r\n /**\r\n * Gets the aria-sort attribute that should be applied to this sort header. If this header\r\n * is not sorted, returns null so that the attribute is removed from the host element. Aria spec\r\n * says that the aria-sort property should only be present on one header at a time, so removing\r\n * ensures this is true.\r\n */\r\n _getAriaSortAttribute() {\r\n if (!this._isSorted()) {\r\n return null;\r\n }\r\n\r\n return this._sort.direction === 'asc' ? 'ascending' : 'descending';\r\n }\r\n}\r\n", + "sourceCode": "import { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n Input,\n OnDestroy,\n OnInit,\n Optional,\n ViewEncapsulation,\n Inject,\n ElementRef,\n IterableDiffers,\n NgZone,\n} from '@angular/core';\nimport { CanDisable, CanDisableCtor, mixinDisabled } from '../core';\nimport { merge, Subscription } from 'rxjs';\nimport { OuiSort, OuiSortable } from './sort';\nimport { ouiSortAnimations } from './sort-animations';\nimport { SortDirection } from './sort-direction';\nimport { getSortHeaderNotContainedWithinSortError } from './sort-errors';\nimport { OuiSortHeaderIntl } from './sort-header-intl';\nimport { FocusMonitor } from '@angular/cdk/a11y';\n\n// Boilerplate for applying mixins to the sort header.\n/** @docs-private */\nexport class OuiSortHeaderBase {}\nexport const _OuiSortHeaderMixinBase: CanDisableCtor &\n typeof OuiSortHeaderBase = mixinDisabled(OuiSortHeaderBase);\n\n/**\n * Valid positions for the arrow to be in for its opacity and translation. If the state is a\n * sort direction, the position of the arrow will be above/below and opacity 0. If the state is\n * hint, the arrow will be in the center with a slight opacity. Active state means the arrow will\n * be fully opaque in the center.\n *\n * @docs-private\n */\nexport type ArrowViewState = SortDirection | 'hint' | 'active';\n\n/**\n * States describing the arrow's animated position (animating fromState to toState).\n * If the fromState is not defined, there will be no animated transition to the toState.\n *\n * @docs-private\n */\nexport interface ArrowViewStateTransition {\n fromState?: ArrowViewState;\n toState: ArrowViewState;\n}\n\n/** Column definition associated with a `OuiSortHeader`. */\ninterface OuiSortHeaderColumnDef {\n name: string;\n}\n\n/**\n * Applies sorting behavior (click to change sort) and styles to an element, including an\n * arrow to display the current sort direction.\n *\n * Must be provided with an id and contained within a parent OuiSort directive.\n *\n * If used on header cells in a CdkTable, it will automatically default its id from its containing\n * column definition.\n */\n@Component({\n // eslint-disable-next-line\n selector: '[oui-sort-header]',\n exportAs: 'ouiSortHeader',\n templateUrl: 'sort-header.html',\n styleUrls: ['sort-header.scss'],\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n '(click)': '_handleClick()',\n '(mouseenter)': '_setIndicatorHintVisible(true)',\n '(longpress)': '_setIndicatorHintVisible(true)',\n '(mouseleave)': '_setIndicatorHintVisible(false)',\n '[attr.aria-sort]': '_getAriaSortAttribute()',\n '[class.oui-sort-header-disabled]': '_isDisabled()',\n },\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\n inputs: ['disabled'],\n animations: [\n ouiSortAnimations.indicator,\n ouiSortAnimations.leftPointer,\n ouiSortAnimations.rightPointer,\n ouiSortAnimations.arrowOpacity,\n ouiSortAnimations.arrowPosition,\n ouiSortAnimations.allowChildren,\n ],\n})\nexport class OuiSortHeader\n extends _OuiSortHeaderMixinBase\n implements CanDisable, OuiSortable, OnDestroy, OnInit\n{\n private _rerenderSubscription: Subscription;\n\n /**\n * Flag set to true when the indicator should be displayed while the sort is not active. Used to\n * provide an affordance that the header is sortable by showing on focus and hover.\n */\n _showIndicatorHint = false;\n\n /**\n * The view transition state of the arrow (translation/ opacity) - indicates its `from` and `to`\n * position through the animation. If animations are currently disabled, the fromState is removed\n * so that there is no animation displayed.\n */\n _viewState: ArrowViewStateTransition;\n\n /** The direction the arrow should be facing according to the current state. */\n _arrowDirection: SortDirection = '';\n\n /**\n * Whether the view state animation should show the transition between the `from` and `to` states.\n */\n _disableViewStateAnimation = false;\n\n /**\n * ID of this sort header. If used within the context of a CdkColumnDef, this will default to\n * the column's name.\n */\n // eslint-disable-next-line @angular-eslint/no-input-rename\n @Input('oui-sort-header') id: string;\n\n /** Sets the position of the arrow that displays when sorted. */\n @Input() arrowPosition: 'before' | 'after' = 'after';\n\n /** Overrides the sort start value of the containing OuiSort for this OuiSortable. */\n @Input() start: 'asc' | 'desc';\n\n // To set browser tooltip\n title: string;\n\n /** Overrides the disable clear value of the containing OuiSort for this OuiSortable. */\n @Input()\n get disableClear(): boolean {\n return this._disableClear;\n }\n set disableClear(v) {\n this._disableClear = coerceBooleanProperty(v);\n }\n private _disableClear: boolean;\n private _monitorSubscription: Subscription = Subscription.EMPTY;\n constructor(\n public _intl: OuiSortHeaderIntl,\n changeDetectorRef: ChangeDetectorRef,\n protected elementRef: ElementRef,\n protected _differs: IterableDiffers,\n private _focusMonitor: FocusMonitor,\n private _ngZone: NgZone,\n @Optional() public _sort: OuiSort,\n @Inject('OUI_SORT_HEADER_COLUMN_DEF')\n @Optional()\n public _columnDef: OuiSortHeaderColumnDef,\n private _elementRef: ElementRef\n ) {\n // Note that we use a string token for the `_columnDef`, because the value is provided both by\n // `once-ui/table` and `cdk/table` and we can't have the CDK depending on once-ui,\n // and we want to avoid having the sort header depending on the CDK table because\n // of this single reference.\n super();\n this._monitorSubscription = this._focusMonitor\n .monitor(this.elementRef, true)\n .subscribe(() => this._ngZone.run(() => {}));\n\n if (!_sort) {\n throw getSortHeaderNotContainedWithinSortError();\n }\n\n this._rerenderSubscription = merge(\n _sort.sortChange,\n _sort._stateChanges,\n _intl.changes\n ).subscribe(() => {\n if (this._isSorted()) {\n this._updateArrowDirection();\n }\n\n // If this header was recently active and now no longer sorted, animate away the arrow.\n if (\n !this._isSorted() &&\n this._viewState &&\n this._viewState.toState === 'active'\n ) {\n this._disableViewStateAnimation = false;\n this._setAnimationTransitionState({\n fromState: 'active',\n toState: this._arrowDirection,\n });\n }\n\n changeDetectorRef.markForCheck();\n });\n }\n\n ngOnInit() {\n const columnHeading: string = this._elementRef.nativeElement.innerText;\n this.title = 'Sort by ' + columnHeading;\n if (!this.id && this._columnDef) {\n this.id = this._columnDef.name;\n }\n\n // Initialize the direction of the arrow and set the view state to be immediately that state.\n this._updateArrowDirection();\n this._setAnimationTransitionState({\n toState: this._isSorted() ? 'active' : this._arrowDirection,\n });\n\n this._sort.register(this);\n }\n\n ngOnDestroy() {\n this._sort.deregister(this);\n this._rerenderSubscription.unsubscribe();\n this._focusMonitor.stopMonitoring(this.elementRef);\n this._monitorSubscription.unsubscribe();\n }\n\n /**\n * Sets the \"hint\" state such that the arrow will be semi-transparently displayed as a hint to the\n * user showing what the active sort will become. If set to false, the arrow will fade away.\n */\n _setIndicatorHintVisible(visible: boolean) {\n // No-op if the sort header is disabled - should not make the hint visible.\n if (this._isDisabled() && visible) {\n return;\n }\n\n this._showIndicatorHint = visible;\n\n if (!this._isSorted()) {\n this._updateArrowDirection();\n if (this._showIndicatorHint) {\n this._setAnimationTransitionState({\n fromState: this._arrowDirection,\n toState: 'hint',\n });\n } else {\n this._setAnimationTransitionState({\n fromState: 'hint',\n toState: this._arrowDirection,\n });\n }\n }\n }\n\n /**\n * Sets the animation transition view state for the arrow's position and opacity. If the\n * `disableViewStateAnimation` flag is set to true, the `fromState` will be ignored so that\n * no animation appears.\n */\n _setAnimationTransitionState(viewState: ArrowViewStateTransition) {\n this._viewState = viewState;\n\n // If the animation for arrow position state (opacity/translation) should be disabled,\n // remove the fromState so that it jumps right to the toState.\n if (this._disableViewStateAnimation) {\n this._viewState = { toState: viewState.toState };\n }\n }\n\n /** Triggers the sort on this sort header and removes the indicator hint. */\n _handleClick() {\n if (this._isDisabled()) {\n return;\n }\n\n this._sort.sort(this);\n\n // Do not show the animation if the header was already shown in the right position.\n if (\n this._viewState.toState === 'hint' ||\n this._viewState.toState === 'active'\n ) {\n this._disableViewStateAnimation = true;\n }\n\n // If the arrow is now sorted, animate the arrow into place. Otherwise, animate it away into\n // the direction it is facing.\n const viewState: ArrowViewStateTransition = this._isSorted()\n ? { fromState: this._arrowDirection, toState: 'active' }\n : { fromState: 'active', toState: this._arrowDirection };\n this._setAnimationTransitionState(viewState);\n\n this._showIndicatorHint = false;\n }\n\n /** Whether this OuiSortHeader is currently sorted in either ascending or descending order. */\n _isSorted() {\n return (\n this._sort.active === this.id &&\n (this._sort.direction === 'asc' || this._sort.direction === 'desc')\n );\n }\n\n /** Returns the animation state for the arrow direction (indicator and pointers). */\n _getArrowDirectionState() {\n return `${this._isSorted() ? 'active-' : ''}${this._arrowDirection}`;\n }\n\n /** Returns the arrow position state (opacity, translation). */\n _getArrowViewState() {\n const fromState = this._viewState.fromState;\n return (fromState ? `${fromState}-to-` : '') + this._viewState.toState;\n }\n\n /**\n * Updates the direction the arrow should be pointing. If it is not sorted, the arrow should be\n * facing the start direction. Otherwise if it is sorted, the arrow should point in the currently\n * active sorted direction. The reason this is updated through a function is because the direction\n * should only be changed at specific times - when deactivated but the hint is displayed and when\n * the sort is active and the direction changes. Otherwise the arrow's direction should linger\n * in cases such as the sort becoming deactivated but we want to animate the arrow away while\n * preserving its direction, even though the next sort direction is actually different and should\n * only be changed once the arrow displays again (hint or activation).\n */\n _updateArrowDirection() {\n this._arrowDirection = this._isSorted()\n ? this._sort.direction\n : this.start || this._sort.start;\n }\n\n _isDisabled() {\n return this._sort.disabled || this.disabled;\n }\n\n /**\n * Gets the aria-sort attribute that should be applied to this sort header. If this header\n * is not sorted, returns null so that the attribute is removed from the host element. Aria spec\n * says that the aria-sort property should only be present on one header at a time, so removing\n * ensures this is true.\n */\n _getAriaSortAttribute() {\n if (!this._isSorted()) {\n return null;\n }\n\n return this._sort.direction === 'asc' ? 'ascending' : 'descending';\n }\n}\n", "properties": [ { "name": "fromState", @@ -111,12 +142,12 @@ }, { "name": "CanColor", - "id": "interface-CanColor-144aa9c94314d03109fbdbe5df792c631239505b30cef42f98e8b4f4b49d37c0e0e52dd220558050be3dfb4e75756b158953e962867a2afb1dd69ce0aa6b9552", + "id": "interface-CanColor-5421b741813d2055e2f33197931e7cb30d04d694af0f71cf956b909505d983943f4d1253be77c7bcdd57272dceb3b1cd52a324d3703a1d733a55e07a2a9ac1d4", "file": "ui/src/components/core/common-behaviors/color.ts", "deprecated": false, "deprecationMessage": "", "type": "interface", - "sourceCode": "import { Constructor } from './constructor';\r\nimport { ElementRef } from '@angular/core';\r\n\r\n/** @docs-private */\r\nexport interface CanColor {\r\n /** Theme color palette for the component. */\r\n color: ThemePalette;\r\n}\r\n\r\n/** Possible color palette values. */\r\nexport type ThemePalette = 'primary' | 'accent' | 'warn' | undefined;\r\n\r\n/** @docs-private */\r\nexport interface HasElementRef {\r\n _elementRef: ElementRef;\r\n}\r\n\r\n/** @docs-private */\r\nexport type CanColorCtor = Constructor;\r\n\r\n/** Mixin to augment a directive with a `color` property. */\r\nexport function mixinColor>(\r\n base: T,\r\n defaultColor?: ThemePalette\r\n): CanColorCtor & T {\r\n return class extends base {\r\n private _color: ThemePalette;\r\n\r\n get color(): ThemePalette {\r\n return this._color;\r\n }\r\n set color(value: ThemePalette) {\r\n const colorPalette = value || defaultColor;\r\n\r\n if (colorPalette !== this._color) {\r\n if (this._color) {\r\n this._elementRef.nativeElement.classList.remove(`oui-${this._color}`);\r\n }\r\n if (colorPalette) {\r\n this._elementRef.nativeElement.classList.add(`oui-${colorPalette}`);\r\n }\r\n\r\n this._color = colorPalette;\r\n }\r\n }\r\n\r\n constructor(...args: any[]) {\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\r\n super(...args);\r\n // Set the default color that can be specified from the mixin.\r\n this.color = defaultColor;\r\n }\r\n };\r\n}\r\n", + "sourceCode": "import { Constructor } from './constructor';\nimport { ElementRef } from '@angular/core';\n\n/** @docs-private */\nexport interface CanColor {\n /** Theme color palette for the component. */\n color: ThemePalette;\n}\n\n/** Possible color palette values. */\nexport type ThemePalette = 'primary' | 'accent' | 'warn' | undefined;\n\n/** @docs-private */\nexport interface HasElementRef {\n _elementRef: ElementRef;\n}\n\n/** @docs-private */\nexport type CanColorCtor = Constructor;\n\n/** Mixin to augment a directive with a `color` property. */\nexport function mixinColor>(\n base: T,\n defaultColor?: ThemePalette\n): CanColorCtor & T {\n return class extends base {\n private _color: ThemePalette;\n\n get color(): ThemePalette {\n return this._color;\n }\n set color(value: ThemePalette) {\n const colorPalette = value || defaultColor;\n\n if (colorPalette !== this._color) {\n if (this._color) {\n this._elementRef.nativeElement.classList.remove(`oui-${this._color}`);\n }\n if (colorPalette) {\n this._elementRef.nativeElement.classList.add(`oui-${colorPalette}`);\n }\n\n this._color = colorPalette;\n }\n }\n\n constructor(...args: any[]) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n super(...args);\n // Set the default color that can be specified from the mixin.\n this.color = defaultColor;\n }\n };\n}\n", "properties": [ { "name": "color", @@ -135,12 +166,12 @@ }, { "name": "CanDisable", - "id": "interface-CanDisable-5177ffe03c7f2d12da477ee3075d1d7ce3d26361d328d8c4aced0bc16161e14aa2249efa12dccf5b9b68570425ae41407d0d2077672e6a4010d3bb5a13b1e42f", + "id": "interface-CanDisable-03398f82b22a0b5551c917cf163e2adfda027b2863b10aa9547debd79688d819c739e08fe646e9d1ee133a0ac292d89ea9a869f0a9628e184b7f59f9779d04f9", "file": "ui/src/components/core/common-behaviors/disabled.ts", "deprecated": false, "deprecationMessage": "", "type": "interface", - "sourceCode": "import { coerceBooleanProperty } from '@angular/cdk/coercion';\r\nimport { Constructor } from './constructor';\r\n\r\nexport interface CanDisable {\r\n /** Whether the component is disabled. */\r\n disabled: boolean;\r\n}\r\n\r\n/** @docs-private */\r\nexport type CanDisableCtor = Constructor;\r\n\r\n/** Mixin to augment a directive with a `disabled` property. */\r\nexport function mixinDisabled>(\r\n base: T\r\n): CanDisableCtor & T {\r\n return class extends base {\r\n private _disabled = false;\r\n\r\n get disabled() {\r\n return this._disabled;\r\n }\r\n set disabled(value: any) {\r\n this._disabled = coerceBooleanProperty(value);\r\n }\r\n\r\n constructor(...args: any[]) {\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\r\n super(...args);\r\n }\r\n };\r\n}\r\n", + "sourceCode": "import { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { Constructor } from './constructor';\n\nexport interface CanDisable {\n /** Whether the component is disabled. */\n disabled: boolean;\n}\n\n/** @docs-private */\nexport type CanDisableCtor = Constructor;\n\n/** Mixin to augment a directive with a `disabled` property. */\nexport function mixinDisabled>(\n base: T\n): CanDisableCtor & T {\n return class extends base {\n private _disabled = false;\n\n get disabled() {\n return this._disabled;\n }\n set disabled(value: any) {\n this._disabled = coerceBooleanProperty(value);\n }\n\n constructor(...args: any[]) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n super(...args);\n }\n };\n}\n", "properties": [ { "name": "disabled", @@ -157,14 +188,38 @@ "kind": 165, "methods": [] }, + { + "name": "CanDisableRipple", + "id": "interface-CanDisableRipple-4d795a002c4d107ac6516f922e0445d37fe48b77293f773dd6e8bd2a9cf3b080989782637dd018c7e918f23ec852db9c65f91880021b3dbe71fbddaca20fe9fe", + "file": "ui/src/components/core/common-behaviors/disable-ripple.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "interface", + "sourceCode": "import {coerceBooleanProperty} from '@angular/cdk/coercion';\r\nimport {AbstractConstructor, Constructor} from './constructor';\r\n\r\n/** @docs-private */\r\nexport interface CanDisableRipple {\r\n /** Whether ripples are disabled. */\r\n disableRipple: boolean;\r\n}\r\n\r\ntype CanDisableRippleCtor = Constructor & AbstractConstructor;\r\n\r\n/** Mixin to augment a directive with a `disableRipple` property. */\r\nexport function mixinDisableRipple>(\r\n base: T,\r\n): CanDisableRippleCtor & T;\r\nexport function mixinDisableRipple>(base: T): CanDisableRippleCtor & T {\r\n return class extends base {\r\n private _disableRipple: boolean = false;\r\n\r\n /** Whether the ripple effect is disabled or not. */\r\n get disableRipple(): boolean {\r\n return this._disableRipple;\r\n }\r\n set disableRipple(value: any) {\r\n this._disableRipple = coerceBooleanProperty(value);\r\n }\r\n\r\n constructor(...args: any[]) {\r\n super(...args);\r\n }\r\n };\r\n}\r\n", + "properties": [ + { + "name": "disableRipple", + "deprecated": false, + "deprecationMessage": "", + "type": "boolean", + "optional": false, + "description": "

Whether ripples are disabled.

\n", + "line": 15, + "rawdescription": "\nWhether ripples are disabled." + } + ], + "indexSignatures": [], + "kind": 165, + "methods": [] + }, { "name": "CanProgress", - "id": "interface-CanProgress-d16d67b321614ec93ef035c8b80dc0a34761addeb45455aab279aaececf54182991dd0dcd0d4230cccd677c1c0db3421294803ad98634e17d6d427135b7175fb", + "id": "interface-CanProgress-79dffe92cb11fdbf967d46770d67b8acb6a5b3962ae830f30863bb7504cd7f80c1d1487bd39a6c88e940665ae4712248a8a1eed8620d7beeb19ef2ac20038fe1", "file": "ui/src/components/button/progress.ts", "deprecated": false, "deprecationMessage": "", "type": "interface", - "sourceCode": "import { Constructor } from '../core/common-behaviors/constructor';\r\nimport { ElementRef, ChangeDetectorRef } from '@angular/core';\r\n\r\n/** @docs-private */\r\nexport interface CanProgress {\r\n /** progress texts */\r\n progress: string[] | string;\r\n setToProgress: Function;\r\n setToDone: Function;\r\n setToDefault: Function;\r\n}\r\n\r\n/** @docs-private */\r\nexport interface HasElementRef {\r\n _elementRef: ElementRef;\r\n}\r\n\r\n/** @docs-private */\r\nexport type CanProgressCtor = Constructor;\r\n\r\n/** Mixin to augment a directive with a `color` property. */\r\nexport function mixinProgress>(\r\n base: T\r\n): CanProgressCtor & T {\r\n return class extends base {\r\n private _progress: string[] | string;\r\n private _cdr: ChangeDetectorRef;\r\n private _stage: 'default' | 'progress' | 'done' = 'default';\r\n get progress(): string[] | string {\r\n return this._progress;\r\n }\r\n set progress(value: string[] | string) {\r\n if (value === '') {\r\n this._progress = ['Save', 'Saving...', 'Saved'];\r\n } else {\r\n this._progress = value;\r\n }\r\n setTimeout(() => {\r\n this._changeStage();\r\n this._cdr.detectChanges();\r\n }, 0);\r\n }\r\n\r\n constructor(...args: any[]) {\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\r\n super(...args);\r\n this._cdr = args[1];\r\n this._progress = null;\r\n }\r\n\r\n private _setButtonText(text: string) {\r\n this._elementRef.nativeElement.children[0].innerHTML = text;\r\n }\r\n\r\n private _addClass(className: string) {\r\n this._elementRef.nativeElement.classList.add(className);\r\n }\r\n\r\n private _checkAttribute() {\r\n if (this._elementRef.nativeElement.tagName === 'A') {\r\n throw Error('please use \n \n \n\n", + "templateUrl": [], + "viewProviders": [], + "inputsClass": [], + "outputsClass": [], + "propertiesClass": [], + "methodsClass": [], + "deprecated": false, + "deprecationMessage": "", + "hostBindings": [], + "hostListeners": [], + "description": "", + "rawdescription": "\n", + "type": "component", + "sourceCode": "import { Component } from '@angular/core';\r\n\r\n@Component({\r\n selector: 'mat-card-storybook',\r\n template: `\r\n \r\n \r\n
\r\n Shiba Inu\r\n Dog Breed\r\n
\r\n \"Photo\r\n \r\n

\r\n The Shiba Inu is the smallest of the six original and distinct spitz breeds of dog from Japan.\r\n A small, agile dog that copes very well with mountainous terrain, the Shiba Inu was originally\r\n bred for hunting.\r\n

\r\n
\r\n \r\n \r\n \r\n \r\n
\r\n `,\r\n})\r\nexport class MatCardStorybook {\r\n constructor() {}\r\n}", + "assetsDirs": [], + "styleUrlsData": "", + "stylesData": "", + "constructorObj": { + "name": "constructor", + "description": "", + "deprecated": false, + "deprecationMessage": "", + "args": [], + "line": 27 + } + }, + { + "name": "MatCardTitleGroup", + "id": "component-MatCardTitleGroup-e101d0527ba593851c5542e8fa2ff347520c14d47723e8cab74b8e83b12893b57aafc5abe593087dfef8aae5e32dd66f438829d2cd1674144cc7fb5ec49feb51", + "file": "ui/src/components/card/card.ts", + "changeDetection": "ChangeDetectionStrategy.OnPush", + "encapsulation": [ + "ViewEncapsulation.None" + ], + "entryComponents": [], + "host": {}, + "inputs": [], + "outputs": [], + "providers": [], + "selector": "mat-card-title-group", + "styleUrls": [], + "styles": [], + "templateUrl": [ + "card-title-group.html" ], + "viewProviders": [], + "inputsClass": [], "outputsClass": [], + "propertiesClass": [], + "methodsClass": [], "deprecated": false, "deprecationMessage": "", "hostBindings": [], "hostListeners": [], - "propertiesClass": [ - { - "name": "_ariaDescribedby", - "deprecated": false, - "deprecationMessage": "", - "type": "string", - "optional": false, - "description": "

The aria-describedby attribute on the input for improved a11y.

\n", - "line": 129, - "rawdescription": "\nThe aria-describedby attribute on the input for improved a11y." - }, + "description": "

Container intended to be used within the <mat-card> component. Can contain exactly one\n<mat-card-title>, one <mat-card-subtitle> and one content image of any size\n(e.g. <img matCardLgImage>).

\n", + "rawdescription": "\n\nContainer intended to be used within the `` component. Can contain exactly one\n``, one `` and one content image of any size\n(e.g. ``).\n", + "type": "component", + "sourceCode": "import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n Directive,\r\n Inject,\r\n InjectionToken,\r\n Input,\r\n Optional,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\n\r\nexport type MatCardAppearance = 'outlined' | 'raised';\r\n\r\n/** Object that can be used to configure the default options for the card module. */\r\nexport interface MatCardConfig {\r\n /** Default appearance for cards. */\r\n appearance?: MatCardAppearance;\r\n}\r\n\r\n/** Injection token that can be used to provide the default options the card module. */\r\nexport const MAT_CARD_CONFIG = new InjectionToken('MAT_CARD_CONFIG');\r\n\r\n/**\r\n * Material Design card component. Cards contain content and actions about a single subject.\r\n * See https://material.io/design/components/cards.html\r\n *\r\n * MatCard provides no behaviors, instead serving as a purely visual treatment.\r\n */\r\n@Component({\r\n selector: 'mat-card',\r\n templateUrl: 'card.html',\r\n styleUrls: ['./card.scss'],\r\n host: {\r\n 'class': 'mat-mdc-card mdc-card',\r\n '[class.mat-mdc-card-outlined]': 'appearance === \"outlined\"',\r\n '[class.mdc-card--outlined]': 'appearance === \"outlined\"',\r\n },\r\n exportAs: 'matCard',\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class MatCard {\r\n @Input() appearance: MatCardAppearance;\r\n\r\n constructor(@Inject(MAT_CARD_CONFIG) @Optional() config?: MatCardConfig) {\r\n this.appearance = config?.appearance || 'raised';\r\n }\r\n}\r\n\r\n// TODO(jelbourn): add `MatActionCard`, which is a card that acts like a button (and has a ripple).\r\n// Supported in MDC with `.mdc-card__primary-action`. Will require additional a11y docs for users.\r\n\r\n/**\r\n * Title of a card, intended for use within ``. This component is an optional\r\n * convenience for one variety of card title; any custom title element may be used in its place.\r\n *\r\n * MatCardTitle provides no behaviors, instead serving as a purely visual treatment.\r\n */\r\n@Directive({\r\n selector: `mat-card-title, [mat-card-title], [matCardTitle]`,\r\n host: {'class': 'mat-mdc-card-title'},\r\n})\r\nexport class MatCardTitle {}\r\n\r\n/**\r\n * Container intended to be used within the `` component. Can contain exactly one\r\n * ``, one `` and one content image of any size\r\n * (e.g. ``).\r\n */\r\n@Component({\r\n selector: 'mat-card-title-group',\r\n templateUrl: 'card-title-group.html',\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n host: {'class': 'mat-mdc-card-title-group'},\r\n})\r\nexport class MatCardTitleGroup {}\r\n\r\n/**\r\n * Content of a card, intended for use within ``. This component is an optional\r\n * convenience for use with other convenience elements, such as ``; any custom\r\n * content block element may be used in its place.\r\n *\r\n * MatCardContent provides no behaviors, instead serving as a purely visual treatment.\r\n */\r\n@Directive({\r\n selector: 'mat-card-content',\r\n host: {'class': 'mat-mdc-card-content'},\r\n})\r\nexport class MatCardContent {}\r\n\r\n/**\r\n * Sub-title of a card, intended for use within `` beneath a ``. This\r\n * component is an optional convenience for use with other convenience elements, such as\r\n * ``.\r\n *\r\n * MatCardSubtitle provides no behaviors, instead serving as a purely visual treatment.\r\n */\r\n@Directive({\r\n selector: `mat-card-subtitle, [mat-card-subtitle], [matCardSubtitle]`,\r\n host: {'class': 'mat-mdc-card-subtitle'},\r\n})\r\nexport class MatCardSubtitle {}\r\n\r\n/**\r\n * Bottom area of a card that contains action buttons, intended for use within ``.\r\n * This component is an optional convenience for use with other convenience elements, such as\r\n * ``; any custom action block element may be used in its place.\r\n *\r\n * MatCardActions provides no behaviors, instead serving as a purely visual treatment.\r\n */\r\n@Directive({\r\n selector: 'mat-card-actions',\r\n exportAs: 'matCardActions',\r\n host: {\r\n 'class': 'mat-mdc-card-actions mdc-card__actions',\r\n '[class.mat-mdc-card-actions-align-end]': 'align === \"end\"',\r\n },\r\n})\r\nexport class MatCardActions {\r\n // TODO(jelbourn): deprecate `align` in favor of `actionPosition` or `actionAlignment`\r\n // as to not conflict with the native `align` attribute.\r\n\r\n /** Position of the actions inside the card. */\r\n @Input() align: 'start' | 'end' = 'start';\r\n\r\n // TODO(jelbourn): support `.mdc-card__actions--full-bleed`.\r\n\r\n // TODO(jelbourn): support `.mdc-card__action-buttons` and `.mdc-card__action-icons`.\r\n\r\n // TODO(jelbourn): figure out how to use `.mdc-card__action`, `.mdc-card__action--button`, and\r\n // `mdc-card__action--icon`. They're used primarily for positioning, which we might be able to\r\n // do implicitly.\r\n}\r\n\r\n/**\r\n * Header region of a card, intended for use within ``. This header captures\r\n * a card title, subtitle, and avatar. This component is an optional convenience for use with\r\n * other convenience elements, such as ``; any custom header block element may be\r\n * used in its place.\r\n *\r\n * MatCardHeader provides no behaviors, instead serving as a purely visual treatment.\r\n */\r\n@Component({\r\n selector: 'mat-card-header',\r\n templateUrl: 'card-header.html',\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n host: {'class': 'mat-mdc-card-header'},\r\n})\r\nexport class MatCardHeader {}\r\n\r\n/**\r\n * Footer area a card, intended for use within ``.\r\n * This component is an optional convenience for use with other convenience elements, such as\r\n * ``; any custom footer block element may be used in its place.\r\n *\r\n * MatCardFooter provides no behaviors, instead serving as a purely visual treatment.\r\n */\r\n@Directive({\r\n selector: 'mat-card-footer',\r\n host: {'class': 'mat-mdc-card-footer'},\r\n})\r\nexport class MatCardFooter {}\r\n\r\n// TODO(jelbourn): deprecate the \"image\" selectors to replace with \"media\".\r\n\r\n// TODO(jelbourn): support `.mdc-card__media-content`.\r\n\r\n/**\r\n * Primary image content for a card, intended for use within ``. Can be applied to\r\n * any media element, such as `` or ``.\r\n *\r\n * This component is an optional convenience for use with other convenience elements, such as\r\n * ``; any custom media element may be used in its place.\r\n *\r\n * MatCardImage provides no behaviors, instead serving as a purely visual treatment.\r\n */\r\n@Directive({\r\n selector: '[mat-card-image], [matCardImage]',\r\n host: {'class': 'mat-mdc-card-image mdc-card__media'},\r\n})\r\nexport class MatCardImage {\r\n // TODO(jelbourn): support `.mdc-card__media--square` and `.mdc-card__media--16-9`.\r\n}\r\n\r\n/** Same as `MatCardImage`, but small. */\r\n@Directive({\r\n selector: '[mat-card-sm-image], [matCardImageSmall]',\r\n host: {'class': 'mat-mdc-card-sm-image mdc-card__media'},\r\n})\r\nexport class MatCardSmImage {}\r\n\r\n/** Same as `MatCardImage`, but medium. */\r\n@Directive({\r\n selector: '[mat-card-md-image], [matCardImageMedium]',\r\n host: {'class': 'mat-mdc-card-md-image mdc-card__media'},\r\n})\r\nexport class MatCardMdImage {}\r\n\r\n/** Same as `MatCardImage`, but large. */\r\n@Directive({\r\n selector: '[mat-card-lg-image], [matCardImageLarge]',\r\n host: {'class': 'mat-mdc-card-lg-image mdc-card__media'},\r\n})\r\nexport class MatCardLgImage {}\r\n\r\n/** Same as `MatCardImage`, but extra-large. */\r\n@Directive({\r\n selector: '[mat-card-xl-image], [matCardImageXLarge]',\r\n host: {'class': 'mat-mdc-card-xl-image mdc-card__media'},\r\n})\r\nexport class MatCardXlImage {}\r\n\r\n/**\r\n * Avatar image content for a card, intended for use within ``. Can be applied to\r\n * any media element, such as `` or ``.\r\n *\r\n * This component is an optional convenience for use with other convenience elements, such as\r\n * ``; any custom media element may be used in its place.\r\n *\r\n * MatCardAvatar provides no behaviors, instead serving as a purely visual treatment.\r\n */\r\n@Directive({\r\n selector: '[mat-card-avatar], [matCardAvatar]',\r\n host: {'class': 'mat-mdc-card-avatar'},\r\n})\r\nexport class MatCardAvatar {}\r\n", + "assetsDirs": [], + "styleUrlsData": "", + "stylesData": "", + "readme": "

<mat-card> is a content container for text, photos, and actions in the context of a single subject.

\n\n\n

Basic card sections

\n

The most basic card needs only an <mat-card> element with some content. However, Angular Material\nprovides a number of preset sections that you can use inside a <mat-card>:

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
ElementDescription
<mat-card-header>Section anchored to the top of the card (adds padding)
<mat-card-content>Primary card content (adds padding)
<img mat-card-image>Card image. Stretches the image to the container width
<mat-card-actions>Container for buttons at the bottom of the card (adds padding)
<mat-card-footer>Section anchored to the bottom of the card
\n

These elements primary serve as pre-styled content containers without any additional APIs. \nHowever, the align property on <mat-card-actions> can be used to position the actions at the \n'start' or 'end' of the container.

\n

Card padding

\n

The <mat-card> element itself does not add any padding around its content. This allows developers\nto customize the padding to their liking by applying padding to the elements they put in the card.

\n

In many cases developers may just want the standard padding specified in the Material Design spec.\nIn this case, the <mat-card-header>, <mat-card-content>, and <mat-card-footer> sections can be\nused.

\n
    \n
  • <mat-card-content> adds standard padding along its sides, as well as along the top if it is the\nfirst element in the <mat-card>, and along the bottom if it is the last element in the\n<mat-card>.
  • \n
  • <mat-card-header> adds standard padding along its sides and top.
  • \n
  • <mat-card-actions> adds padding appropriate for the action buttons at the bottom of a card.
  • \n
\n

Card headers

\n

A <mat-card-header> can contain any content, but there are several predefined elements\nthat can be used to create a rich header to a card. These include:

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
ElementDescription
<mat-card-title>A title within the header
<mat-card-subtitle>A subtitle within the header
<img mat-card-avatar>An image used as an avatar within the header
\n

In addition to using <mat-card-title> and <mat-card-subtitle> directly within the\n<mat-card-header>, they can be further nested inside a <mat-card-title-group> in order arrange\nthem with a (non-avatar) image.

\n

Title groups

\n

<mat-card-title-group> can be used to combine a title, subtitle, and image into a single section.\nThis element can contain:

\n
    \n
  • <mat-card-title>
  • \n
  • <mat-card-subtitle>
  • \n
  • One of:
      \n
    • <img mat-card-sm-image>
    • \n
    • <img mat-card-md-image>
    • \n
    • <img mat-card-lg-image>
    • \n
    \n
  • \n
\n

Accessibility

\n

Cards serve a wide variety of scenarios and may contain many different types of content.\nDue to this flexible nature, the appropriate accessibility treatment depends on how you use\n<mat-card>.

\n

Group, region, and landmarks

\n

There are several ARIA roles that communicate that a portion of the UI represents some semantically\nmeaningful whole. Depending on what the content of the card means to your application, you can apply\none of role="group", role="region", or\none of the landmark roles to the <mat-card> element.

\n

You do not need to apply a role when using a card as a purely decorative container that does not\nconvey a meaningful grouping of related content for a single subject. In these cases, the content\nof the card should follow standard practices for document content.

\n

Focus

\n

Depending on how cards are used, it may be appropriate to apply a tabindex to the <mat-card>\nelement.

\n
    \n
  • If cards are a primary mechanism through which user interacts with the application, tabindex="0"\nmay be appropriate.
  • \n
  • If attention can be sent to the card, but it's not part of the document flow, tabindex="-1" may\nbe appropriate.
  • \n
  • If the card acts as a purely decorative container, it does not need to be tabbable. In this case,\nthe card content should follow normal best practices for tab order.
  • \n
\n

Always test your application to verify the behavior that works best for your users.

\n", + "templateData": "
\r\n \r\n
\r\n\r\n\r\n" + }, + { + "name": "MatTab", + "id": "component-MatTab-1f3f64efa21e5749aed2699b11c6ce6ddeedcf9d3d7bcdd7aa8aaab2cc52004f74a0a32dfddd41f1394b6c0e17cefe461b30789ec7e249a0b7d36468efec9f2b", + "file": "ui/src/components/tabs/tab.ts", + "changeDetection": "ChangeDetectionStrategy.Default", + "encapsulation": [ + "ViewEncapsulation.None" + ], + "entryComponents": [], + "exportAs": "MatTab", + "inputs": [ + "disabled" + ], + "outputs": [], + "providers": [ { - "name": "_disabled", - "defaultValue": "false", - "deprecated": false, - "deprecationMessage": "", - "type": "", - "optional": false, - "description": "", - "line": 189, - "modifierKind": [ - 122 - ] - }, + "name": "{ provide: MAT_TAB, useExisting: MatTab }" + } + ], + "selector": "mat-tab", + "styleUrls": [], + "styles": [], + "templateUrl": [ + "tab.html" + ], + "viewProviders": [], + "inputsClass": [ { - "name": "_elementRef", + "name": "aria-label", "deprecated": false, "deprecationMessage": "", - "type": "ElementRef", - "optional": false, - "description": "", - "line": 284, - "modifierKind": [ - 123 - ] + "rawdescription": "\nAria label for the tab.", + "description": "

Aria label for the tab.

\n", + "line": 93, + "type": "string", + "decorators": [] }, { - "name": "_id", + "name": "aria-labelledby", "deprecated": false, "deprecationMessage": "", + "rawdescription": "\n\nReference to the element that the tab is labelled by.\nWill be cleared if `aria-label` is set at the same time.\n", + "description": "

Reference to the element that the tab is labelled by.\nWill be cleared if aria-label is set at the same time.

\n", + "line": 101, "type": "string", - "optional": false, - "description": "", - "line": 203, - "modifierKind": [ - 122 - ] + "decorators": [] }, { - "name": "_inputValueAccessor", + "name": "bodyClass", "deprecated": false, "deprecationMessage": "", - "type": "literal type", - "optional": false, - "description": "", + "rawdescription": "\n\nClasses to be passed to the tab mat-tab-body container.\nSupports string and string array values, same as `ngClass`.\n", + "description": "

Classes to be passed to the tab mat-tab-body container.\nSupports string and string array values, same as ngClass.

\n", "line": 113, - "modifierKind": [ - 121 - ] - }, - { - "name": "_isNativeSelect", - "defaultValue": "false", - "deprecated": false, - "deprecationMessage": "", - "type": "", - "optional": false, - "description": "

Whether the component is a native html select.

\n", - "line": 135, - "rawdescription": "\nWhether the component is a native html select." - }, - { - "name": "_isServer", - "defaultValue": "false", - "deprecated": false, - "deprecationMessage": "", - "type": "", - "optional": false, - "description": "

Whether the component is being rendered on the server.

\n", - "line": 132, - "rawdescription": "\nWhether the component is being rendered on the server." - }, - { - "name": "_neverEmptyInputTypes", - "defaultValue": "[\r\n 'date',\r\n 'datetime',\r\n 'datetime-local',\r\n 'month',\r\n 'time',\r\n 'week',\r\n ].filter((t) => getSupportedInputTypes().has(t))", - "deprecated": false, - "deprecationMessage": "", - "type": "", - "optional": false, - "description": "", - "line": 274, - "modifierKind": [ - 122 - ] - }, - { - "name": "_parentForm", - "deprecated": false, - "deprecationMessage": "", - "type": "NgForm", - "optional": false, - "description": "", - "line": 298, - "modifierKind": [ - 123 - ] - }, - { - "name": "_parentFormGroup", - "deprecated": false, - "deprecationMessage": "", - "type": "FormGroupDirective", - "optional": false, - "description": "", - "line": 299, - "modifierKind": [ - 123 - ] + "type": "string | string[]", + "decorators": [] }, { - "name": "_previousNativeValue", + "name": "color", + "defaultValue": "'primary'", "deprecated": false, "deprecationMessage": "", - "type": "any", - "optional": false, - "description": "", - "line": 112, - "modifierKind": [ - 122 - ] + "line": 95, + "type": "string", + "decorators": [] }, { - "name": "_readonly", - "defaultValue": "false", + "name": "label", + "defaultValue": "''", "deprecated": false, "deprecationMessage": "", - "type": "", - "optional": false, - "description": "", - "line": 272, - "modifierKind": [ - 121 - ] + "rawdescription": "\nPlain text label for the tab, used when there is no template label.", + "description": "

Plain text label for the tab, used when there is no template label.

\n", + "line": 88, + "type": "string", + "decorators": [] }, { - "name": "_required", - "defaultValue": "false", + "name": "labelClass", "deprecated": false, "deprecationMessage": "", - "type": "", - "optional": false, - "description": "", - "line": 225, - "modifierKind": [ - 122 - ] + "rawdescription": "\n\nClasses to be passed to the tab label inside the mat-tab-header container.\nSupports string and string array values, same as `ngClass`.\n", + "description": "

Classes to be passed to the tab label inside the mat-tab-header container.\nSupports string and string array values, same as ngClass.

\n", + "line": 107, + "type": "string | string[]", + "decorators": [] }, { - "name": "_type", - "defaultValue": "'text'", + "name": "text", + "defaultValue": "''", "deprecated": false, "deprecationMessage": "", + "line": 90, "type": "string", - "optional": false, - "description": "", - "line": 243, - "modifierKind": [ - 122 - ] - }, + "decorators": [] + } + ], + "outputsClass": [], + "propertiesClass": [ { - "name": "_uid", - "defaultValue": "`oui-input-${nextUniqueId++}`", + "name": "_closestTabGroup", "deprecated": false, "deprecationMessage": "", - "type": "", + "type": "any", "optional": false, "description": "", - "line": 111, - "modifierKind": [ - 122 - ] - }, - { - "name": "autofilled", - "defaultValue": "false", - "deprecated": false, - "deprecationMessage": "", - "type": "", - "optional": false, - "description": "

Implemented as part of OuiFormFieldControl.

\n", - "line": 165, - "rawdescription": "\n\nImplemented as part of OuiFormFieldControl.\n\n", - "jsdoctags": [ + "line": 146, + "decorators": [ { - "pos": 4838, - "end": 4856, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 325, - "tagName": { - "pos": 4839, - "end": 4851, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 79, - "escapedText": "docs-private" - }, - "comment": "" - } - ] - }, - { - "name": "color", - "deprecated": false, - "deprecationMessage": "", - "type": "any", - "optional": false, - "description": "

Implemented as part of CanColor.

\n", - "line": 118, - "rawdescription": "\n\nImplemented as part of CanColor.\n" - }, - { - "name": "controlType", - "defaultValue": "'oui-input'", - "deprecated": false, - "deprecationMessage": "", - "type": "string", - "optional": false, - "description": "

Implemented as part of OuiFormFieldControl.

\n", - "line": 158, - "rawdescription": "\n\nImplemented as part of OuiFormFieldControl.\n\n", - "jsdoctags": [ + "name": "Inject", + "stringifiedArguments": "MAT_TAB_GROUP" + }, { - "pos": 4638, - "end": 4656, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 325, - "tagName": { - "pos": 4639, - "end": 4651, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 79, - "escapedText": "docs-private" - }, - "comment": "" + "name": "Optional", + "stringifiedArguments": "" } + ], + "modifierKind": [ + 123 ] }, { - "name": "errorState", - "defaultValue": "false", + "name": "_contentPortal", + "defaultValue": "null", "deprecated": false, "deprecationMessage": "", - "type": "boolean", + "type": "TemplatePortal | null", "optional": false, - "description": "

Implemented as part of CanUpdateErrorState.

\n", - "line": 126, - "rawdescription": "\n\nImplemented as part of CanUpdateErrorState.\n\n", - "jsdoctags": [ - { - "pos": 3818, - "end": 3836, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 325, - "tagName": { - "pos": 3819, - "end": 3831, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 79, - "escapedText": "docs-private" - }, - "comment": "" - } + "description": "

Portal that will be the hosted content of the tab

\n", + "line": 116, + "rawdescription": "\nPortal that will be the hosted content of the tab", + "modifierKind": [ + 121 ] }, { - "name": "focused", - "defaultValue": "false", + "name": "_explicitContent", + "defaultValue": "undefined!", "deprecated": false, "deprecationMessage": "", - "type": "boolean", + "type": "TemplateRef", "optional": false, - "description": "

Implemented as part of OuiFormFieldControl.

\n", - "line": 143, - "rawdescription": "\n\nImplemented as part of OuiFormFieldControl.\n\n", - "jsdoctags": [ + "description": "

Template provided in the tab content that will be used if present, used to enable lazy-loading

\n", + "line": 82, + "rawdescription": "\n\nTemplate provided in the tab content that will be used if present, used to enable lazy-loading\n", + "decorators": [ { - "pos": 4292, - "end": 4310, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 325, - "tagName": { - "pos": 4293, - "end": 4305, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 79, - "escapedText": "docs-private" - }, - "comment": "" + "name": "ContentChild", + "stringifiedArguments": "MatTabContent, {read: TemplateRef, static: true}" } + ], + "modifierKind": [ + 121 ] }, { - "name": "ngControl", + "name": "_implicitContent", "deprecated": false, "deprecationMessage": "", - "type": "NgControl", + "type": "TemplateRef", "optional": false, - "description": "", - "line": 291, - "rawdescription": "", + "description": "

Template inside the MatTab view that contains an <ng-content>.

\n", + "line": 85, + "rawdescription": "\nTemplate inside the MatTab view that contains an ``.", "decorators": [ { - "name": "Optional", - "stringifiedArguments": "" - }, - { - "name": "Self", - "stringifiedArguments": "" - } - ], - "modifierKind": [ - 123 - ], - "jsdoctags": [ - { - "pos": 7932, - "end": 7946, - "flags": 4194304, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 325, - "tagName": { - "pos": 7933, - "end": 7945, - "flags": 4194304, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 79, - "escapedText": "docs-private" - }, - "comment": "" + "name": "ViewChild", + "stringifiedArguments": "TemplateRef, {static: true}" } ] }, { - "name": "stateChanges", + "name": "_stateChanges", "defaultValue": "new Subject()", "deprecated": false, "deprecationMessage": "", - "type": "Subject", + "type": "", "optional": false, - "description": "

Implemented as part of OuiFormFieldControl.

\n", - "line": 150, - "rawdescription": "\n\nImplemented as part of OuiFormFieldControl.\n\n", + "description": "

Emits whenever the internal state of the tab changes.

\n", + "line": 124, + "rawdescription": "\nEmits whenever the internal state of the tab changes.", "modifierKind": [ 144 - ], - "jsdoctags": [ - { - "pos": 4483, - "end": 4501, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 325, - "tagName": { - "pos": 4484, - "end": 4496, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 79, - "escapedText": "docs-private" - }, - "comment": "" - } - ] - } - ], - "methodsClass": [ - { - "name": "_dirtyCheckNativeValue", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 396, - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nDoes some manual dirty checking on the native input `value` property.", - "description": "

Does some manual dirty checking on the native input value property.

\n", - "modifierKind": [ - 122 ] }, { - "name": "_isBadInput", - "args": [], - "optional": false, - "returnType": "any", - "typeParameters": [], - "line": 418, + "name": "_tab1", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nChecks whether the input is invalid based on the native validation.", - "description": "

Checks whether the input is invalid based on the native validation.

\n", - "modifierKind": [ - 122 - ] - }, - { - "name": "_isNeverEmpty", - "args": [], + "type": "ElementRef", "optional": false, - "returnType": "boolean", - "typeParameters": [], - "line": 413, - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nChecks whether the input type is one of the types that are never empty.", - "description": "

Checks whether the input type is one of the types that are never empty.

\n", - "modifierKind": [ - 122 + "description": "", + "line": 143, + "decorators": [ + { + "name": "ViewChild", + "stringifiedArguments": "'tab1'" + } ] }, { - "name": "_isTextarea", - "args": [], - "optional": false, - "returnType": "boolean", - "typeParameters": [], - "line": 426, + "name": "_templateLabel", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nDetermines if the component host is a textarea.", - "description": "

Determines if the component host is a textarea.

\n", - "modifierKind": [ - 122 - ] - }, - { - "name": "_onInput", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 359, - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "_validateType", - "args": [], + "type": "MatTabLabel", "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 406, - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nMake sure the input is a supported type.", - "description": "

Make sure the input is a supported type.

\n", + "description": "

Content for the tab label given by <ng-template mat-tab-label>.

\n", + "line": 66, + "rawdescription": "\nContent for the tab label given by ``.", "modifierKind": [ - 122 + 121 ] }, { - "name": "addClass", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 384, + "name": "disabled", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nAdding class dynamically based of type", - "description": "

Adding class dynamically based of type

\n", - "modifierKind": [ - 122 - ] + "type": "any", + "optional": false, + "description": "", + "line": 67 }, { - "name": "focus", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 355, + "name": "isActive", + "defaultValue": "false", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nFocuses the input.", - "description": "

Focuses the input.

\n" + "type": "", + "optional": false, + "description": "

Whether the tab is currently active.

\n", + "line": 141, + "rawdescription": "\n\nWhether the tab is currently active.\n" }, { - "name": "getHostElement", - "args": [], - "optional": false, - "returnType": "any", - "typeParameters": [], - "line": 370, + "name": "origin", + "defaultValue": "null", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nGetting native host element", - "description": "

Getting native host element

\n" + "type": "number | null", + "optional": false, + "description": "

The initial relatively index origin of the tab if it was created and selected after there\nwas already a selected tab. Provides context of what position the tab should originate from.

\n", + "line": 136, + "rawdescription": "\n\nThe initial relatively index origin of the tab if it was created and selected after there\nwas already a selected tab. Provides context of what position the tab should originate from.\n" }, { - "name": "hasHostAttributes", + "name": "position", + "defaultValue": "null", + "deprecated": false, + "deprecationMessage": "", + "type": "number | null", + "optional": false, + "description": "

The relatively indexed position where 0 represents the center, negative is left, and positive\nrepresents the right.

\n", + "line": 130, + "rawdescription": "\n\nThe relatively indexed position where 0 represents the center, negative is left, and positive\nrepresents the right.\n" + } + ], + "methodsClass": [ + { + "name": "_setTemplateLabelInput", "args": [ { - "name": "attributes", - "type": "string[]", + "name": "value", + "type": "MatTabLabel | undefined", "deprecated": false, - "deprecationMessage": "", - "dotDotDotToken": true + "deprecationMessage": "" } ], "optional": false, - "returnType": "any", + "returnType": "void", "typeParameters": [], - "line": 375, + "line": 192, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nGets whether the button has one of the given attributes.", - "description": "

Gets whether the button has one of the given attributes.

\n", + "rawdescription": "\n\nThis has been extracted to a util because of TS 4 and VE.\nView Engine doesn't support property rename inheritance.\nTS 4.0 doesn't allow properties to override accessors or vice-versa.\n", + "description": "

This has been extracted to a util because of TS 4 and VE.\nView Engine doesn't support property rename inheritance.\nTS 4.0 doesn't allow properties to override accessors or vice-versa.

\n", + "modifierKind": [ + 121 + ], "jsdoctags": [ { - "name": "attributes", - "type": "string[]", + "name": "value", + "type": "MatTabLabel | undefined", "deprecated": false, "deprecationMessage": "", - "dotDotDotToken": true, "tagName": { "text": "param" } @@ -14842,54 +24515,31 @@ ] }, { - "name": "ngOnChanges", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 342, - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "ngOnDestroy", + "name": "addThemeColor", "args": [], "optional": false, "returnType": "void", "typeParameters": [], - "line": 346, + "line": 162, "deprecated": false, "deprecationMessage": "" }, { - "name": "ngOnInit", + "name": "ngAfterViewInit", "args": [], "optional": false, "returnType": "void", "typeParameters": [], - "line": 331, + "line": 170, "deprecated": false, "deprecationMessage": "" }, { - "name": "onContainerClick", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 485, - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\n\nImplemented as part of OuiFormFieldControl.\n\n", - "description": "

Implemented as part of OuiFormFieldControl.

\n", - "jsdoctags": [] - }, - { - "name": "setDescribedByIds", + "name": "ngOnChanges", "args": [ { - "name": "ids", - "type": "string[]", + "name": "changes", + "type": "SimpleChanges", "deprecated": false, "deprecationMessage": "" } @@ -14897,15 +24547,13 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 476, + "line": 153, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\n\nImplemented as part of OuiFormFieldControl.\n\n", - "description": "

Implemented as part of OuiFormFieldControl.

\n", "jsdoctags": [ { - "name": "ids", - "type": "string[]", + "name": "changes", + "type": "SimpleChanges", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -14915,24 +24563,37 @@ ] }, { - "name": "updateErrorState", + "name": "ngOnDestroy", "args": [], "optional": false, "returnType": "void", "typeParameters": [], - "line": 381, + "line": 174, + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "ngOnInit", + "args": [], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 178, "deprecated": false, "deprecationMessage": "" } ], - "extends": "_OuiInputMixinBase", - "implements": [ - "OuiFormFieldControl", - "OnChanges", - "OnDestroy", - "OnInit", - "CanColor" - ], + "deprecated": false, + "deprecationMessage": "", + "hostBindings": [], + "hostListeners": [], + "description": "", + "rawdescription": "\n", + "type": "component", + "sourceCode": "import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n ContentChild,\r\n ElementRef,\r\n Inject,\r\n InjectionToken,\r\n Input,\r\n OnChanges,\r\n OnDestroy,\r\n OnInit,\r\n Optional,\r\n SimpleChanges,\r\n TemplateRef,\r\n ViewChild,\r\n ViewContainerRef,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\nimport {MatTabContent} from './tab-content';\r\nimport {MAT_TAB, MatTabLabel} from './tab-label';\r\nimport {CanDisable, mixinColor} from '../core';\r\nimport {TemplatePortal} from '@angular/cdk/portal';\r\nimport {Subject} from 'rxjs';\r\n\r\n\r\nexport class OuiTabsBase {\r\n constructor(public _elementRef: ElementRef) {}\r\n}\r\n// Boilerplate for applying mixins to MatTab.\r\n/** @docs-private */\r\nconst _MatTabMixinBase: typeof OuiTabsBase = mixinColor(OuiTabsBase);\r\n\r\n/**\r\n * Used to provide a tab group to a tab without causing a circular dependency.\r\n * @docs-private\r\n */\r\nexport const MAT_TAB_GROUP = new InjectionToken('MAT_TAB_GROUP');\r\n\r\n/** Default color palette for the tab */\r\nconst DEFAULT_COLOR = 'primary';\r\n\r\n@Component({\r\n selector: 'mat-tab',\r\n\r\n // Note that usually we'd go through a bit more trouble and set up another class so that\r\n // the inlined template of `MatTab` isn't duplicated, however the template is small enough\r\n // that creating the extra class will generate more code than just duplicating the template.\r\n templateUrl: 'tab.html',\r\n inputs: ['disabled'],\r\n // tslint:disable-next-line:validate-decorators\r\n changeDetection: ChangeDetectionStrategy.Default,\r\n encapsulation: ViewEncapsulation.None,\r\n exportAs: 'MatTab',\r\n providers: [{provide: MAT_TAB, useExisting: MatTab}],\r\n})\r\nexport class MatTab extends _MatTabMixinBase implements CanDisable, OnInit, OnChanges, OnDestroy {\r\n /** Content for the tab label given by ``. */\r\n private _templateLabel: MatTabLabel;\r\n disabled: any;\r\n @ContentChild(MatTabLabel)\r\n get templateLabel(): MatTabLabel {\r\n return this._templateLabel;\r\n }\r\n set templateLabel(value: MatTabLabel) {\r\n console.log(value)\r\n this._setTemplateLabelInput(value);\r\n }\r\n\r\n /**\r\n * Template provided in the tab content that will be used if present, used to enable lazy-loading\r\n */\r\n @ContentChild(MatTabContent, {read: TemplateRef, static: true})\r\n // We need an initializer here to avoid a TS error. The value will be set in `ngAfterViewInit`.\r\n private _explicitContent: TemplateRef = undefined!;\r\n\r\n /** Template inside the MatTab view that contains an ``. */\r\n @ViewChild(TemplateRef, {static: true}) _implicitContent: TemplateRef;\r\n\r\n /** Plain text label for the tab, used when there is no template label. */\r\n @Input('label') textLabel: string = '';\r\n\r\n @Input('text') givenText: string = '';\r\n\r\n /** Aria label for the tab. */\r\n @Input('aria-label') ariaLabel: string;\r\n\r\n @Input() color = 'primary';\r\n\r\n /**\r\n * Reference to the element that the tab is labelled by.\r\n * Will be cleared if `aria-label` is set at the same time.\r\n */\r\n @Input('aria-labelledby') ariaLabelledby: string;\r\n\r\n /**\r\n * Classes to be passed to the tab label inside the mat-tab-header container.\r\n * Supports string and string array values, same as `ngClass`.\r\n */\r\n @Input() labelClass: string | string[];\r\n\r\n /**\r\n * Classes to be passed to the tab mat-tab-body container.\r\n * Supports string and string array values, same as `ngClass`.\r\n */\r\n @Input() bodyClass: string | string[];\r\n\r\n /** Portal that will be the hosted content of the tab */\r\n private _contentPortal: TemplatePortal | null = null;\r\n\r\n /** @docs-private */\r\n get content(): TemplatePortal | null {\r\n return this._contentPortal;\r\n }\r\n\r\n /** Emits whenever the internal state of the tab changes. */\r\n readonly _stateChanges = new Subject();\r\n\r\n /**\r\n * The relatively indexed position where 0 represents the center, negative is left, and positive\r\n * represents the right.\r\n */\r\n position: number | null = null;\r\n\r\n /**\r\n * The initial relatively index origin of the tab if it was created and selected after there\r\n * was already a selected tab. Provides context of what position the tab should originate from.\r\n */\r\n origin: number | null = null;\r\n\r\n /**\r\n * Whether the tab is currently active.\r\n */\r\n isActive = false;\r\n\r\n @ViewChild('tab1') _tab1: ElementRef;\r\n constructor(\r\n private _viewContainerRef: ViewContainerRef,\r\n @Inject(MAT_TAB_GROUP) @Optional() public _closestTabGroup: any,\r\n _elementRef: ElementRef\r\n ) {\r\n super(_elementRef);\r\n this.addThemeColor();\r\n }\r\n\r\n ngOnChanges(changes: SimpleChanges): void {\r\n if (changes.hasOwnProperty('textLabel') || changes.hasOwnProperty('disabled')) {\r\n this._stateChanges.next();\r\n }\r\n if (changes.hasOwnProperty('_tab2') || changes.hasOwnProperty('disabled')) {\r\n this._stateChanges.next();\r\n }\r\n }\r\n\r\n addThemeColor() {\r\n console.log('sssssss123', this.color)\r\n if (!this.color) {\r\n this.color = DEFAULT_COLOR;\r\n }\r\n console.log('after', this.color)\r\n }\r\n\r\n ngAfterViewInit() {\r\n console.log(\"this._tab1.nativeElement.innerText;\", this._tab1);\r\n }\r\n\r\n ngOnDestroy(): void {\r\n this._stateChanges.complete();\r\n }\r\n\r\n ngOnInit(): void {\r\n this._contentPortal = new TemplatePortal(\r\n this._explicitContent || this._implicitContent,\r\n this._viewContainerRef, this._tab1\r\n );\r\n console.log('_tab1', this._contentPortal);\r\n }\r\n\r\n /**\r\n * This has been extracted to a util because of TS 4 and VE.\r\n * View Engine doesn't support property rename inheritance.\r\n * TS 4.0 doesn't allow properties to override accessors or vice-versa.\r\n * @docs-private\r\n */\r\n private _setTemplateLabelInput(value: MatTabLabel | undefined) {\r\n // Only update the label if the query managed to find one. This works around an issue where a\r\n // user may have manually set `templateLabel` during creation mode, which would then get\r\n // clobbered by `undefined` when the query resolves. Also note that we check that the closest\r\n // tab matches the current one so that we don't pick up labels from nested tabs.\r\n if (value && value._closestTab === this) {\r\n this._templateLabel = value;\r\n }\r\n }\r\n}\r\n", + "assetsDirs": [], + "styleUrlsData": "", + "stylesData": "", "constructorObj": { "name": "constructor", "description": "", @@ -14940,422 +24601,84 @@ "deprecationMessage": "", "args": [ { - "name": "_elementRef", - "type": "ElementRef", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "_platform", - "type": "Platform", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "ngControl", - "type": "NgControl", + "name": "_viewContainerRef", + "type": "ViewContainerRef", "deprecated": false, "deprecationMessage": "" }, { - "name": "inputValueAccessor", + "name": "_closestTabGroup", "type": "any", "deprecated": false, "deprecationMessage": "" }, { - "name": "_defaultErrorStateMatcher", - "type": "ErrorStateMatcher", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "_autofillMonitor", - "type": "AutofillMonitor", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "_parentForm", - "type": "NgForm", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "_parentFormGroup", - "type": "FormGroupDirective", + "name": "_elementRef", + "type": "ElementRef", "deprecated": false, "deprecationMessage": "" } ], - "line": 281, + "line": 143, "jsdoctags": [ { - "name": "_elementRef", - "type": "ElementRef", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "_platform", - "type": "Platform", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "ngControl", - "type": "NgControl", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "inputValueAccessor", - "type": "any", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "_defaultErrorStateMatcher", - "type": "ErrorStateMatcher", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "_autofillMonitor", - "type": "AutofillMonitor", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "_parentForm", - "type": "NgForm", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "_parentFormGroup", - "type": "FormGroupDirective", + "name": "_viewContainerRef", + "type": "ViewContainerRef", "deprecated": false, "deprecationMessage": "", "tagName": { "text": "param" - } - } - ] - }, - "accessors": { - "disabled": { - "name": "disabled", - "setSignature": { - "name": "disabled", - "type": "void", - "deprecated": false, - "deprecationMessage": "", - "args": [ - { - "name": "value", - "type": "boolean", - "deprecated": false, - "deprecationMessage": "" - } - ], - "returnType": "void", - "line": 179, - "jsdoctags": [ - { - "name": "value", - "type": "boolean", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } - ] - }, - "getSignature": { - "name": "disabled", - "type": "boolean", - "returnType": "boolean", - "line": 173, - "rawdescription": "\n\nImplemented as part of OuiFormFieldControl.\n\n", - "description": "

Implemented as part of OuiFormFieldControl.

\n", - "jsdoctags": [ - { - "pos": 4953, - "end": 4971, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 325, - "tagName": { - "pos": 4954, - "end": 4966, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 79, - "escapedText": "docs-private" - }, - "comment": "" - } - ] - } - }, - "id": { - "name": "id", - "setSignature": { - "name": "id", - "type": "void", - "deprecated": false, - "deprecationMessage": "", - "args": [ - { - "name": "value", - "type": "string", - "deprecated": false, - "deprecationMessage": "" - } - ], - "returnType": "void", - "line": 200, - "jsdoctags": [ - { - "name": "value", - "type": "string", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } - ] - }, - "getSignature": { - "name": "id", - "type": "string", - "returnType": "string", - "line": 197, - "rawdescription": "\n\nImplemented as part of OuiFormFieldControl.\n\n", - "description": "

Implemented as part of OuiFormFieldControl.

\n", - "jsdoctags": [ - { - "pos": 5603, - "end": 5621, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 325, - "tagName": { - "pos": 5604, - "end": 5616, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 79, - "escapedText": "docs-private" - }, - "comment": "" - } - ] - } - }, - "required": { - "name": "required", - "setSignature": { - "name": "required", - "type": "void", - "deprecated": false, - "deprecationMessage": "", - "args": [ - { - "name": "value", - "type": "boolean", - "deprecated": false, - "deprecationMessage": "" - } - ], - "returnType": "void", - "line": 222, - "jsdoctags": [ - { - "name": "value", - "type": "boolean", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } - ] - }, - "getSignature": { - "name": "required", - "type": "boolean", - "returnType": "boolean", - "line": 219, - "rawdescription": "\n\nImplemented as part of OuiFormFieldControl.\n\n", - "description": "

Implemented as part of OuiFormFieldControl.

\n", - "jsdoctags": [ - { - "pos": 5978, - "end": 5996, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 325, - "tagName": { - "pos": 5979, - "end": 5991, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 79, - "escapedText": "docs-private" - }, - "comment": "" - } - ] - } - }, - "type": { - "name": "type", - "setSignature": { - "name": "type", - "type": "void", - "deprecated": false, - "deprecationMessage": "", - "args": [ - { - "name": "value", - "type": "string", - "deprecated": false, - "deprecationMessage": "" - } - ], - "returnType": "void", - "line": 232, - "jsdoctags": [ - { - "name": "value", - "type": "string", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } - ] - }, - "getSignature": { - "name": "type", - "type": "string", - "returnType": "string", - "line": 229, - "rawdescription": "\nInput type of the element.", - "description": "

Input type of the element.

\n" - } - }, - "value": { - "name": "value", - "setSignature": { - "name": "value", - "type": "void", - "deprecated": false, - "deprecationMessage": "", - "args": [ - { - "name": "value", - "type": "string", - "deprecated": false, - "deprecationMessage": "" - } - ], - "returnType": "void", - "line": 257, - "jsdoctags": [ - { - "name": "value", - "type": "string", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } - ] - }, - "getSignature": { - "name": "value", - "type": "string", - "returnType": "string", - "line": 254, - "rawdescription": "\n\nImplemented as part of OuiFormFieldControl.\n\n", - "description": "

Implemented as part of OuiFormFieldControl.

\n", - "jsdoctags": [ - { - "pos": 7069, - "end": 7087, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 325, - "tagName": { - "pos": 7070, - "end": 7082, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 79, - "escapedText": "docs-private" - }, - "comment": "" - } - ] + } + }, + { + "name": "_closestTabGroup", + "type": "any", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "_elementRef", + "type": "ElementRef", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } } - }, - "readonly": { - "name": "readonly", + ] + }, + "extends": "_MatTabMixinBase", + "implements": [ + "CanDisable", + "OnInit", + "OnChanges", + "OnDestroy" + ], + "accessors": { + "templateLabel": { + "name": "templateLabel", "setSignature": { - "name": "readonly", + "name": "templateLabel", "type": "void", "deprecated": false, "deprecationMessage": "", "args": [ { "name": "value", - "type": "boolean", + "type": "MatTabLabel", "deprecated": false, "deprecationMessage": "" } ], "returnType": "void", - "line": 269, + "line": 72, "jsdoctags": [ { "name": "value", - "type": "boolean", + "type": "MatTabLabel", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -15365,65 +24688,32 @@ ] }, "getSignature": { - "name": "readonly", - "type": "boolean", - "returnType": "boolean", - "line": 266, - "rawdescription": "\nWhether the element is readonly.", - "description": "

Whether the element is readonly.

\n" - } - }, - "empty": { - "name": "empty", - "getSignature": { - "name": "empty", - "type": "boolean", - "returnType": "boolean", - "line": 435, - "rawdescription": "\n\nImplemented as part of OuiFormFieldControl.\n\n", - "description": "

Implemented as part of OuiFormFieldControl.

\n", - "jsdoctags": [ - { - "pos": 12344, - "end": 12362, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 325, - "tagName": { - "pos": 12345, - "end": 12357, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 79, - "escapedText": "docs-private" - }, - "comment": "" - } - ] + "name": "templateLabel", + "type": "", + "returnType": "MatTabLabel", + "line": 69 } }, - "shouldLabelFloat": { - "name": "shouldLabelFloat", + "content": { + "name": "content", "getSignature": { - "name": "shouldLabelFloat", - "type": "boolean", - "returnType": "boolean", - "line": 449, - "rawdescription": "\n\nImplemented as part of OuiFormFieldControl.\n\n", - "description": "

Implemented as part of OuiFormFieldControl.

\n", + "name": "content", + "type": "", + "returnType": "TemplatePortal | null", + "line": 119, + "rawdescription": "", + "description": "", "jsdoctags": [ { - "pos": 12623, - "end": 12641, + "pos": 3767, + "end": 3781, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, "kind": 325, "tagName": { - "pos": 12624, - "end": 12636, + "pos": 3768, + "end": 3780, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -15435,88 +24725,224 @@ ] } } - } + }, + "templateData": "\r\n\r\n" }, { - "name": "OuiMenuContent", - "id": "directive-OuiMenuContent-b51472f4f7d70e51d9081e8d515428cad8f11c0d06aa6930604d4768ba5a9c6d2b8b98523f53364727d9a46d71d74e07ae8b26bffd7cfe841c2770bbf9a47662", - "file": "ui/src/components/menu/menu-content.ts", - "type": "directive", - "description": "

Menu content that will be rendered lazily once the menu is opened.

\n", - "rawdescription": "\n\nMenu content that will be rendered lazily once the menu is opened.\n", - "sourceCode": "import {\r\n Directive,\r\n TemplateRef,\r\n ComponentFactoryResolver,\r\n ApplicationRef,\r\n Injector,\r\n ViewContainerRef,\r\n Inject,\r\n OnDestroy,\r\n} from '@angular/core';\r\nimport { TemplatePortal, DomPortalOutlet } from '@angular/cdk/portal';\r\nimport { DOCUMENT } from '@angular/common';\r\nimport { Subject } from 'rxjs';\r\n\r\n/**\r\n * Menu content that will be rendered lazily once the menu is opened.\r\n */\r\n@Directive({\r\n selector: 'ng-template[ouiMenuContent]',\r\n})\r\nexport class OuiMenuContent implements OnDestroy {\r\n private _portal: TemplatePortal;\r\n private _outlet: DomPortalOutlet;\r\n\r\n /** Emits when the menu content has been attached. */\r\n _attached = new Subject();\r\n\r\n constructor(\r\n private _template: TemplateRef,\r\n private _componentFactoryResolver: ComponentFactoryResolver,\r\n private _appRef: ApplicationRef,\r\n private _injector: Injector,\r\n private _viewContainerRef: ViewContainerRef,\r\n @Inject(DOCUMENT) private _document: Document\r\n ) {}\r\n\r\n /**\r\n * Attaches the content with a particular context.\r\n */\r\n attach(context: any = {}) {\r\n if (!this._portal) {\r\n this._portal = new TemplatePortal(this._template, this._viewContainerRef);\r\n }\r\n\r\n this.detach();\r\n\r\n if (!this._outlet) {\r\n this._outlet = new DomPortalOutlet(\r\n this._document.createElement('div'),\r\n this._componentFactoryResolver,\r\n this._appRef,\r\n this._injector\r\n );\r\n }\r\n\r\n const element: HTMLElement = this._template.elementRef.nativeElement;\r\n\r\n // Because we support opening the same menu from different triggers (which in turn have their\r\n // own `OverlayRef` panel), we have to re-insert the host element every time, otherwise we\r\n // risk it staying attached to a pane that's no longer in the DOM.\r\n element.parentNode!.insertBefore(this._outlet.outletElement, element);\r\n this._portal.attach(this._outlet, context);\r\n this._attached.next();\r\n }\r\n\r\n /**\r\n * Detaches the content.\r\n */\r\n detach() {\r\n if (this._portal.isAttached) {\r\n this._portal.detach();\r\n }\r\n }\r\n\r\n ngOnDestroy() {\r\n if (this._outlet) {\r\n this._outlet.dispose();\r\n }\r\n }\r\n}\r\n", - "selector": "ng-template[ouiMenuContent]", + "name": "MatTabBody", + "id": "component-MatTabBody-9104ddd7815c78bf9ebed92c9c59f0061af631ff42f40c1fbad492b4b5fd3204f3bcb81800b57fb224bf78c5cc90aa743bf68c1967d28cc0a71a22bb4249e55c", + "file": "ui/src/components/tabs/tab-body.ts", + "changeDetection": "ChangeDetectionStrategy.Default", + "encapsulation": [ + "ViewEncapsulation.None" + ], + "entryComponents": [], + "host": {}, + "inputs": [], + "outputs": [], "providers": [], - "inputsClass": [], - "outputsClass": [], - "deprecated": false, - "deprecationMessage": "", - "hostBindings": [], - "hostListeners": [], + "selector": "mat-tab-body", + "styleUrls": [], + "styles": [], + "templateUrl": [ + "tab-body.html" + ], + "viewProviders": [], + "inputsClass": [ + { + "name": "animationDuration", + "defaultValue": "'0'", + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nDuration for the tab's animation.", + "description": "

Duration for the tab's animation.

\n", + "line": 159, + "type": "string", + "decorators": [] + }, + { + "name": "content", + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nThe tab body content to display.", + "description": "

The tab body content to display.

\n", + "line": 151, + "type": "any", + "decorators": [] + }, + { + "name": "origin", + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nPosition that will be used when the tab is immediately becoming visible after creation.", + "description": "

Position that will be used when the tab is immediately becoming visible after creation.

\n", + "line": 154, + "type": "number | null", + "decorators": [] + }, + { + "name": "position", + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nThe shifted index position of the tab body, where zero represents the active center tab.", + "description": "

The shifted index position of the tab body, where zero represents the active center tab.

\n", + "line": 166, + "type": "number", + "decorators": [] + }, + { + "name": "preserveContent", + "defaultValue": "false", + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nWhether the tab's content should be kept in the DOM while it's off-screen.", + "description": "

Whether the tab's content should be kept in the DOM while it's off-screen.

\n", + "line": 162, + "type": "boolean", + "decorators": [] + } + ], + "outputsClass": [ + { + "name": "_afterLeavingCenter", + "defaultValue": "new EventEmitter()", + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nEvent emitted before the centering of the tab begins.", + "description": "

Event emitted before the centering of the tab begins.

\n", + "line": 142, + "type": "EventEmitter" + }, + { + "name": "_beforeCentering", + "defaultValue": "new EventEmitter()", + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nEvent emitted before the centering of the tab begins.", + "description": "

Event emitted before the centering of the tab begins.

\n", + "line": 139, + "type": "EventEmitter" + }, + { + "name": "_onCentered", + "defaultValue": "new EventEmitter(true)", + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nEvent emitted when the tab completes its animation towards the center.", + "description": "

Event emitted when the tab completes its animation towards the center.

\n", + "line": 145, + "type": "EventEmitter" + }, + { + "name": "_onCentering", + "defaultValue": "new EventEmitter()", + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nEvent emitted when the tab begins to animate towards the center as the active tab.", + "description": "

Event emitted when the tab begins to animate towards the center as the active tab.

\n", + "line": 136, + "type": "EventEmitter" + } + ], "propertiesClass": [ { - "name": "_attached", - "defaultValue": "new Subject()", + "name": "_dirChangeSubscription", + "defaultValue": "Subscription.EMPTY", "deprecated": false, "deprecationMessage": "", "type": "", "optional": false, - "description": "

Emits when the menu content has been attached.

\n", - "line": 26, - "rawdescription": "\nEmits when the menu content has been attached." + "description": "

Subscription to the directionality change observable.

\n", + "line": 127, + "rawdescription": "\nSubscription to the directionality change observable.", + "modifierKind": [ + 121 + ] }, { - "name": "_outlet", + "name": "_portalHost", "deprecated": false, "deprecationMessage": "", - "type": "DomPortalOutlet", + "type": "CdkPortalOutlet", "optional": false, - "description": "", - "line": 23, + "description": "

The portal host inside of this container into which the tab body content will be loaded.

\n", + "line": 148, + "rawdescription": "\nThe portal host inside of this container into which the tab body content will be loaded.", + "decorators": [ + { + "name": "ViewChild", + "stringifiedArguments": "CdkPortalOutlet" + } + ] + }, + { + "name": "_position", + "deprecated": false, + "deprecationMessage": "", + "type": "MatTabBodyPositionState", + "optional": false, + "description": "

Tab body position state. Used by the animation trigger for the current state.

\n", + "line": 130, + "rawdescription": "\nTab body position state. Used by the animation trigger for the current state." + }, + { + "name": "_positionIndex", + "deprecated": false, + "deprecationMessage": "", + "type": "number", + "optional": false, + "description": "

Current position of the tab-body in the tab-group. Zero means that the tab is visible.

\n", + "line": 124, + "rawdescription": "\nCurrent position of the tab-body in the tab-group. Zero means that the tab is visible.", "modifierKind": [ 121 ] }, { - "name": "_portal", + "name": "_translateTabComplete", + "defaultValue": "new Subject()", "deprecated": false, "deprecationMessage": "", - "type": "TemplatePortal", + "type": "", "optional": false, - "description": "", - "line": 22, + "description": "

Emits when an animation on the tab is complete.

\n", + "line": 133, + "rawdescription": "\nEmits when an animation on the tab is complete.", "modifierKind": [ - 121 + 144 ] } ], "methodsClass": [ { - "name": "attach", + "name": "_computePositionAnimationState", "args": [ { - "name": "context", - "type": "any", + "name": "dir", + "type": "Direction", "deprecated": false, "deprecationMessage": "", - "defaultValue": "{}" + "defaultValue": "this._getLayoutDirection()" } ], "optional": false, "returnType": "void", "typeParameters": [], - "line": 40, + "line": 240, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\n\nAttaches the content with a particular context.\n", - "description": "

Attaches the content with a particular context.

\n", + "rawdescription": "\nComputes the position state that will be used for the tab-body animation trigger.", + "description": "

Computes the position state that will be used for the tab-body animation trigger.

\n", + "modifierKind": [ + 121 + ], "jsdoctags": [ { - "name": "context", - "type": "any", + "name": "dir", + "type": "Direction", "deprecated": false, "deprecationMessage": "", - "defaultValue": "{}", + "defaultValue": "this._getLayoutDirection()", "tagName": { "text": "param" } @@ -15524,16 +24950,107 @@ ] }, { - "name": "detach", + "name": "_computePositionFromOrigin", + "args": [ + { + "name": "origin", + "type": "number", + "deprecated": false, + "deprecationMessage": "" + } + ], + "optional": false, + "returnType": "MatTabBodyPositionState", + "typeParameters": [], + "line": 254, + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\n\nComputes the position state based on the specified origin position. This is used if the\ntab is becoming visible immediately after creation.\n", + "description": "

Computes the position state based on the specified origin position. This is used if the\ntab is becoming visible immediately after creation.

\n", + "modifierKind": [ + 121 + ], + "jsdoctags": [ + { + "name": "origin", + "type": "number", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + }, + { + "name": "_getLayoutDirection", "args": [], "optional": false, + "returnType": "Direction", + "typeParameters": [], + "line": 228, + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nThe text direction of the containing app.", + "description": "

The text direction of the containing app.

\n" + }, + { + "name": "_isCenterPosition", + "args": [ + { + "name": "position", + "type": "MatTabBodyPositionState | string", + "deprecated": false, + "deprecationMessage": "" + } + ], + "optional": false, + "returnType": "boolean", + "typeParameters": [], + "line": 233, + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nWhether the provided position state is considered center, regardless of origin.", + "description": "

Whether the provided position state is considered center, regardless of origin.

\n", + "jsdoctags": [ + { + "name": "position", + "type": "MatTabBodyPositionState | string", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + }, + { + "name": "_onTranslateTabStarted", + "args": [ + { + "name": "event", + "type": "AnimationEvent", + "deprecated": false, + "deprecationMessage": "" + } + ], + "optional": false, "returnType": "void", "typeParameters": [], - "line": 69, + "line": 219, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\n\nDetaches the content.\n", - "description": "

Detaches the content.

\n" + "jsdoctags": [ + { + "name": "event", + "type": "AnimationEvent", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] }, { "name": "ngOnDestroy", @@ -15541,14 +25058,34 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 75, + "line": 214, "deprecated": false, "deprecationMessage": "" + }, + { + "name": "ngOnInit", + "args": [], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 207, + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\n\nAfter initialized, check if the content is centered and has an origin. If so, set the\nspecial position states that transition the tab from the left or right before centering.\n", + "description": "

After initialized, check if the content is centered and has an origin. If so, set the\nspecial position states that transition the tab from the left or right before centering.

\n" } ], - "implements": [ - "OnDestroy" - ], + "deprecated": false, + "deprecationMessage": "", + "hostBindings": [], + "hostListeners": [], + "description": "

Wrapper for the contents of a tab.

\n", + "rawdescription": "\n\nWrapper for the contents of a tab.\n", + "type": "component", + "sourceCode": "import {\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n ComponentFactoryResolver,\r\n Directive,\r\n ElementRef,\r\n EventEmitter,\r\n // forwardRef,\r\n Inject,\r\n Input,\r\n OnDestroy,\r\n OnInit,\r\n Optional,\r\n Output,\r\n ViewChild,\r\n ViewContainerRef,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\nimport {CdkPortalOutlet} from '@angular/cdk/portal';\r\nimport {Direction, Directionality} from '@angular/cdk/bidi';\r\nimport {DOCUMENT} from '@angular/common';\r\nimport {Subject, Subscription} from 'rxjs';\r\nimport {distinctUntilChanged, \r\n // startWith\r\n} from 'rxjs/operators';\r\nimport {AnimationEvent} from '@angular/animations';\r\nimport {matTabsAnimations} from './tabs-animations';\r\n\r\n/**\r\n * The portal host directive for the contents of the tab.\r\n * @docs-private\r\n */\r\n\r\n@Directive({\r\n selector: `MatTabBodyHost`\r\n})\r\nexport class MatTabBodyPortal extends CdkPortalOutlet implements OnInit, OnDestroy {\r\n /** Subscription to events for when the tab body begins centering. */\r\n private _centeringSub = Subscription.EMPTY;\r\n /** Subscription to events for when the tab body finishes leaving from center position. */\r\n private _leavingSub = Subscription.EMPTY;\r\n\r\n constructor(\r\n componentFactoryResolver: ComponentFactoryResolver,\r\n viewContainerRef: ViewContainerRef,\r\n // @Inject(forwardRef(() => MatTabBody)) private _host: MatTabBody,\r\n @Inject(DOCUMENT) _document: any,\r\n ) {\r\n super(componentFactoryResolver, viewContainerRef, _document);\r\n }\r\n\r\n /** Set initial visibility or set up subscription for changing visibility. */\r\n override ngOnInit(): void {\r\n super.ngOnInit();\r\n \r\n console.log('in directive...')\r\n // this._centeringSub = this._host._beforeCentering\r\n // .pipe(startWith(this._host._isCenterPosition(this._host._position)))\r\n // .subscribe((isCentering: boolean) => {\r\n // if (isCentering && !this.hasAttached()) {\r\n // this.attach(this._host._content);\r\n // }\r\n // });\r\n\r\n // this._leavingSub = this._host._afterLeavingCenter.subscribe(() => {\r\n // if (!this._host.preserveContent) {\r\n // this.detach();\r\n // }\r\n // });\r\n }\r\n\r\n /** Clean up centering subscription. */\r\n override ngOnDestroy(): void {\r\n super.ngOnDestroy();\r\n this._centeringSub.unsubscribe();\r\n this._leavingSub.unsubscribe();\r\n }\r\n}\r\n\r\n/**\r\n * These position states are used internally as animation states for the tab body. Setting the\r\n * position state to left, right, or center will transition the tab body from its current\r\n * position to its respective state. If there is not current position (void, in the case of a new\r\n * tab body), then there will be no transition animation to its state.\r\n *\r\n * In the case of a new tab body that should immediately be centered with an animating transition,\r\n * then left-origin-center or right-origin-center can be used, which will use left or right as its\r\n * pseudo-prior state.\r\n */\r\nexport type MatTabBodyPositionState =\r\n | 'left'\r\n | 'center'\r\n | 'right'\r\n | 'left-origin-center'\r\n | 'right-origin-center';\r\n\r\n/**\r\n * Wrapper for the contents of a tab.\r\n * @docs-private\r\n */\r\n@Component({\r\n selector: 'mat-tab-body',\r\n templateUrl: 'tab-body.html',\r\n // styleUrls: ['tab-body.css'],\r\n encapsulation: ViewEncapsulation.None,\r\n // tslint:disable-next-line:validate-decorators\r\n changeDetection: ChangeDetectionStrategy.Default,\r\n animations: [matTabsAnimations.translateTab],\r\n host: {\r\n 'class': 'mat-mdc-tab-body',\r\n },\r\n})\r\nexport class MatTabBody implements OnInit, OnDestroy {\r\n /** Current position of the tab-body in the tab-group. Zero means that the tab is visible. */\r\n private _positionIndex: number;\r\n\r\n /** Subscription to the directionality change observable. */\r\n private _dirChangeSubscription = Subscription.EMPTY;\r\n\r\n /** Tab body position state. Used by the animation trigger for the current state. */\r\n _position: MatTabBodyPositionState;\r\n\r\n /** Emits when an animation on the tab is complete. */\r\n readonly _translateTabComplete = new Subject();\r\n\r\n /** Event emitted when the tab begins to animate towards the center as the active tab. */\r\n @Output() readonly _onCentering: EventEmitter = new EventEmitter();\r\n\r\n /** Event emitted before the centering of the tab begins. */\r\n @Output() readonly _beforeCentering: EventEmitter = new EventEmitter();\r\n\r\n /** Event emitted before the centering of the tab begins. */\r\n @Output() readonly _afterLeavingCenter: EventEmitter = new EventEmitter();\r\n\r\n /** Event emitted when the tab completes its animation towards the center. */\r\n @Output() readonly _onCentered: EventEmitter = new EventEmitter(true);\r\n\r\n /** The portal host inside of this container into which the tab body content will be loaded. */\r\n @ViewChild(CdkPortalOutlet) _portalHost: CdkPortalOutlet;\r\n\r\n /** The tab body content to display. */\r\n @Input('content') _content: any;\r\n\r\n /** Position that will be used when the tab is immediately becoming visible after creation. */\r\n @Input() origin: number | null;\r\n\r\n // Note that the default value will always be overwritten by `MatTabBody`, but we need one\r\n // anyway to prevent the animations module from throwing an error if the body is used on its own.\r\n /** Duration for the tab's animation. */\r\n @Input() animationDuration: string = '0';\r\n\r\n /** Whether the tab's content should be kept in the DOM while it's off-screen. */\r\n @Input() preserveContent: boolean = false;\r\n\r\n /** The shifted index position of the tab body, where zero represents the active center tab. */\r\n @Input()\r\n set position(position: number) {\r\n this._positionIndex = position;\r\n this._computePositionAnimationState();\r\n }\r\n\r\n constructor(\r\n private _elementRef: ElementRef,\r\n @Optional() private _dir: Directionality,\r\n changeDetectorRef: ChangeDetectorRef,\r\n ) {\r\n if (_dir) {\r\n this._dirChangeSubscription = _dir.change.subscribe((dir: Direction) => {\r\n this._computePositionAnimationState(dir);\r\n changeDetectorRef.markForCheck();\r\n });\r\n }\r\n\r\n // Ensure that we get unique animation events, because the `.done` callback can get\r\n // invoked twice in some browsers. See https://github.com/angular/angular/issues/24084.\r\n this._translateTabComplete\r\n .pipe(\r\n distinctUntilChanged((x, y) => {\r\n return x.fromState === y.fromState && x.toState === y.toState;\r\n }),\r\n )\r\n .subscribe(event => {\r\n // If the transition to the center is complete, emit an event.\r\n if (this._isCenterPosition(event.toState) && this._isCenterPosition(this._position)) {\r\n this._onCentered.emit();\r\n }\r\n\r\n if (this._isCenterPosition(event.fromState) && !this._isCenterPosition(this._position)) {\r\n this._afterLeavingCenter.emit();\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * After initialized, check if the content is centered and has an origin. If so, set the\r\n * special position states that transition the tab from the left or right before centering.\r\n */\r\n ngOnInit() {\r\n if (this._position == 'center' && this.origin != null) {\r\n this._position = this._computePositionFromOrigin(this.origin);\r\n }\r\n console.log('sssssssss', this._content)\r\n }\r\n\r\n ngOnDestroy() {\r\n this._dirChangeSubscription.unsubscribe();\r\n this._translateTabComplete.complete();\r\n }\r\n\r\n _onTranslateTabStarted(event: AnimationEvent): void {\r\n const isCentering = this._isCenterPosition(event.toState);\r\n this._beforeCentering.emit(isCentering);\r\n if (isCentering) {\r\n this._onCentering.emit(this._elementRef.nativeElement.clientHeight);\r\n }\r\n }\r\n\r\n /** The text direction of the containing app. */\r\n _getLayoutDirection(): Direction {\r\n return this._dir && this._dir.value === 'rtl' ? 'rtl' : 'ltr';\r\n }\r\n\r\n /** Whether the provided position state is considered center, regardless of origin. */\r\n _isCenterPosition(position: MatTabBodyPositionState | string): boolean {\r\n return (\r\n position == 'center' || position == 'left-origin-center' || position == 'right-origin-center'\r\n );\r\n }\r\n\r\n /** Computes the position state that will be used for the tab-body animation trigger. */\r\n private _computePositionAnimationState(dir: Direction = this._getLayoutDirection()) {\r\n if (this._positionIndex < 0) {\r\n this._position = dir == 'ltr' ? 'left' : 'right';\r\n } else if (this._positionIndex > 0) {\r\n this._position = dir == 'ltr' ? 'right' : 'left';\r\n } else {\r\n this._position = 'center';\r\n }\r\n }\r\n\r\n /**\r\n * Computes the position state based on the specified origin position. This is used if the\r\n * tab is becoming visible immediately after creation.\r\n */\r\n private _computePositionFromOrigin(origin: number): MatTabBodyPositionState {\r\n const dir = this._getLayoutDirection();\r\n\r\n if ((dir == 'ltr' && origin <= 0) || (dir == 'rtl' && origin > 0)) {\r\n return 'left-origin-center';\r\n }\r\n\r\n return 'right-origin-center';\r\n }\r\n}\r\n\r\n/**\r\n * The origin state is an internally used state that is set on a new tab body indicating if it\r\n * began to the left or right of the prior selected index. For example, if the selected index was\r\n * set to 1, and a new tab is created and selected at index 2, then the tab body would have an\r\n * origin of right because its index was greater than the prior selected index.\r\n */\r\nexport type MatTabBodyOriginState = 'left' | 'right';\r\n", + "assetsDirs": [], + "styleUrlsData": "", + "stylesData": "", "constructorObj": { "name": "constructor", "description": "", @@ -15556,65 +25093,29 @@ "deprecationMessage": "", "args": [ { - "name": "_template", - "type": "TemplateRef", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "_componentFactoryResolver", - "type": "ComponentFactoryResolver", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "_appRef", - "type": "ApplicationRef", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "_injector", - "type": "Injector", + "name": "_elementRef", + "type": "ElementRef", "deprecated": false, "deprecationMessage": "" }, { - "name": "_viewContainerRef", - "type": "ViewContainerRef", + "name": "_dir", + "type": "Directionality", "deprecated": false, "deprecationMessage": "" }, { - "name": "_document", - "type": "Document", + "name": "changeDetectorRef", + "type": "ChangeDetectorRef", "deprecated": false, "deprecationMessage": "" } ], - "line": 26, + "line": 169, "jsdoctags": [ { - "name": "_template", - "type": "TemplateRef", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "_componentFactoryResolver", - "type": "ComponentFactoryResolver", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "_appRef", - "type": "ApplicationRef", + "name": "_elementRef", + "type": "ElementRef", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -15622,8 +25123,8 @@ } }, { - "name": "_injector", - "type": "Injector", + "name": "_dir", + "type": "Directionality", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -15631,295 +25132,552 @@ } }, { - "name": "_viewContainerRef", - "type": "ViewContainerRef", + "name": "changeDetectorRef", + "type": "ChangeDetectorRef", "deprecated": false, "deprecationMessage": "", "tagName": { "text": "param" } - }, - { - "name": "_document", - "type": "Document", + } + ] + }, + "implements": [ + "OnInit", + "OnDestroy" + ], + "accessors": { + "position": { + "name": "position", + "setSignature": { + "name": "position", + "type": "void", "deprecated": false, "deprecationMessage": "", - "tagName": { - "text": "param" - } + "args": [ + { + "name": "position", + "type": "number", + "deprecated": false, + "deprecationMessage": "" + } + ], + "returnType": "void", + "line": 166, + "rawdescription": "\nThe shifted index position of the tab body, where zero represents the active center tab.", + "description": "

The shifted index position of the tab body, where zero represents the active center tab.

\n", + "jsdoctags": [ + { + "name": "position", + "type": "number", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] } - ] - } + } + }, + "templateData": "
\r\n \r\n \r\n
\r\n" }, { - "name": "OuiMenuTrigger", - "id": "directive-OuiMenuTrigger-ee6aed2ed264cc4674c6464371b5ddbebfcfa567a8096b9cba0077541bf975b55400db3a5f40acb2e1d6f621476af9c7b8d005c3f7fcedb9035e02a24e1f3d66", - "file": "ui/src/components/menu/menu-trigger.ts", - "type": "directive", - "description": "

This directive is intended to be used in conjunction with an oui-menu tag. It is\nresponsible for toggling the display of the provided menu instance.

\n", - "rawdescription": "\n\nThis directive is intended to be used in conjunction with an oui-menu tag. It is\nresponsible for toggling the display of the provided menu instance.\n", - "sourceCode": "import {\r\n FocusMonitor,\r\n FocusOrigin,\r\n isFakeMousedownFromScreenReader,\r\n} from '@angular/cdk/a11y';\r\nimport { RIGHT_ARROW, SPACE } from '@angular/cdk/keycodes';\r\nimport {\r\n FlexibleConnectedPositionStrategy,\r\n HorizontalConnectionPos,\r\n Overlay,\r\n OverlayConfig,\r\n OverlayRef,\r\n VerticalConnectionPos,\r\n ScrollStrategy,\r\n} from '@angular/cdk/overlay';\r\nimport { TemplatePortal } from '@angular/cdk/portal';\r\nimport {\r\n AfterContentInit,\r\n Directive,\r\n ElementRef,\r\n EventEmitter,\r\n Inject,\r\n InjectionToken,\r\n Input,\r\n OnDestroy,\r\n Optional,\r\n Output,\r\n Self,\r\n ViewContainerRef,\r\n} from '@angular/core';\r\nimport { normalizePassiveListenerOptions } from '@angular/cdk/platform';\r\nimport {\r\n asapScheduler,\r\n merge,\r\n Observable,\r\n of as observableOf,\r\n Subscription,\r\n} from 'rxjs';\r\nimport { delay, filter } from 'rxjs/operators';\r\nimport { OuiMenu } from './menu-directive';\r\nimport { throwOuiMenuMissingError } from './menu-errors';\r\nimport { OuiMenuItem } from './menu-item';\r\nimport { OuiMenuPanel } from './menu-panel';\r\nimport { MenuPositionX, MenuPositionY } from './menu-positions';\r\n\r\n/** Injection token that determines the scroll handling while the menu is open. */\r\nexport const OUI_MENU_SCROLL_STRATEGY = new InjectionToken<\r\n () => ScrollStrategy\r\n>('oui-menu-scroll-strategy');\r\n\r\n/** @docs-private */\r\nexport function OUI_MENU_SCROLL_STRATEGY_FACTORY(\r\n overlay: Overlay\r\n): () => ScrollStrategy {\r\n return () => overlay.scrollStrategies.close();\r\n}\r\n\r\n/** @docs-private */\r\nexport const OUI_MENU_SCROLL_STRATEGY_FACTORY_PROVIDER = {\r\n provide: OUI_MENU_SCROLL_STRATEGY,\r\n deps: [Overlay],\r\n useFactory: OUI_MENU_SCROLL_STRATEGY_FACTORY,\r\n};\r\n\r\n/** Default top padding of the menu panel. */\r\nexport const MENU_PANEL_TOP_PADDING = 11;\r\n\r\n/** Default left padding of the menu panel */\r\nexport const MENU_PANEL_LEFT_PADDING = 2;\r\n\r\n/** Options for binding a passive event listener. */\r\nconst passiveEventListenerOptions = normalizePassiveListenerOptions({\r\n passive: true,\r\n});\r\n\r\n// TODO(andrewseguin): Remove the kebab versions in favor of camelCased attribute selectors\r\n\r\n/**\r\n * This directive is intended to be used in conjunction with an oui-menu tag. It is\r\n * responsible for toggling the display of the provided menu instance.\r\n */\r\n@Directive({\r\n selector: `[oui-menu-trigger-for], [ouiMenuTriggerFor]`,\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n 'aria-haspopup': 'true',\r\n '[attr.aria-expanded]': 'menuOpen || null',\r\n '(mousedown)': '_handleMousedown($event)',\r\n '(keydown)': '_handleKeydown($event)',\r\n '(click)': '_handleClick($event)',\r\n },\r\n exportAs: 'ouiMenuTrigger',\r\n})\r\nexport class OuiMenuTrigger implements AfterContentInit, OnDestroy {\r\n private _portal: TemplatePortal;\r\n private _overlayRef: OverlayRef | null = null;\r\n private _menuOpen = false;\r\n private _closeSubscription = Subscription.EMPTY;\r\n private _hoverSubscription = Subscription.EMPTY;\r\n private _menuCloseSubscription = Subscription.EMPTY;\r\n private _scrollStrategy: () => ScrollStrategy;\r\n private _openViaFocus = false;\r\n\r\n // Tracking input type is necessary so it's possible to only auto-focus\r\n // the first item of the list when the menu is opened via the keyboard\r\n _openedBy: 'mouse' | 'touch' | null = null;\r\n\r\n /** References the menu instance that the trigger is associated with. */\r\n @Input('ouiMenuTriggerFor')\r\n get menu() {\r\n return this._menu;\r\n }\r\n set menu(menu: OuiMenuPanel) {\r\n if (menu === this._menu) {\r\n return;\r\n }\r\n this._menu = menu;\r\n this._menuCloseSubscription.unsubscribe();\r\n\r\n if (menu) {\r\n this._menuCloseSubscription = menu.close\r\n .asObservable()\r\n .subscribe((event) => {\r\n this._destroyMenu(event);\r\n // If a click closed the menu, we should close the entire chain of nested menus.\r\n if ((event === 'click' || event === 'tab') && this._parentMenu) {\r\n this._parentMenu.closed.emit(event);\r\n }\r\n });\r\n }\r\n }\r\n private _menu: OuiMenuPanel;\r\n\r\n /** Data to be passed along to any lazily-rendered content. */\r\n\r\n // eslint-disable-next-line @angular-eslint/no-input-rename\r\n @Input('ouiMenuTriggerData')\r\n menuData: any;\r\n\r\n /** Event emitted when the associated menu is opened. */\r\n @Output()\r\n readonly menuOpened: EventEmitter = new EventEmitter();\r\n\r\n /** Event emitted when the associated menu is closed. */\r\n @Output()\r\n readonly menuClosed: EventEmitter = new EventEmitter();\r\n\r\n /**\r\n * Handles touch start events on the trigger.\r\n * Needs to be an arrow function so we can easily use addEventListener and removeEventListener.\r\n */\r\n private _handleTouchStart = () => (this._openedBy = 'touch');\r\n\r\n constructor(\r\n private _overlay: Overlay,\r\n private _element: ElementRef,\r\n private _viewContainerRef: ViewContainerRef,\r\n @Inject(OUI_MENU_SCROLL_STRATEGY) scrollStrategy: any,\r\n @Optional() private _parentMenu: OuiMenu,\r\n @Optional()\r\n @Self()\r\n private _menuItemInstance: OuiMenuItem,\r\n // TODO(crisbeto): make the _focusMonitor required when doing breaking changes.\r\n // @breaking-change 8.0.0\r\n private _focusMonitor?: FocusMonitor\r\n ) {\r\n _element.nativeElement.addEventListener(\r\n 'touchstart',\r\n this._handleTouchStart,\r\n passiveEventListenerOptions\r\n );\r\n\r\n if (_menuItemInstance) {\r\n _menuItemInstance._triggersSubmenu = this.triggersSubmenu();\r\n }\r\n\r\n this._scrollStrategy = scrollStrategy;\r\n }\r\n\r\n ngAfterContentInit() {\r\n this._checkMenu();\r\n this._handleHover();\r\n }\r\n\r\n ngOnDestroy() {\r\n if (this._overlayRef) {\r\n this._overlayRef.dispose();\r\n this._overlayRef = null;\r\n }\r\n\r\n this._element.nativeElement.removeEventListener(\r\n 'touchstart',\r\n this._handleTouchStart,\r\n passiveEventListenerOptions\r\n );\r\n\r\n this._cleanUpSubscriptions();\r\n }\r\n\r\n /** Whether the menu is open. */\r\n get menuOpen(): boolean {\r\n return this._menuOpen;\r\n }\r\n\r\n /** Whether the menu triggers a sub-menu or a top-level one. */\r\n triggersSubmenu(): boolean {\r\n return !!(this._menuItemInstance && this._parentMenu);\r\n }\r\n\r\n /** Toggles the menu between the open and closed states. */\r\n toggleMenu(): void {\r\n return this._menuOpen ? this.closeMenu() : this.openMenu();\r\n }\r\n\r\n /** Opens the menu. */\r\n openMenu(): void {\r\n if (this._menuOpen) {\r\n return;\r\n }\r\n\r\n this._checkMenu();\r\n\r\n const overlayRef = this._createOverlay();\r\n const overlayConfig = overlayRef.getConfig();\r\n\r\n this._setPosition(\r\n overlayConfig.positionStrategy as FlexibleConnectedPositionStrategy\r\n );\r\n overlayConfig.hasBackdrop =\r\n this.menu.hasBackdrop == null\r\n ? !this.triggersSubmenu()\r\n : this.menu.hasBackdrop;\r\n overlayRef.attach(this._getPortal());\r\n\r\n if (this.menu.lazyContent) {\r\n this.menu.lazyContent.attach(this.menuData);\r\n }\r\n\r\n this._closeSubscription = this._menuClosingActions().subscribe(() => {\r\n this.closeMenu();\r\n });\r\n this._initMenu();\r\n }\r\n\r\n /** Closes the menu. */\r\n closeMenu(event?: 'click' | 'keydown' | 'tab'): void {\r\n this.menu.close.emit(event);\r\n }\r\n\r\n /**\r\n * Focuses the menu trigger.\r\n *\r\n * @param origin Source of the menu trigger's focus.\r\n */\r\n focus(origin: FocusOrigin = 'program') {\r\n if (this._focusMonitor) {\r\n this._focusMonitor.focusVia(this._element.nativeElement, origin);\r\n } else {\r\n this._element.nativeElement.focus();\r\n }\r\n }\r\n\r\n /** Closes the menu and does the necessary cleanup. */\r\n private _destroyMenu(event) {\r\n // (TODO)\r\n if (!this._overlayRef || !this.menuOpen) {\r\n return;\r\n }\r\n\r\n const menu = this.menu;\r\n\r\n this._closeSubscription.unsubscribe();\r\n this._overlayRef.detach();\r\n this._resetMenu(event);\r\n\r\n if (!(menu instanceof OuiMenu) && menu.lazyContent) {\r\n menu.lazyContent.detach();\r\n }\r\n }\r\n\r\n /**\r\n * This method sets the menu state to open and focuses the first item if\r\n * the menu was opened via the keyboard.\r\n */\r\n private _initMenu(): void {\r\n this.menu.parentMenu = this.triggersSubmenu()\r\n ? this._parentMenu\r\n : undefined;\r\n this._setIsMenuOpen(true);\r\n this.menu.focusFirstItem(this._openedBy || 'program');\r\n }\r\n\r\n /**\r\n * This method resets the menu when it's closed, most importantly restoring\r\n * focus to the menu trigger if the menu was opened via the keyboard.\r\n */\r\n private _resetMenu(event): void {\r\n this._setIsMenuOpen(false);\r\n\r\n // We should reset focus if the user is navigating using a keyboard or\r\n // if we have a top-level trigger which might cause focus to be lost\r\n // when clicking on the backdrop.\r\n // Focus back to menu only when clicking outside of menu on window or ESC key pressed.\r\n if ((!event && !this.triggersSubmenu()) || event === 'keydown') {\r\n if (!this._openedBy) {\r\n // Note that the focus style will show up both for `program` and\r\n // `keyboard` so we don't have to specify which one it is.\r\n this.focus();\r\n } else if (!this.triggersSubmenu()) {\r\n this.focus(this._openedBy);\r\n }\r\n }\r\n this._openedBy = null;\r\n }\r\n\r\n // set state rather than toggle to support triggers sharing a menu\r\n private _setIsMenuOpen(isOpen: boolean): void {\r\n this._menuOpen = isOpen;\r\n this._menuOpen ? this.menuOpened.emit() : this.menuClosed.emit();\r\n\r\n if (this.triggersSubmenu()) {\r\n this._menuItemInstance._highlighted = isOpen;\r\n }\r\n }\r\n\r\n /**\r\n * This method checks that a valid instance of OuiMenu has been passed into\r\n * ouiMenuTriggerFor. If not, an exception is thrown.\r\n */\r\n private _checkMenu() {\r\n if (!this.menu) {\r\n throwOuiMenuMissingError();\r\n }\r\n }\r\n\r\n /**\r\n * This method creates the overlay from the provided menu's template and saves its\r\n * OverlayRef so that it can be attached to the DOM when openMenu is called.\r\n */\r\n private _createOverlay(): OverlayRef {\r\n if (!this._overlayRef) {\r\n const config = this._getOverlayConfig();\r\n this._subscribeToPositions(\r\n config.positionStrategy as FlexibleConnectedPositionStrategy\r\n );\r\n this._overlayRef = this._overlay.create(config);\r\n\r\n // Consume the `keydownEvents` in order to prevent them from going to another overlay.\r\n // Ideally we'd also have our keyboard event logic in here, however doing so will\r\n // break anybody that may have implemented the `OuiMenuPanel` themselves.\r\n this._overlayRef.keydownEvents().subscribe();\r\n }\r\n\r\n return this._overlayRef;\r\n }\r\n\r\n /**\r\n * This method builds the configuration object needed to create the overlay, the OverlayState.\r\n *\r\n * @returns OverlayConfig\r\n */\r\n private _getOverlayConfig(): OverlayConfig {\r\n return new OverlayConfig({\r\n positionStrategy: this._overlay\r\n .position()\r\n .flexibleConnectedTo(this._element)\r\n .withLockedPosition()\r\n .withTransformOriginOn('.oui-menu-panel'),\r\n backdropClass:\r\n this.menu.backdropClass || 'cdk-overlay-transparent-backdrop',\r\n scrollStrategy: this._scrollStrategy(),\r\n direction: 'ltr',\r\n });\r\n }\r\n\r\n /**\r\n * Listens to changes in the position of the overlay and sets the correct classes\r\n * on the menu based on the new position. This ensures the animation origin is always\r\n * correct, even if a fallback position is used for the overlay.\r\n */\r\n private _subscribeToPositions(\r\n position: FlexibleConnectedPositionStrategy\r\n ): void {\r\n if (this.menu.setPositionClasses) {\r\n position.positionChanges.subscribe((change) => {\r\n const posX: MenuPositionX =\r\n change.connectionPair.overlayX === 'start' ? 'after' : 'before';\r\n const posY: MenuPositionY =\r\n change.connectionPair.overlayY === 'top' ? 'below' : 'above';\r\n\r\n this.menu.setPositionClasses!(posX, posY);\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Sets the appropriate positions on a position strategy\r\n * so the overlay connects with the trigger correctly.\r\n *\r\n * @param positionStrategy Strategy whose position to update.\r\n */\r\n private _setPosition(positionStrategy: FlexibleConnectedPositionStrategy) {\r\n let [originX, originFallbackX]: HorizontalConnectionPos[] =\r\n this.menu.xPosition === 'before' ? ['end', 'start'] : ['start', 'end'];\r\n\r\n const [overlayY, overlayFallbackY]: VerticalConnectionPos[] =\r\n this.menu.yPosition === 'above' ? ['bottom', 'top'] : ['top', 'bottom'];\r\n\r\n let [originY, originFallbackY] = [overlayY, overlayFallbackY];\r\n let [overlayX, overlayFallbackX] = [originX, originFallbackX];\r\n let offsetY = 0;\r\n let offsetX = 0;\r\n\r\n if (this.triggersSubmenu()) {\r\n // When the menu is a sub-menu, it should always align itself\r\n // to the edges of the trigger, instead of overlapping it.\r\n overlayFallbackX = originX =\r\n this.menu.xPosition === 'before' ? 'start' : 'end';\r\n originFallbackX = overlayX = originX === 'end' ? 'start' : 'end';\r\n offsetY =\r\n overlayY === 'bottom'\r\n ? MENU_PANEL_TOP_PADDING\r\n : -MENU_PANEL_TOP_PADDING;\r\n\r\n offsetX =\r\n overlayX === 'start'\r\n ? MENU_PANEL_LEFT_PADDING\r\n : -MENU_PANEL_LEFT_PADDING;\r\n } else if (!this.menu.overlapTrigger) {\r\n originY = overlayY === 'top' ? 'bottom' : 'top';\r\n originFallbackY = overlayFallbackY === 'top' ? 'bottom' : 'top';\r\n }\r\n\r\n positionStrategy.withPositions([\r\n { originX, originY, overlayX, overlayY, offsetY, offsetX },\r\n {\r\n originX: originFallbackX,\r\n originY,\r\n overlayX: overlayFallbackX,\r\n overlayY,\r\n offsetY,\r\n offsetX,\r\n },\r\n {\r\n originX,\r\n originY: originFallbackY,\r\n overlayX,\r\n overlayY: overlayFallbackY,\r\n offsetY: -offsetY,\r\n offsetX,\r\n },\r\n {\r\n originX: originFallbackX,\r\n originY: originFallbackY,\r\n overlayX: overlayFallbackX,\r\n overlayY: overlayFallbackY,\r\n offsetY: -offsetY,\r\n offsetX: -offsetX,\r\n },\r\n ]);\r\n }\r\n\r\n /** Cleans up the active subscriptions. */\r\n private _cleanUpSubscriptions(): void {\r\n this._closeSubscription.unsubscribe();\r\n this._hoverSubscription.unsubscribe();\r\n }\r\n\r\n /** Returns a stream that emits whenever an action that should close the menu occurs. */\r\n private _menuClosingActions() {\r\n const backdrop = this._overlayRef!.backdropClick();\r\n const detachments = this._overlayRef!.detachments();\r\n const parentClose = this._parentMenu\r\n ? this._parentMenu.closed\r\n : observableOf();\r\n const hover = this._parentMenu\r\n ? this._parentMenu._hovered().pipe(\r\n filter((active) => active !== this._menuItemInstance),\r\n filter(() => this._menuOpen)\r\n )\r\n : observableOf();\r\n\r\n return merge(backdrop, parentClose as Observable, hover, detachments);\r\n }\r\n\r\n /** Handles mouse presses on the trigger. */\r\n _handleMousedown(event: MouseEvent): void {\r\n if (!isFakeMousedownFromScreenReader(event)) {\r\n // Since right or middle button clicks won't trigger the `click` event,\r\n // we shouldn't consider the menu as opened by mouse in those cases.\r\n this._openedBy = event.button === 0 ? 'mouse' : null;\r\n\r\n // Since clicking on the trigger won't close the menu if it opens a sub-menu,\r\n // we should prevent focus from moving onto it via click to avoid the\r\n // highlight from lingering on the menu item.\r\n if (this.triggersSubmenu()) {\r\n event.preventDefault();\r\n }\r\n }\r\n }\r\n\r\n /** Handles key presses on the trigger. */\r\n _handleKeydown(event: KeyboardEvent): void {\r\n const keyCode = event.keyCode;\r\n if (this.triggersSubmenu() && keyCode === RIGHT_ARROW) {\r\n this.openMenu();\r\n } else if (keyCode === SPACE) {\r\n this.openMenu();\r\n }\r\n }\r\n\r\n /** Handles click events on the trigger. */\r\n _handleClick(event: MouseEvent): void {\r\n if (this.triggersSubmenu()) {\r\n // Stop event propagation to avoid closing the parent menu.\r\n event.stopPropagation();\r\n this.openMenu();\r\n } else {\r\n this.toggleMenu();\r\n }\r\n }\r\n\r\n _handleFocus(): void {\r\n if (!this._openViaFocus) {\r\n this.toggleMenu();\r\n }\r\n this._openViaFocus = !this._openViaFocus;\r\n }\r\n\r\n /** Handles the cases where the user hovers over the trigger. */\r\n private _handleHover() {\r\n // Subscribe to changes in the hovered item in order to toggle the panel.\r\n if (!this.triggersSubmenu()) {\r\n return;\r\n }\r\n\r\n this._hoverSubscription = this._parentMenu\r\n ._hovered()\r\n // Since we might have multiple competing triggers for the same menu (e.g. a sub-menu\r\n // with different data and triggers), we have to delay it by a tick to ensure that\r\n // it won't be closed immediately after it is opened.\r\n .pipe(\r\n filter(\r\n (active) => active === this._menuItemInstance && !active.disabled\r\n ),\r\n delay(0, asapScheduler)\r\n )\r\n .subscribe(() => {\r\n this._openedBy = 'mouse';\r\n\r\n // If the same menu is used between multiple triggers, it might still be animating\r\n // while the new trigger tries to re-open it. Wait for the animation to finish\r\n // before doing so. Also interrupt if the user moves to another item.\r\n // (TODO)\r\n this.openMenu();\r\n });\r\n }\r\n\r\n /** Gets the portal that should be attached to the overlay. */\r\n private _getPortal(): TemplatePortal {\r\n // Note that we can avoid this check by keeping the portal on the menu panel.\r\n // While it would be cleaner, we'd have to introduce another required method on\r\n // `OuiMenuPanel`, making it harder to consume.\r\n if (!this._portal || this._portal.templateRef !== this.menu.templateRef) {\r\n this._portal = new TemplatePortal(\r\n this.menu.templateRef,\r\n this._viewContainerRef\r\n );\r\n }\r\n\r\n return this._portal;\r\n }\r\n}\r\n", - "selector": "[oui-menu-trigger-for], [ouiMenuTriggerFor]", - "providers": [], + "name": "MatTabGroup", + "id": "component-MatTabGroup-1b4765c4c0214d2bb0047fe990a8c831e74ec01426fde3087faf95fa37c1812b302b55dd400f096a3402297d23287ad90081434f43f4a3fe13b2758e728b5774", + "file": "ui/src/components/tabs/tab-group.ts", + "changeDetection": "ChangeDetectionStrategy.Default", + "encapsulation": [ + "ViewEncapsulation.None" + ], + "entryComponents": [], + "exportAs": "matTabGroup", + "host": {}, + "inputs": [ + "color", + "disableRipple" + ], + "outputs": [], + "providers": [ + { + "name": "{\n provide: MAT_TAB_GROUP, useExisting: MatTabGroup,\n}" + } + ], + "selector": "mat-tab-group", + "styleUrls": [ + "tab-group.scss" + ], + "styles": [], + "templateUrl": [ + "tab-group.html" + ], + "viewProviders": [], "inputsClass": [ { - "name": "ouiMenuTriggerData", + "name": "animationDuration", + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nDuration for the tab animation. Will be normalized to milliseconds if no units are set.", + "description": "

Duration for the tab animation. Will be normalized to milliseconds if no units are set.

\n", + "line": 181, + "type": "string", + "decorators": [] + }, + { + "name": "backgroundColor", + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nBackground color of the tab group.", + "description": "

Background color of the tab group.

\n", + "line": 241, + "type": "ThemePalette", + "decorators": [] + }, + { + "name": "contentTabIndex", + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\n\n`tabindex` to be set on the inner element that wraps the tab content. Can be used for improved\naccessibility when the tab does not have focusable elements or if it has scrollable content.\nThe `tabindex` will be removed automatically for inactive tabs.\nRead more at https://www.w3.org/TR/wai-aria-practices/examples/tabs/tabs-2/tabs.html\n", + "description": "

tabindex to be set on the inner element that wraps the tab content. Can be used for improved\naccessibility when the tab does not have focusable elements or if it has scrollable content.\nThe tabindex will be removed automatically for inactive tabs.\nRead more at https://www.w3.org/TR/wai-aria-practices/examples/tabs/tabs-2/tabs.html

\n", + "line": 198, + "type": "number | null", + "decorators": [] + }, + { + "name": "disablePagination", + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\n\nWhether pagination should be disabled. This can be used to avoid unnecessary\nlayout recalculations if it's known that pagination won't be required.\n", + "description": "

Whether pagination should be disabled. This can be used to avoid unnecessary\nlayout recalculations if it's known that pagination won't be required.

\n", + "line": 213, + "type": "boolean", + "decorators": [] + }, + { + "name": "dynamicHeight", + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nWhether the tab group should grow to the size of the active tab.", + "description": "

Whether the tab group should grow to the size of the active tab.

\n", + "line": 154, + "type": "boolean", + "decorators": [] + }, + { + "name": "fitInkBarToContent", + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nWhether the ink bar should fit its width to the size of the tab label content.", + "description": "

Whether the ink bar should fit its width to the size of the tab label content.

\n", + "line": 133, + "type": "boolean", + "decorators": [] + }, + { + "name": "headerPosition", + "defaultValue": "'above'", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nData to be passed along to any lazily-rendered content.", - "description": "

Data to be passed along to any lazily-rendered content.

\n", - "line": 138, - "type": "any", + "rawdescription": "\nPosition of the tab header.", + "description": "

Position of the tab header.

\n", + "line": 177, + "type": "MatTabHeaderPosition", "decorators": [] }, { - "name": "ouiMenuTriggerFor", + "name": "mat-stretch-tabs", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nReferences the menu instance that the trigger is associated with.", - "description": "

References the menu instance that the trigger is associated with.

\n", - "line": 110, - "type": "OuiMenuPanel", + "rawdescription": "\nWhether tabs should be stretched to fill the header.", + "description": "

Whether tabs should be stretched to fill the header.

\n", + "line": 144, + "type": "boolean", + "decorators": [] + }, + { + "name": "preserveContent", + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\n\nBy default tabs remove their content from the DOM while it's off-screen.\nSetting this to `true` will keep it in the DOM which will prevent elements\nlike iframes and videos from reloading next time it comes back into the view.\n", + "description": "

By default tabs remove their content from the DOM while it's off-screen.\nSetting this to true will keep it in the DOM which will prevent elements\nlike iframes and videos from reloading next time it comes back into the view.

\n", + "line": 229, + "type": "boolean", + "decorators": [] + }, + { + "name": "selectedIndex", + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nThe index of the active tab.", + "description": "

The index of the active tab.

\n", + "line": 166, + "type": "number | null", "decorators": [] } ], "outputsClass": [ { - "name": "menuClosed", + "name": "animationDone", "defaultValue": "new EventEmitter()", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nEvent emitted when the associated menu is closed.", - "description": "

Event emitted when the associated menu is closed.

\n", - "line": 146, + "rawdescription": "\nEvent emitted when the body animation has completed", + "description": "

Event emitted when the body animation has completed

\n", + "line": 268, "type": "EventEmitter" }, { - "name": "menuOpened", - "defaultValue": "new EventEmitter()", + "name": "focusChange", + "defaultValue": "new EventEmitter()", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nEvent emitted when the associated menu is opened.", - "description": "

Event emitted when the associated menu is opened.

\n", - "line": 142, - "type": "EventEmitter" + "rawdescription": "\nEvent emitted when focus has changed within a tab group.", + "description": "

Event emitted when focus has changed within a tab group.

\n", + "line": 264, + "type": "EventEmitter" + }, + { + "name": "selectedIndexChange", + "defaultValue": "new EventEmitter()", + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nOutput to enable support for two-way binding on `[(selectedIndex)]`", + "description": "

Output to enable support for two-way binding on [(selectedIndex)]

\n", + "line": 261, + "type": "EventEmitter" + }, + { + "name": "selectedTabChange", + "defaultValue": "new EventEmitter(true)", + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nEvent emitted when the tab selection has changed.", + "description": "

Event emitted when the tab selection has changed.

\n", + "line": 271, + "type": "EventEmitter" } ], - "deprecated": false, - "deprecationMessage": "", - "hostBindings": [], - "hostListeners": [], "propertiesClass": [ { - "name": "_closeSubscription", - "defaultValue": "Subscription.EMPTY", + "name": "_allTabs", "deprecated": false, "deprecationMessage": "", - "type": "", + "type": "QueryList", + "optional": false, + "description": "

All tabs inside the tab group. This includes tabs that belong to groups that are nested\ninside the current one. We filter out only the tabs that belong to this group in _tabs.

\n", + "line": 109, + "rawdescription": "\n\nAll tabs inside the tab group. This includes tabs that belong to groups that are nested\ninside the current one. We filter out only the tabs that belong to this group in `_tabs`.\n", + "decorators": [ + { + "name": "ContentChildren", + "stringifiedArguments": "MatTab, {descendants: true}" + } + ] + }, + { + "name": "_animationDuration", + "deprecated": false, + "deprecationMessage": "", + "type": "string", "optional": false, "description": "", - "line": 98, + "line": 189, "modifierKind": [ 121 ] }, { - "name": "_handleTouchStart", - "defaultValue": "() => {...}", + "name": "_animationMode", "deprecated": false, "deprecationMessage": "", - "type": "", + "type": "string", + "optional": true, + "description": "", + "line": 283, + "decorators": [ + { + "name": "Optional", + "stringifiedArguments": "" + }, + { + "name": "Inject", + "stringifiedArguments": "ANIMATION_MODULE_TYPE" + } + ], + "modifierKind": [ + 123 + ] + }, + { + "name": "_backgroundColor", + "deprecated": false, + "deprecationMessage": "", + "type": "ThemePalette", "optional": false, - "description": "

Handles touch start events on the trigger.\nNeeds to be an arrow function so we can easily use addEventListener and removeEventListener.

\n", - "line": 152, - "rawdescription": "\n\nHandles touch start events on the trigger.\nNeeds to be an arrow function so we can easily use addEventListener and removeEventListener.\n", + "description": "", + "line": 258, "modifierKind": [ 121 ] }, { - "name": "_hoverSubscription", - "defaultValue": "Subscription.EMPTY", + "name": "_contentTabIndex", "deprecated": false, "deprecationMessage": "", - "type": "", + "type": "number | null", "optional": false, "description": "", - "line": 99, + "line": 206, "modifierKind": [ 121 ] }, { - "name": "_menu", + "name": "_disablePagination", + "defaultValue": "false", "deprecated": false, "deprecationMessage": "", - "type": "OuiMenuPanel", + "type": "boolean", "optional": false, "description": "", - "line": 132, + "line": 221, "modifierKind": [ 121 ] }, { - "name": "_menuCloseSubscription", - "defaultValue": "Subscription.EMPTY", + "name": "_dynamicHeight", + "defaultValue": "false", "deprecated": false, "deprecationMessage": "", - "type": "", + "type": "boolean", "optional": false, "description": "", - "line": 100, + "line": 162, "modifierKind": [ 121 ] }, { - "name": "_menuOpen", + "name": "_fitInkBarToContent", "defaultValue": "false", "deprecated": false, "deprecationMessage": "", "type": "", "optional": false, "description": "", - "line": 97, + "line": 140, "modifierKind": [ 121 ] }, { - "name": "_openedBy", - "defaultValue": "null", + "name": "_groupId", "deprecated": false, "deprecationMessage": "", - "type": "\"mouse\" | \"touch\" | null", + "type": "number", "optional": false, "description": "", - "line": 106 + "line": 274, + "modifierKind": [ + 121 + ] }, { - "name": "_openViaFocus", - "defaultValue": "false", + "name": "_indexToSelect", + "defaultValue": "0", "deprecated": false, "deprecationMessage": "", - "type": "", + "type": "number | null", "optional": false, - "description": "", - "line": 102, + "description": "

The tab index that should be selected after the content has been checked.

\n", + "line": 117, + "rawdescription": "\nThe tab index that should be selected after the content has been checked.", "modifierKind": [ 121 ] }, { - "name": "_overlayRef", + "name": "_lastFocusedTabIndex", "defaultValue": "null", "deprecated": false, "deprecationMessage": "", - "type": "OverlayRef | null", + "type": "number | null", + "optional": false, + "description": "

Index of the tab that was focused last.

\n", + "line": 120, + "rawdescription": "\nIndex of the tab that was focused last.", + "modifierKind": [ + 121 + ] + }, + { + "name": "_preserveContent", + "defaultValue": "false", + "deprecated": false, + "deprecationMessage": "", + "type": "boolean", "optional": false, "description": "", - "line": 96, + "line": 237, "modifierKind": [ 121 ] }, { - "name": "_portal", + "name": "_selectedIndex", + "defaultValue": "null", "deprecated": false, "deprecationMessage": "", - "type": "TemplatePortal", + "type": "number | null", "optional": false, "description": "", - "line": 95, + "line": 174, "modifierKind": [ 121 ] }, { - "name": "_scrollStrategy", + "name": "_stretchTabs", + "defaultValue": "true", "deprecated": false, "deprecationMessage": "", - "type": "function", + "type": "", "optional": false, "description": "", - "line": 101, + "line": 150, "modifierKind": [ 121 ] - } - ], - "methodsClass": [ + }, { - "name": "_checkMenu", - "args": [], + "name": "_tabBodyWrapper", + "deprecated": false, + "deprecationMessage": "", + "type": "ElementRef", "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 330, + "description": "", + "line": 110, + "decorators": [ + { + "name": "ViewChild", + "stringifiedArguments": "'tabBodyWrapper'" + } + ] + }, + { + "name": "_tabBodyWrapperHeight", + "defaultValue": "0", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\n\nThis method checks that a valid instance of OuiMenu has been passed into\nouiMenuTriggerFor. If not, an exception is thrown.\n", - "description": "

This method checks that a valid instance of OuiMenu has been passed into\nouiMenuTriggerFor. If not, an exception is thrown.

\n", + "type": "number", + "optional": false, + "description": "

Snapshot of the height of the tab body wrapper before another tab is activated.

\n", + "line": 123, + "rawdescription": "\nSnapshot of the height of the tab body wrapper before another tab is activated.", "modifierKind": [ 121 ] }, { - "name": "_cleanUpSubscriptions", - "args": [], + "name": "_tabHeader", + "deprecated": false, + "deprecationMessage": "", + "type": "MatTabHeader", "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 464, + "description": "", + "line": 111, + "decorators": [ + { + "name": "ViewChild", + "stringifiedArguments": "'tabHeader'" + } + ] + }, + { + "name": "_tabLabelSubscription", + "defaultValue": "Subscription.EMPTY", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nCleans up the active subscriptions.", - "description": "

Cleans up the active subscriptions.

\n", + "type": "", + "optional": false, + "description": "

Subscription to changes in the tab labels.

\n", + "line": 129, + "rawdescription": "\nSubscription to changes in the tab labels.", "modifierKind": [ 121 ] }, { - "name": "_createOverlay", - "args": [], + "name": "_tabs", + "defaultValue": "new QueryList()", + "deprecated": false, + "deprecationMessage": "", + "type": "QueryList", "optional": false, - "returnType": "OverlayRef", - "typeParameters": [], - "line": 340, + "description": "

All of the tabs that belong to the group.

\n", + "line": 114, + "rawdescription": "\nAll of the tabs that belong to the group." + }, + { + "name": "_tabsSubscription", + "defaultValue": "Subscription.EMPTY", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\n\nThis method creates the overlay from the provided menu's template and saves its\nOverlayRef so that it can be attached to the DOM when openMenu is called.\n", - "description": "

This method creates the overlay from the provided menu's template and saves its\nOverlayRef so that it can be attached to the DOM when openMenu is called.

\n", + "type": "", + "optional": false, + "description": "

Subscription to tabs being added/removed.

\n", + "line": 126, + "rawdescription": "\nSubscription to tabs being added/removed.", "modifierKind": [ 121 ] }, { - "name": "_destroyMenu", + "name": "getHTMLText", + "deprecated": false, + "deprecationMessage": "", + "type": "any", + "optional": false, + "description": "", + "line": 275 + }, + { + "name": "updatedTabHTML", + "deprecated": false, + "deprecationMessage": "", + "type": "any", + "optional": false, + "description": "", + "line": 276 + } + ], + "methodsClass": [ + { + "name": "_clampTabIndex", "args": [ { - "name": "event", - "type": "", + "name": "index", + "type": "number | null", "deprecated": false, "deprecationMessage": "" } ], "optional": false, - "returnType": "void", + "returnType": "number", "typeParameters": [], - "line": 264, + "line": 508, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nCloses the menu and does the necessary cleanup.", - "description": "

Closes the menu and does the necessary cleanup.

\n", + "rawdescription": "\nClamps the given index to the bounds of 0 and the tabs length.", + "description": "

Clamps the given index to the bounds of 0 and the tabs length.

\n", "modifierKind": [ 121 ], "jsdoctags": [ { - "name": "event", - "type": "", + "name": "index", + "type": "number | null", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -15929,55 +25687,42 @@ ] }, { - "name": "_getOverlayConfig", - "args": [], + "name": "_createChangeEvent", + "args": [ + { + "name": "index", + "type": "number", + "deprecated": false, + "deprecationMessage": "" + } + ], "optional": false, - "returnType": "OverlayConfig", + "returnType": "MatTabChangeEvent", "typeParameters": [], - "line": 362, + "line": 474, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\n\nThis method builds the configuration object needed to create the overlay, the OverlayState.\n\n", - "description": "

This method builds the configuration object needed to create the overlay, the OverlayState.

\n", "modifierKind": [ 121 ], "jsdoctags": [ { + "name": "index", + "type": "number", + "deprecated": false, + "deprecationMessage": "", "tagName": { - "pos": 10858, - "end": 10865, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 79, - "escapedText": "returns" - }, - "comment": "

OverlayConfig

\n" + "text": "param" + } } ] }, { - "name": "_getPortal", - "args": [], - "optional": false, - "returnType": "TemplatePortal", - "typeParameters": [], - "line": 560, - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nGets the portal that should be attached to the overlay.", - "description": "

Gets the portal that should be attached to the overlay.

\n", - "modifierKind": [ - 121 - ] - }, - { - "name": "_handleClick", + "name": "_focusChanged", "args": [ { - "name": "event", - "type": "MouseEvent", + "name": "index", + "type": "number", "deprecated": false, "deprecationMessage": "" } @@ -15985,15 +25730,13 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 513, + "line": 469, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nHandles click events on the trigger.", - "description": "

Handles click events on the trigger.

\n", "jsdoctags": [ { - "name": "event", - "type": "MouseEvent", + "name": "index", + "type": "number", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -16003,52 +25746,27 @@ ] }, { - "name": "_handleFocus", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 523, - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "_handleHover", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 531, - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nHandles the cases where the user hovers over the trigger.", - "description": "

Handles the cases where the user hovers over the trigger.

\n", - "modifierKind": [ - 121 - ] - }, - { - "name": "_handleKeydown", + "name": "_getTabContentId", "args": [ { - "name": "event", - "type": "KeyboardEvent", + "name": "i", + "type": "number", "deprecated": false, "deprecationMessage": "" } ], "optional": false, - "returnType": "void", + "returnType": "string", "typeParameters": [], - "line": 503, + "line": 521, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nHandles key presses on the trigger.", - "description": "

Handles key presses on the trigger.

\n", + "rawdescription": "\nReturns a unique id for each tab content element", + "description": "

Returns a unique id for each tab content element

\n", "jsdoctags": [ { - "name": "event", - "type": "KeyboardEvent", + "name": "i", + "type": "number", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -16058,27 +25776,27 @@ ] }, { - "name": "_handleMousedown", + "name": "_getTabIndex", "args": [ { - "name": "event", - "type": "MouseEvent", + "name": "index", + "type": "number", "deprecated": false, "deprecationMessage": "" } ], "optional": false, - "returnType": "void", + "returnType": "number", "typeParameters": [], - "line": 487, + "line": 566, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nHandles mouse presses on the trigger.", - "description": "

Handles mouse presses on the trigger.

\n", + "rawdescription": "\nRetrieves the tabindex for the tab.", + "description": "

Retrieves the tabindex for the tab.

\n", "jsdoctags": [ { - "name": "event", - "type": "MouseEvent", + "name": "index", + "type": "number", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -16088,60 +25806,27 @@ ] }, { - "name": "_initMenu", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 285, - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\n\nThis method sets the menu state to open and focuses the first item if\nthe menu was opened via the keyboard.\n", - "description": "

This method sets the menu state to open and focuses the first item if\nthe menu was opened via the keyboard.

\n", - "modifierKind": [ - 121 - ] - }, - { - "name": "_menuClosingActions", - "args": [], - "optional": false, - "returnType": "any", - "typeParameters": [], - "line": 470, - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nReturns a stream that emits whenever an action that should close the menu occurs.", - "description": "

Returns a stream that emits whenever an action that should close the menu occurs.

\n", - "modifierKind": [ - 121 - ] - }, - { - "name": "_resetMenu", + "name": "_getTabLabelId", "args": [ { - "name": "event", - "type": "", + "name": "i", + "type": "number", "deprecated": false, "deprecationMessage": "" } ], "optional": false, - "returnType": "void", + "returnType": "string", "typeParameters": [], - "line": 297, + "line": 516, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\n\nThis method resets the menu when it's closed, most importantly restoring\nfocus to the menu trigger if the menu was opened via the keyboard.\n", - "description": "

This method resets the menu when it's closed, most importantly restoring\nfocus to the menu trigger if the menu was opened via the keyboard.

\n", - "modifierKind": [ - 121 - ], + "rawdescription": "\nReturns a unique id for each tab label element", + "description": "

Returns a unique id for each tab label element

\n", "jsdoctags": [ { - "name": "event", - "type": "", + "name": "i", + "type": "number", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -16151,11 +25836,23 @@ ] }, { - "name": "_setIsMenuOpen", + "name": "_handleClick", "args": [ { - "name": "isOpen", - "type": "boolean", + "name": "tab", + "type": "MatTab", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "tabHeader", + "type": "MatTabGroupBaseHeader", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "index", + "type": "number", "deprecated": false, "deprecationMessage": "" } @@ -16163,16 +25860,33 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 317, + "line": 554, "deprecated": false, "deprecationMessage": "", - "modifierKind": [ - 121 - ], + "rawdescription": "\nHandle click events, setting new selected index if appropriate.", + "description": "

Handle click events, setting new selected index if appropriate.

\n", "jsdoctags": [ { - "name": "isOpen", - "type": "boolean", + "name": "tab", + "type": "MatTab", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "tabHeader", + "type": "MatTabGroupBaseHeader", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "index", + "type": "number", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -16182,11 +25896,33 @@ ] }, { - "name": "_setPosition", + "name": "_handleEnter", + "args": [], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 487, + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "_removeTabBodyWrapperHeight", + "args": [], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 546, + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nRemoves the height of the tab body wrapper.", + "description": "

Removes the height of the tab body wrapper.

\n" + }, + { + "name": "_setTabBodyWrapperHeight", "args": [ { - "name": "positionStrategy", - "type": "FlexibleConnectedPositionStrategy", + "name": "tabHeight", + "type": "number", "deprecated": false, "deprecationMessage": "" } @@ -16194,100 +25930,92 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 402, + "line": 529, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\n\nSets the appropriate positions on a position strategy\nso the overlay connects with the trigger correctly.\n\n", - "description": "

Sets the appropriate positions on a position strategy\nso the overlay connects with the trigger correctly.

\n", - "modifierKind": [ - 121 - ], + "rawdescription": "\n\nSets the height of the body wrapper to the height of the activating tab if dynamic\nheight property is true.\n", + "description": "

Sets the height of the body wrapper to the height of the activating tab if dynamic\nheight property is true.

\n", "jsdoctags": [ { - "name": { - "pos": 12232, - "end": 12248, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 79, - "escapedText": "positionStrategy" - }, - "type": "FlexibleConnectedPositionStrategy", + "name": "tabHeight", + "type": "number", "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 12226, - "end": 12231, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 79, - "escapedText": "param" - }, - "comment": "

Strategy whose position to update.

\n" + "text": "param" + } } ] }, { - "name": "_subscribeToPositions", - "args": [ - { - "name": "position", - "type": "FlexibleConnectedPositionStrategy", - "deprecated": false, - "deprecationMessage": "" - } - ], + "name": "_subscribeToAllTabChanges", + "args": [], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 410, + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nListens to changes in all of the tabs.", + "description": "

Listens to changes in all of the tabs.

\n", + "modifierKind": [ + 121 + ] + }, + { + "name": "_subscribeToTabLabels", + "args": [], "optional": false, "returnType": "void", "typeParameters": [], - "line": 381, + "line": 497, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\n\nListens to changes in the position of the overlay and sets the correct classes\non the menu based on the new position. This ensures the animation origin is always\ncorrect, even if a fallback position is used for the overlay.\n", - "description": "

Listens to changes in the position of the overlay and sets the correct classes\non the menu based on the new position. This ensures the animation origin is always\ncorrect, even if a fallback position is used for the overlay.

\n", + "rawdescription": "\n\nSubscribes to changes in the tab labels. This is needed, because the @Input for the label is\non the MatTab component, whereas the data binding is inside the MatTabGroup. In order for the\nbinding to be updated, we need to subscribe to changes in it and trigger change detection\nmanually.\n", + "description": "

Subscribes to changes in the tab labels. This is needed, because the @Input for the label is\non the MatTab component, whereas the data binding is inside the MatTabGroup. In order for the\nbinding to be updated, we need to subscribe to changes in it and trigger change detection\nmanually.

\n", "modifierKind": [ 121 - ], - "jsdoctags": [ - { - "name": "position", - "type": "FlexibleConnectedPositionStrategy", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } ] }, { - "name": "closeMenu", + "name": "_tabFocusChanged", "args": [ { - "name": "event", - "type": "\"click\" | \"keydown\" | \"tab\"", + "name": "focusOrigin", + "type": "FocusOrigin", "deprecated": false, - "deprecationMessage": "", - "optional": true + "deprecationMessage": "" + }, + { + "name": "index", + "type": "number", + "deprecated": false, + "deprecationMessage": "" } ], "optional": false, "returnType": "void", "typeParameters": [], - "line": 246, + "line": 573, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nCloses the menu.", - "description": "

Closes the menu.

\n", + "rawdescription": "\nCallback for when the focused state of a tab has changed.", + "description": "

Callback for when the focused state of a tab has changed.

\n", "jsdoctags": [ { - "name": "event", - "type": "\"click\" | \"keydown\" | \"tab\"", + "name": "focusOrigin", + "type": "FocusOrigin", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "index", + "type": "number", "deprecated": false, "deprecationMessage": "", - "optional": true, "tagName": { "text": "param" } @@ -16295,113 +26023,123 @@ ] }, { - "name": "focus", + "name": "focusTab", "args": [ { - "name": "origin", - "type": "FocusOrigin", + "name": "index", + "type": "number", "deprecated": false, - "deprecationMessage": "", - "defaultValue": "'program'" + "deprecationMessage": "" } ], "optional": false, "returnType": "void", "typeParameters": [], - "line": 255, + "line": 461, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\n\nFocuses the menu trigger.\n\n", - "description": "

Focuses the menu trigger.

\n", + "rawdescription": "\n\nSets focus to a particular tab.\n", + "description": "

Sets focus to a particular tab.

\n", "jsdoctags": [ { "name": { - "pos": 7358, - "end": 7364, + "pos": 16176, + "end": 16181, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, "kind": 79, - "escapedText": "origin" + "escapedText": "index" }, - "type": "FocusOrigin", + "type": "number", "deprecated": false, "deprecationMessage": "", - "defaultValue": "'program'", "tagName": { - "pos": 7352, - "end": 7357, + "pos": 16170, + "end": 16175, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, "kind": 79, "escapedText": "param" }, - "comment": "

Source of the menu trigger's focus.

\n" + "comment": "

Index of the tab to be focused.

\n" } ] }, { - "name": "ngAfterContentInit", + "name": "ngAfterContentChecked", "args": [], "optional": false, "returnType": "void", "typeParameters": [], - "line": 180, + "line": 313, "deprecated": false, - "deprecationMessage": "" + "deprecationMessage": "", + "rawdescription": "\n\nAfter the content is checked, this component knows what tabs have been defined\nand what the selected index should be. This is where we can know exactly what position\neach tab should be in according to the new selected index, and additionally we know how\na new selected tab should transition in (from the left or right).\n", + "description": "

After the content is checked, this component knows what tabs have been defined\nand what the selected index should be. This is where we can know exactly what position\neach tab should be in according to the new selected index, and additionally we know how\na new selected tab should transition in (from the left or right).

\n" }, { - "name": "ngOnDestroy", + "name": "ngAfterContentInit", "args": [], "optional": false, "returnType": "void", "typeParameters": [], - "line": 185, + "line": 363, "deprecated": false, "deprecationMessage": "" }, { - "name": "openMenu", + "name": "ngOnDestroy", "args": [], "optional": false, "returnType": "void", "typeParameters": [], - "line": 216, + "line": 431, "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nOpens the menu.", - "description": "

Opens the menu.

\n" + "deprecationMessage": "" }, { - "name": "toggleMenu", + "name": "realignInkBar", "args": [], "optional": false, "returnType": "void", "typeParameters": [], - "line": 211, + "line": 438, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nToggles the menu between the open and closed states.", - "description": "

Toggles the menu between the open and closed states.

\n" + "rawdescription": "\nRe-aligns the ink bar to the selected tab element.", + "description": "

Re-aligns the ink bar to the selected tab element.

\n" }, { - "name": "triggersSubmenu", + "name": "updatePagination", "args": [], "optional": false, - "returnType": "boolean", + "returnType": "void", "typeParameters": [], - "line": 206, + "line": 451, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nWhether the menu triggers a sub-menu or a top-level one.", - "description": "

Whether the menu triggers a sub-menu or a top-level one.

\n" + "rawdescription": "\n\nRecalculates the tab group's pagination dimensions.\n\nWARNING: Calling this method can be very costly in terms of performance. It should be called\nas infrequently as possible from outside of the Tabs component as it causes a reflow of the\npage.\n", + "description": "

Recalculates the tab group's pagination dimensions.

\n

WARNING: Calling this method can be very costly in terms of performance. It should be called\nas infrequently as possible from outside of the Tabs component as it causes a reflow of the\npage.

\n" } ], - "implements": [ - "AfterContentInit", - "OnDestroy" + "deprecated": false, + "deprecationMessage": "", + "hostBindings": [], + "hostListeners": [], + "description": "

Material design tab-group component. Supports basic tab pairs (label + content) and includes\nanimated ink-bar, keyboard navigation, and screen reader.\nSee: https://material.io/design/components/tabs.html

\n", + "rawdescription": "\n\nMaterial design tab-group component. Supports basic tab pairs (label + content) and includes\nanimated ink-bar, keyboard navigation, and screen reader.\nSee: https://material.io/design/components/tabs.html\n", + "type": "component", + "sourceCode": "import {\r\n AfterContentChecked,\r\n AfterContentInit,\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n ContentChildren,\r\n ElementRef,\r\n EventEmitter,\r\n Inject,\r\n Input,\r\n OnDestroy,\r\n Optional,\r\n Output,\r\n QueryList,\r\n ViewChild,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\nimport {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations';\r\nimport {MAT_TAB_GROUP, MatTab} from './tab';\r\nimport {MatTabHeader} from './tab-header';\r\nimport {\r\n BooleanInput,\r\n coerceBooleanProperty,\r\n coerceNumberProperty,\r\n NumberInput,\r\n} from '@angular/cdk/coercion';\r\nimport {\r\n CanColor,\r\n CanDisableRipple,\r\n mixinColor,\r\n mixinDisableRipple,\r\n ThemePalette,\r\n} from '../core';\r\nimport {merge, Subscription} from 'rxjs';\r\nimport {MAT_TABS_CONFIG, MatTabsConfig} from './tab-config';\r\nimport {startWith} from 'rxjs/operators';\r\nimport {FocusOrigin} from '@angular/cdk/a11y';\r\n\r\n/** Used to generate unique ID's for each tab component */\r\nlet nextId = 0;\r\n\r\n// Boilerplate for applying mixins to MatTabGroup.\r\n/** @docs-private */\r\nconst _MatTabGroupMixinBase = mixinColor(\r\n mixinDisableRipple(\r\n class {\r\n constructor(public _elementRef: ElementRef) {}\r\n },\r\n ),\r\n 'primary',\r\n);\r\n\r\n/** @docs-private */\r\nexport interface MatTabGroupBaseHeader {\r\n _alignInkBarToSelectedTab(): void;\r\n updatePagination(): void;\r\n focusIndex: number;\r\n}\r\n\r\n/** Possible positions for the tab header. */\r\nexport type MatTabHeaderPosition = 'above' | 'below';\r\n\r\n/**\r\n * Material design tab-group component. Supports basic tab pairs (label + content) and includes\r\n * animated ink-bar, keyboard navigation, and screen reader.\r\n * See: https://material.io/design/components/tabs.html\r\n */\r\n@Component({\r\n selector: 'mat-tab-group',\r\n exportAs: 'matTabGroup',\r\n templateUrl: 'tab-group.html',\r\n styleUrls: ['tab-group.scss'],\r\n encapsulation: ViewEncapsulation.None,\r\n // tslint:disable-next-line:validate-decorators\r\n changeDetection: ChangeDetectionStrategy.Default,\r\n inputs: ['color', 'disableRipple'],\r\n providers: [\r\n {\r\n provide: MAT_TAB_GROUP,\r\n useExisting: MatTabGroup,\r\n },\r\n ],\r\n host: {\r\n 'ngSkipHydration': '',\r\n 'class': 'mat-mdc-tab-group oui-tab',\r\n '[class.mat-mdc-tab-group-dynamic-height]': 'dynamicHeight',\r\n '[class.mat-mdc-tab-group-inverted-header]': 'headerPosition === \"below\"',\r\n '[class.mat-mdc-tab-group-stretch-tabs]': 'stretchTabs',\r\n '[style.--mat-tab-animation-duration]': 'animationDuration',\r\n },\r\n})\r\nexport class MatTabGroup\r\n extends _MatTabGroupMixinBase\r\n implements AfterContentInit, AfterContentChecked, OnDestroy, CanColor, CanDisableRipple\r\n{\r\n /**\r\n * All tabs inside the tab group. This includes tabs that belong to groups that are nested\r\n * inside the current one. We filter out only the tabs that belong to this group in `_tabs`.\r\n */\r\n @ContentChildren(MatTab, {descendants: true}) _allTabs: QueryList;\r\n @ViewChild('tabBodyWrapper') _tabBodyWrapper: ElementRef;\r\n @ViewChild('tabHeader') _tabHeader: MatTabHeader;\r\n\r\n /** All of the tabs that belong to the group. */\r\n _tabs: QueryList = new QueryList();\r\n\r\n /** The tab index that should be selected after the content has been checked. */\r\n private _indexToSelect: number | null = 0;\r\n\r\n /** Index of the tab that was focused last. */\r\n private _lastFocusedTabIndex: number | null = null;\r\n\r\n /** Snapshot of the height of the tab body wrapper before another tab is activated. */\r\n private _tabBodyWrapperHeight: number = 0;\r\n\r\n /** Subscription to tabs being added/removed. */\r\n private _tabsSubscription = Subscription.EMPTY;\r\n\r\n /** Subscription to changes in the tab labels. */\r\n private _tabLabelSubscription = Subscription.EMPTY;\r\n\r\n /** Whether the ink bar should fit its width to the size of the tab label content. */\r\n @Input()\r\n get fitInkBarToContent(): boolean {\r\n return this._fitInkBarToContent;\r\n }\r\n set fitInkBarToContent(v: BooleanInput) {\r\n this._fitInkBarToContent = coerceBooleanProperty(v);\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n private _fitInkBarToContent = false;\r\n\r\n /** Whether tabs should be stretched to fill the header. */\r\n @Input('mat-stretch-tabs')\r\n get stretchTabs(): boolean {\r\n return this._stretchTabs;\r\n }\r\n set stretchTabs(v: BooleanInput) {\r\n this._stretchTabs = coerceBooleanProperty(v);\r\n }\r\n private _stretchTabs = true;\r\n\r\n /** Whether the tab group should grow to the size of the active tab. */\r\n @Input()\r\n get dynamicHeight(): boolean {\r\n return this._dynamicHeight;\r\n }\r\n\r\n set dynamicHeight(value: BooleanInput) {\r\n this._dynamicHeight = coerceBooleanProperty(value);\r\n }\r\n\r\n private _dynamicHeight: boolean = false;\r\n\r\n /** The index of the active tab. */\r\n @Input()\r\n get selectedIndex(): number | null {\r\n return this._selectedIndex;\r\n }\r\n\r\n set selectedIndex(value: NumberInput) {\r\n this._indexToSelect = coerceNumberProperty(value, null);\r\n }\r\n\r\n private _selectedIndex: number | null = null;\r\n\r\n /** Position of the tab header. */\r\n @Input() headerPosition: MatTabHeaderPosition = 'above';\r\n\r\n /** Duration for the tab animation. Will be normalized to milliseconds if no units are set. */\r\n @Input()\r\n get animationDuration(): string {\r\n return this._animationDuration;\r\n }\r\n\r\n set animationDuration(value: NumberInput) {\r\n this._animationDuration = /^\\d+$/.test(value + '') ? value + 'ms' : (value as string);\r\n }\r\n\r\n private _animationDuration: string;\r\n\r\n /**\r\n * `tabindex` to be set on the inner element that wraps the tab content. Can be used for improved\r\n * accessibility when the tab does not have focusable elements or if it has scrollable content.\r\n * The `tabindex` will be removed automatically for inactive tabs.\r\n * Read more at https://www.w3.org/TR/wai-aria-practices/examples/tabs/tabs-2/tabs.html\r\n */\r\n @Input()\r\n get contentTabIndex(): number | null {\r\n return this._contentTabIndex;\r\n }\r\n\r\n set contentTabIndex(value: NumberInput) {\r\n this._contentTabIndex = coerceNumberProperty(value, null);\r\n }\r\n\r\n private _contentTabIndex: number | null;\r\n\r\n /**\r\n * Whether pagination should be disabled. This can be used to avoid unnecessary\r\n * layout recalculations if it's known that pagination won't be required.\r\n */\r\n @Input()\r\n get disablePagination(): boolean {\r\n return this._disablePagination;\r\n }\r\n\r\n set disablePagination(value: BooleanInput) {\r\n this._disablePagination = coerceBooleanProperty(value);\r\n }\r\n\r\n private _disablePagination: boolean = false;\r\n\r\n /**\r\n * By default tabs remove their content from the DOM while it's off-screen.\r\n * Setting this to `true` will keep it in the DOM which will prevent elements\r\n * like iframes and videos from reloading next time it comes back into the view.\r\n */\r\n @Input()\r\n get preserveContent(): boolean {\r\n return this._preserveContent;\r\n }\r\n\r\n set preserveContent(value: BooleanInput) {\r\n this._preserveContent = coerceBooleanProperty(value);\r\n }\r\n\r\n private _preserveContent: boolean = false;\r\n\r\n /** Background color of the tab group. */\r\n @Input()\r\n get backgroundColor(): ThemePalette {\r\n return this._backgroundColor;\r\n }\r\n\r\n set backgroundColor(value: ThemePalette) {\r\n console.log('value value', value);\r\n const classList: DOMTokenList = this._elementRef.nativeElement.classList;\r\n\r\n classList.remove('mat-tabs-with-background', `mat-background-${this.backgroundColor}`);\r\n\r\n if (value) {\r\n classList.add('mat-tabs-with-background', `mat-background-${value}`);\r\n }\r\n\r\n this._backgroundColor = value;\r\n }\r\n\r\n private _backgroundColor: ThemePalette;\r\n\r\n /** Output to enable support for two-way binding on `[(selectedIndex)]` */\r\n @Output() readonly selectedIndexChange: EventEmitter = new EventEmitter();\r\n\r\n /** Event emitted when focus has changed within a tab group. */\r\n @Output() readonly focusChange: EventEmitter =\r\n new EventEmitter();\r\n\r\n /** Event emitted when the body animation has completed */\r\n @Output() readonly animationDone: EventEmitter = new EventEmitter();\r\n\r\n /** Event emitted when the tab selection has changed. */\r\n @Output() readonly selectedTabChange: EventEmitter =\r\n new EventEmitter(true);\r\n\r\n private _groupId: number;\r\n getHTMLText: any;\r\n updatedTabHTML: any;\r\n\r\n\r\n constructor(\r\n elementRef: ElementRef,\r\n private _changeDetectorRef: ChangeDetectorRef,\r\n @Inject(MAT_TABS_CONFIG) @Optional() defaultConfig?: MatTabsConfig,\r\n @Optional() @Inject(ANIMATION_MODULE_TYPE) public _animationMode?: string,\r\n ) {\r\n super(elementRef);\r\n console.log(elementRef)\r\n this._groupId = nextId++;\r\n this.animationDuration =\r\n defaultConfig && defaultConfig.animationDuration ? defaultConfig.animationDuration : '500ms';\r\n this.disablePagination =\r\n defaultConfig && defaultConfig.disablePagination != null\r\n ? defaultConfig.disablePagination\r\n : false;\r\n this.dynamicHeight =\r\n defaultConfig && defaultConfig.dynamicHeight != null ? defaultConfig.dynamicHeight : false;\r\n this.contentTabIndex = defaultConfig?.contentTabIndex ?? null;\r\n this.preserveContent = !!defaultConfig?.preserveContent;\r\n console.log(this.preserveContent)\r\n this.fitInkBarToContent =\r\n defaultConfig && defaultConfig.fitInkBarToContent != null\r\n ? defaultConfig.fitInkBarToContent\r\n : false;\r\n this.stretchTabs =\r\n defaultConfig && defaultConfig.stretchTabs != null ? defaultConfig.stretchTabs : true;\r\n }\r\n\r\n /**\r\n * After the content is checked, this component knows what tabs have been defined\r\n * and what the selected index should be. This is where we can know exactly what position\r\n * each tab should be in according to the new selected index, and additionally we know how\r\n * a new selected tab should transition in (from the left or right).\r\n */\r\n ngAfterContentChecked() {\r\n // Don't clamp the `indexToSelect` immediately in the setter because it can happen that\r\n // the amount of tabs changes before the actual change detection runs.\r\n const indexToSelect = (this._indexToSelect = this._clampTabIndex(this._indexToSelect));\r\n\r\n // If there is a change in selected index, emit a change event. Should not trigger if\r\n // the selected index has not yet been initialized.\r\n if (this._selectedIndex != indexToSelect) {\r\n const isFirstRun = this._selectedIndex == null;\r\n\r\n if (!isFirstRun) {\r\n this.selectedTabChange.emit(this._createChangeEvent(indexToSelect));\r\n // Preserve the height so page doesn't scroll up during tab change.\r\n // Fixes https://stackblitz.com/edit/mat-tabs-scroll-page-top-on-tab-change\r\n const wrapper = this._tabBodyWrapper.nativeElement;\r\n wrapper.style.minHeight = wrapper.clientHeight + 'px';\r\n }\r\n\r\n // Changing these values after change detection has run\r\n // since the checked content may contain references to them.\r\n Promise.resolve().then(() => {\r\n this._tabs.forEach((tab, index) => (tab.isActive = index === indexToSelect));\r\n\r\n if (!isFirstRun) {\r\n this.selectedIndexChange.emit(indexToSelect);\r\n // Clear the min-height, this was needed during tab change to avoid\r\n // unnecessary scrolling.\r\n this._tabBodyWrapper.nativeElement.style.minHeight = '';\r\n }\r\n });\r\n }\r\n\r\n // Setup the position for each tab and optionally setup an origin on the next selected tab.\r\n this._tabs.forEach((tab: MatTab, index: number) => {\r\n tab.position = index - indexToSelect;\r\n\r\n // If there is already a selected tab, then set up an origin for the next selected tab\r\n // if it doesn't have one already.\r\n if (this._selectedIndex != null && tab.position == 0 && !tab.origin) {\r\n tab.origin = indexToSelect - this._selectedIndex;\r\n }\r\n });\r\n\r\n if (this._selectedIndex !== indexToSelect) {\r\n this._selectedIndex = indexToSelect;\r\n this._lastFocusedTabIndex = null;\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n }\r\n\r\n ngAfterContentInit() {\r\n console.log(';;;;;;;;;;;;', this._tabs)\r\n this._subscribeToAllTabChanges();\r\n this._subscribeToTabLabels();\r\n console.log(this._tabs)\r\n // Subscribe to changes in the amount of tabs, in order to be\r\n // able to re-render the content as new tabs are added or removed.\r\n this._tabsSubscription = this._tabs.changes.subscribe(() => {\r\n console.log(\"change\")\r\n const indexToSelect = this._clampTabIndex(this._indexToSelect);\r\n\r\n // Maintain the previously-selected tab if a new tab is added or removed and there is no\r\n // explicit change that selects a different tab.\r\n console.log('>>>>>>>>>>>>>', indexToSelect, this._selectedIndex)\r\n if (indexToSelect === this._selectedIndex) {\r\n const tabs = this._tabs.toArray();\r\n let selectedTab: MatTab | undefined;\r\n\r\n for (let i = 0; i < tabs.length; i++) {\r\n console.log('tabsi',tabs[i])\r\n if (tabs[i].isActive) {\r\n // Assign both to the `_indexToSelect` and `_selectedIndex` so we don't fire a changed\r\n // event, otherwise the consumer may end up in an infinite loop in some edge cases like\r\n // adding a tab within the `selectedIndexChange` event.\r\n this._indexToSelect = this._selectedIndex = i;\r\n this._lastFocusedTabIndex = null;\r\n selectedTab = tabs[i];\r\n break;\r\n }\r\n }\r\n\r\n // If we haven't found an active tab and a tab exists at the selected index, it means\r\n // that the active tab was swapped out. Since this won't be picked up by the rendering\r\n // loop in `ngAfterContentChecked`, we need to sync it up manually.\r\n if (!selectedTab && tabs[indexToSelect]) {\r\n Promise.resolve().then(() => {\r\n tabs[indexToSelect].isActive = true;\r\n this.selectedTabChange.emit(this._createChangeEvent(indexToSelect));\r\n });\r\n }\r\n }\r\n\r\n this._changeDetectorRef.markForCheck();\r\n });\r\n }\r\n\r\n /** Listens to changes in all of the tabs. */\r\n private _subscribeToAllTabChanges() {\r\n // Since we use a query with `descendants: true` to pick up the tabs, we may end up catching\r\n // some that are inside of nested tab groups. We filter them out manually by checking that\r\n // the closest group to the tab is the current one.\r\n console.log(\"_subscribeToAllTabChanges\", this._allTabs)\r\n this.getHTMLText = this._allTabs['_results'][0].givenText;\r\n this.updatedTabHTML = this.getHTMLText;\r\n this._allTabs['_results'].forEach((tabData)=>{\r\n this._tabs.myKey = tabData.givenText;\r\n })\r\n this._allTabs.changes.pipe(startWith(this._allTabs)).subscribe((tabs: QueryList) => {\r\n this._tabs.reset(\r\n tabs.filter(tab => {\r\n console.log(tab)\r\n return tab._closestTabGroup === this || !tab._closestTabGroup;\r\n }),\r\n );\r\n this._tabs.notifyOnChanges();\r\n });\r\n }\r\n\r\n ngOnDestroy() {\r\n this._tabs.destroy();\r\n this._tabsSubscription.unsubscribe();\r\n this._tabLabelSubscription.unsubscribe();\r\n }\r\n\r\n /** Re-aligns the ink bar to the selected tab element. */\r\n realignInkBar() {\r\n if (this._tabHeader) {\r\n this._tabHeader._alignInkBarToSelectedTab();\r\n }\r\n }\r\n\r\n /**\r\n * Recalculates the tab group's pagination dimensions.\r\n *\r\n * WARNING: Calling this method can be very costly in terms of performance. It should be called\r\n * as infrequently as possible from outside of the Tabs component as it causes a reflow of the\r\n * page.\r\n */\r\n updatePagination() {\r\n if (this._tabHeader) {\r\n this._tabHeader.updatePagination();\r\n }\r\n }\r\n\r\n /**\r\n * Sets focus to a particular tab.\r\n * @param index Index of the tab to be focused.\r\n */\r\n focusTab(index: number) {\r\n const header = this._tabHeader;\r\n\r\n if (header) {\r\n header.focusIndex = index;\r\n }\r\n }\r\n\r\n _focusChanged(index: number) {\r\n this._lastFocusedTabIndex = index;\r\n this.focusChange.emit(this._createChangeEvent(index));\r\n }\r\n\r\n private _createChangeEvent(index: number): MatTabChangeEvent {\r\n const event = new MatTabChangeEvent();\r\n event.index = index;\r\n if (this._tabs && this._tabs.length) {\r\n event.tab = this._tabs.toArray()[index];\r\n console.log('_createChangeEvent', event, this._tabs);\r\n this.updatedTabHTML = event.tab.givenText;\r\n console.log(this.updatedTabHTML)\r\n }\r\n return event;\r\n }\r\n\r\n \r\n _handleEnter() {\r\n this.getHTMLText = this.updatedTabHTML;\r\n }\r\n\r\n /**\r\n * Subscribes to changes in the tab labels. This is needed, because the @Input for the label is\r\n * on the MatTab component, whereas the data binding is inside the MatTabGroup. In order for the\r\n * binding to be updated, we need to subscribe to changes in it and trigger change detection\r\n * manually.\r\n */\r\n private _subscribeToTabLabels() {\r\n if (this._tabLabelSubscription) {\r\n this._tabLabelSubscription.unsubscribe();\r\n }\r\n\r\n this._tabLabelSubscription = merge(...this._tabs.map(tab => tab._stateChanges)).subscribe(() =>\r\n this._changeDetectorRef.markForCheck(),\r\n );\r\n }\r\n\r\n /** Clamps the given index to the bounds of 0 and the tabs length. */\r\n private _clampTabIndex(index: number | null): number {\r\n // Note the `|| 0`, which ensures that values like NaN can't get through\r\n // and which would otherwise throw the component into an infinite loop\r\n // (since Math.max(NaN, 0) === NaN).\r\n return Math.min(this._tabs.length - 1, Math.max(index || 0, 0));\r\n }\r\n\r\n /** Returns a unique id for each tab label element */\r\n _getTabLabelId(i: number): string {\r\n return `mat-tab-label-${this._groupId}-${i}`;\r\n }\r\n\r\n /** Returns a unique id for each tab content element */\r\n _getTabContentId(i: number): string {\r\n return `mat-tab-content-${this._groupId}-${i}`;\r\n }\r\n\r\n /**\r\n * Sets the height of the body wrapper to the height of the activating tab if dynamic\r\n * height property is true.\r\n */\r\n _setTabBodyWrapperHeight(tabHeight: number): void {\r\n if (!this._dynamicHeight || !this._tabBodyWrapperHeight) {\r\n return;\r\n }\r\n\r\n const wrapper: HTMLElement = this._tabBodyWrapper.nativeElement;\r\n\r\n wrapper.style.height = this._tabBodyWrapperHeight + 'px';\r\n\r\n // This conditional forces the browser to paint the height so that\r\n // the animation to the new height can have an origin.\r\n if (this._tabBodyWrapper.nativeElement.offsetHeight) {\r\n wrapper.style.height = tabHeight + 'px';\r\n }\r\n }\r\n\r\n /** Removes the height of the tab body wrapper. */\r\n _removeTabBodyWrapperHeight(): void {\r\n const wrapper = this._tabBodyWrapper.nativeElement;\r\n this._tabBodyWrapperHeight = wrapper.clientHeight;\r\n wrapper.style.height = '';\r\n this.animationDone.emit();\r\n }\r\n\r\n /** Handle click events, setting new selected index if appropriate. */\r\n _handleClick(tab: MatTab, tabHeader: MatTabGroupBaseHeader, index: number) {\r\n // console.log(\"click\")\r\n tabHeader.focusIndex = index;\r\n this.getHTMLText = this.updatedTabHTML;\r\n\r\n if (!tab.disabled) {\r\n console.log('pppppppppppp')\r\n this.selectedIndex = index;\r\n }\r\n }\r\n\r\n /** Retrieves the tabindex for the tab. */\r\n _getTabIndex(index: number): number {\r\n // console.log('get tab index')\r\n const targetIndex = this._lastFocusedTabIndex ?? this.selectedIndex;\r\n return index === targetIndex ? 0 : -1;\r\n }\r\n\r\n /** Callback for when the focused state of a tab has changed. */\r\n _tabFocusChanged(focusOrigin: FocusOrigin, index: number) {\r\n // console.log('focusssss', focusOrigin)\r\n // Mouse/touch focus happens during the `mousedown`/`touchstart` phase which\r\n // can cause the tab to be moved out from under the pointer, interrupting the\r\n // click sequence (see #21898). We don't need to scroll the tab into view for\r\n // such cases anyway, because it will be done when the tab becomes selected.\r\n if (focusOrigin && focusOrigin !== 'mouse' && focusOrigin !== 'touch') {\r\n this._tabHeader.focusIndex = index;\r\n }\r\n }\r\n}\r\n\r\n/** A simple change event emitted on focus or selection changes. */\r\nexport class MatTabChangeEvent {\r\n /** Index of the currently-selected tab. */\r\n index: number;\r\n /** Reference to the currently-selected tab. */\r\n tab: MatTab;\r\n event: Event;\r\n}\r\n", + "assetsDirs": [], + "styleUrlsData": [ + { + "data": "@use '../core/style/private';\r\n@use '../core/style/variables';\r\n@use './tabs-common';\r\n\r\n@include tabs-common.structural-styles;\r\n\r\n.mat-mdc-tab {\r\n @include tabs-common.tab;\r\n\r\n // Note that we only want to target direct descendant tabs.\r\n .mat-mdc-tab-group.mat-mdc-tab-group-stretch-tabs > .mat-mdc-tab-header & {\r\n flex-grow: 1;\r\n }\r\n}\r\n\r\n.mat-mdc-focus-indicator {\r\n position: relative;\r\n}\r\n\r\n.mdc-tab__content {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n height: inherit;\r\n pointer-events: none;\r\n}\r\n\r\n.mat-mdc-tab.mdc-tab--active .mdc-tab__text-label {\r\n color: #333;\r\n}\r\n\r\n.mat-mdc-tab-group {\r\n// @include tabs-common.paginated-tab-header-with-background('.mat-mdc-tab-header', '.mat-mdc-tab');\r\n display: flex;\r\n flex-direction: column;\r\n\r\n // Fixes pagination issues inside flex containers (see #23157).\r\n max-width: 100%;\r\n\r\n &.mat-mdc-tab-group-inverted-header {\r\n flex-direction: column-reverse;\r\n\r\n .mdc-tab-indicator__content--underline {\r\n align-self: flex-start;\r\n }\r\n }\r\n}\r\n\r\n// The bottom section of the view; contains the tab bodies\r\n.mat-mdc-tab-body-wrapper {\r\n// @include private.private-animation-noop();\r\n position: relative;\r\n overflow: hidden;\r\n display: flex;\r\n// transition: height tabs-common.$mat-tab-animation-duration variables.$ease-in-out-curve-function;\r\n}\r\n", + "styleUrl": "tab-group.scss" + } ], + "stylesData": "", "constructorObj": { "name": "constructor", "description": "", @@ -16409,81 +26147,37 @@ "deprecationMessage": "", "args": [ { - "name": "_overlay", - "type": "Overlay", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "_element", - "type": "ElementRef", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "_viewContainerRef", - "type": "ViewContainerRef", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "scrollStrategy", - "type": "any", + "name": "elementRef", + "type": "ElementRef", "deprecated": false, "deprecationMessage": "" }, { - "name": "_parentMenu", - "type": "OuiMenu", + "name": "_changeDetectorRef", + "type": "ChangeDetectorRef", "deprecated": false, "deprecationMessage": "" }, { - "name": "_menuItemInstance", - "type": "OuiMenuItem", + "name": "defaultConfig", + "type": "MatTabsConfig", "deprecated": false, - "deprecationMessage": "" + "deprecationMessage": "", + "optional": true }, { - "name": "_focusMonitor", - "type": "FocusMonitor", + "name": "_animationMode", + "type": "string", "deprecated": false, "deprecationMessage": "", "optional": true } ], - "line": 152, + "line": 276, "jsdoctags": [ { - "name": "_overlay", - "type": "Overlay", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "_element", - "type": "ElementRef", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "_viewContainerRef", - "type": "ViewContainerRef", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "scrollStrategy", - "type": "any", + "name": "elementRef", + "type": "ElementRef", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -16491,8 +26185,8 @@ } }, { - "name": "_parentMenu", - "type": "OuiMenu", + "name": "_changeDetectorRef", + "type": "ChangeDetectorRef", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -16500,17 +26194,18 @@ } }, { - "name": "_menuItemInstance", - "type": "OuiMenuItem", + "name": "defaultConfig", + "type": "MatTabsConfig", "deprecated": false, "deprecationMessage": "", + "optional": true, "tagName": { "text": "param" } }, { - "name": "_focusMonitor", - "type": "FocusMonitor", + "name": "_animationMode", + "type": "string", "deprecated": false, "deprecationMessage": "", "optional": true, @@ -16520,28 +26215,36 @@ } ] }, + "extends": "_MatTabGroupMixinBase", + "implements": [ + "AfterContentInit", + "AfterContentChecked", + "OnDestroy", + "CanColor", + "CanDisableRipple" + ], "accessors": { - "menu": { - "name": "menu", + "fitInkBarToContent": { + "name": "fitInkBarToContent", "setSignature": { - "name": "menu", + "name": "fitInkBarToContent", "type": "void", "deprecated": false, "deprecationMessage": "", "args": [ { - "name": "menu", - "type": "OuiMenuPanel", + "name": "v", + "type": "BooleanInput", "deprecated": false, "deprecationMessage": "" } ], "returnType": "void", - "line": 113, + "line": 136, "jsdoctags": [ { - "name": "menu", - "type": "OuiMenuPanel", + "name": "v", + "type": "BooleanInput", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -16551,1294 +26254,1200 @@ ] }, "getSignature": { - "name": "menu", - "type": "", - "returnType": "", - "line": 110, - "rawdescription": "\nReferences the menu instance that the trigger is associated with.", - "description": "

References the menu instance that the trigger is associated with.

\n" + "name": "fitInkBarToContent", + "type": "boolean", + "returnType": "boolean", + "line": 133, + "rawdescription": "\nWhether the ink bar should fit its width to the size of the tab label content.", + "description": "

Whether the ink bar should fit its width to the size of the tab label content.

\n" } }, - "menuOpen": { - "name": "menuOpen", + "stretchTabs": { + "name": "stretchTabs", + "setSignature": { + "name": "stretchTabs", + "type": "void", + "deprecated": false, + "deprecationMessage": "", + "args": [ + { + "name": "v", + "type": "BooleanInput", + "deprecated": false, + "deprecationMessage": "" + } + ], + "returnType": "void", + "line": 147, + "jsdoctags": [ + { + "name": "v", + "type": "BooleanInput", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + }, "getSignature": { - "name": "menuOpen", + "name": "stretchTabs", "type": "boolean", "returnType": "boolean", - "line": 201, - "rawdescription": "\nWhether the menu is open.", - "description": "

Whether the menu is open.

\n" + "line": 144, + "rawdescription": "\nWhether tabs should be stretched to fill the header.", + "description": "

Whether tabs should be stretched to fill the header.

\n" } - } - } - }, - { - "name": "OuiPanelContent", - "id": "directive-OuiPanelContent-1c45697fb34645f56ab049286f10f2790086bc21a44fe4355cdf5fdda7653c6a39ea6fb7bb608d9c07d9ff2dc66212fba485cfc9d6cfbc49394076f685c2cf4a", - "file": "ui/src/components/panel/panel-content.ts", - "type": "directive", - "description": "

Panel content that will be rendered lazily once the panel is opened.

\n", - "rawdescription": "\n\nPanel content that will be rendered lazily once the panel is opened.\n", - "sourceCode": "import {\r\n Directive,\r\n TemplateRef,\r\n ComponentFactoryResolver,\r\n ApplicationRef,\r\n Injector,\r\n ViewContainerRef,\r\n Inject,\r\n OnDestroy,\r\n} from '@angular/core';\r\nimport { TemplatePortal, DomPortalOutlet } from '@angular/cdk/portal';\r\nimport { DOCUMENT } from '@angular/common';\r\nimport { Subject } from 'rxjs';\r\n\r\n/**\r\n * Panel content that will be rendered lazily once the panel is opened.\r\n */\r\n@Directive({\r\n selector: 'ng-template[ouiPanelContent]',\r\n})\r\nexport class OuiPanelContent implements OnDestroy {\r\n private _portal: TemplatePortal;\r\n private _outlet: DomPortalOutlet;\r\n\r\n /** Emits when the panel content has been attached. */\r\n _attached = new Subject();\r\n\r\n constructor(\r\n private _template: TemplateRef,\r\n private _componentFactoryResolver: ComponentFactoryResolver,\r\n private _appRef: ApplicationRef,\r\n private _injector: Injector,\r\n private _viewContainerRef: ViewContainerRef,\r\n @Inject(DOCUMENT) private _document: Document\r\n ) {}\r\n\r\n /**\r\n * Attaches the content with a particular context.\r\n */\r\n attach(context: any = {}) {\r\n if (!this._portal) {\r\n this._portal = new TemplatePortal(this._template, this._viewContainerRef);\r\n }\r\n\r\n this.detach();\r\n\r\n if (!this._outlet) {\r\n this._outlet = new DomPortalOutlet(\r\n this._document.createElement('div'),\r\n this._componentFactoryResolver,\r\n this._appRef,\r\n this._injector\r\n );\r\n }\r\n\r\n const element: HTMLElement = this._template.elementRef.nativeElement;\r\n\r\n // Because we support opening the same menu from different triggers (which in turn have their\r\n // own `OverlayRef` panel), we have to re-insert the host element every time, otherwise we\r\n // risk it staying attached to a pane that's no longer in the DOM.\r\n element.parentNode!.insertBefore(this._outlet.outletElement, element);\r\n this._portal.attach(this._outlet, context);\r\n this._attached.next();\r\n }\r\n\r\n /**\r\n * Detaches the content.\r\n */\r\n detach() {\r\n if (this._portal.isAttached) {\r\n this._portal.detach();\r\n }\r\n }\r\n\r\n ngOnDestroy() {\r\n if (this._outlet) {\r\n this._outlet.dispose();\r\n }\r\n }\r\n}\r\n", - "selector": "ng-template[ouiPanelContent]", - "providers": [], - "inputsClass": [], - "outputsClass": [], - "deprecated": false, - "deprecationMessage": "", - "hostBindings": [], - "hostListeners": [], - "propertiesClass": [ - { - "name": "_attached", - "defaultValue": "new Subject()", - "deprecated": false, - "deprecationMessage": "", - "type": "", - "optional": false, - "description": "

Emits when the panel content has been attached.

\n", - "line": 26, - "rawdescription": "\nEmits when the panel content has been attached." - }, - { - "name": "_outlet", - "deprecated": false, - "deprecationMessage": "", - "type": "DomPortalOutlet", - "optional": false, - "description": "", - "line": 23, - "modifierKind": [ - 121 - ] - }, - { - "name": "_portal", - "deprecated": false, - "deprecationMessage": "", - "type": "TemplatePortal", - "optional": false, - "description": "", - "line": 22, - "modifierKind": [ - 121 - ] - } - ], - "methodsClass": [ - { - "name": "attach", - "args": [ - { - "name": "context", - "type": "any", - "deprecated": false, - "deprecationMessage": "", - "defaultValue": "{}" - } - ], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 40, - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\n\nAttaches the content with a particular context.\n", - "description": "

Attaches the content with a particular context.

\n", - "jsdoctags": [ - { - "name": "context", - "type": "any", - "deprecated": false, - "deprecationMessage": "", - "defaultValue": "{}", - "tagName": { - "text": "param" - } - } - ] - }, - { - "name": "detach", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 69, - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\n\nDetaches the content.\n", - "description": "

Detaches the content.

\n" }, - { - "name": "ngOnDestroy", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 75, - "deprecated": false, - "deprecationMessage": "" - } - ], - "implements": [ - "OnDestroy" - ], - "constructorObj": { - "name": "constructor", - "description": "", - "deprecated": false, - "deprecationMessage": "", - "args": [ - { - "name": "_template", - "type": "TemplateRef", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "_componentFactoryResolver", - "type": "ComponentFactoryResolver", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "_appRef", - "type": "ApplicationRef", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "_injector", - "type": "Injector", + "dynamicHeight": { + "name": "dynamicHeight", + "setSignature": { + "name": "dynamicHeight", + "type": "void", "deprecated": false, - "deprecationMessage": "" + "deprecationMessage": "", + "args": [ + { + "name": "value", + "type": "BooleanInput", + "deprecated": false, + "deprecationMessage": "" + } + ], + "returnType": "void", + "line": 158, + "jsdoctags": [ + { + "name": "value", + "type": "BooleanInput", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] }, - { - "name": "_viewContainerRef", - "type": "ViewContainerRef", + "getSignature": { + "name": "dynamicHeight", + "type": "boolean", + "returnType": "boolean", + "line": 154, + "rawdescription": "\nWhether the tab group should grow to the size of the active tab.", + "description": "

Whether the tab group should grow to the size of the active tab.

\n" + } + }, + "selectedIndex": { + "name": "selectedIndex", + "setSignature": { + "name": "selectedIndex", + "type": "void", "deprecated": false, - "deprecationMessage": "" + "deprecationMessage": "", + "args": [ + { + "name": "value", + "type": "NumberInput", + "deprecated": false, + "deprecationMessage": "" + } + ], + "returnType": "void", + "line": 170, + "jsdoctags": [ + { + "name": "value", + "type": "NumberInput", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] }, - { - "name": "_document", - "type": "Document", - "deprecated": false, - "deprecationMessage": "" + "getSignature": { + "name": "selectedIndex", + "type": "", + "returnType": "number | null", + "line": 166, + "rawdescription": "\nThe index of the active tab.", + "description": "

The index of the active tab.

\n" } - ], - "line": 26, - "jsdoctags": [ - { - "name": "_template", - "type": "TemplateRef", + }, + "animationDuration": { + "name": "animationDuration", + "setSignature": { + "name": "animationDuration", + "type": "void", "deprecated": false, "deprecationMessage": "", - "tagName": { - "text": "param" - } + "args": [ + { + "name": "value", + "type": "NumberInput", + "deprecated": false, + "deprecationMessage": "" + } + ], + "returnType": "void", + "line": 185, + "jsdoctags": [ + { + "name": "value", + "type": "NumberInput", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] }, - { - "name": "_componentFactoryResolver", - "type": "ComponentFactoryResolver", + "getSignature": { + "name": "animationDuration", + "type": "string", + "returnType": "string", + "line": 181, + "rawdescription": "\nDuration for the tab animation. Will be normalized to milliseconds if no units are set.", + "description": "

Duration for the tab animation. Will be normalized to milliseconds if no units are set.

\n" + } + }, + "contentTabIndex": { + "name": "contentTabIndex", + "setSignature": { + "name": "contentTabIndex", + "type": "void", "deprecated": false, "deprecationMessage": "", - "tagName": { - "text": "param" - } + "args": [ + { + "name": "value", + "type": "NumberInput", + "deprecated": false, + "deprecationMessage": "" + } + ], + "returnType": "void", + "line": 202, + "jsdoctags": [ + { + "name": "value", + "type": "NumberInput", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] }, - { - "name": "_appRef", - "type": "ApplicationRef", + "getSignature": { + "name": "contentTabIndex", + "type": "", + "returnType": "number | null", + "line": 198, + "rawdescription": "\n\n`tabindex` to be set on the inner element that wraps the tab content. Can be used for improved\naccessibility when the tab does not have focusable elements or if it has scrollable content.\nThe `tabindex` will be removed automatically for inactive tabs.\nRead more at https://www.w3.org/TR/wai-aria-practices/examples/tabs/tabs-2/tabs.html\n", + "description": "

tabindex to be set on the inner element that wraps the tab content. Can be used for improved\naccessibility when the tab does not have focusable elements or if it has scrollable content.\nThe tabindex will be removed automatically for inactive tabs.\nRead more at https://www.w3.org/TR/wai-aria-practices/examples/tabs/tabs-2/tabs.html

\n" + } + }, + "disablePagination": { + "name": "disablePagination", + "setSignature": { + "name": "disablePagination", + "type": "void", "deprecated": false, "deprecationMessage": "", - "tagName": { - "text": "param" - } + "args": [ + { + "name": "value", + "type": "BooleanInput", + "deprecated": false, + "deprecationMessage": "" + } + ], + "returnType": "void", + "line": 217, + "jsdoctags": [ + { + "name": "value", + "type": "BooleanInput", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] }, - { - "name": "_injector", - "type": "Injector", + "getSignature": { + "name": "disablePagination", + "type": "boolean", + "returnType": "boolean", + "line": 213, + "rawdescription": "\n\nWhether pagination should be disabled. This can be used to avoid unnecessary\nlayout recalculations if it's known that pagination won't be required.\n", + "description": "

Whether pagination should be disabled. This can be used to avoid unnecessary\nlayout recalculations if it's known that pagination won't be required.

\n" + } + }, + "preserveContent": { + "name": "preserveContent", + "setSignature": { + "name": "preserveContent", + "type": "void", "deprecated": false, "deprecationMessage": "", - "tagName": { - "text": "param" - } + "args": [ + { + "name": "value", + "type": "BooleanInput", + "deprecated": false, + "deprecationMessage": "" + } + ], + "returnType": "void", + "line": 233, + "jsdoctags": [ + { + "name": "value", + "type": "BooleanInput", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] }, - { - "name": "_viewContainerRef", - "type": "ViewContainerRef", + "getSignature": { + "name": "preserveContent", + "type": "boolean", + "returnType": "boolean", + "line": 229, + "rawdescription": "\n\nBy default tabs remove their content from the DOM while it's off-screen.\nSetting this to `true` will keep it in the DOM which will prevent elements\nlike iframes and videos from reloading next time it comes back into the view.\n", + "description": "

By default tabs remove their content from the DOM while it's off-screen.\nSetting this to true will keep it in the DOM which will prevent elements\nlike iframes and videos from reloading next time it comes back into the view.

\n" + } + }, + "backgroundColor": { + "name": "backgroundColor", + "setSignature": { + "name": "backgroundColor", + "type": "void", "deprecated": false, "deprecationMessage": "", - "tagName": { - "text": "param" - } + "args": [ + { + "name": "value", + "type": "ThemePalette", + "deprecated": false, + "deprecationMessage": "" + } + ], + "returnType": "void", + "line": 245, + "jsdoctags": [ + { + "name": "value", + "type": "ThemePalette", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] }, - { - "name": "_document", - "type": "Document", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } + "getSignature": { + "name": "backgroundColor", + "type": "", + "returnType": "ThemePalette", + "line": 241, + "rawdescription": "\nBackground color of the tab group.", + "description": "

Background color of the tab group.

\n" } - ] - } + } + }, + "templateData": "\r\n\r\n
\r\n \r\n\r\n \r\n
\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n {{tab.textLabel}}\r\n \r\n \r\n \r\n
\r\n\r\n\r\n \r\n \r\n\r\n" }, { - "name": "OuiPanelTrigger", - "id": "directive-OuiPanelTrigger-6f1fb3e0e0bb5b386c23c6c8cf2d965fbc76f5026dbd9ec534e8ce7279c52941431b43316d79f4428028b9392eaf96907b1a0359547ac852d83c5a69a7e68e95", - "file": "ui/src/components/panel/panel-trigger.ts", - "type": "directive", - "description": "

This directive is intended to be used in conjunction with an oui-panel tag. It is\nresponsible for toggling the display of the provided panel instance.

\n", - "rawdescription": "\n\nThis directive is intended to be used in conjunction with an oui-panel tag. It is\nresponsible for toggling the display of the provided panel instance.\n", - "sourceCode": "import {\r\n InjectionToken,\r\n Directive,\r\n OnDestroy,\r\n Input,\r\n Output,\r\n EventEmitter,\r\n ElementRef,\r\n ViewContainerRef,\r\n Inject,\r\n} from '@angular/core';\r\nimport {\r\n ScrollStrategy,\r\n Overlay,\r\n OverlayRef,\r\n FlexibleConnectedPositionStrategy,\r\n OverlayConfig,\r\n} from '@angular/cdk/overlay';\r\nimport { Subscription, Observable, Subject } from 'rxjs';\r\nimport { TemplatePortal } from '@angular/cdk/portal';\r\nimport { OuiPanelOverlay } from './panel-overlay';\r\nimport {\r\n PanelPositionY,\r\n PanelPositionX,\r\n PanelFlexiblePosition,\r\n} from './panel-positions';\r\nimport { merge } from 'rxjs';\r\nimport { debounceTime, filter } from 'rxjs/operators';\r\nimport { SPACE } from '@angular/cdk/keycodes';\r\nimport { FocusTrap, ConfigurableFocusTrapFactory } from '@angular/cdk/a11y';\r\n\r\n/** Injection token that determines the scroll handling while the panel-overlay is open. */\r\nexport const OUI_PANEL_SCROLL_STRATEGY = new InjectionToken<\r\n () => ScrollStrategy\r\n>('oui-panel-scroll-strategy');\r\n\r\n/** @docs-private */\r\nexport function OUI_PANEL_SCROLL_STRATEGY_FACTORY(\r\n overlay: Overlay\r\n): () => ScrollStrategy {\r\n return () => overlay.scrollStrategies.close();\r\n}\r\n\r\n/** @docs-private */\r\nexport const OUI_PANEL_SCROLL_STRATEGY_FACTORY_PROVIDER = {\r\n provide: OUI_PANEL_SCROLL_STRATEGY,\r\n deps: [Overlay],\r\n useFactory: OUI_PANEL_SCROLL_STRATEGY_FACTORY,\r\n};\r\n\r\n/**\r\n * This directive is intended to be used in conjunction with an oui-panel tag. It is\r\n * responsible for toggling the display of the provided panel instance.\r\n */\r\n@Directive({\r\n selector: `[oui-panel-trigger-for], [ouiPanelTriggerFor]`,\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n 'aria-haspopup': 'true',\r\n '[attr.aria-expanded]': 'panelOpen || null',\r\n '(mouseenter)': '_handleMouseEnter($event)',\r\n '(mouseleave)': '_handelMouseLeave($event)',\r\n '(keydown)': '_handleKeydown($event)',\r\n },\r\n exportAs: 'ouiPanelTrigger',\r\n})\r\nexport class OuiPanelTrigger implements OnDestroy {\r\n private _portal: TemplatePortal;\r\n private _overlayRef: OverlayRef | null = null;\r\n private _panelOpen = false;\r\n private _closeSubscription = Subscription.EMPTY;\r\n private _hoverSubscription = Subscription.EMPTY;\r\n private _keyboardEventSubscription: Subscription = Subscription.EMPTY;\r\n private _escapeEventSubscription: Subscription = Subscription.EMPTY;\r\n private _panelCloseSubscription = Subscription.EMPTY;\r\n private _mouseLeave: Subject = new Subject();\r\n private _mouseEnter: Subject = new Subject();\r\n private _scrollStrategy: () => ScrollStrategy;\r\n\r\n /** The class that traps and manages focus within the panel. */\r\n private _focusTrap: FocusTrap;\r\n\r\n /** Element that was focused before the panel was opened. Save this to restore upon close. */\r\n private _currentFocusElement: HTMLElement = null;\r\n\r\n /** References the panel instance that the trigger is associated with. */\r\n @Input('ouiPanelTriggerFor')\r\n get panel() {\r\n return this._panel;\r\n }\r\n set panel(panel: OuiPanelOverlay) {\r\n if (panel === this._panel) {\r\n return;\r\n }\r\n this._panel = panel;\r\n this._panelCloseSubscription.unsubscribe();\r\n\r\n if (panel) {\r\n this._panelCloseSubscription = panel.closed\r\n .asObservable()\r\n .subscribe(() => {\r\n this._destroyPanel();\r\n });\r\n this._escapeEventSubscription = this.panel.escapeEvent.subscribe(() => {\r\n this.closePanel();\r\n });\r\n }\r\n }\r\n private _panel: OuiPanelOverlay;\r\n\r\n /** Event emitted when the associated panel is opened. */\r\n @Output()\r\n readonly panelOpened: EventEmitter = new EventEmitter();\r\n\r\n /** Event emitted when the associated panel is closed. */\r\n @Output()\r\n readonly panelClosed: EventEmitter = new EventEmitter();\r\n\r\n constructor(\r\n private _overlay: Overlay,\r\n private _element: ElementRef,\r\n private _viewContainerRef: ViewContainerRef,\r\n private _focusTrapFactory: ConfigurableFocusTrapFactory,\r\n @Inject(OUI_PANEL_SCROLL_STRATEGY) scrollStrategy: any\r\n ) {\r\n this._scrollStrategy = scrollStrategy;\r\n }\r\n\r\n /** Whether the panel is open. */\r\n get panelOpen(): boolean {\r\n return this._panelOpen;\r\n }\r\n\r\n /** Toggles the panel between the open and closed states. */\r\n togglePanel(): void {\r\n return this._panelOpen ? this.closePanel() : this.openPanel();\r\n }\r\n\r\n /** Ensures the option is selected when activated from the keyboard. */\r\n _handleKeydown(event: KeyboardEvent): void {\r\n const keyCode = event.keyCode;\r\n if (keyCode === SPACE) {\r\n this.openPanel();\r\n this._trapFocus();\r\n event.preventDefault();\r\n // On tab it will focus on the element itself\r\n this._currentFocusElement = event.target as HTMLElement;\r\n }\r\n }\r\n\r\n /** Opens The Panel */\r\n openPanel(): void {\r\n if (this._panelOpen) {\r\n return;\r\n }\r\n const overlayRef = this._createOverlay();\r\n const overlayConfig = overlayRef.getConfig();\r\n\r\n this._setPosition(\r\n overlayConfig.positionStrategy as FlexibleConnectedPositionStrategy\r\n );\r\n // overlayConfig.hasBackdrop = true;\r\n overlayRef.attach(this._getPortal());\r\n this._setLargeWidth();\r\n this._closeSubscription = this._panelClosingActions().subscribe(() => {\r\n this.closePanel('mouserHover');\r\n });\r\n this._setIsPanelOpen(true);\r\n }\r\n\r\n // set state rather than toggle to support triggers sharing a panel\r\n private _setIsPanelOpen(isOpen: boolean): void {\r\n this._panelOpen = isOpen;\r\n this._panelOpen ? this.panelOpened.emit() : this.panelClosed.emit();\r\n }\r\n\r\n /**\r\n * This method creates the overlay from the provided panel's template and saves its\r\n * OverlayRef so that it can be attached to the DOM when openPanel is called.\r\n */\r\n private _createOverlay(): OverlayRef {\r\n if (document.querySelector('.oui-panel')) {\r\n document.querySelector('.oui-panel').remove();\r\n }\r\n if (!this._overlayRef) {\r\n const config = this._getOverlayConfig();\r\n this._subscribeToPositions(\r\n config.positionStrategy as FlexibleConnectedPositionStrategy\r\n );\r\n this._overlayRef = this._overlay.create(config);\r\n\r\n // Consume the `keydownEvents` in order to prevent them from going to another overlay.\r\n // Ideally we'd also have our keyboard event logic in here, however doing so will\r\n // break anybody that may have implemented the `OuiPanelOverlay` themselves.\r\n this._keyboardEventSubscription = this._overlayRef\r\n .keydownEvents()\r\n .pipe(filter((event) => event.key === 'Escape'))\r\n .subscribe(() => this.closePanel());\r\n }\r\n return this._overlayRef;\r\n }\r\n\r\n /**\r\n * This method builds the configuration object needed to create the overlay, the OverlayState.\r\n *\r\n * @returns OverlayConfig\r\n */\r\n private _getOverlayConfig(): OverlayConfig {\r\n return new OverlayConfig({\r\n positionStrategy: this._overlay\r\n .position()\r\n .flexibleConnectedTo(this._element)\r\n .withLockedPosition()\r\n .withTransformOriginOn('.oui-panel-overlay'),\r\n backdropClass: 'cdk-overlay-transparent-backdrop',\r\n scrollStrategy: this._scrollStrategy(),\r\n direction: 'ltr',\r\n });\r\n }\r\n\r\n /**\r\n * Listens to changes in the position of the overlay and sets the correct classes\r\n * on the menu based on the new position. This ensures the animation origin is always\r\n * correct, even if a fallback position is used for the overlay.\r\n */\r\n private _subscribeToPositions(\r\n position: FlexibleConnectedPositionStrategy\r\n ): void {\r\n if (this.panel.setPositionClasses) {\r\n position.positionChanges.subscribe((change) => {\r\n const posX: PanelPositionX =\r\n change.connectionPair.overlayX === 'start' ? 'after' : 'before';\r\n const posY: PanelPositionY =\r\n change.connectionPair.overlayY === 'top' ? 'below' : 'above';\r\n\r\n this.panel.setPositionClasses!(posX, posY);\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Sets the appropriate positions on a position strategy\r\n * so the overlay connects with the trigger correctly.\r\n *\r\n * @param positionStrategy Strategy whose position to update.\r\n */\r\n private _setPosition(positionStrategy: FlexibleConnectedPositionStrategy) {\r\n const panelPositions = new PanelFlexiblePosition(\r\n this.panel.xPosition,\r\n this.panel.yPosition\r\n );\r\n positionStrategy.withPositions(panelPositions.getPosition());\r\n }\r\n /** assign large width if overlay element contains img tag */\r\n private _setLargeWidth() {\r\n const imageTag = this._overlayRef.overlayElement.querySelector('img');\r\n if (imageTag) {\r\n const content: HTMLDivElement =\r\n this._overlayRef.overlayElement.querySelector('.oui-panel-content');\r\n content.classList.add('oui-panel-content-large');\r\n }\r\n }\r\n\r\n /** Cleans up the active subscriptions. */\r\n private _cleanUpSubscriptions(): void {\r\n this._closeSubscription.unsubscribe();\r\n this._hoverSubscription.unsubscribe();\r\n this._escapeEventSubscription.unsubscribe();\r\n this._keyboardEventSubscription.unsubscribe();\r\n }\r\n\r\n /** Closes the menu and does the necessary cleanup. */\r\n private _destroyPanel() {\r\n if (!this._overlayRef || !this.panelOpen) {\r\n return;\r\n }\r\n this._setIsPanelOpen(false);\r\n const panel = this.panel;\r\n\r\n this._closeSubscription.unsubscribe();\r\n this._overlayRef.detach();\r\n\r\n if (panel.lazyContent) {\r\n panel.lazyContent.detach();\r\n }\r\n }\r\n\r\n /** Closes The Panel */\r\n closePanel(hoverType?) {\r\n this.panel.closed.emit();\r\n if (!hoverType) {\r\n this._restoreFocus();\r\n }\r\n }\r\n\r\n /** Moves the focus inside the focus trap. */\r\n public _trapFocus() {\r\n const element: HTMLDivElement =\r\n this._overlayRef.overlayElement.querySelector('.oui-panel-content');\r\n\r\n if (!this._focusTrap) {\r\n this._focusTrap = this._focusTrapFactory.create(element);\r\n }\r\n element.focus();\r\n }\r\n\r\n /** Restores focus to the element that was focused before the panel opened. */\r\n public _restoreFocus() {\r\n const toFocus = this._currentFocusElement;\r\n // We need the extra check, because IE can set the `activeElement` to null in some cases.\r\n if (toFocus && typeof toFocus.focus === 'function') {\r\n toFocus.focus();\r\n }\r\n\r\n if (this._focusTrap) {\r\n this._focusTrap = null;\r\n }\r\n }\r\n\r\n /** Gets the portal that should be attached to the overlay. */\r\n private _getPortal(): TemplatePortal {\r\n // Note that we can avoid this check by keeping the portal on the menu panel.\r\n // While it would be cleaner, we'd have to introduce another required method on\r\n // `OuiPanelOverlay`, making it harder to consume.\r\n if (!this._portal || this._portal.templateRef !== this.panel.templateRef) {\r\n this._portal = new TemplatePortal(\r\n this.panel.templateRef,\r\n this._viewContainerRef\r\n );\r\n }\r\n return this._portal;\r\n }\r\n\r\n public _handleMouseEnter(event: MouseEvent): void {\r\n // On hover it will focus on the element itself\r\n const focusElement = event.target as HTMLElement;\r\n this._currentFocusElement = focusElement.querySelector('oui-icon');\r\n this._mouseEnter.next(event);\r\n this.openPanel();\r\n event.stopImmediatePropagation();\r\n }\r\n\r\n public _handelMouseLeave(event: MouseEvent): void {\r\n this._mouseLeave.next(event);\r\n event.stopImmediatePropagation();\r\n }\r\n\r\n /** Returns a stream that emits whenever an action that should close the panel occurs. */\r\n private _panelClosingActions(): Observable {\r\n const detachments = this._overlayRef!.detachments();\r\n const mouseLeave = merge(\r\n this._mouseLeave.asObservable(),\r\n this._mouseEnter.asObservable(),\r\n this.panel.mouseLeave,\r\n this.panel.mouseEnter\r\n ).pipe(\r\n debounceTime(200),\r\n filter((event) => event.type === 'mouseleave')\r\n );\r\n return merge(detachments, mouseLeave);\r\n }\r\n\r\n ngOnDestroy() {\r\n if (this._overlayRef) {\r\n this._overlayRef.dispose();\r\n this._overlayRef = null;\r\n }\r\n this._cleanUpSubscriptions();\r\n }\r\n}\r\n", - "selector": "[oui-panel-trigger-for], [ouiPanelTriggerFor]", + "name": "MatTabHeader", + "id": "component-MatTabHeader-7d595962d69ee4f628e03bb47f3b71a8836c0e7c6e6a503b42f1149baf0ae6bdba4a28352f78d13da7cc942129123a0330150148db17dcc9b708a8233717cad6", + "file": "ui/src/components/tabs/tab-header.ts", + "changeDetection": "ChangeDetectionStrategy.Default", + "encapsulation": [ + "ViewEncapsulation.None" + ], + "entryComponents": [], + "host": {}, + "inputs": [ + "selectedIndex" + ], + "outputs": [ + "selectFocusedIndex", + "indexFocused" + ], "providers": [], + "selector": "mat-tab-header", + "styleUrls": [ + "tab-header.scss" + ], + "styles": [], + "templateUrl": [ + "tab-header.html" + ], + "viewProviders": [], "inputsClass": [ { - "name": "ouiPanelTriggerFor", + "name": "disableRipple", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nReferences the panel instance that the trigger is associated with.", - "description": "

References the panel instance that the trigger is associated with.

\n", - "line": 88, - "type": "OuiPanelOverlay", + "rawdescription": "\nWhether the ripple effect is disabled or not.", + "description": "

Whether the ripple effect is disabled or not.

\n", + "line": 72, + "type": "boolean", "decorators": [] - } - ], - "outputsClass": [ - { - "name": "panelClosed", - "defaultValue": "new EventEmitter()", - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nEvent emitted when the associated panel is closed.", - "description": "

Event emitted when the associated panel is closed.

\n", - "line": 117, - "type": "EventEmitter" }, { - "name": "panelOpened", - "defaultValue": "new EventEmitter()", + "name": "disablePagination", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nEvent emitted when the associated panel is opened.", - "description": "

Event emitted when the associated panel is opened.

\n", - "line": 113, - "type": "EventEmitter" + "rawdescription": "\n\nWhether pagination should be disabled. This can be used to avoid unnecessary\nlayout recalculations if it's known that pagination won't be required.\n", + "description": "

Whether pagination should be disabled. This can be used to avoid unnecessary\nlayout recalculations if it's known that pagination won't be required.

\n", + "line": 132, + "type": "boolean", + "decorators": [], + "inheritance": { + "file": "MatPaginatedTabHeader" + } } ], - "deprecated": false, - "deprecationMessage": "", - "hostBindings": [], - "hostListeners": [], + "outputsClass": [], "propertiesClass": [ { - "name": "_closeSubscription", - "defaultValue": "Subscription.EMPTY", + "name": "_disableRipple", + "defaultValue": "false", "deprecated": false, "deprecationMessage": "", - "type": "", + "type": "boolean", "optional": false, "description": "", - "line": 71, - "modifierKind": [ - 121 - ] - }, - { - "name": "_currentFocusElement", - "defaultValue": "null", - "deprecated": false, - "deprecationMessage": "", - "type": "HTMLElement", - "optional": false, - "description": "

Element that was focused before the panel was opened. Save this to restore upon close.

\n", - "line": 84, - "rawdescription": "\nElement that was focused before the panel was opened. Save this to restore upon close.", + "line": 80, "modifierKind": [ 121 ] }, { - "name": "_escapeEventSubscription", - "defaultValue": "Subscription.EMPTY", + "name": "_inkBar", "deprecated": false, "deprecationMessage": "", - "type": "Subscription", + "type": "MatInkBar", "optional": false, "description": "", - "line": 74, - "modifierKind": [ - 121 - ] - }, - { - "name": "_focusTrap", - "deprecated": false, - "deprecationMessage": "", - "type": "FocusTrap", - "optional": false, - "description": "

The class that traps and manages focus within the panel.

\n", - "line": 81, - "rawdescription": "\nThe class that traps and manages focus within the panel.", - "modifierKind": [ - 121 - ] + "line": 68, + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_hoverSubscription", - "defaultValue": "Subscription.EMPTY", + "name": "_items", "deprecated": false, "deprecationMessage": "", - "type": "", + "type": "QueryList", "optional": false, "description": "", - "line": 72, - "modifierKind": [ - 121 - ] + "line": 62, + "decorators": [ + { + "name": "ContentChildren", + "stringifiedArguments": "MatTabLabelWrapper, {descendants: false}" + } + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_keyboardEventSubscription", - "defaultValue": "Subscription.EMPTY", + "name": "_nextPaginator", "deprecated": false, "deprecationMessage": "", - "type": "Subscription", + "type": "ElementRef", "optional": false, "description": "", - "line": 73, - "modifierKind": [ - 121 - ] + "line": 66, + "decorators": [ + { + "name": "ViewChild", + "stringifiedArguments": "'nextPaginator'" + } + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_mouseEnter", - "defaultValue": "new Subject()", + "name": "_previousPaginator", "deprecated": false, "deprecationMessage": "", - "type": "Subject", + "type": "ElementRef", "optional": false, "description": "", - "line": 77, - "modifierKind": [ - 121 - ] + "line": 67, + "decorators": [ + { + "name": "ViewChild", + "stringifiedArguments": "'previousPaginator'" + } + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_mouseLeave", - "defaultValue": "new Subject()", + "name": "_tabList", "deprecated": false, "deprecationMessage": "", - "type": "Subject", + "type": "ElementRef", "optional": false, "description": "", - "line": 76, - "modifierKind": [ - 121 - ] + "line": 64, + "decorators": [ + { + "name": "ViewChild", + "stringifiedArguments": "'tabList', {static: true}" + } + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_overlayRef", - "defaultValue": "null", + "name": "_tabListContainer", "deprecated": false, "deprecationMessage": "", - "type": "OverlayRef | null", + "type": "ElementRef", "optional": false, "description": "", - "line": 69, - "modifierKind": [ - 121 - ] + "line": 63, + "decorators": [ + { + "name": "ViewChild", + "stringifiedArguments": "'tabListContainer', {static: true}" + } + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_panel", + "name": "_tabListInner", "deprecated": false, "deprecationMessage": "", - "type": "OuiPanelOverlay", + "type": "ElementRef", "optional": false, "description": "", - "line": 109, - "modifierKind": [ - 121 - ] + "line": 65, + "decorators": [ + { + "name": "ViewChild", + "stringifiedArguments": "'tabListInner', {static: true}" + } + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_panelCloseSubscription", - "defaultValue": "Subscription.EMPTY", + "name": "_animationMode", "deprecated": false, "deprecationMessage": "", - "type": "", - "optional": false, + "type": "string", + "optional": true, "description": "", - "line": 75, + "line": 171, + "decorators": [ + { + "name": "Optional", + "stringifiedArguments": "" + }, + { + "name": "Inject", + "stringifiedArguments": "ANIMATION_MODULE_TYPE" + } + ], "modifierKind": [ - 121 - ] + 123 + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_panelOpen", - "defaultValue": "false", + "name": "_currentTextContent", "deprecated": false, "deprecationMessage": "", - "type": "", + "type": "string", "optional": false, - "description": "", - "line": 70, + "description": "

Cached text content of the header.

\n", + "line": 122, + "rawdescription": "\nCached text content of the header.", "modifierKind": [ 121 - ] + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_portal", + "name": "_destroyed", + "defaultValue": "new Subject()", "deprecated": false, "deprecationMessage": "", - "type": "TemplatePortal", + "type": "", "optional": false, - "description": "", - "line": 68, + "description": "

Emits when the component is destroyed.

\n", + "line": 98, + "rawdescription": "\nEmits when the component is destroyed.", "modifierKind": [ - 121 - ] + 122, + 144 + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_scrollStrategy", + "name": "_disablePagination", + "defaultValue": "false", "deprecated": false, "deprecationMessage": "", - "type": "function", + "type": "boolean", "optional": false, "description": "", - "line": 78, - "modifierKind": [ - 121 - ] - } - ], - "methodsClass": [ - { - "name": "_cleanUpSubscriptions", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 265, - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nCleans up the active subscriptions.", - "description": "

Cleans up the active subscriptions.

\n", - "modifierKind": [ - 121 - ] - }, - { - "name": "_createOverlay", - "args": [], - "optional": false, - "returnType": "OverlayRef", - "typeParameters": [], - "line": 181, - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\n\nThis method creates the overlay from the provided panel's template and saves its\nOverlayRef so that it can be attached to the DOM when openPanel is called.\n", - "description": "

This method creates the overlay from the provided panel's template and saves its\nOverlayRef so that it can be attached to the DOM when openPanel is called.

\n", - "modifierKind": [ - 121 - ] - }, - { - "name": "_destroyPanel", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 273, - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nCloses the menu and does the necessary cleanup.", - "description": "

Closes the menu and does the necessary cleanup.

\n", - "modifierKind": [ - 121 - ] - }, - { - "name": "_getOverlayConfig", - "args": [], - "optional": false, - "returnType": "OverlayConfig", - "typeParameters": [], - "line": 208, - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\n\nThis method builds the configuration object needed to create the overlay, the OverlayState.\n\n", - "description": "

This method builds the configuration object needed to create the overlay, the OverlayState.

\n", + "line": 138, "modifierKind": [ 121 ], - "jsdoctags": [ - { - "tagName": { - "pos": 6885, - "end": 6892, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 79, - "escapedText": "returns" - }, - "comment": "

OverlayConfig

\n" - } - ] + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_getPortal", - "args": [], - "optional": false, - "returnType": "TemplatePortal", - "typeParameters": [], - "line": 321, + "name": "_disableScrollAfter", + "defaultValue": "true", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nGets the portal that should be attached to the overlay.", - "description": "

Gets the portal that should be attached to the overlay.

\n", - "modifierKind": [ - 121 - ] - }, - { - "name": "_handelMouseLeave", - "args": [ - { - "name": "event", - "type": "MouseEvent", - "deprecated": false, - "deprecationMessage": "" - } - ], + "type": "", "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 343, - "deprecated": false, - "deprecationMessage": "", - "modifierKind": [ - 123 - ], - "jsdoctags": [ - { - "name": "event", - "type": "MouseEvent", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } - ] + "description": "

Whether the tab list can be scrolled more towards the end of the tab label list.

\n", + "line": 104, + "rawdescription": "\nWhether the tab list can be scrolled more towards the end of the tab label list.", + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_handleKeydown", - "args": [ - { - "name": "event", - "type": "KeyboardEvent", - "deprecated": false, - "deprecationMessage": "" - } - ], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 140, + "name": "_disableScrollBefore", + "defaultValue": "true", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nEnsures the option is selected when activated from the keyboard.", - "description": "

Ensures the option is selected when activated from the keyboard.

\n", - "jsdoctags": [ - { - "name": "event", - "type": "KeyboardEvent", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } - ] - }, - { - "name": "_handleMouseEnter", - "args": [ - { - "name": "event", - "type": "MouseEvent", - "deprecated": false, - "deprecationMessage": "" - } - ], + "type": "", "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 334, + "description": "

Whether the tab list can be scrolled more towards the beginning of the tab label list.

\n", + "line": 107, + "rawdescription": "\nWhether the tab list can be scrolled more towards the beginning of the tab label list.", + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_keyManager", "deprecated": false, "deprecationMessage": "", + "type": "FocusKeyManager", + "optional": false, + "description": "

Used to manage focus between the tabs.

\n", + "line": 119, + "rawdescription": "\nUsed to manage focus between the tabs.", "modifierKind": [ - 123 + 121 ], - "jsdoctags": [ - { - "name": "event", - "type": "MouseEvent", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } - ] + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_panelClosingActions", - "args": [], - "optional": false, - "returnType": "Observable", - "typeParameters": [], - "line": 349, + "name": "_scrollDistance", + "defaultValue": "0", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nReturns a stream that emits whenever an action that should close the panel occurs.", - "description": "

Returns a stream that emits whenever an action that should close the panel occurs.

\n", + "type": "number", + "optional": false, + "description": "

The distance in pixels that the tab labels should be translated to the left.

\n", + "line": 92, + "rawdescription": "\nThe distance in pixels that the tab labels should be translated to the left.", "modifierKind": [ 121 - ] + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_restoreFocus", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 308, + "name": "_scrollDistanceChanged", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nRestores focus to the element that was focused before the panel opened.", - "description": "

Restores focus to the element that was focused before the panel opened.

\n", + "type": "boolean", + "optional": false, + "description": "

Whether the scroll distance has changed and should be applied after the view is checked.

\n", + "line": 116, + "rawdescription": "\nWhether the scroll distance has changed and should be applied after the view is checked.", "modifierKind": [ - 123 - ] + 121 + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_setIsPanelOpen", - "args": [ - { - "name": "isOpen", - "type": "boolean", - "deprecated": false, - "deprecationMessage": "" - } - ], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 172, + "name": "_selectedIndex", + "defaultValue": "0", "deprecated": false, "deprecationMessage": "", + "type": "number", + "optional": false, + "description": "", + "line": 156, "modifierKind": [ 121 ], - "jsdoctags": [ - { - "name": "isOpen", - "type": "boolean", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } - ] + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_setLargeWidth", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 255, + "name": "_selectedIndexChanged", + "defaultValue": "false", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nassign large width if overlay element contains img tag", - "description": "

assign large width if overlay element contains img tag

\n", + "type": "", + "optional": false, + "description": "

Whether the header should scroll to the selected index after the view has been checked.

\n", + "line": 95, + "rawdescription": "\nWhether the header should scroll to the selected index after the view has been checked.", "modifierKind": [ 121 - ] + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_setPosition", - "args": [ - { - "name": "positionStrategy", - "type": "FlexibleConnectedPositionStrategy", - "deprecated": false, - "deprecationMessage": "" - } - ], + "name": "_showPaginationControls", + "defaultValue": "false", + "deprecated": false, + "deprecationMessage": "", + "type": "", "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 247, + "description": "

Whether the controls for pagination should be displayed

\n", + "line": 101, + "rawdescription": "\nWhether the controls for pagination should be displayed", + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_stopScrolling", + "defaultValue": "new Subject()", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\n\nSets the appropriate positions on a position strategy\nso the overlay connects with the trigger correctly.\n\n", - "description": "

Sets the appropriate positions on a position strategy\nso the overlay connects with the trigger correctly.

\n", + "type": "", + "optional": false, + "description": "

Stream that will stop the automated scrolling.

\n", + "line": 125, + "rawdescription": "\nStream that will stop the automated scrolling.", "modifierKind": [ 121 ], - "jsdoctags": [ - { - "name": { - "pos": 8230, - "end": 8246, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 79, - "escapedText": "positionStrategy" - }, - "type": "FlexibleConnectedPositionStrategy", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "pos": 8224, - "end": 8229, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 79, - "escapedText": "param" - }, - "comment": "

Strategy whose position to update.

\n" - } - ] + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_subscribeToPositions", - "args": [ - { - "name": "position", - "type": "FlexibleConnectedPositionStrategy", - "deprecated": false, - "deprecationMessage": "" - } - ], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 226, + "name": "_tabLabelCount", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\n\nListens to changes in the position of the overlay and sets the correct classes\non the menu based on the new position. This ensures the animation origin is always\ncorrect, even if a fallback position is used for the overlay.\n", - "description": "

Listens to changes in the position of the overlay and sets the correct classes\non the menu based on the new position. This ensures the animation origin is always\ncorrect, even if a fallback position is used for the overlay.

\n", + "type": "number", + "optional": false, + "description": "

The number of tab labels that are displayed on the header. When this changes, the header\nshould re-evaluate the scroll position.

\n", + "line": 113, + "rawdescription": "\n\nThe number of tab labels that are displayed on the header. When this changes, the header\nshould re-evaluate the scroll position.\n", "modifierKind": [ 121 ], - "jsdoctags": [ - { - "name": "position", - "type": "FlexibleConnectedPositionStrategy", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } - ] + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_trapFocus", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 297, + "name": "indexFocused", + "defaultValue": "new EventEmitter()", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nMoves the focus inside the focus trap.", - "description": "

Moves the focus inside the focus trap.

\n", + "type": "EventEmitter", + "optional": false, + "description": "

Event emitted when a label is focused.

\n", + "line": 162, + "rawdescription": "\nEvent emitted when a label is focused.", "modifierKind": [ - 123 - ] + 144 + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "closePanel", + "name": "selectFocusedIndex", + "defaultValue": "new EventEmitter()", + "deprecated": false, + "deprecationMessage": "", + "type": "EventEmitter", + "optional": false, + "description": "

Event emitted when the option is selected.

\n", + "line": 159, + "rawdescription": "\nEvent emitted when the option is selected.", + "modifierKind": [ + 144 + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } + } + ], + "methodsClass": [ + { + "name": "_itemSelected", "args": [ { - "name": "hoverType", - "type": "", + "name": "event", + "type": "KeyboardEvent", "deprecated": false, - "deprecationMessage": "", - "optional": true + "deprecationMessage": "" } ], "optional": false, "returnType": "void", "typeParameters": [], - "line": 289, + "line": 99, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nCloses The Panel", - "description": "

Closes The Panel

\n", + "modifierKind": [ + 122 + ], "jsdoctags": [ { - "name": "hoverType", - "type": "", + "name": "event", + "type": "KeyboardEvent", "deprecated": false, "deprecationMessage": "", - "optional": true, "tagName": { "text": "param" } } - ] + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "ngOnDestroy", + "name": "ngAfterContentInit", "args": [], "optional": false, "returnType": "void", "typeParameters": [], - "line": 363, + "line": 94, "deprecated": false, - "deprecationMessage": "" + "deprecationMessage": "", + "modifierKind": [ + 158 + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "openPanel", + "name": "_alignInkBarToSelectedTab", "args": [], "optional": false, "returnType": "void", "typeParameters": [], - "line": 152, + "line": 592, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nOpens The Panel", - "description": "

Opens The Panel

\n" + "rawdescription": "\nTells the ink-bar to align itself to the current label wrapper", + "description": "

Tells the ink-bar to align itself to the current label wrapper

\n", + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "togglePanel", + "name": "_checkPaginationEnabled", "args": [], "optional": false, "returnType": "void", "typeParameters": [], - "line": 135, - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nToggles the panel between the open and closed states.", - "description": "

Toggles the panel between the open and closed states.

\n" - } - ], - "implements": [ - "OnDestroy" - ], - "constructorObj": { - "name": "constructor", - "description": "", - "deprecated": false, - "deprecationMessage": "", - "args": [ - { - "name": "_overlay", - "type": "Overlay", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "_element", - "type": "ElementRef", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "_viewContainerRef", - "type": "ViewContainerRef", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "_focusTrapFactory", - "type": "ConfigurableFocusTrapFactory", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "scrollStrategy", - "type": "any", - "deprecated": false, - "deprecationMessage": "" - } - ], - "line": 117, - "jsdoctags": [ - { - "name": "_overlay", - "type": "Overlay", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "_element", - "type": "ElementRef", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "_viewContainerRef", - "type": "ViewContainerRef", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "_focusTrapFactory", - "type": "ConfigurableFocusTrapFactory", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "scrollStrategy", - "type": "any", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } - ] - }, - "accessors": { - "panel": { - "name": "panel", - "setSignature": { - "name": "panel", - "type": "void", - "deprecated": false, - "deprecationMessage": "", - "args": [ - { - "name": "panel", - "type": "OuiPanelOverlay", - "deprecated": false, - "deprecationMessage": "" - } - ], - "returnType": "void", - "line": 91, - "jsdoctags": [ - { - "name": "panel", - "type": "OuiPanelOverlay", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } - ] - }, - "getSignature": { - "name": "panel", - "type": "", - "returnType": "", - "line": 88, - "rawdescription": "\nReferences the panel instance that the trigger is associated with.", - "description": "

References the panel instance that the trigger is associated with.

\n" - } - }, - "panelOpen": { - "name": "panelOpen", - "getSignature": { - "name": "panelOpen", - "type": "boolean", - "returnType": "boolean", - "line": 130, - "rawdescription": "\nWhether the panel is open.", - "description": "

Whether the panel is open.

\n" - } - } - } - }, - { - "name": "OuiRowDef", - "id": "directive-OuiRowDef-2c11beafcdd8c12a01329de8f4f053505e5dbbd179540cefba04203174c4dbaef3f70b9edd0846497b3f172b61f71885e2ae7d091d3b9bd6ed01366b24783bc2", - "file": "ui/src/components/table/row.ts", - "type": "directive", - "description": "

Data row definition for the oui-table.\nCaptures the data row's template and other properties such as the columns to display and\na when predicate that describes when this row should be used.

\n", - "rawdescription": "\n\nData row definition for the oui-table.\nCaptures the data row's template and other properties such as the columns to display and\na when predicate that describes when this row should be used.\n", - "sourceCode": "import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n Directive,\r\n ViewEncapsulation,\r\n OnDestroy,\r\n ElementRef,\r\n NgZone,\r\n IterableDiffers,\r\n} from '@angular/core';\r\nimport {\r\n CDK_ROW_TEMPLATE,\r\n CdkFooterRow,\r\n CdkFooterRowDef,\r\n CdkHeaderRow,\r\n CdkHeaderRowDef,\r\n CdkRow,\r\n CdkRowDef,\r\n} from '@angular/cdk/table';\r\nimport { FocusMonitor } from '@angular/cdk/a11y';\r\nimport { Subscription } from 'rxjs';\r\n\r\n/**\r\n * Header row definition for the oui-table.\r\n * Captures the header row's template and other header properties such as the columns to display.\r\n */\r\n@Directive({\r\n selector: '[ouiHeaderRowDef]',\r\n providers: [{ provide: CdkHeaderRowDef, useExisting: OuiHeaderRowDef }],\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['columns: ouiHeaderRowDef'],\r\n})\r\nexport class OuiHeaderRowDef extends CdkHeaderRowDef {}\r\n\r\n/**\r\n * Footer row definition for the oui-table.\r\n * Captures the footer row's template and other footer properties such as the columns to display.\r\n */\r\n@Directive({\r\n selector: '[ouiFooterRowDef]',\r\n providers: [{ provide: CdkFooterRowDef, useExisting: OuiFooterRowDef }],\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['columns: ouiFooterRowDef'],\r\n})\r\nexport class OuiFooterRowDef extends CdkFooterRowDef {}\r\n\r\n/**\r\n * Data row definition for the oui-table.\r\n * Captures the data row's template and other properties such as the columns to display and\r\n * a when predicate that describes when this row should be used.\r\n */\r\n@Directive({\r\n selector: '[ouiRowDef]',\r\n providers: [{ provide: CdkRowDef, useExisting: OuiRowDef }],\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['columns: ouiRowDefColumns', 'when: ouiRowDefWhen'],\r\n})\r\nexport class OuiRowDef extends CdkRowDef {}\r\n\r\n/** Footer template container that contains the cell outlet. Adds the right class and role. */\r\n@Component({\r\n selector: 'oui-header-row, tr[oui-header-row]',\r\n template: CDK_ROW_TEMPLATE,\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-header-row',\r\n role: 'row',\r\n },\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n exportAs: 'ouiHeaderRow',\r\n providers: [{ provide: CdkHeaderRow, useExisting: OuiHeaderRow }],\r\n})\r\nexport class OuiHeaderRow extends CdkHeaderRow {}\r\n\r\n/** Footer template container that contains the cell outlet. Adds the right class and role. */\r\n@Component({\r\n selector: 'oui-footer-row, tr[oui-footer-row]',\r\n template: CDK_ROW_TEMPLATE,\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-footer-row',\r\n role: 'row',\r\n },\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n exportAs: 'ouiFooterRow',\r\n providers: [{ provide: CdkFooterRow, useExisting: OuiFooterRow }],\r\n})\r\nexport class OuiFooterRow extends CdkFooterRow {}\r\n\r\n/** Data row template container that contains the cell outlet. Adds the right class and role. */\r\n@Component({\r\n selector: 'oui-row, tr[oui-row]',\r\n template: CDK_ROW_TEMPLATE,\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-row',\r\n role: 'row',\r\n tabindex: '0',\r\n },\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n exportAs: 'ouiRow',\r\n providers: [{ provide: CdkRow, useExisting: OuiRow }],\r\n})\r\nexport class OuiRow extends CdkRow implements OnDestroy {\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n constructor(\r\n protected elementRef: ElementRef,\r\n protected _differs: IterableDiffers,\r\n private _focusMonitor: FocusMonitor,\r\n private _ngZone: NgZone\r\n ) {\r\n super();\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this.elementRef, true)\r\n .subscribe(() => this._ngZone.run(() => {}));\r\n }\r\n ngOnDestroy(): void {\r\n this._focusMonitor.stopMonitoring(this.elementRef);\r\n this._monitorSubscription.unsubscribe();\r\n }\r\n}\r\n", - "selector": "[ouiRowDef]", - "providers": [ - { - "name": "{ provide: CdkRowDef, useExisting: OuiRowDef }" - } - ], - "inputsClass": [], - "outputsClass": [], - "deprecated": false, - "deprecationMessage": "", - "hostBindings": [], - "hostListeners": [], - "propertiesClass": [], - "methodsClass": [], - "extends": "CdkRowDef" - }, - { - "name": "OuiSelectTrigger", - "id": "directive-OuiSelectTrigger-c31c47d801410216b9011bcf6d1f92b501c90e5c5ad2b6ed2698d000df2b61d44f5d16435aa84af435a6f974ecdd41804312d09195c0e468908788e414878869", - "file": "ui/src/components/select/select.component.ts", - "type": "directive", - "description": "

Allows the user to customize the trigger that is displayed when the select has a value.

\n", - "rawdescription": "\n\nAllows the user to customize the trigger that is displayed when the select has a value.\n", - "sourceCode": "import { ActiveDescendantKeyManager, FocusMonitor } from '@angular/cdk/a11y';\r\nimport { Directionality } from '@angular/cdk/bidi';\r\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\r\nimport { SelectionModel } from '@angular/cdk/collections';\r\nimport {\r\n A,\r\n DOWN_ARROW,\r\n END,\r\n ENTER,\r\n HOME,\r\n LEFT_ARROW,\r\n RIGHT_ARROW,\r\n SPACE,\r\n UP_ARROW,\r\n hasModifierKey,\r\n} from '@angular/cdk/keycodes';\r\nimport { CdkConnectedOverlay } from '@angular/cdk/overlay';\r\nimport {\r\n AfterContentInit,\r\n Attribute,\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n ContentChild,\r\n ContentChildren,\r\n Directive,\r\n DoCheck,\r\n ElementRef,\r\n EventEmitter,\r\n Input,\r\n isDevMode,\r\n NgZone,\r\n OnChanges,\r\n OnDestroy,\r\n OnInit,\r\n Optional,\r\n Output,\r\n QueryList,\r\n Self,\r\n SimpleChanges,\r\n ViewChild,\r\n ViewEncapsulation,\r\n Inject,\r\n} from '@angular/core';\r\nimport {\r\n ControlValueAccessor,\r\n FormGroupDirective,\r\n NgControl,\r\n NgForm,\r\n} from '@angular/forms';\r\nimport {\r\n _countGroupLabelsBeforeOption,\r\n _getOptionScrollPosition,\r\n CanDisable,\r\n CanDisableCtor,\r\n CanUpdateErrorState,\r\n CanUpdateErrorStateCtor,\r\n HasTabIndex,\r\n HasTabIndexCtor,\r\n OuiOptionSelectionChange,\r\n mixinErrorState,\r\n mixinTabIndex,\r\n mixinDisabled,\r\n} from '../core';\r\nimport { OuiFormField, OuiFormFieldControl } from '../form-field/public-api';\r\nimport { DOCUMENT } from '@angular/common';\r\nimport { OUI_OPTION_PARENT_COMPONENT, OuiOption } from '../core/option/option';\r\nimport { OuiOptgroup } from '../core/option/optgroup';\r\nimport { ErrorStateMatcher } from '../core/error/error-options';\r\nimport { defer, merge, Observable, Subject } from 'rxjs';\r\nimport {\r\n distinctUntilChanged,\r\n filter,\r\n map,\r\n startWith,\r\n switchMap,\r\n take,\r\n takeUntil,\r\n} from 'rxjs/operators';\r\nimport {\r\n getOuiSelectDynamicMultipleError,\r\n getOuiSelectNonArrayValueError,\r\n getOuiSelectNonFunctionValueError,\r\n} from './select-errors';\r\nimport { OuiIconRegistry } from '../icon/icon-registery';\r\nimport { DomSanitizer } from '@angular/platform-browser';\r\nimport { ICONS } from '../core/shared/icons';\r\n\r\nlet nextUniqueId = 0;\r\n\r\n/**\r\n * The following style constants are necessary to save here in order\r\n * to properly calculate the alignment of the selected option over\r\n * the trigger element.\r\n */\r\n\r\n/** The height of each select option. */\r\nexport const SELECT_OPTION_HEIGHT = 40;\r\n\r\n/** The panel's padding on the x-axis */\r\nexport const SELECT_PANEL_PADDING_X = 16;\r\n\r\n/** The panel's x axis padding if it is indented (e.g. there is an option group). */\r\nexport const SELECT_PANEL_INDENT_PADDING_X = SELECT_PANEL_PADDING_X * 2;\r\n\r\n/** The height of the select items in `em` units. */\r\nexport const SELECT_ITEM_HEIGHT_EM = 3;\r\n\r\n/** The total height of the select panel. */\r\nexport const SELECT_PANEL_HEIGHT = 200;\r\n\r\n// TODO(josephperrott): Revert to a constant after 2018 spec updates are fully merged.\r\n/**\r\n * Distance between the panel edge and the option text in\r\n * multi-selection mode.\r\n *\r\n * Calculated as:\r\n * (SELECT_PANEL_PADDING_X * 1.5) + 20 = 44\r\n * The padding is multiplied by 1.5 because the checkbox's margin is half the padding.\r\n * The checkbox width is 16px.\r\n */\r\nexport let SELECT_MULTIPLE_PANEL_PADDING_X = 0;\r\n\r\n/**\r\n * The select panel will only \"fit\" inside the viewport if it is positioned at\r\n * this value or more away from the viewport boundary.\r\n */\r\nexport const SELECT_PANEL_VIEWPORT_PADDING = 8;\r\n\r\n/** Change event object that is emitted when the select value has changed. */\r\nexport class OuiSelectChange {\r\n constructor(\r\n /** Reference to the select that emitted the change event. */\r\n public source: OuiSelect,\r\n /** Current value of the select that emitted the event. */\r\n public value: any\r\n ) {}\r\n}\r\n\r\n// Boilerplate for applying mixins to OuiSelect.\r\n/** @docs-private */\r\nexport class OuiSelectBase {\r\n constructor(\r\n public _elementRef: ElementRef,\r\n public _defaultErrorStateMatcher: ErrorStateMatcher,\r\n public _parentForm: NgForm,\r\n public _parentFormGroup: FormGroupDirective,\r\n public ngControl: NgControl\r\n ) {}\r\n}\r\n\r\nexport const _OuiSelectMixinBase: CanDisableCtor &\r\n HasTabIndexCtor &\r\n CanUpdateErrorStateCtor &\r\n typeof OuiSelectBase = mixinTabIndex(\r\n mixinDisabled(mixinErrorState(OuiSelectBase))\r\n);\r\n\r\n/**\r\n * Allows the user to customize the trigger that is displayed when the select has a value.\r\n */\r\n@Directive({\r\n // eslint-disable-next-line @angular-eslint/directive-selector\r\n selector: 'oui-select-trigger',\r\n})\r\nexport class OuiSelectTrigger {}\r\n\r\n@Component({\r\n selector: 'oui-select',\r\n exportAs: 'ouiSelect',\r\n templateUrl: 'select.html',\r\n styleUrls: ['select.scss'],\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['disabled', 'tabIndex'],\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n role: 'listbox',\r\n '[attr.id]': 'id',\r\n '[attr.tabindex]': 'tabIndex',\r\n '[attr.aria-label]': '_getAriaLabel()',\r\n '[attr.aria-labelledby]': '_getAriaLabelledby()',\r\n '[attr.aria-required]': 'required.toString()',\r\n '[attr.aria-disabled]': 'disabled.toString()',\r\n '[attr.aria-invalid]': 'errorState',\r\n '[attr.aria-owns]': 'panelOpen ? _optionIds : null',\r\n '[attr.aria-multiselectable]': 'multiple',\r\n '[attr.aria-describedby]': '_ariaDescribedby || null',\r\n '[attr.aria-activedescendant]': '_getAriaActiveDescendant()',\r\n '[class.oui-select-disabled]': 'disabled',\r\n '[class.oui-select-invalid]': 'errorState',\r\n '[class.oui-select-required]': 'required',\r\n '[class.oui-select-empty]': 'empty',\r\n class: 'oui-select oui-input',\r\n '(keydown)': '_handleKeydown($event)',\r\n '(focus)': '_onFocus()',\r\n '(blur)': '_onBlur()',\r\n },\r\n providers: [\r\n { provide: OuiFormFieldControl, useExisting: OuiSelect },\r\n { provide: OUI_OPTION_PARENT_COMPONENT, useExisting: OuiSelect },\r\n ],\r\n})\r\nexport class OuiSelect\r\n extends _OuiSelectMixinBase\r\n implements\r\n AfterContentInit,\r\n OnChanges,\r\n OnDestroy,\r\n OnInit,\r\n DoCheck,\r\n ControlValueAccessor,\r\n CanDisable,\r\n HasTabIndex,\r\n OuiFormFieldControl,\r\n CanUpdateErrorState\r\n{\r\n /**Holds selected values after done */\r\n @Input() savedValues = [];\r\n /**Done button disabled until dropdown is dirty */\r\n disableDoneButton = true;\r\n /** Whether or not the overlay panel is open. */\r\n private _panelOpen = false;\r\n\r\n /** Whether filling out the select is required in the form. */\r\n private _required = false;\r\n\r\n /** Whether filling out the select is required in the form. */\r\n private _actionItems = false;\r\n\r\n /** The scroll position of the overlay panel, calculated to center the selected option. */\r\n private _scrollTop = 0;\r\n\r\n /** The placeholder displayed in the trigger of the select. */\r\n private _placeholder: string;\r\n\r\n /** Whether the component is in multiple selection mode. */\r\n private _multiple = false;\r\n\r\n /** Search input field **/\r\n isSearchFieldPresent: boolean;\r\n\r\n /** Unique id for this input. */\r\n private _uid = `oui-select-${nextUniqueId++}`;\r\n\r\n /** The last measured value for the trigger's client bounding rect. */\r\n _triggerRect: ClientRect;\r\n\r\n /** The aria-describedby attribute on the select for improved a11y. */\r\n _ariaDescribedby: string;\r\n\r\n /** The cached font-size of the trigger element. */\r\n _triggerFontSize = 0;\r\n\r\n /** Deals with the selection logic. */\r\n _selectionModel: SelectionModel;\r\n\r\n /** Manages keyboard events for options in the panel. */\r\n _keyManager: ActiveDescendantKeyManager;\r\n\r\n /** The IDs of child options to be passed to the aria-owns attribute. */\r\n _optionIds = '';\r\n\r\n /** The value of the select panel's transform-origin property. */\r\n _transformOrigin = 'top';\r\n\r\n /** If there is search input field a class is added dynamically to the perfect scrollbar **/\r\n ouiSelectInputOuterClassName: string;\r\n\r\n /** Adding top class to overlay panel */\r\n cdkConnectionOverlayPanel = '';\r\n\r\n /**\r\n * The y-offset of the overlay panel in relation to the trigger's top start corner.\r\n * This must be adjusted to align the selected option text over the trigger text.\r\n * when the panel opens. Will change based on the y-position of the selected option.\r\n */\r\n _offsetY = 0;\r\n\r\n /**\r\n * This position config ensures that the top \"start\" corner of the overlay\r\n * is aligned with with the top \"start\" of the origin by default (overlapping\r\n * the trigger completely). If the panel cannot fit below the trigger, it\r\n * will fall back to a position above the trigger.\r\n */\r\n _positions = [\r\n {\r\n originX: 'start',\r\n originY: 'top',\r\n overlayX: 'start',\r\n overlayY: 'top',\r\n },\r\n {\r\n originX: 'start',\r\n originY: 'bottom',\r\n overlayX: 'start',\r\n overlayY: 'bottom',\r\n },\r\n ];\r\n /** Emits whenever the component is destroyed. */\r\n private readonly _destroy = new Subject();\r\n\r\n /** Whether the component is disabling centering of the active option over the trigger. */\r\n private _disableOptionCentering = false;\r\n\r\n private _focused = false;\r\n\r\n /** A name for this control that can be used by `oui-form-field`. */\r\n controlType = 'oui-select';\r\n\r\n /** Trigger that opens the select. */\r\n @ViewChild('trigger') trigger: ElementRef;\r\n\r\n /** Panel containing the select options. */\r\n @ViewChild('panel', { read: ElementRef }) panel: ElementRef;\r\n\r\n private _value: any;\r\n\r\n /**\r\n * Function used to sort the values in a select in multiple mode.\r\n * Follows the same logic as `Array.prototype.sort`.\r\n */\r\n @Input() sortComparator: (\r\n a: OuiOption,\r\n b: OuiOption,\r\n options: OuiOption[]\r\n ) => number;\r\n\r\n /** Aria label of the select. If not specified, the placeholder will be used as label. */\r\n @Input('aria-label') ariaLabel = '';\r\n\r\n /** Input that can be used to specify the `aria-labelledby` attribute. */\r\n @Input('aria-labelledby') ariaLabelledby: string;\r\n private _large = false;\r\n _monitorSubscription: any;\r\n\r\n /** Whether the oui-select is of large size. */\r\n @Input()\r\n get large(): boolean {\r\n return this._large;\r\n }\r\n set large(value) {\r\n this._large = coerceBooleanProperty(value);\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n\r\n private _id: string;\r\n\r\n /** Event emitted when the select panel has been toggled. */\r\n @Output()\r\n readonly openedChange: EventEmitter = new EventEmitter();\r\n\r\n /** Combined stream of all of the child options' change events. */\r\n readonly optionSelectionChanges: Observable = defer(\r\n (): Observable => {\r\n if (this.options) {\r\n return merge(...this.options.map((option) => option.onSelectionChange));\r\n }\r\n\r\n return this._ngZone.onStable.asObservable().pipe(\r\n take(1),\r\n switchMap(() => this.optionSelectionChanges)\r\n );\r\n }\r\n );\r\n\r\n /**\r\n * Event that emits whenever the raw value of the select changes. This is here primarily\r\n * to facilitate the two-way binding for the `value` input.\r\n *\r\n * @docs-private\r\n */\r\n @Output() readonly valueChange: EventEmitter = new EventEmitter();\r\n\r\n /** Object used to control when error messages are shown. */\r\n @Input() errorStateMatcher: ErrorStateMatcher;\r\n\r\n /** All of the defined select options. */\r\n @ContentChildren(OuiOption, { descendants: true })\r\n options: QueryList;\r\n\r\n /** Event emitted when the select has been opened. */\r\n // eslint-disable-next-line @angular-eslint/no-output-rename\r\n @Output('opened')\r\n readonly _openedStream: Observable = this.openedChange.pipe(\r\n filter((o) => o),\r\n map(() => {})\r\n );\r\n\r\n /** Event emitted when the select has been closed. */\r\n // eslint-disable-next-line @angular-eslint/no-output-rename\r\n @Output('closed')\r\n readonly _closedStream: Observable = this.openedChange.pipe(\r\n filter((o) => !o),\r\n map(() => {\r\n this.isSearchFieldPresent = false;\r\n })\r\n );\r\n\r\n /** Event emitted when the selected value has been changed by the user. */\r\n @Output()\r\n readonly selectionChange: EventEmitter = new EventEmitter();\r\n\r\n /** Event emitted when the selected value has been changed and saved by the user. */\r\n @Output()\r\n readonly saveSelectionChange: EventEmitter = new EventEmitter();\r\n\r\n /** All of the defined groups of options. */\r\n @ContentChildren(OuiOptgroup) optionGroups: QueryList;\r\n\r\n /** User-supplied override of the trigger element. */\r\n @ContentChild(OuiSelectTrigger)\r\n customTrigger: OuiSelectTrigger;\r\n\r\n /** Classes to be passed to the select panel. Supports the same syntax as `ngClass`. */\r\n @Input() panelClass: string | string[] | Set | { [key: string]: any };\r\n\r\n /** Overlay pane containing the options. */\r\n @ViewChild(CdkConnectedOverlay)\r\n overlayDir: CdkConnectedOverlay;\r\n\r\n /** Emits when the panel element is finished transforming in. */\r\n _panelDoneAnimatingStream = new Subject();\r\n\r\n /** Comparison function to specify which option is displayed. Defaults to object equality. */\r\n private _compareWith = (o1: any, o2: any) => o1 === o2;\r\n\r\n /** Whether the select is focused. */\r\n get focused(): boolean {\r\n return this._focused || this._panelOpen;\r\n }\r\n /**\r\n * @deprecated Setter to be removed as this property is intended to be readonly.\r\n */\r\n set focused(value: boolean) {\r\n this._focused = value;\r\n }\r\n /** `View -> model callback called when value changes` */\r\n _onChange: (value: any) => void = () => {};\r\n\r\n /** `View -> model callback called when select has been touched` */\r\n _onTouched = () => {};\r\n\r\n /** Placeholder to be shown if no value has been selected. */\r\n @Input()\r\n get placeholder(): string {\r\n return this._placeholder;\r\n }\r\n set placeholder(value: string) {\r\n this._placeholder = value;\r\n this.stateChanges.next();\r\n }\r\n\r\n /** Whether the component is required. */\r\n @Input()\r\n get required(): boolean {\r\n return this._required;\r\n }\r\n set required(value: boolean) {\r\n this._required = coerceBooleanProperty(value);\r\n this.stateChanges.next();\r\n }\r\n\r\n /** Whether the user should be allowed to select multiple options. */\r\n @Input()\r\n get multiple(): boolean {\r\n return this._multiple;\r\n }\r\n set multiple(value: boolean) {\r\n if (this._selectionModel) {\r\n throw getOuiSelectDynamicMultipleError();\r\n }\r\n\r\n this._multiple = coerceBooleanProperty(value);\r\n }\r\n\r\n /** Whether the action items are required and use saveSelectionChange instead of selectionChange. */\r\n @Input()\r\n get actionItems(): boolean {\r\n return this._actionItems;\r\n }\r\n set actionItems(value: boolean) {\r\n if (this._multiple) {\r\n this._actionItems = coerceBooleanProperty(value);\r\n this.stateChanges.next();\r\n }\r\n }\r\n\r\n /** Whether to center the active option over the trigger. */\r\n @Input()\r\n get disableOptionCentering(): boolean {\r\n return this._disableOptionCentering;\r\n }\r\n set disableOptionCentering(value: boolean) {\r\n this._disableOptionCentering = coerceBooleanProperty(value);\r\n }\r\n\r\n /**\r\n * Function to compare the option values with the selected values. The first argument\r\n * is a value from an option. The second is a value from the selection. A boolean\r\n * should be returned.\r\n */\r\n @Input()\r\n get compareWith() {\r\n return this._compareWith;\r\n }\r\n set compareWith(fn: (o1: any, o2: any) => boolean) {\r\n if (typeof fn !== 'function') {\r\n throw getOuiSelectNonFunctionValueError();\r\n }\r\n this._compareWith = fn;\r\n if (this._selectionModel) {\r\n // A different comparator means the selection could change.\r\n this._initializeSelection();\r\n }\r\n }\r\n\r\n /** Value of the select control. */\r\n @Input()\r\n get value(): any {\r\n return this._value;\r\n }\r\n set value(newValue: any) {\r\n if (newValue !== this._value) {\r\n this.writeValue(newValue);\r\n this._value = newValue;\r\n }\r\n }\r\n\r\n /** Unique id of the element. */\r\n @Input()\r\n get id(): string {\r\n return this._id;\r\n }\r\n set id(value: string) {\r\n this._id = value || this._uid;\r\n this.stateChanges.next();\r\n }\r\n\r\n constructor(\r\n private _changeDetectorRef: ChangeDetectorRef,\r\n private _ngZone: NgZone,\r\n _defaultErrorStateMatcher: ErrorStateMatcher,\r\n elementRef: ElementRef,\r\n private _focusMonitor: FocusMonitor,\r\n @Optional() private _dir: Directionality,\r\n @Optional() _parentForm: NgForm,\r\n @Optional() _parentFormGroup: FormGroupDirective,\r\n @Optional() private _parentFormField: OuiFormField,\r\n @Self() @Optional() public ngControl: NgControl,\r\n @Attribute('tabindex') tabIndex: string,\r\n @Optional() @Inject(DOCUMENT) private _document: any,\r\n public _elementRef: ElementRef,\r\n public _ouiIconRegistry: OuiIconRegistry,\r\n private _domSanitizer: DomSanitizer\r\n ) {\r\n super(\r\n elementRef,\r\n _defaultErrorStateMatcher,\r\n _parentForm,\r\n _parentFormGroup,\r\n ngControl\r\n );\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this._elementRef, true)\r\n .subscribe(() => this._ngZone.run(() => {}));\r\n this._ouiIconRegistry.addSvgIconLiteral(\r\n `select-arrow-icon`,\r\n this._domSanitizer.bypassSecurityTrustHtml(ICONS.SELECT_ARROW_ICON)\r\n );\r\n\r\n if (this.ngControl) {\r\n // Note: we provide the value accessor through here, instead of\r\n // the `providers` to avoid running into a circular import.\r\n this.ngControl.valueAccessor = this;\r\n }\r\n\r\n this.tabIndex = parseInt(tabIndex, 10) || 0;\r\n\r\n // Force setter to be called in case id was not specified.\r\n this.id = this.id;\r\n }\r\n\r\n ngOnInit() {\r\n this._selectionModel = new SelectionModel(this.multiple);\r\n this.stateChanges.next();\r\n\r\n // We need `distinctUntilChanged` here, because some browsers will\r\n // fire the animation end event twice for the same animation. See:\r\n // https://github.com/angular/angular/issues/24084\r\n this._panelDoneAnimatingStream\r\n .pipe(distinctUntilChanged(), takeUntil(this._destroy))\r\n .subscribe(() => {\r\n if (this.panelOpen) {\r\n this._scrollTop = 0;\r\n this.openedChange.emit(true);\r\n } else {\r\n this.openedChange.emit(false);\r\n this.overlayDir.offsetX = 0;\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n });\r\n }\r\n\r\n ngAfterContentInit() {\r\n this._initKeyManager();\r\n\r\n this._selectionModel.changed\r\n .pipe(takeUntil(this._destroy))\r\n .subscribe((event) => {\r\n event.added.forEach((option) => option.select());\r\n event.removed.forEach((option) => option.deselect());\r\n });\r\n\r\n this.options.changes\r\n .pipe(startWith(null), takeUntil(this._destroy))\r\n .subscribe(() => {\r\n this._resetOptions();\r\n this._initializeSelection();\r\n });\r\n }\r\n\r\n ngDoCheck() {\r\n if (this.ngControl) {\r\n this.updateErrorState();\r\n }\r\n }\r\n\r\n ngOnChanges(changes: SimpleChanges) {\r\n // Updating the disabled state is handled by `mixinDisabled`, but we need to additionally let\r\n // the parent form field know to run change detection when the disabled state changes.\r\n if (changes.disabled) {\r\n this.stateChanges.next();\r\n }\r\n }\r\n\r\n ngOnDestroy() {\r\n this._monitorSubscription.unsubscribe();\r\n this._focusMonitor.stopMonitoring(this._elementRef);\r\n this._destroy.next();\r\n this._destroy.complete();\r\n this.stateChanges.complete();\r\n }\r\n\r\n /** Toggles the overlay panel open or closed. */\r\n toggle(): void {\r\n this.panelOpen ? this.close() : this.open();\r\n }\r\n\r\n /** Opens the overlay panel. */\r\n open(): void {\r\n if (\r\n this.disabled ||\r\n !this.options ||\r\n !this.options.length ||\r\n this._panelOpen\r\n ) {\r\n return;\r\n }\r\n\r\n this._triggerRect = this.trigger.nativeElement.getBoundingClientRect();\r\n\r\n this._panelOpen = true;\r\n this._keyManager.withHorizontalOrientation(null);\r\n\r\n this._highlightCorrectOption();\r\n this._changeDetectorRef.markForCheck();\r\n this.openedChange.emit(true);\r\n this._elementRef.nativeElement.classList.add(\r\n 'oui-select-list-options-opened'\r\n );\r\n }\r\n\r\n /** Closes the overlay panel and focuses the host element. */\r\n close(): void {\r\n if (this._panelOpen) {\r\n this._panelOpen = false;\r\n this._keyManager.withHorizontalOrientation(this._isRtl() ? 'rtl' : 'ltr');\r\n this._changeDetectorRef.markForCheck();\r\n this._onTouched();\r\n this.openedChange.emit(false);\r\n this._elementRef.nativeElement.classList.remove(\r\n 'oui-select-list-options-opened'\r\n );\r\n setTimeout((_) => this._document.activeElement.blur());\r\n }\r\n }\r\n\r\n /**\r\n * Sets the select's value. Part of the ControlValueAccessor interface\r\n * required to integrate with Angular's core forms API.\r\n *\r\n * @param value New value to be written to the model.\r\n */\r\n writeValue(value: any): void {\r\n if (this.options) {\r\n this._setSelectionByValue(value);\r\n }\r\n }\r\n\r\n /**\r\n * Saves a callback function to be invoked when the select's value\r\n * changes from user input. Part of the ControlValueAccessor interface\r\n * required to integrate with Angular's core forms API.\r\n *\r\n * @param fn Callback to be triggered when the value changes.\r\n */\r\n registerOnChange(fn: (value: any) => void): void {\r\n this._onChange = fn;\r\n }\r\n\r\n /**\r\n * Saves a callback function to be invoked when the select is blurred\r\n * by the user. Part of the ControlValueAccessor interface required\r\n * to integrate with Angular's core forms API.\r\n *\r\n * @param fn Callback to be triggered when the component has been touched.\r\n */\r\n registerOnTouched(fn: () => {}): void {\r\n this._onTouched = fn;\r\n }\r\n\r\n /**\r\n * Disables the select. Part of the ControlValueAccessor interface required\r\n * to integrate with Angular's core forms API.\r\n *\r\n * @param isDisabled Sets whether the component is disabled.\r\n */\r\n setDisabledState(isDisabled: boolean): void {\r\n this.disabled = isDisabled;\r\n this._changeDetectorRef.markForCheck();\r\n this.stateChanges.next();\r\n }\r\n\r\n /** Whether or not the overlay panel is open. */\r\n get panelOpen(): boolean {\r\n return this._panelOpen;\r\n }\r\n\r\n /** The currently selected option. */\r\n get selected(): OuiOption | OuiOption[] {\r\n return this.multiple\r\n ? this._selectionModel.selected\r\n : this._selectionModel.selected[0];\r\n }\r\n\r\n /** The value displayed in the trigger. */\r\n get triggerValue(): string {\r\n if (this.empty) {\r\n return '';\r\n }\r\n if (this._multiple) {\r\n const selectedOptions = this._selectionModel.selected.map(\r\n (option) => option.viewValueForSelect\r\n );\r\n\r\n if (this._isRtl()) {\r\n selectedOptions.reverse();\r\n }\r\n return selectedOptions.join(', ');\r\n }\r\n return this._selectionModel.selected[0].viewValueForSelect;\r\n }\r\n\r\n /** Whether the element is in RTL mode. */\r\n _isRtl(): boolean {\r\n return this._dir ? this._dir.value === 'rtl' : false;\r\n }\r\n\r\n /** Handles all keydown events on the select. */\r\n _handleKeydown(event: KeyboardEvent): void {\r\n if (!this.disabled) {\r\n this.panelOpen\r\n ? this._handleOpenKeydown(event)\r\n : this._handleClosedKeydown(event);\r\n }\r\n }\r\n\r\n /** Handles keyboard events while the select is closed. */\r\n private _handleClosedKeydown(event: KeyboardEvent): void {\r\n const keyCode = event.keyCode;\r\n const isArrowKey =\r\n keyCode === DOWN_ARROW ||\r\n keyCode === UP_ARROW ||\r\n keyCode === LEFT_ARROW ||\r\n keyCode === RIGHT_ARROW;\r\n const isOpenKey = keyCode === ENTER || keyCode === SPACE;\r\n const manager = this._keyManager;\r\n\r\n // Open the select on ALT + arrow key to match the native \r\n event.preventDefault();\r\n this.close();\r\n } else if (\r\n (keyCode === ENTER || keyCode === SPACE) &&\r\n manager.activeItem &&\r\n !hasModifierKey(event)\r\n ) {\r\n event.preventDefault();\r\n manager.activeItem._selectViaInteraction();\r\n } else if (this._multiple && keyCode === A && event.ctrlKey) {\r\n event.preventDefault();\r\n this.handleCtrlKey();\r\n } else {\r\n this.handleScrolling(manager, event, isArrowKey, keyCode);\r\n }\r\n }\r\n\r\n /**\r\n * Handle ctrl key\r\n */\r\n private handleCtrlKey() {\r\n const hasDeselectedOptions = this.options.some(\r\n (opt) => !opt.disabled && !opt.selected\r\n );\r\n\r\n this.options.forEach((option) => {\r\n if (!option.disabled) {\r\n hasDeselectedOptions ? option.select() : option.deselect();\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * @param manager\r\n * @param event\r\n * @param isArrowKey\r\n * @param keyCode\r\n */\r\n private handleScrolling(\r\n manager: ActiveDescendantKeyManager,\r\n event: KeyboardEvent,\r\n isArrowKey: boolean,\r\n keyCode: number\r\n ) {\r\n const previouslyFocusedIndex = manager.activeItemIndex;\r\n\r\n manager.onKeydown(event);\r\n\r\n if (\r\n this._multiple &&\r\n isArrowKey &&\r\n event.shiftKey &&\r\n manager.activeItem &&\r\n manager.activeItemIndex !== previouslyFocusedIndex\r\n ) {\r\n manager.activeItem._selectViaInteraction();\r\n }\r\n if (isArrowKey && manager.activeItemIndex !== previouslyFocusedIndex) {\r\n this._scrollToOption();\r\n } else {\r\n // First or last\r\n if (keyCode === DOWN_ARROW) {\r\n manager.setFirstItemActive();\r\n this._setScrollTop(0);\r\n }\r\n if (keyCode === UP_ARROW) {\r\n manager.setLastItemActive();\r\n this._scrollToOption();\r\n }\r\n }\r\n }\r\n\r\n _onFocus() {\r\n if (!this.disabled) {\r\n this._focused = true;\r\n this.stateChanges.next();\r\n }\r\n }\r\n\r\n /**\r\n * Calls the touched callback only if the panel is closed. Otherwise, the trigger will\r\n * \"blur\" to the panel when it opens, causing a false positive.\r\n */\r\n _onBlur() {\r\n this._focused = false;\r\n this.isSearchFieldPresent = false;\r\n\r\n if (!this.disabled && !this.panelOpen) {\r\n this._onTouched();\r\n this._changeDetectorRef.markForCheck();\r\n this.stateChanges.next();\r\n }\r\n }\r\n\r\n /**\r\n * Callback that is invoked when the overlay panel has been attached.\r\n */\r\n _onAttached(): void {\r\n this.overlayDir.positionChange.pipe(take(1)).subscribe(() => {\r\n this._setPseudoCheckboxPaddingSize();\r\n this._changeDetectorRef.detectChanges();\r\n this.panel.nativeElement.scrollTop = this._scrollTop;\r\n });\r\n }\r\n\r\n /** Returns the theme to be used on the panel. */\r\n _getPanelTheme(): string {\r\n return this._parentFormField ? `oui-${this._parentFormField.color}` : '';\r\n }\r\n\r\n // TODO(josephperrott): Remove after 2018 spec updates are fully merged.\r\n /** Sets the pseudo checkbox padding size based on the width of the pseudo checkbox. */\r\n private _setPseudoCheckboxPaddingSize() {\r\n if (!SELECT_MULTIPLE_PANEL_PADDING_X && this.multiple) {\r\n const pseudoCheckbox = this.panel.nativeElement.querySelector(\r\n '.oui-pseudo-checkbox'\r\n );\r\n if (pseudoCheckbox) {\r\n SELECT_MULTIPLE_PANEL_PADDING_X =\r\n SELECT_PANEL_PADDING_X * 1.5 + pseudoCheckbox.offsetWidth;\r\n }\r\n }\r\n }\r\n\r\n /** Whether the select has a value. */\r\n get empty(): boolean {\r\n return !this._selectionModel || this._selectionModel.isEmpty();\r\n }\r\n\r\n private _initializeSelection(): void {\r\n // Defer setting the value in order to avoid the \"Expression\r\n // has changed after it was checked\" errors from Angular.\r\n Promise.resolve().then(() => {\r\n this._setSelectionByValue(\r\n this.ngControl ? this.ngControl.value : this._value\r\n );\r\n this.savedValues = this.ngControl ? this.ngControl.value : this._value;\r\n });\r\n }\r\n\r\n /**\r\n * Sets the selected option based on a value. If no option can be\r\n * found with the designated value, the select trigger is cleared.\r\n */\r\n private _setSelectionByValue(value: any | any[]): void {\r\n if (this.multiple && value) {\r\n if (!Array.isArray(value)) {\r\n throw getOuiSelectNonArrayValueError();\r\n }\r\n\r\n this._selectionModel.clear();\r\n value.forEach((currentValue: any) => this._selectValue(currentValue));\r\n this._sortValues();\r\n } else {\r\n this._selectionModel.clear();\r\n const correspondingOption = this._selectValue(value);\r\n // Shift focus to the active item. Note that we shouldn't do this in multiple\r\n // mode, because we don't know what option the user interacted with last.\r\n if (correspondingOption) {\r\n this._keyManager.setActiveItem(correspondingOption);\r\n }\r\n }\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n\r\n /**\r\n * Finds and selects and option based on its value.\r\n *\r\n * @returns Option that has the corresponding value.\r\n */\r\n private _selectValue(value: any): OuiOption | undefined {\r\n const correspondingOption = this.options.find((option: OuiOption) => {\r\n try {\r\n // Treat null as a special reset value.\r\n return option.value != null && this._compareWith(option.value, value);\r\n } catch (error) {\r\n if (isDevMode()) {\r\n // Notify developers of errors in their comparator.\r\n console.warn(error);\r\n }\r\n return false;\r\n }\r\n });\r\n\r\n if (correspondingOption) {\r\n this._selectionModel.select(correspondingOption);\r\n }\r\n\r\n return correspondingOption;\r\n }\r\n\r\n /** Sets up a key manager to listen to keyboard events on the overlay panel. */\r\n private _initKeyManager() {\r\n this._keyManager = new ActiveDescendantKeyManager(this.options)\r\n .withTypeAhead()\r\n .withVerticalOrientation()\r\n .withHorizontalOrientation(this._isRtl() ? 'rtl' : 'ltr')\r\n .withAllowedModifierKeys(['shiftKey']);\r\n\r\n this._keyManager.tabOut.pipe(takeUntil(this._destroy)).subscribe(() => {\r\n // Restore focus to the trigger before closing. Ensures that the focus\r\n // position won't be lost if the user got focus into the overlay.\r\n this.focus();\r\n this.close();\r\n });\r\n\r\n this._keyManager.change.pipe(takeUntil(this._destroy)).subscribe(() => {\r\n if (this._panelOpen && this.panel) {\r\n // Panel is opened\r\n // Need not to scroll\r\n } else if (\r\n !this._panelOpen &&\r\n !this.multiple &&\r\n this._keyManager.activeItem\r\n ) {\r\n this._keyManager.activeItem._selectViaInteraction();\r\n }\r\n });\r\n }\r\n\r\n /** Drops current option subscriptions and IDs and resets from scratch. */\r\n private _resetOptions(): void {\r\n const changedOrDestroyed = merge(this.options.changes, this._destroy);\r\n\r\n this.optionSelectionChanges\r\n .pipe(takeUntil(changedOrDestroyed))\r\n .subscribe((event) => {\r\n this._onSelect(event.source, event.isUserInput);\r\n\r\n if (event.isUserInput && !this.multiple && this._panelOpen) {\r\n this.close();\r\n this.focus();\r\n }\r\n });\r\n\r\n // Listen to changes in the internal state of the options and react accordingly.\r\n // Handles cases like the labels of the selected options changing.\r\n merge(...this.options.map((option) => option._stateChanges))\r\n .pipe(takeUntil(changedOrDestroyed))\r\n .subscribe(() => {\r\n this._changeDetectorRef.markForCheck();\r\n this.stateChanges.next();\r\n });\r\n\r\n this._setOptionIds();\r\n }\r\n\r\n /** Invoked when an option is clicked. */\r\n private _onSelect(option: OuiOption, isUserInput: boolean): void {\r\n const wasSelected = this._selectionModel.isSelected(option);\r\n\r\n if (option.value == null && !this._multiple) {\r\n option.deselect();\r\n this._selectionModel.clear();\r\n this._propagateChanges(option.value);\r\n } else {\r\n option.selected\r\n ? this._selectionModel.select(option)\r\n : this._selectionModel.deselect(option);\r\n\r\n if (isUserInput) {\r\n this._keyManager.setActiveItem(option);\r\n }\r\n\r\n if (this.multiple) {\r\n this._sortValues();\r\n\r\n if (isUserInput) {\r\n // In case the user selected the option with their mouse, we\r\n // want to restore focus back to the trigger, in order to\r\n // prevent the select keyboard controls from clashing with\r\n // the ones from `oui-option`.\r\n this.focus();\r\n }\r\n }\r\n }\r\n\r\n if (wasSelected !== this._selectionModel.isSelected(option)) {\r\n this._propagateChanges();\r\n }\r\n this.disableDoneButton = false;\r\n this.stateChanges.next();\r\n }\r\n discardRecentChanges() {\r\n this.value = this.savedValues;\r\n this._setSelectionByValue(this.value);\r\n this.disableDoneButton = true;\r\n this.close();\r\n }\r\n doneRecentChanges() {\r\n this.savedValues = this.value;\r\n this.disableDoneButton = true;\r\n this.saveSelectionChange.emit(new OuiSelectChange(this, this.value));\r\n this.close();\r\n }\r\n /** Sorts the selected values in the selected based on their order in the panel. */\r\n private _sortValues() {\r\n if (this.multiple) {\r\n const options = this.options.toArray();\r\n\r\n this._selectionModel.sort((a, b) =>\r\n this.sortComparator\r\n ? this.sortComparator(a, b, options)\r\n : options.indexOf(a) - options.indexOf(b)\r\n );\r\n this.stateChanges.next();\r\n }\r\n }\r\n\r\n /** Emits change event to set the model value. */\r\n private _propagateChanges(fallbackValue?: any): void {\r\n let valueToEmit: any = null;\r\n\r\n if (this.multiple) {\r\n valueToEmit = (this.selected as OuiOption[]).map(\r\n (option) => option.value\r\n );\r\n } else {\r\n valueToEmit = this.selected\r\n ? (this.selected as OuiOption).value\r\n : fallbackValue;\r\n }\r\n\r\n this._value = valueToEmit;\r\n this.valueChange.emit(valueToEmit);\r\n this._onChange(valueToEmit);\r\n this.selectionChange.emit(new OuiSelectChange(this, valueToEmit));\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n\r\n /** Records option IDs to pass to the aria-owns property. */\r\n private _setOptionIds() {\r\n this._optionIds = this.options.map((option) => option.id).join(' ');\r\n }\r\n\r\n /**\r\n * Highlights the selected item. If no option is selected, it will highlight\r\n * the first item instead.\r\n */\r\n private _highlightCorrectOption(): void {\r\n if (this._keyManager) {\r\n if (this.empty) {\r\n this._keyManager.setFirstItemActive();\r\n } else {\r\n this._keyManager.setActiveItem(this._selectionModel.selected[0]);\r\n }\r\n }\r\n }\r\n\r\n /** Focuses the select element. */\r\n focus(): void {\r\n this._elementRef.nativeElement.focus();\r\n }\r\n\r\n /** Returns the aria-label of the select component. */\r\n _getAriaLabel(): string | null {\r\n // If an ariaLabelledby value has been set by the consumer, the select should not overwrite the\r\n // `aria-labelledby` value by setting the ariaLabel to the placeholder.\r\n return this.ariaLabelledby ? null : this.ariaLabel || this.placeholder;\r\n }\r\n\r\n /** Returns the aria-labelledby of the select component. */\r\n _getAriaLabelledby(): string | null {\r\n if (this.ariaLabelledby) {\r\n return this.ariaLabelledby;\r\n }\r\n\r\n return null;\r\n }\r\n\r\n /** Determines the `aria-activedescendant` to be set on the host. */\r\n _getAriaActiveDescendant(): string | null {\r\n if (this.panelOpen && this._keyManager && this._keyManager.activeItem) {\r\n return this._keyManager.activeItem.id;\r\n }\r\n\r\n return null;\r\n }\r\n\r\n /**\r\n * Implemented as part of OuiFormFieldControl.\r\n *\r\n * @docs-private\r\n */\r\n setDescribedByIds(ids: string[]) {\r\n this._ariaDescribedby = ids.join(' ');\r\n }\r\n\r\n /**\r\n * Implemented as part of OuiFormFieldControl.\r\n *\r\n * @docs-private\r\n */\r\n onContainerClick() {\r\n this.focus();\r\n this.open();\r\n }\r\n\r\n /**\r\n * Implemented as part of OuiFormFieldControl.\r\n *\r\n * @docs-private\r\n */\r\n get shouldLabelFloat(): boolean {\r\n return this._panelOpen || !this.empty;\r\n }\r\n\r\n /**\r\n * Add outer class to perfect scrollbar\r\n * This is added only when there is a search field\r\n */\r\n ouiSelectInputOuter() {\r\n this.ouiSelectInputOuterClassName = 'oui-select-input-outer';\r\n }\r\n\r\n /**\r\n * Custom overlay class for cdk overlay container\r\n */\r\n openCdk() {\r\n this.overlayDir.positionChange.pipe(take(1)).subscribe((e) => {\r\n this.cdkConnectionOverlayPanel = '';\r\n if (e.connectionPair.originY === 'top') {\r\n this.cdkConnectionOverlayPanel = 'select-overlay-top';\r\n }\r\n this._changeDetectorRef.detectChanges();\r\n setTimeout((_) => this._scrollToOption());\r\n });\r\n\r\n const cdkOverLayContainer = this._document.querySelector(\r\n '.cdk-overlay-container'\r\n );\r\n const ouiSelectPanel = this._document.querySelector('.oui-select-panel');\r\n cdkOverLayContainer.classList.add('oui-select-overlay-container');\r\n const containerWidth = this._elementRef.nativeElement.offsetWidth;\r\n ouiSelectPanel.style.width = `${containerWidth}px`;\r\n const searchQueryString = '.oui-select-search-inner';\r\n if (this._document.querySelector(searchQueryString)) {\r\n this.scrollCalc(searchQueryString);\r\n }\r\n const actionItemsQueryString = '.oui-select-action-items';\r\n if (this._document.querySelector(actionItemsQueryString)) {\r\n this.scrollCalc(actionItemsQueryString);\r\n }\r\n }\r\n scrollCalc(selectQueryString: string) {\r\n const searchInput = this._document.querySelector(selectQueryString);\r\n const outter = this._document.querySelector('.oui-select-panel');\r\n let inner = this._document.querySelector('.oui-option');\r\n if (inner === null) {\r\n inner = 0;\r\n }\r\n const scrollbarWidth = outter.offsetWidth - inner.offsetWidth;\r\n if (scrollbarWidth > 5) {\r\n searchInput.style.width = `${inner.offsetWidth}px`;\r\n } else {\r\n searchInput.style.width = `calc(100% + 8px)`;\r\n }\r\n }\r\n\r\n /**\r\n * Given that we are not actually focusing active options, we must manually adjust scroll\r\n * to reveal options below the fold. First, we find the offset of the option from the top\r\n * of the panel. If that offset is below the fold, the new scrollTop will be the offset -\r\n * the panel height + the option height, so the active option will be just visible at the\r\n * bottom of the panel. If that offset is above the top of the visible panel, the new scrollTop\r\n * will become the offset. If that offset is visible within the panel already, the scrollTop is\r\n * not adjusted.\r\n */\r\n private _scrollToOption(): void {\r\n const manager = this._keyManager;\r\n const index = manager.activeItemIndex || 0;\r\n const labelCount = _countGroupLabelsBeforeOption(\r\n index,\r\n this.options,\r\n this.optionGroups\r\n );\r\n const scrollTop = this._getScrollTop();\r\n const newScrollPosition = _getOptionScrollPosition(\r\n index + labelCount,\r\n SELECT_OPTION_HEIGHT,\r\n scrollTop,\r\n SELECT_PANEL_HEIGHT\r\n );\r\n this._setScrollTop(newScrollPosition);\r\n }\r\n\r\n /**\r\n * Sets the panel scrollTop. This allows us to manually scroll to display options\r\n * above or below the fold, as they are not actually being focused when active.\r\n */\r\n _setScrollTop(scrollTop: number): void {\r\n if (this.panel) {\r\n this.panel.nativeElement.scrollTop = scrollTop;\r\n }\r\n }\r\n\r\n /** Returns the panel's scrollTop. */\r\n _getScrollTop(): number {\r\n return this.panel ? this.panel.nativeElement.scrollTop : 0;\r\n }\r\n}\r\n", - "selector": "oui-select-trigger", - "providers": [], - "inputsClass": [], - "outputsClass": [], - "deprecated": false, - "deprecationMessage": "", - "hostBindings": [], - "hostListeners": [], - "propertiesClass": [], - "methodsClass": [] - }, - { - "name": "OuiSort", - "id": "directive-OuiSort-d0101ab249682d7d71ed7803c8547b61cb9f4afa9bd6f639390a141e3cf8b87e69d029e8ac7b405c1aba87637f3db9b8b724bf41d29c92485e7f8ac4e0137cab", - "file": "ui/src/components/sort/sort.ts", - "type": "directive", - "description": "

Container for OuiSortables to manage the sort state and provide default sort parameters.

\n", - "rawdescription": "\nContainer for OuiSortables to manage the sort state and provide default sort parameters.", - "sourceCode": "import { coerceBooleanProperty } from '@angular/cdk/coercion';\r\nimport {\r\n Directive,\r\n EventEmitter,\r\n Input,\r\n isDevMode,\r\n OnChanges,\r\n OnDestroy,\r\n OnInit,\r\n Output,\r\n} from '@angular/core';\r\nimport {\r\n CanDisable,\r\n CanDisableCtor,\r\n HasInitialized,\r\n HasInitializedCtor,\r\n mixinDisabled,\r\n mixinInitialized,\r\n} from '../core';\r\nimport { Subject } from 'rxjs';\r\nimport { SortDirection } from './sort-direction';\r\nimport {\r\n getSortDuplicateSortableIdError,\r\n getSortHeaderMissingIdError,\r\n getSortInvalidDirectionError,\r\n} from './sort-errors';\r\n\r\n/** Interface for a directive that holds sorting state consumed by `OuiSortHeader`. */\r\nexport interface OuiSortable {\r\n /** The id of the column being sorted. */\r\n id: string;\r\n\r\n /** Starting sort direction. */\r\n start: 'asc' | 'desc';\r\n\r\n /** Whether to disable clearing the sorting state. */\r\n disableClear: boolean;\r\n}\r\n\r\n/** The current sort state. */\r\nexport interface Sort {\r\n /** The id of the column being sorted. */\r\n active: string;\r\n\r\n /** The sort direction. */\r\n direction: SortDirection;\r\n}\r\n\r\n// Boilerplate for applying mixins to OuiSort.\r\n/** @docs-private */\r\nexport class OuiSortBase {}\r\nexport const _OuiSortMixinBase: HasInitializedCtor &\r\n CanDisableCtor &\r\n typeof OuiSortBase = mixinInitialized(mixinDisabled(OuiSortBase));\r\n\r\n/** Container for OuiSortables to manage the sort state and provide default sort parameters. */\r\n@Directive({\r\n selector: '[ouiSort]',\r\n exportAs: 'ouiSort',\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['disabled: ouiSortDisabled'],\r\n})\r\nexport class OuiSort\r\n extends _OuiSortMixinBase\r\n implements CanDisable, HasInitialized, OnChanges, OnDestroy, OnInit\r\n{\r\n /** Collection of all registered sortables that this directive manages. */\r\n sortables = new Map();\r\n\r\n /** Used to notify any child components listening to state changes. */\r\n readonly _stateChanges = new Subject();\r\n\r\n /** The id of the most recently sorted OuiSortable. */\r\n // eslint-disable-next-line @angular-eslint/no-input-rename\r\n @Input('ouiSortActive') active: string;\r\n\r\n /**\r\n * The direction to set when an OuiSortable is initially sorted.\r\n * May be overriden by the OuiSortable's sort start.\r\n */\r\n // eslint-disable-next-line @angular-eslint/no-input-rename\r\n @Input('ouiSortStart') start: 'asc' | 'desc' = 'asc';\r\n\r\n /** The sort direction of the currently active OuiSortable. */\r\n @Input('ouiSortDirection')\r\n get direction(): SortDirection {\r\n return this._direction;\r\n }\r\n set direction(direction: SortDirection) {\r\n if (\r\n isDevMode() &&\r\n direction &&\r\n direction !== 'asc' &&\r\n direction !== 'desc'\r\n ) {\r\n throw getSortInvalidDirectionError(direction);\r\n }\r\n this._direction = direction;\r\n }\r\n private _direction: SortDirection = '';\r\n\r\n /**\r\n * Whether to disable the user from clearing the sort by finishing the sort direction cycle.\r\n * May be overriden by the OuiSortable's disable clear input.\r\n */\r\n @Input('ouiSortDisableClear')\r\n get disableClear(): boolean {\r\n return this._disableClear;\r\n }\r\n set disableClear(v: boolean) {\r\n this._disableClear = coerceBooleanProperty(v);\r\n }\r\n private _disableClear: boolean;\r\n\r\n /** Event emitted when the user changes either the active sort or sort direction. */\r\n // eslint-disable-next-line @angular-eslint/no-output-rename\r\n @Output('ouiSortChange')\r\n readonly sortChange: EventEmitter = new EventEmitter();\r\n\r\n /**\r\n * Register function to be used by the contained OuiSortables. Adds the OuiSortable to the\r\n * collection of OuiSortables.\r\n */\r\n register(sortable: OuiSortable): void {\r\n if (!sortable.id) {\r\n throw getSortHeaderMissingIdError();\r\n }\r\n\r\n if (this.sortables.has(sortable.id)) {\r\n throw getSortDuplicateSortableIdError(sortable.id);\r\n }\r\n this.sortables.set(sortable.id, sortable);\r\n }\r\n\r\n /**\r\n * Unregister function to be used by the contained OuiSortables. Removes the OuiSortable from the\r\n * collection of contained OuiSortables.\r\n */\r\n deregister(sortable: OuiSortable): void {\r\n this.sortables.delete(sortable.id);\r\n }\r\n\r\n /** Sets the active sort id and determines the new sort direction. */\r\n sort(sortable: OuiSortable): void {\r\n if (this.active !== sortable.id) {\r\n this.active = sortable.id;\r\n this.direction = sortable.start ? sortable.start : this.start;\r\n } else {\r\n this.direction = this.getNextSortDirection(sortable);\r\n }\r\n\r\n this.sortChange.emit({ active: this.active, direction: this.direction });\r\n }\r\n\r\n /** Returns the next sort direction of the active sortable, checking for potential overrides. */\r\n getNextSortDirection(sortable: OuiSortable): SortDirection {\r\n if (!sortable) {\r\n return '';\r\n }\r\n\r\n const sortDirectionCycle = getSortDirectionCycle(\r\n sortable.start || this.start\r\n );\r\n\r\n // Get and return the next direction in the cycle\r\n let nextDirectionIndex = sortDirectionCycle.indexOf(this.direction) + 1;\r\n if (nextDirectionIndex >= sortDirectionCycle.length) {\r\n nextDirectionIndex = 0;\r\n }\r\n return sortDirectionCycle[nextDirectionIndex];\r\n }\r\n\r\n ngOnInit() {\r\n this._markInitialized();\r\n }\r\n\r\n ngOnChanges() {\r\n this._stateChanges.next();\r\n }\r\n\r\n ngOnDestroy() {\r\n this._stateChanges.complete();\r\n }\r\n}\r\n\r\n/** Returns the sort direction cycle to use given the provided parameters of order and clear. */\r\nfunction getSortDirectionCycle(start: 'asc' | 'desc'): SortDirection[] {\r\n const sortOrder: SortDirection[] = ['asc', 'desc'];\r\n if (start === 'desc') {\r\n sortOrder.reverse();\r\n }\r\n\r\n return sortOrder;\r\n}\r\n", - "selector": "[ouiSort]", - "providers": [], - "inputsClass": [ - { - "name": "ouiSortActive", - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nThe id of the most recently sorted OuiSortable.", - "description": "

The id of the most recently sorted OuiSortable.

\n", - "line": 75, - "type": "string", - "decorators": [] - }, - { - "name": "ouiSortDirection", - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nThe sort direction of the currently active OuiSortable.", - "description": "

The sort direction of the currently active OuiSortable.

\n", - "line": 86, - "type": "SortDirection", - "decorators": [] - }, - { - "name": "ouiSortDisableClear", - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\n\nWhether to disable the user from clearing the sort by finishing the sort direction cycle.\nMay be overriden by the OuiSortable's disable clear input.\n", - "description": "

Whether to disable the user from clearing the sort by finishing the sort direction cycle.\nMay be overriden by the OuiSortable's disable clear input.

\n", - "line": 107, - "type": "boolean", - "decorators": [] - }, - { - "name": "ouiSortStart", - "defaultValue": "'asc'", - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\n\nThe direction to set when an OuiSortable is initially sorted.\nMay be overriden by the OuiSortable's sort start.\n", - "description": "

The direction to set when an OuiSortable is initially sorted.\nMay be overriden by the OuiSortable's sort start.

\n", - "line": 82, - "type": "\"asc\" | \"desc\"", - "decorators": [] - } - ], - "outputsClass": [ - { - "name": "ouiSortChange", - "defaultValue": "new EventEmitter()", - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nEvent emitted when the user changes either the active sort or sort direction.", - "description": "

Event emitted when the user changes either the active sort or sort direction.

\n", - "line": 118, - "type": "EventEmitter" - } - ], - "deprecated": false, - "deprecationMessage": "", - "hostBindings": [], - "hostListeners": [], - "propertiesClass": [ - { - "name": "_direction", - "defaultValue": "''", + "line": 539, "deprecated": false, "deprecationMessage": "", - "type": "SortDirection", - "optional": false, - "description": "", - "line": 100, - "modifierKind": [ - 121 - ] + "rawdescription": "\n\nEvaluate whether the pagination controls should be displayed. If the scroll width of the\ntab list is wider than the size of the header container, then the pagination controls should\nbe shown.\n\nThis is an expensive call that forces a layout reflow to compute box and scroll metrics and\nshould be called sparingly.\n", + "description": "

Evaluate whether the pagination controls should be displayed. If the scroll width of the\ntab list is wider than the size of the header container, then the pagination controls should\nbe shown.

\n

This is an expensive call that forces a layout reflow to compute box and scroll metrics and\nshould be called sparingly.

\n", + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_disableClear", + "name": "_checkScrollingControls", + "args": [], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 567, "deprecated": false, "deprecationMessage": "", - "type": "boolean", - "optional": false, - "description": "", - "line": 113, - "modifierKind": [ - 121 - ] + "rawdescription": "\n\nEvaluate whether the before and after controls should be enabled or disabled.\nIf the header is at the beginning of the list (scroll distance is equal to 0) then disable the\nbefore button. If the header is at the end of the list (scroll distance is equal to the\nmaximum distance we can scroll), then disable the after button.\n\nThis is an expensive call that forces a layout reflow to compute box and scroll metrics and\nshould be called sparingly.\n", + "description": "

Evaluate whether the before and after controls should be enabled or disabled.\nIf the header is at the beginning of the list (scroll distance is equal to 0) then disable the\nbefore button. If the header is at the end of the list (scroll distance is equal to the\nmaximum distance we can scroll), then disable the after button.

\n

This is an expensive call that forces a layout reflow to compute box and scroll metrics and\nshould be called sparingly.

\n", + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_stateChanges", - "defaultValue": "new Subject()", + "name": "_getLayoutDirection", + "args": [], + "optional": false, + "returnType": "Direction", + "typeParameters": [], + "line": 425, "deprecated": false, "deprecationMessage": "", - "type": "", + "rawdescription": "\nThe layout direction of the containing app.", + "description": "

The layout direction of the containing app.

\n", + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_getMaxScrollDistance", + "args": [], "optional": false, - "description": "

Used to notify any child components listening to state changes.

\n", - "line": 71, - "rawdescription": "\nUsed to notify any child components listening to state changes.", - "modifierKind": [ - 144 - ] + "returnType": "number", + "typeParameters": [], + "line": 585, + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\n\nDetermines what is the maximum length in pixels that can be set for the scroll distance. This\nis equal to the difference in width between the tab list container and tab header container.\n\nThis is an expensive call that forces a layout reflow to compute box and scroll metrics and\nshould be called sparingly.\n", + "description": "

Determines what is the maximum length in pixels that can be set for the scroll distance. This\nis equal to the difference in width between the tab list container and tab header container.

\n

This is an expensive call that forces a layout reflow to compute box and scroll metrics and\nshould be called sparingly.

\n", + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "sortables", - "defaultValue": "new Map()", + "name": "_handleKeydown", + "args": [ + { + "name": "event", + "type": "KeyboardEvent", + "deprecated": false, + "deprecationMessage": "" + } + ], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 318, "deprecated": false, "deprecationMessage": "", - "type": "", + "rawdescription": "\nHandles keyboard events on the header.", + "description": "

Handles keyboard events on the header.

\n", + "jsdoctags": [ + { + "name": "event", + "type": "KeyboardEvent", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_handlePaginatorClick", + "args": [ + { + "name": "direction", + "type": "ScrollDirection", + "deprecated": false, + "deprecationMessage": "" + } + ], "optional": false, - "description": "

Collection of all registered sortables that this directive manages.

\n", - "line": 68, - "rawdescription": "\nCollection of all registered sortables that this directive manages." - } - ], - "methodsClass": [ + "returnType": "void", + "typeParameters": [], + "line": 481, + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nHandles click events on the pagination arrows.", + "description": "

Handles click events on the pagination arrows.

\n", + "jsdoctags": [ + { + "name": "direction", + "type": "ScrollDirection", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, { - "name": "deregister", + "name": "_handlePaginatorPress", "args": [ { - "name": "sortable", - "type": "OuiSortable", + "name": "direction", + "type": "ScrollDirection", "deprecated": false, "deprecationMessage": "" + }, + { + "name": "mouseEvent", + "type": "MouseEvent", + "deprecated": false, + "deprecationMessage": "", + "optional": true } ], "optional": false, "returnType": "void", "typeParameters": [], - "line": 139, + "line": 614, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\n\nUnregister function to be used by the contained OuiSortables. Removes the OuiSortable from the\ncollection of contained OuiSortables.\n", - "description": "

Unregister function to be used by the contained OuiSortables. Removes the OuiSortable from the\ncollection of contained OuiSortables.

\n", + "rawdescription": "\n\nHandles the user pressing down on one of the paginators.\nStarts scrolling the header after a certain amount of time.\n", + "description": "

Handles the user pressing down on one of the paginators.\nStarts scrolling the header after a certain amount of time.

\n", "jsdoctags": [ { - "name": "sortable", - "type": "OuiSortable", + "name": { + "pos": 22780, + "end": 22789, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "direction" + }, + "type": "ScrollDirection", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "pos": 22774, + "end": 22779, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "param" + }, + "comment": "

In which direction the paginator should be scrolled.

\n" + }, + { + "name": "mouseEvent", + "type": "MouseEvent", "deprecated": false, "deprecationMessage": "", + "optional": true, "tagName": { "text": "param" } } - ] + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "getNextSortDirection", + "name": "_isValidIndex", "args": [ { - "name": "sortable", - "type": "OuiSortable", + "name": "index", + "type": "number", "deprecated": false, "deprecationMessage": "" } ], "optional": false, - "returnType": "SortDirection", + "returnType": "boolean", "typeParameters": [], - "line": 156, + "line": 394, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nReturns the next sort direction of the active sortable, checking for potential overrides.", - "description": "

Returns the next sort direction of the active sortable, checking for potential overrides.

\n", + "rawdescription": "\n\nDetermines if an index is valid. If the tabs are not ready yet, we assume that the user is\nproviding a valid index and return true.\n", + "description": "

Determines if an index is valid. If the tabs are not ready yet, we assume that the user is\nproviding a valid index and return true.

\n", "jsdoctags": [ { - "name": "sortable", - "type": "OuiSortable", + "name": "index", + "type": "number", "deprecated": false, "deprecationMessage": "", "tagName": { "text": "param" } } - ] + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "ngOnChanges", + "name": "_itemsResized", "args": [], "optional": false, - "returnType": "void", + "returnType": "Observable", "typeParameters": [], - "line": 177, + "line": 255, "deprecated": false, - "deprecationMessage": "" + "deprecationMessage": "", + "rawdescription": "\nSends any changes that could affect the layout of the items.", + "description": "

Sends any changes that could affect the layout of the items.

\n", + "modifierKind": [ + 121 + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "ngOnDestroy", + "name": "_onContentChanges", "args": [], "optional": false, "returnType": "void", "typeParameters": [], - "line": 181, + "line": 344, "deprecated": false, - "deprecationMessage": "" + "deprecationMessage": "", + "rawdescription": "\n\nCallback for when the MutationObserver detects that the content has changed.\n", + "description": "

Callback for when the MutationObserver detects that the content has changed.

\n", + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "ngOnInit", - "args": [], + "name": "_scrollHeader", + "args": [ + { + "name": "direction", + "type": "ScrollDirection", + "deprecated": false, + "deprecationMessage": "" + } + ], "optional": false, - "returnType": "void", + "returnType": "{ maxScrollDistance: number; distance: number; }", "typeParameters": [], - "line": 173, + "line": 471, "deprecated": false, - "deprecationMessage": "" + "deprecationMessage": "", + "rawdescription": "\n\nMoves the tab list in the 'before' or 'after' direction (towards the beginning of the list or\nthe end of the list, respectively). The distance to scroll is computed to be a third of the\nlength of the tab list view window.\n\nThis is an expensive call that forces a layout reflow to compute box and scroll metrics and\nshould be called sparingly.\n", + "description": "

Moves the tab list in the 'before' or 'after' direction (towards the beginning of the list or\nthe end of the list, respectively). The distance to scroll is computed to be a third of the\nlength of the tab list view window.

\n

This is an expensive call that forces a layout reflow to compute box and scroll metrics and\nshould be called sparingly.

\n", + "jsdoctags": [ + { + "name": "direction", + "type": "ScrollDirection", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "register", + "name": "_scrollTo", "args": [ { - "name": "sortable", - "type": "OuiSortable", + "name": "position", + "type": "number", + "deprecated": false, + "deprecationMessage": "" + } + ], + "optional": false, + "returnType": "{ maxScrollDistance: number; distance: number; }", + "typeParameters": [], + "line": 643, + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\n\nScrolls the header to a given position.\n", + "description": "

Scrolls the header to a given position.

\n", + "modifierKind": [ + 121 + ], + "jsdoctags": [ + { + "name": { + "pos": 23946, + "end": 23954, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "position" + }, + "type": "number", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "pos": 23940, + "end": 23945, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "param" + }, + "comment": "

Position to which to scroll.

\n" + }, + { + "tagName": { + "pos": 23991, + "end": 23998, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "returns" + }, + "comment": "

Information on the current scroll distance and the maximum.

\n" + } + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_scrollToLabel", + "args": [ + { + "name": "labelIndex", + "type": "number", "deprecated": false, "deprecationMessage": "" } @@ -17846,29 +27455,32 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 124, + "line": 492, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\n\nRegister function to be used by the contained OuiSortables. Adds the OuiSortable to the\ncollection of OuiSortables.\n", - "description": "

Register function to be used by the contained OuiSortables. Adds the OuiSortable to the\ncollection of OuiSortables.

\n", + "rawdescription": "\n\nMoves the tab list such that the desired tab label (marked by index) is moved into view.\n\nThis is an expensive call that forces a layout reflow to compute box and scroll metrics and\nshould be called sparingly.\n", + "description": "

Moves the tab list such that the desired tab label (marked by index) is moved into view.

\n

This is an expensive call that forces a layout reflow to compute box and scroll metrics and\nshould be called sparingly.

\n", "jsdoctags": [ { - "name": "sortable", - "type": "OuiSortable", + "name": "labelIndex", + "type": "number", "deprecated": false, "deprecationMessage": "", "tagName": { "text": "param" } } - ] + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "sort", + "name": "_setTabFocus", "args": [ { - "name": "sortable", - "type": "OuiSortable", + "name": "tabIndex", + "type": "number", "deprecated": false, "deprecationMessage": "" } @@ -17876,92 +27488,274 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 144, + "line": 402, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nSets the active sort id and determines the new sort direction.", - "description": "

Sets the active sort id and determines the new sort direction.

\n", + "rawdescription": "\n\nSets focus on the HTML element for the label wrapper and scrolls it into the view if\nscrolling is enabled.\n", + "description": "

Sets focus on the HTML element for the label wrapper and scrolls it into the view if\nscrolling is enabled.

\n", "jsdoctags": [ { - "name": "sortable", - "type": "OuiSortable", + "name": "tabIndex", + "type": "number", "deprecated": false, "deprecationMessage": "", "tagName": { "text": "param" } } - ] + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_stopInterval", + "args": [], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 605, + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nStops the currently-running paginator interval.", + "description": "

Stops the currently-running paginator interval.

\n", + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_updateTabScrollPosition", + "args": [], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 430, + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nPerforms the CSS transformation on the tab list that will cause the list to scroll.", + "description": "

Performs the CSS transformation on the tab list that will cause the list to scroll.

\n", + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "ngAfterContentChecked", + "args": [], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 283, + "deprecated": false, + "deprecationMessage": "", + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "ngAfterViewInit", + "args": [], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 186, + "deprecated": false, + "deprecationMessage": "", + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "ngOnDestroy", + "args": [], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 310, + "deprecated": false, + "deprecationMessage": "", + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "updatePagination", + "args": [], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 370, + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\n\nUpdates the view whether pagination should be enabled or not.\n\nWARNING: Calling this method can be very costly in terms of performance. It should be called\nas infrequently as possible from outside of the Tabs component as it causes a reflow of the\npage.\n", + "description": "

Updates the view whether pagination should be enabled or not.

\n

WARNING: Calling this method can be very costly in terms of performance. It should be called\nas infrequently as possible from outside of the Tabs component as it causes a reflow of the\npage.

\n", + "inheritance": { + "file": "MatPaginatedTabHeader" + } } ], - "extends": "_OuiSortMixinBase", - "implements": [ - "CanDisable", - "HasInitialized", - "OnChanges", - "OnDestroy", - "OnInit" + "deprecated": false, + "deprecationMessage": "", + "hostBindings": [], + "hostListeners": [], + "description": "

The header of the tab group which displays a list of all the tabs in the tab group. Includes\nan ink bar that follows the currently selected tab. When the tabs list's width exceeds the\nwidth of the header container, then arrows will be displayed to allow the user to scroll\nleft and right across the header.

\n", + "rawdescription": "\n\nThe header of the tab group which displays a list of all the tabs in the tab group. Includes\nan ink bar that follows the currently selected tab. When the tabs list's width exceeds the\nwidth of the header container, then arrows will be displayed to allow the user to scroll\nleft and right across the header.\n", + "type": "component", + "sourceCode": "import {\r\n AfterContentChecked,\r\n AfterContentInit,\r\n AfterViewInit,\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n ContentChildren,\r\n ElementRef,\r\n Inject,\r\n Input,\r\n NgZone,\r\n OnDestroy,\r\n Optional,\r\n QueryList,\r\n ViewChild,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\nimport {ViewportRuler} from '@angular/cdk/scrolling';\r\nimport {Platform} from '@angular/cdk/platform';\r\nimport {Directionality} from '@angular/cdk/bidi';\r\nimport {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations';\r\nimport {MatTabLabelWrapper} from './tab-label-wrapper';\r\nimport {MatInkBar} from './ink-bar';\r\nimport {MatPaginatedTabHeader} from './paginated-tab-header';\r\nimport {BooleanInput, coerceBooleanProperty} from '@angular/cdk/coercion';\r\n\r\n/**\r\n * The header of the tab group which displays a list of all the tabs in the tab group. Includes\r\n * an ink bar that follows the currently selected tab. When the tabs list's width exceeds the\r\n * width of the header container, then arrows will be displayed to allow the user to scroll\r\n * left and right across the header.\r\n * @docs-private\r\n */\r\n@Component({\r\n selector: 'mat-tab-header',\r\n templateUrl: 'tab-header.html',\r\n styleUrls: ['tab-header.scss'],\r\n inputs: ['selectedIndex'],\r\n outputs: ['selectFocusedIndex', 'indexFocused'],\r\n encapsulation: ViewEncapsulation.None,\r\n // tslint:disable-next-line:validate-decorators\r\n changeDetection: ChangeDetectionStrategy.Default,\r\n host: {\r\n 'class': 'mat-mdc-tab-header',\r\n '[class.mat-mdc-tab-header-pagination-controls-enabled]': '_showPaginationControls',\r\n '[class.mat-mdc-tab-header-rtl]': \"_getLayoutDirection() == 'rtl'\",\r\n },\r\n})\r\nexport class MatTabHeader\r\n extends MatPaginatedTabHeader\r\n implements AfterContentChecked, AfterContentInit, AfterViewInit, OnDestroy\r\n{\r\n @ContentChildren(MatTabLabelWrapper, {descendants: false}) _items: QueryList;\r\n @ViewChild('tabListContainer', {static: true}) _tabListContainer: ElementRef;\r\n @ViewChild('tabList', {static: true}) _tabList: ElementRef;\r\n @ViewChild('tabListInner', {static: true}) _tabListInner: ElementRef;\r\n @ViewChild('nextPaginator') _nextPaginator: ElementRef;\r\n @ViewChild('previousPaginator') _previousPaginator: ElementRef;\r\n _inkBar: MatInkBar;\r\n\r\n /** Whether the ripple effect is disabled or not. */\r\n @Input()\r\n get disableRipple(): boolean {\r\n return this._disableRipple;\r\n }\r\n\r\n set disableRipple(value: BooleanInput) {\r\n this._disableRipple = coerceBooleanProperty(value);\r\n }\r\n\r\n private _disableRipple: boolean = false;\r\n\r\n constructor(\r\n elementRef: ElementRef,\r\n changeDetectorRef: ChangeDetectorRef,\r\n viewportRuler: ViewportRuler,\r\n @Optional() dir: Directionality,\r\n ngZone: NgZone,\r\n platform: Platform,\r\n @Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string,\r\n ) {\r\n super(elementRef, changeDetectorRef, viewportRuler, dir, ngZone, platform, animationMode);\r\n }\r\n\r\n override ngAfterContentInit() {\r\n this._inkBar = new MatInkBar(this._items);\r\n super.ngAfterContentInit();\r\n }\r\n\r\n protected _itemSelected(event: KeyboardEvent) {\r\n event.preventDefault();\r\n }\r\n}\r\n", + "assetsDirs": [], + "styleUrlsData": [ + { + "data": "@use '@angular/cdk';\r\n@use './tabs-theme';\r\n@use './tabs-common';\r\n\r\n@include tabs-common.paginated-tab-header;\r\n\r\n.mat-mdc-tab-label-container {\r\n @include tabs-common.paginated-tab-header-container;\r\n}\r\n\r\n.mat-mdc-tab-labels {\r\n @include tabs-common.paginated-tab-header-item-wrapper('.mat-mdc-tab-header');\r\n}\r\n\r\n.mat-mdc-tab {\r\n // For the tab element, default inset/offset values are necessary to ensure that\r\n // the focus indicator is sufficiently contrastive and renders appropriately.\r\n &::before {\r\n margin: 5px;\r\n }\r\n\r\n @include cdk.high-contrast(active, off) {\r\n // When a tab is disabled in high contrast mode, set the text color to the disabled\r\n // color, which is (unintuitively) named \"GrayText\".\r\n &[aria-disabled='true'] {\r\n color: GrayText;\r\n }\r\n }\r\n}\r\n", + "styleUrl": "tab-header.scss" + } ], - "accessors": { - "direction": { - "name": "direction", - "setSignature": { - "name": "direction", - "type": "void", + "stylesData": "", + "constructorObj": { + "name": "constructor", + "description": "", + "deprecated": false, + "deprecationMessage": "", + "args": [ + { + "name": "elementRef", + "type": "ElementRef", "deprecated": false, - "deprecationMessage": "", - "args": [ - { - "name": "direction", - "type": "SortDirection", - "deprecated": false, - "deprecationMessage": "" - } - ], - "returnType": "void", - "line": 89, - "jsdoctags": [ - { - "name": "direction", - "type": "SortDirection", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } - ] + "deprecationMessage": "" }, - "getSignature": { - "name": "direction", - "type": "", - "returnType": "SortDirection", - "line": 86, - "rawdescription": "\nThe sort direction of the currently active OuiSortable.", - "description": "

The sort direction of the currently active OuiSortable.

\n" + { + "name": "changeDetectorRef", + "type": "ChangeDetectorRef", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "viewportRuler", + "type": "ViewportRuler", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "dir", + "type": "Directionality", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "ngZone", + "type": "NgZone", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "platform", + "type": "Platform", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "animationMode", + "type": "string", + "deprecated": false, + "deprecationMessage": "", + "optional": true } - }, - "disableClear": { - "name": "disableClear", + ], + "line": 80, + "jsdoctags": [ + { + "name": "elementRef", + "type": "ElementRef", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "changeDetectorRef", + "type": "ChangeDetectorRef", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "viewportRuler", + "type": "ViewportRuler", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "dir", + "type": "Directionality", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "ngZone", + "type": "NgZone", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "platform", + "type": "Platform", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "animationMode", + "type": "string", + "deprecated": false, + "deprecationMessage": "", + "optional": true, + "tagName": { + "text": "param" + } + } + ] + }, + "extends": "MatPaginatedTabHeader", + "implements": [ + "AfterContentChecked", + "AfterContentInit", + "AfterViewInit", + "OnDestroy" + ], + "accessors": { + "disableRipple": { + "name": "disableRipple", "setSignature": { - "name": "disableClear", + "name": "disableRipple", "type": "void", "deprecated": false, "deprecationMessage": "", "args": [ { - "name": "v", - "type": "boolean", + "name": "value", + "type": "BooleanInput", "deprecated": false, "deprecationMessage": "" } ], "returnType": "void", - "line": 110, + "line": 76, "jsdoctags": [ { - "name": "v", - "type": "boolean", + "name": "value", + "type": "BooleanInput", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -17971,73 +27765,70 @@ ] }, "getSignature": { - "name": "disableClear", + "name": "disableRipple", "type": "boolean", "returnType": "boolean", - "line": 107, - "rawdescription": "\n\nWhether to disable the user from clearing the sort by finishing the sort direction cycle.\nMay be overriden by the OuiSortable's disable clear input.\n", - "description": "

Whether to disable the user from clearing the sort by finishing the sort direction cycle.\nMay be overriden by the OuiSortable's disable clear input.

\n" + "line": 72, + "rawdescription": "\nWhether the ripple effect is disabled or not.", + "description": "

Whether the ripple effect is disabled or not.

\n" } } - } + }, + "templateData": "\r\n\r\n\r\n\r\n \r\n
\r\n \r\n
\r\n \r\n\r\n\r\n\r\n\r\n" }, { - "name": "OuiTooltip", - "id": "directive-OuiTooltip-2635a23757a2778a9b0834f26c6fb831b8ab7b27762e9933e089e3fc21ad4414a443d4aefabf1700c29b2acc24f80f96188097be469a76acba03896269552c1a", - "file": "ui/src/components/tooltip/tooltip.ts", - "type": "directive", - "description": "

Directive that attaches a tooltip to the host element. Animates the showing and\nhiding of a tooltip provided position (defaults to below the element).

\n", - "rawdescription": "\n\nDirective that attaches a tooltip to the host element. Animates the showing and\nhiding of a tooltip provided position (defaults to below the element).\n", - "sourceCode": "import { AnimationEvent } from '@angular/animations';\r\nimport { AriaDescriber, FocusMonitor } from '@angular/cdk/a11y';\r\nimport { Directionality } from '@angular/cdk/bidi';\r\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\r\nimport {\r\n BreakpointObserver,\r\n Breakpoints,\r\n BreakpointState,\r\n} from '@angular/cdk/layout';\r\nimport {\r\n FlexibleConnectedPositionStrategy,\r\n HorizontalConnectionPos,\r\n OriginConnectionPosition,\r\n Overlay,\r\n OverlayConnectionPosition,\r\n OverlayRef,\r\n VerticalConnectionPos,\r\n ScrollStrategy,\r\n} from '@angular/cdk/overlay';\r\nimport { ScrollDispatcher } from '@angular/cdk/scrolling';\r\nimport { Platform } from '@angular/cdk/platform';\r\nimport { ComponentPortal } from '@angular/cdk/portal';\r\nimport { take, takeUntil } from 'rxjs/operators';\r\nimport {\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n Directive,\r\n ElementRef,\r\n Inject,\r\n InjectionToken,\r\n Input,\r\n NgZone,\r\n OnDestroy,\r\n Optional,\r\n ViewContainerRef,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\nimport { Subject, Observable } from 'rxjs';\r\nimport { ouiTooltipAnimations } from './tooltip-animations';\r\nimport { CanDisable } from '../core';\r\n\r\nexport type TooltipPosition = 'left' | 'right' | 'above' | 'below';\r\n\r\n/** Time in ms to throttle repositioning after scroll events. */\r\nexport const SCROLL_THROTTLE_MS = 20;\r\n\r\n/** CSS class that will be attached to the overlay panel. */\r\nexport const TOOLTIP_PANEL_CLASS = 'oui-tooltip-panel';\r\n\r\n/**\r\n * Creates an error to be thrown if the user supplied an invalid tooltip position.\r\n *\r\n * @docs-private\r\n */\r\nexport function getOuiTooltipInvalidPositionError(position: string) {\r\n return Error(`Tooltip position \"${position}\" is invalid.`);\r\n}\r\n\r\n/** Injection token that determines the scroll handling while a tooltip is visible. */\r\nexport const OUI_TOOLTIP_SCROLL_STRATEGY = new InjectionToken<\r\n () => ScrollStrategy\r\n>('oui-tooltip-scroll-strategy');\r\n\r\n/** @docs-private */\r\nexport function OUI_TOOLTIP_SCROLL_STRATEGY_FACTORY(\r\n overlay: Overlay\r\n): () => ScrollStrategy {\r\n return () =>\r\n overlay.scrollStrategies.reposition({ scrollThrottle: SCROLL_THROTTLE_MS });\r\n}\r\n\r\n/** @docs-private */\r\nexport const OUI_TOOLTIP_SCROLL_STRATEGY_FACTORY_PROVIDER = {\r\n provide: OUI_TOOLTIP_SCROLL_STRATEGY,\r\n deps: [Overlay],\r\n useFactory: OUI_TOOLTIP_SCROLL_STRATEGY_FACTORY,\r\n};\r\n\r\n/** Default `ouiTooltip` options that can be overridden. */\r\nexport interface OuiTooltipDefaultOptions {\r\n showDelay: number;\r\n hideDelay: number;\r\n touchendHideDelay: number;\r\n}\r\n\r\nexport interface NewCSSStyleDeclaration extends CSSStyleDeclaration {\r\n msUserSelect: string;\r\n}\r\n\r\n/** Injection token to be used to override the default options for `ouiTooltip`. */\r\nexport const OUI_TOOLTIP_DEFAULT_OPTIONS =\r\n new InjectionToken('oui-tooltip-default-options', {\r\n providedIn: 'root',\r\n factory: OUI_TOOLTIP_DEFAULT_OPTIONS_FACTORY,\r\n });\r\n\r\n/** @docs-private */\r\nexport function OUI_TOOLTIP_DEFAULT_OPTIONS_FACTORY(): OuiTooltipDefaultOptions {\r\n return {\r\n showDelay: 0,\r\n hideDelay: 0,\r\n touchendHideDelay: 1500,\r\n };\r\n}\r\n\r\nexport type TooltipVisibility = 'initial' | 'visible' | 'hidden';\r\n\r\n/**\r\n * Internal component that wraps the tooltip's content.\r\n *\r\n * @docs-private\r\n */\r\n@Component({\r\n selector: 'oui-tooltip-component',\r\n templateUrl: 'tooltip.html',\r\n styleUrls: ['tooltip.scss'],\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n animations: [ouiTooltipAnimations.tooltipState],\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n // Forces the element to have a layout in IE and Edge. This fixes issues where the element\r\n // won't be rendered if the animations are disabled or there is no web animations polyfill.\r\n '[style.zoom]': '_visibility === \"visible\" ? 1 : null',\r\n '(body:click)': 'this._handleBodyInteraction()',\r\n 'aria-hidden': 'true',\r\n },\r\n})\r\nexport class TooltipComponent {\r\n /** Message to display in the tooltip */\r\n message: string;\r\n /** Classes to be added to the tooltip. Supports the same syntax as `ngClass`. */\r\n tooltipClass: string | string[] | Set | { [key: string]: any };\r\n\r\n /** The timeout ID of any current timer set to show the tooltip */\r\n _showTimeoutId: number | null;\r\n\r\n /** The timeout ID of any current timer set to hide the tooltip */\r\n _hideTimeoutId: number | null;\r\n\r\n /** Property watched by the animation framework to show or hide the tooltip */\r\n _visibility: TooltipVisibility = 'initial';\r\n\r\n /** Whether interactions on the page should close the tooltip */\r\n private _closeOnInteraction = false;\r\n\r\n /** Subject for notifying that the tooltip has been hidden from the view */\r\n private readonly _onHide: Subject = new Subject();\r\n\r\n /** Stream that emits whether the user has a handset-sized display. */\r\n _isHandset: Observable = this._breakpointObserver.observe(\r\n Breakpoints.Handset\r\n );\r\n\r\n constructor(\r\n private _changeDetectorRef: ChangeDetectorRef,\r\n private _breakpointObserver: BreakpointObserver\r\n ) {}\r\n\r\n /**\r\n * Shows the tooltip with an animation originating from the provided origin\r\n *\r\n * @param delay Amount of milliseconds to the delay showing the tooltip.\r\n */\r\n show(): void {\r\n // Cancel the delayed hide if it is scheduled\r\n if (this._hideTimeoutId) {\r\n clearTimeout(this._hideTimeoutId);\r\n this._hideTimeoutId = null;\r\n }\r\n\r\n // Body interactions should cancel the tooltip if there is a delay in showing.\r\n this._closeOnInteraction = true;\r\n setTimeout(() => {\r\n this._visibility = 'visible';\r\n this._showTimeoutId = null;\r\n\r\n // Mark for check so if any parent component has set the\r\n // ChangeDetectionStrategy to OnPush it will be checked anyways\r\n this._markForCheck();\r\n }, 0);\r\n }\r\n\r\n /**\r\n * Begins the animation to hide the tooltip after the provided delay in ms.\r\n *\r\n * @param delay Amount of milliseconds to delay showing the tooltip.\r\n */\r\n hide(): void {\r\n // Cancel the delayed show if it is scheduled\r\n if (this._showTimeoutId) {\r\n clearTimeout(this._showTimeoutId);\r\n this._showTimeoutId = null;\r\n }\r\n setTimeout(() => {\r\n this._visibility = 'hidden';\r\n this._hideTimeoutId = null;\r\n // Mark for check so if any parent component has set the\r\n // ChangeDetectionStrategy to OnPush it will be checked anyways\r\n this._markForCheck();\r\n }, 0);\r\n }\r\n\r\n /** Returns an observable that notifies when the tooltip has been hidden from view. */\r\n afterHidden(): Observable {\r\n return this._onHide.asObservable();\r\n }\r\n\r\n /** Whether the tooltip is being displayed. */\r\n isVisible(): boolean {\r\n return this._visibility === 'visible';\r\n }\r\n\r\n _animationStart() {\r\n this._closeOnInteraction = false;\r\n }\r\n\r\n _animationDone(event: AnimationEvent): void {\r\n const toState = event.toState as TooltipVisibility;\r\n\r\n if (toState === 'hidden' && !this.isVisible()) {\r\n this._onHide.next();\r\n }\r\n\r\n if (toState === 'visible' || toState === 'hidden') {\r\n this._closeOnInteraction = true;\r\n }\r\n }\r\n\r\n /**\r\n * Interactions on the HTML body should close the tooltip immediately\r\n */\r\n _handleBodyInteraction(): void {\r\n if (this._closeOnInteraction) {\r\n this.hide();\r\n }\r\n }\r\n\r\n /**\r\n * Marks that the tooltip needs to be checked in the next change detection run.\r\n * Mainly used for rendering the initial text before positioning a tooltip, which\r\n * can be problematic in components with OnPush change detection.\r\n */\r\n _markForCheck(): void {\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n}\r\n\r\n/**\r\n * Directive that attaches a tooltip to the host element. Animates the showing and\r\n * hiding of a tooltip provided position (defaults to below the element).\r\n */\r\n@Directive({\r\n selector: '[ouiTooltip]',\r\n exportAs: 'ouiTooltip',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n '(longpress)': 'show()',\r\n '(keydown)': '_handleKeydown($event)',\r\n '(touchend)': '_handleTouchend()',\r\n '[attr.tabindex]': 'disabled ? -1 : 0',\r\n '[attr.aria-hidden]': 'false',\r\n },\r\n})\r\nexport class OuiTooltip implements OnDestroy, CanDisable {\r\n _overlayRef: OverlayRef | null;\r\n _tooltipInstance: TooltipComponent | null;\r\n\r\n private _portal: ComponentPortal;\r\n private _position: TooltipPosition = 'below';\r\n private _disabled = false;\r\n private _tooltipClass:\r\n | string\r\n | string[]\r\n | Set\r\n | { [key: string]: any };\r\n private _scrollStrategy: () => ScrollStrategy;\r\n\r\n /** Allows the user to define the position of the tooltip relative to the parent element */\r\n @Input('ouiTooltipPosition')\r\n get position(): TooltipPosition {\r\n return this._position;\r\n }\r\n set position(value: TooltipPosition) {\r\n if (value !== this._position) {\r\n this._position = value;\r\n if (this._overlayRef) {\r\n this._updatePosition();\r\n\r\n if (this._tooltipInstance) {\r\n this._tooltipInstance!.show();\r\n }\r\n\r\n this._overlayRef.updatePosition();\r\n }\r\n }\r\n }\r\n\r\n /** Disables the display of the tooltip. */\r\n @Input('ouiTooltipDisabled')\r\n get disabled(): boolean {\r\n return this._disabled;\r\n }\r\n set disabled(value) {\r\n this._disabled = coerceBooleanProperty(value);\r\n\r\n // If tooltip is disabled, hide immediately.\r\n if (this._disabled) {\r\n this.hide();\r\n }\r\n }\r\n\r\n private _message = '';\r\n\r\n /** The message to be displayed in the tooltip */\r\n @Input('ouiTooltip')\r\n get message() {\r\n return this._message;\r\n }\r\n set message(value: string) {\r\n this._ariaDescriber.removeDescription(\r\n this._elementRef.nativeElement,\r\n this._message\r\n );\r\n\r\n // If the message is not a string (e.g. number), convert it to a string and trim it.\r\n this._message = value != null ? `${value}`.trim() : '';\r\n\r\n if (!this._message && this._isTooltipVisible()) {\r\n this.hide();\r\n } else {\r\n this._updateTooltipMessage();\r\n this._ariaDescriber.describe(\r\n this._elementRef.nativeElement,\r\n this.message\r\n );\r\n }\r\n }\r\n\r\n /** Classes to be passed to the tooltip. Supports the same syntax as `ngClass`. */\r\n @Input('ouiTooltipClass')\r\n get tooltipClass() {\r\n return this._tooltipClass;\r\n }\r\n set tooltipClass(\r\n value: string | string[] | Set | { [key: string]: any }\r\n ) {\r\n this._tooltipClass = value;\r\n if (this._tooltipInstance) {\r\n this._tooltipInstance._markForCheck();\r\n this._setTooltipClass(this._tooltipClass);\r\n }\r\n }\r\n\r\n private _manualListeners = new Map<\r\n string,\r\n EventListenerOrEventListenerObject\r\n >();\r\n\r\n /** Emits when the component is destroyed. */\r\n private readonly _destroyed = new Subject();\r\n\r\n constructor(\r\n private _overlay: Overlay,\r\n private _elementRef: ElementRef,\r\n private _scrollDispatcher: ScrollDispatcher,\r\n private _viewContainerRef: ViewContainerRef,\r\n private _ngZone: NgZone,\r\n platform: Platform,\r\n private _ariaDescriber: AriaDescriber,\r\n private _focusMonitor: FocusMonitor,\r\n @Inject(OUI_TOOLTIP_SCROLL_STRATEGY) scrollStrategy: any,\r\n @Optional() private _dir: Directionality\r\n ) {\r\n this._scrollStrategy = scrollStrategy;\r\n const element: HTMLElement = _elementRef.nativeElement;\r\n const elementStyle = element.style as NewCSSStyleDeclaration & {\r\n webkitUserDrag: string;\r\n };\r\n const hasGestures = typeof window === 'undefined' || (window as any).Hammer;\r\n\r\n // The mouse events shouldn't be bound on mobile devices, because they can prevent the\r\n // first tap from firing its click event or can cause the tooltip to open for clicks.\r\n if (!platform.IOS && !platform.ANDROID) {\r\n this._manualListeners\r\n .set('mouseenter', () => this.show())\r\n .set('mouseleave', () => this.hide());\r\n } else if (!hasGestures) {\r\n // there's no way for the user to trigger the tooltip on a touch device.\r\n this._manualListeners.set('touchstart', () => this.show());\r\n }\r\n\r\n this._manualListeners.forEach((listener, event) =>\r\n element.addEventListener(event, listener)\r\n );\r\n\r\n if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\r\n elementStyle.webkitUserSelect =\r\n elementStyle.userSelect =\r\n elementStyle.msUserSelect =\r\n '';\r\n }\r\n\r\n // Hammer applies `-webkit-user-drag: none` on all elements by default,\r\n // which breaks the native drag&drop. If the consumer explicitly made\r\n // the element draggable, clear the `-webkit-user-drag`.\r\n if (element.draggable && elementStyle.webkitUserDrag === 'none') {\r\n elementStyle.webkitUserDrag = '';\r\n }\r\n\r\n _focusMonitor\r\n .monitor(_elementRef)\r\n .pipe(takeUntil(this._destroyed))\r\n .subscribe((origin) => {\r\n // Note that the focus monitor runs outside the Angular zone.\r\n if (!origin) {\r\n _ngZone.run(() => this.hide());\r\n } else if (origin === 'keyboard') {\r\n _ngZone.run(() => this.show());\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * Dispose the tooltip when destroyed.\r\n */\r\n ngOnDestroy() {\r\n if (this._overlayRef) {\r\n this._overlayRef.dispose();\r\n this._tooltipInstance = null;\r\n }\r\n\r\n // Clean up the event listeners set in the constructor\r\n this._manualListeners.forEach((listener, event) => {\r\n this._elementRef.nativeElement.removeEventListener(event, listener);\r\n });\r\n this._manualListeners.clear();\r\n\r\n this._destroyed.next();\r\n this._destroyed.complete();\r\n\r\n this._ariaDescriber.removeDescription(\r\n this._elementRef.nativeElement,\r\n this.message\r\n );\r\n this._focusMonitor.stopMonitoring(this._elementRef);\r\n }\r\n\r\n /** Shows the tooltip after the delay in ms, defaults to tooltip-delay-show or 0ms if no input */\r\n show(): void {\r\n if (\r\n this.disabled ||\r\n !this.message ||\r\n (this._isTooltipVisible() &&\r\n !this._tooltipInstance!._showTimeoutId &&\r\n !this._tooltipInstance!._hideTimeoutId)\r\n ) {\r\n return;\r\n }\r\n\r\n const overlayRef = this._createOverlay();\r\n\r\n this._detach();\r\n this._portal =\r\n this._portal ||\r\n new ComponentPortal(TooltipComponent, this._viewContainerRef);\r\n this._tooltipInstance = overlayRef.attach(this._portal).instance;\r\n this._tooltipInstance\r\n .afterHidden()\r\n .pipe(takeUntil(this._destroyed))\r\n .subscribe(() => this._detach());\r\n this._setTooltipClass(this._tooltipClass);\r\n this._updateTooltipMessage();\r\n this._tooltipInstance!.show();\r\n }\r\n\r\n /** Hides the tooltip after the delay in ms, defaults to tooltip-delay-hide or 0ms if no input */\r\n hide(): void {\r\n if (this._tooltipInstance) {\r\n this._tooltipInstance.hide();\r\n }\r\n }\r\n\r\n /** Shows/hides the tooltip */\r\n toggle(): void {\r\n this._isTooltipVisible() ? this.hide() : this.show();\r\n }\r\n\r\n /** Returns true if the tooltip is currently visible to the user */\r\n _isTooltipVisible(): boolean {\r\n return !!this._tooltipInstance && this._tooltipInstance.isVisible();\r\n }\r\n\r\n /** Handles the keydown events on the host element. */\r\n _handleKeydown(e: KeyboardEvent) {\r\n if (this._isTooltipVisible() && e.key === 'Escape') {\r\n e.stopPropagation();\r\n this.hide();\r\n }\r\n }\r\n\r\n /** Handles the touchend events on the host element. */\r\n _handleTouchend() {\r\n this.hide();\r\n }\r\n\r\n /** Create the overlay config and position strategy */\r\n private _createOverlay(): OverlayRef {\r\n if (this._overlayRef) {\r\n return this._overlayRef;\r\n }\r\n\r\n // Create connected position strategy that listens for scroll events to reposition.\r\n const strategy = this._overlay\r\n .position()\r\n .flexibleConnectedTo(this._elementRef)\r\n .withTransformOriginOn('.oui-tooltip')\r\n .withFlexibleDimensions(false)\r\n .withViewportMargin(8);\r\n\r\n const scrollableAncestors =\r\n this._scrollDispatcher.getAncestorScrollContainers(this._elementRef);\r\n\r\n strategy.withScrollableContainers(scrollableAncestors);\r\n\r\n strategy.positionChanges\r\n .pipe(takeUntil(this._destroyed))\r\n .subscribe((change) => {\r\n if (this._tooltipInstance) {\r\n if (\r\n change.scrollableViewProperties.isOverlayClipped &&\r\n this._tooltipInstance.isVisible()\r\n ) {\r\n // After position changes occur and the overlay is clipped by\r\n // a parent scrollable then close the tooltip.\r\n this._ngZone.run(() => this.hide());\r\n }\r\n }\r\n });\r\n\r\n this._overlayRef = this._overlay.create({\r\n direction: this._dir,\r\n positionStrategy: strategy,\r\n panelClass: TOOLTIP_PANEL_CLASS,\r\n scrollStrategy: this._scrollStrategy(),\r\n });\r\n\r\n this._updatePosition();\r\n\r\n this._overlayRef\r\n .detachments()\r\n .pipe(takeUntil(this._destroyed))\r\n .subscribe(() => this._detach());\r\n\r\n return this._overlayRef;\r\n }\r\n\r\n /** Detaches the currently-attached tooltip. */\r\n private _detach() {\r\n if (this._overlayRef && this._overlayRef.hasAttached()) {\r\n this._overlayRef.detach();\r\n }\r\n\r\n this._tooltipInstance = null;\r\n }\r\n\r\n /** Updates the position of the current tooltip. */\r\n private _updatePosition() {\r\n const position = this._overlayRef!.getConfig()\r\n .positionStrategy as FlexibleConnectedPositionStrategy;\r\n const origin = this._getOrigin();\r\n const overlay = this._getOverlayPosition();\r\n\r\n position.withPositions([\r\n { ...origin.main, ...overlay.main },\r\n { ...origin.fallback, ...overlay.fallback },\r\n ]);\r\n }\r\n\r\n /**\r\n * Returns the origin position and a fallback position based on the user's position preference.\r\n * The fallback position is the inverse of the origin (e.g. `'below' -> 'above'`).\r\n */\r\n _getOrigin(): {\r\n main: OriginConnectionPosition;\r\n fallback: OriginConnectionPosition;\r\n } {\r\n const isLtr = !this._dir || this._dir.value === 'ltr';\r\n const position = this.position;\r\n let originPosition: OriginConnectionPosition;\r\n\r\n if (position === 'above' || position === 'below') {\r\n originPosition = {\r\n originX: 'center',\r\n originY: position === 'above' ? 'top' : 'bottom',\r\n };\r\n } else if (\r\n (position === 'left' && isLtr) ||\r\n (position === 'right' && !isLtr)\r\n ) {\r\n originPosition = { originX: 'start', originY: 'center' };\r\n } else if (\r\n (position === 'right' && isLtr) ||\r\n (position === 'left' && !isLtr)\r\n ) {\r\n originPosition = { originX: 'end', originY: 'center' };\r\n } else {\r\n throw getOuiTooltipInvalidPositionError(position);\r\n }\r\n\r\n const { x, y } = this._invertPosition(\r\n originPosition.originX,\r\n originPosition.originY\r\n );\r\n\r\n return {\r\n main: originPosition,\r\n fallback: { originX: x, originY: y },\r\n };\r\n }\r\n\r\n /** Returns the overlay position and a fallback position based on the user's preference */\r\n _getOverlayPosition(): {\r\n main: OverlayConnectionPosition;\r\n fallback: OverlayConnectionPosition;\r\n } {\r\n const isLtr = !this._dir || this._dir.value === 'ltr';\r\n const position = this.position;\r\n let overlayPosition: OverlayConnectionPosition;\r\n\r\n if (position === 'above') {\r\n overlayPosition = { overlayX: 'center', overlayY: 'bottom' };\r\n } else if (position === 'below') {\r\n overlayPosition = { overlayX: 'center', overlayY: 'top' };\r\n } else if (\r\n (position === 'left' && isLtr) ||\r\n (position === 'right' && !isLtr)\r\n ) {\r\n overlayPosition = { overlayX: 'end', overlayY: 'center' };\r\n } else if (\r\n (position === 'right' && isLtr) ||\r\n (position === 'left' && !isLtr)\r\n ) {\r\n overlayPosition = { overlayX: 'start', overlayY: 'center' };\r\n } else {\r\n throw getOuiTooltipInvalidPositionError(position);\r\n }\r\n\r\n const { x, y } = this._invertPosition(\r\n overlayPosition.overlayX,\r\n overlayPosition.overlayY\r\n );\r\n\r\n return {\r\n main: overlayPosition,\r\n fallback: { overlayX: x, overlayY: y },\r\n };\r\n }\r\n\r\n /** Updates the tooltip message and repositions the overlay according to the new message length */\r\n private _updateTooltipMessage() {\r\n // Must wait for the message to be painted to the tooltip so that the overlay can properly\r\n // calculate the correct positioning based on the size of the text.\r\n if (this._tooltipInstance) {\r\n this._tooltipInstance.message = this.message;\r\n this._tooltipInstance._markForCheck();\r\n\r\n this._ngZone.onMicrotaskEmpty\r\n .asObservable()\r\n .pipe(take(1), takeUntil(this._destroyed))\r\n .subscribe(() => {\r\n if (this._tooltipInstance) {\r\n this._overlayRef!.updatePosition();\r\n }\r\n });\r\n }\r\n }\r\n\r\n /** Updates the tooltip class */\r\n private _setTooltipClass(\r\n tooltipClass: string | string[] | Set | { [key: string]: any }\r\n ) {\r\n if (this._tooltipInstance) {\r\n this._tooltipInstance.tooltipClass = tooltipClass;\r\n this._tooltipInstance._markForCheck();\r\n }\r\n }\r\n\r\n /** Inverts an overlay position. */\r\n private _invertPosition(\r\n x: HorizontalConnectionPos,\r\n y: VerticalConnectionPos\r\n ) {\r\n if (this.position === 'above' || this.position === 'below') {\r\n if (y === 'top') {\r\n y = 'bottom';\r\n } else if (y === 'bottom') {\r\n y = 'top';\r\n }\r\n } else {\r\n if (x === 'end') {\r\n x = 'start';\r\n } else if (x === 'start') {\r\n x = 'end';\r\n }\r\n }\r\n\r\n return { x, y };\r\n }\r\n}\r\n", - "selector": "[ouiTooltip]", + "name": "MatTabLink", + "id": "component-MatTabLink-bd9c0cd29c6bf499bbb3e061668b0c5b33870adbc190c4dc86f938c085db45a7f8c16d1d8d9ef9c004e185e411d20f0eb23946a251271f43623faa6c022a6f23", + "file": "ui/src/components/tabs/tab-nav-bar/tab-nav-bar.ts", + "changeDetection": "ChangeDetectionStrategy.OnPush", + "encapsulation": [ + "ViewEncapsulation.None" + ], + "entryComponents": [], + "exportAs": "matTabLink", + "host": {}, + "inputs": [ + "disabled", + "disableRipple", + "tabIndex", + "active", + "id" + ], + "outputs": [], "providers": [], + "selector": "[mat-tab-link], [matTabLink]", + "styleUrls": [ + "tab-link.scss" + ], + "styles": [], + "templateUrl": [ + "tab-link.html" + ], + "viewProviders": [], "inputsClass": [ { - "name": "ouiTooltip", - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nThe message to be displayed in the tooltip", - "description": "

The message to be displayed in the tooltip

\n", - "line": 318, - "type": "string", - "decorators": [] - }, - { - "name": "ouiTooltipClass", - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nClasses to be passed to the tooltip. Supports the same syntax as `ngClass`.", - "description": "

Classes to be passed to the tooltip. Supports the same syntax as ngClass.

\n", - "line": 343, - "type": "any", - "decorators": [] - }, - { - "name": "ouiTooltipDisabled", + "name": "active", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nDisables the display of the tooltip.", - "description": "

Disables the display of the tooltip.

\n", - "line": 302, + "rawdescription": "\nWhether the link is active.", + "description": "

Whether the link is active.

\n", + "line": 304, "type": "boolean", "decorators": [] }, { - "name": "ouiTooltipPosition", + "name": "id", + "defaultValue": "`mat-tab-link-${nextUniqueId++}`", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nAllows the user to define the position of the tooltip relative to the parent element", - "description": "

Allows the user to define the position of the tooltip relative to the parent element

\n", - "line": 282, - "type": "TooltipPosition", + "rawdescription": "\nUnique id for the tab.", + "description": "

Unique id for the tab.

\n", + "line": 339, + "type": "string", "decorators": [] } ], "outputsClass": [], - "deprecated": false, - "deprecationMessage": "", - "hostBindings": [], - "hostListeners": [], "propertiesClass": [ { "name": "_destroyed", @@ -18046,284 +27837,125 @@ "deprecationMessage": "", "type": "", "optional": false, - "description": "

Emits when the component is destroyed.

\n", - "line": 362, - "rawdescription": "\nEmits when the component is destroyed.", + "description": "", + "line": 297, "modifierKind": [ 121, 144 ] }, { - "name": "_disabled", + "name": "_isActive", "defaultValue": "false", "deprecated": false, "deprecationMessage": "", - "type": "", + "type": "boolean", "optional": false, - "description": "", - "line": 272, + "description": "

Whether the tab link is active or not.

\n", + "line": 300, + "rawdescription": "\nWhether the tab link is active or not.", "modifierKind": [ - 121 + 122 ] }, { - "name": "_manualListeners", - "defaultValue": "new Map<\r\n string,\r\n EventListenerOrEventListenerObject\r\n >()", + "name": "rippleConfig", "deprecated": false, "deprecationMessage": "", "type": "", "optional": false, - "description": "", - "line": 356, - "modifierKind": [ - 121 - ] - }, - { - "name": "_message", - "defaultValue": "''", - "deprecated": false, - "deprecationMessage": "", - "type": "string", - "optional": false, - "description": "", - "line": 314, - "modifierKind": [ - 121 - ] - }, - { - "name": "_overlayRef", - "deprecated": false, - "deprecationMessage": "", - "type": "OverlayRef | null", - "optional": false, - "description": "", - "line": 267 - }, - { - "name": "_portal", - "deprecated": false, - "deprecationMessage": "", - "type": "ComponentPortal", - "optional": false, - "description": "", - "line": 270, - "modifierKind": [ - 121 - ] - }, - { - "name": "_position", - "defaultValue": "'below'", - "deprecated": false, - "deprecationMessage": "", - "type": "TooltipPosition", - "optional": false, - "description": "", - "line": 271, - "modifierKind": [ - 121 - ] - }, - { - "name": "_scrollStrategy", - "deprecated": false, - "deprecationMessage": "", - "type": "function", - "optional": false, - "description": "", - "line": 278, - "modifierKind": [ - 121 - ] - }, - { - "name": "_tooltipClass", - "deprecated": false, - "deprecationMessage": "", - "type": "string | string[] | Set | literal type", - "optional": false, - "description": "", - "line": 273, - "modifierKind": [ - 121 + "description": "

Ripple configuration for ripples that are launched on pointer down. The ripple config\nis set to the global ripple options since we don't have any configurable options for\nthe tab link ripples.

\n", + "line": 323, + "rawdescription": "\n\nRipple configuration for ripples that are launched on pointer down. The ripple config\nis set to the global ripple options since we don't have any configurable options for\nthe tab link ripples.\n", + "jsdoctags": [ + { + "pos": 10055, + "end": 10073, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 325, + "tagName": { + "pos": 10056, + "end": 10068, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "docs-private" + }, + "comment": "" + } ] - }, - { - "name": "_tooltipInstance", - "deprecated": false, - "deprecationMessage": "", - "type": "TooltipComponent | null", - "optional": false, - "description": "", - "line": 268 } ], "methodsClass": [ { - "name": "_createOverlay", + "name": "_getAriaControls", "args": [], "optional": false, - "returnType": "OverlayRef", + "returnType": "string | null", "typeParameters": [], - "line": 509, + "line": 396, "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nCreate the overlay config and position strategy", - "description": "

Create the overlay config and position strategy

\n", - "modifierKind": [ - 121 - ] + "deprecationMessage": "" }, { - "name": "_detach", + "name": "_getAriaCurrent", "args": [], "optional": false, - "returnType": "void", + "returnType": "string | null", "typeParameters": [], - "line": 560, + "line": 410, "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nDetaches the currently-attached tooltip.", - "description": "

Detaches the currently-attached tooltip.

\n", - "modifierKind": [ - 121 - ] + "deprecationMessage": "" }, { - "name": "_getOrigin", + "name": "_getAriaSelected", "args": [], "optional": false, - "returnType": "literal type", + "returnType": "string | null", "typeParameters": [], - "line": 585, + "line": 402, "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\n\nReturns the origin position and a fallback position based on the user's position preference.\nThe fallback position is the inverse of the origin (e.g. `'below' -> 'above'`).\n", - "description": "

Returns the origin position and a fallback position based on the user's position preference.\nThe fallback position is the inverse of the origin (e.g. 'below' -> 'above').

\n" + "deprecationMessage": "" }, { - "name": "_getOverlayPosition", + "name": "_getRole", "args": [], "optional": false, - "returnType": "literal type", - "typeParameters": [], - "line": 624, - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nReturns the overlay position and a fallback position based on the user's preference", - "description": "

Returns the overlay position and a fallback position based on the user's preference

\n" - }, - { - "name": "_handleKeydown", - "args": [ - { - "name": "e", - "type": "KeyboardEvent", - "deprecated": false, - "deprecationMessage": "" - } - ], - "optional": false, - "returnType": "void", + "returnType": "string | null", "typeParameters": [], - "line": 496, + "line": 414, "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nHandles the keydown events on the host element.", - "description": "

Handles the keydown events on the host element.

\n", - "jsdoctags": [ - { - "name": "e", - "type": "KeyboardEvent", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } - ] + "deprecationMessage": "" }, { - "name": "_handleTouchend", + "name": "_getTabIndex", "args": [], "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 504, - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nHandles the touchend events on the host element.", - "description": "

Handles the touchend events on the host element.

\n" - }, - { - "name": "_invertPosition", - "args": [ - { - "name": "x", - "type": "HorizontalConnectionPos", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "y", - "type": "VerticalConnectionPos", - "deprecated": false, - "deprecationMessage": "" - } - ], - "optional": false, - "returnType": "{ x: HorizontalConnectionPos; y: VerticalConnectionPos; }", + "returnType": "number", "typeParameters": [], - "line": 691, + "line": 418, "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nInverts an overlay position.", - "description": "

Inverts an overlay position.

\n", - "modifierKind": [ - 121 - ], - "jsdoctags": [ - { - "name": "x", - "type": "HorizontalConnectionPos", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "y", - "type": "VerticalConnectionPos", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } - ] + "deprecationMessage": "" }, { - "name": "_isTooltipVisible", + "name": "_handleFocus", "args": [], "optional": false, - "returnType": "boolean", + "returnType": "void", "typeParameters": [], - "line": 491, + "line": 382, "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nReturns true if the tooltip is currently visible to the user", - "description": "

Returns true if the tooltip is currently visible to the user

\n" + "deprecationMessage": "" }, { - "name": "_setTooltipClass", + "name": "_handleKeydown", "args": [ { - "name": "tooltipClass", - "type": "string | string[] | Set | literal type", + "name": "event", + "type": "KeyboardEvent", "deprecated": false, "deprecationMessage": "" } @@ -18331,18 +27963,13 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 681, + "line": 388, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nUpdates the tooltip class", - "description": "

Updates the tooltip class

\n", - "modifierKind": [ - 121 - ], "jsdoctags": [ { - "name": "tooltipClass", - "type": "string | string[] | Set | literal type", + "name": "event", + "type": "KeyboardEvent", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -18352,46 +27979,26 @@ ] }, { - "name": "_updatePosition", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 569, - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nUpdates the position of the current tooltip.", - "description": "

Updates the position of the current tooltip.

\n", - "modifierKind": [ - 121 - ] - }, - { - "name": "_updateTooltipMessage", + "name": "focus", "args": [], "optional": false, "returnType": "void", "typeParameters": [], - "line": 662, + "line": 367, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nUpdates the tooltip message and repositions the overlay according to the new message length", - "description": "

Updates the tooltip message and repositions the overlay according to the new message length

\n", - "modifierKind": [ - 121 - ] + "rawdescription": "\nFocuses the tab link.", + "description": "

Focuses the tab link.

\n" }, { - "name": "hide", + "name": "ngAfterViewInit", "args": [], "optional": false, "returnType": "void", "typeParameters": [], - "line": 479, + "line": 371, "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nHides the tooltip after the delay in ms, defaults to tooltip-delay-hide or 0ms if no input", - "description": "

Hides the tooltip after the delay in ms, defaults to tooltip-delay-hide or 0ms if no input

\n" + "deprecationMessage": "" }, { "name": "ngOnDestroy", @@ -18399,41 +28006,30 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 428, - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\n\nDispose the tooltip when destroyed.\n", - "description": "

Dispose the tooltip when destroyed.

\n" - }, - { - "name": "show", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 451, - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nShows the tooltip after the delay in ms, defaults to tooltip-delay-show or 0ms if no input", - "description": "

Shows the tooltip after the delay in ms, defaults to tooltip-delay-show or 0ms if no input

\n" - }, - { - "name": "toggle", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 486, + "line": 375, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nShows/hides the tooltip", - "description": "

Shows/hides the tooltip

\n" + "modifierKind": [ + 158 + ] } ], - "implements": [ - "OnDestroy", - "CanDisable" + "deprecated": false, + "deprecationMessage": "", + "hostBindings": [], + "hostListeners": [], + "description": "

Link inside a mat-tab-nav-bar.

\n", + "rawdescription": "\n\nLink inside a `mat-tab-nav-bar`.\n", + "type": "component", + "sourceCode": "import {\r\n AfterContentChecked,\r\n AfterContentInit,\r\n AfterViewInit,\r\n Attribute,\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n ContentChildren,\r\n ElementRef,\r\n forwardRef,\r\n Inject,\r\n Input,\r\n NgZone,\r\n OnDestroy,\r\n Optional,\r\n QueryList,\r\n ViewChild,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\nimport {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations';\r\nimport {\r\n CanDisable,\r\n CanDisableRipple,\r\n HasTabIndex,\r\n MAT_RIPPLE_GLOBAL_OPTIONS,\r\n mixinDisabled,\r\n mixinDisableRipple,\r\n mixinTabIndex,\r\n RippleConfig,\r\n RippleGlobalOptions,\r\n RippleTarget,\r\n ThemePalette,\r\n} from '../../core';\r\nimport {FocusableOption, FocusMonitor} from '@angular/cdk/a11y';\r\nimport {Directionality} from '@angular/cdk/bidi';\r\nimport {ViewportRuler} from '@angular/cdk/scrolling';\r\nimport {Platform} from '@angular/cdk/platform';\r\nimport {MatInkBar, mixinInkBarItem} from '../ink-bar';\r\nimport {BooleanInput, coerceBooleanProperty, NumberInput} from '@angular/cdk/coercion';\r\nimport {BehaviorSubject, Subject} from 'rxjs';\r\nimport {startWith, takeUntil} from 'rxjs/operators';\r\nimport {ENTER, SPACE} from '@angular/cdk/keycodes';\r\nimport {MAT_TABS_CONFIG, MatTabsConfig} from '../tab-config';\r\nimport {MatPaginatedTabHeader} from '../paginated-tab-header';\r\nimport { isDevMode } from '@angular/core';\r\n\r\n// Increasing integer for generating unique ids for tab nav components.\r\nlet nextUniqueId = 0;\r\n\r\n/**\r\n * Navigation component matching the styles of the tab group header.\r\n * Provides anchored navigation with animated ink bar.\r\n */\r\n@Component({\r\n selector: '[mat-tab-nav-bar]',\r\n exportAs: 'matTabNavBar, matTabNav',\r\n inputs: ['color'],\r\n templateUrl: 'tab-nav-bar.html',\r\n styleUrls: ['tab-nav-bar.scss'],\r\n host: {\r\n '[attr.role]': '_getRole()',\r\n 'class': 'mat-mdc-tab-nav-bar mat-mdc-tab-header',\r\n '[class.mat-mdc-tab-header-pagination-controls-enabled]': '_showPaginationControls',\r\n '[class.mat-mdc-tab-header-rtl]': \"_getLayoutDirection() == 'rtl'\",\r\n '[class.mat-mdc-tab-nav-bar-stretch-tabs]': 'stretchTabs',\r\n '[class.mat-primary]': 'color !== \"warn\" && color !== \"accent\"',\r\n '[class.mat-accent]': 'color === \"accent\"',\r\n '[class.mat-warn]': 'color === \"warn\"',\r\n '[class._mat-animation-noopable]': '_animationMode === \"NoopAnimations\"',\r\n '[style.--mat-tab-animation-duration]': 'animationDuration',\r\n },\r\n encapsulation: ViewEncapsulation.None,\r\n // tslint:disable-next-line:validate-decorators\r\n changeDetection: ChangeDetectionStrategy.Default,\r\n})\r\nexport class MatTabNav\r\n extends MatPaginatedTabHeader\r\n implements AfterContentChecked, AfterContentInit, OnDestroy, AfterViewInit\r\n{\r\n /** Whether the ink bar should fit its width to the size of the tab label content. */\r\n @Input()\r\n get fitInkBarToContent(): boolean {\r\n return this._fitInkBarToContent.value;\r\n }\r\n set fitInkBarToContent(v: BooleanInput) {\r\n this._fitInkBarToContent.next(coerceBooleanProperty(v));\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n _fitInkBarToContent = new BehaviorSubject(false);\r\n\r\n /** Whether tabs should be stretched to fill the header. */\r\n @Input('mat-stretch-tabs')\r\n get stretchTabs(): boolean {\r\n return this._stretchTabs;\r\n }\r\n set stretchTabs(v: BooleanInput) {\r\n this._stretchTabs = coerceBooleanProperty(v);\r\n }\r\n private _stretchTabs = true;\r\n\r\n @Input()\r\n get animationDuration(): string {\r\n return this._animationDuration;\r\n }\r\n\r\n set animationDuration(value: NumberInput) {\r\n this._animationDuration = /^\\d+$/.test(value + '') ? value + 'ms' : (value as string);\r\n }\r\n\r\n private _animationDuration: string;\r\n\r\n /** Query list of all tab links of the tab navigation. */\r\n @ContentChildren(forwardRef(() => MatTabLink), {descendants: true}) _items: QueryList;\r\n\r\n /** Background color of the tab nav. */\r\n @Input()\r\n get backgroundColor(): ThemePalette {\r\n return this._backgroundColor;\r\n }\r\n\r\n set backgroundColor(value: ThemePalette) {\r\n const classList = this._elementRef.nativeElement.classList;\r\n classList.remove('mat-tabs-with-background', `mat-background-${this.backgroundColor}`);\r\n\r\n if (value) {\r\n classList.add('mat-tabs-with-background', `mat-background-${value}`);\r\n }\r\n\r\n this._backgroundColor = value;\r\n }\r\n\r\n private _backgroundColor: ThemePalette;\r\n\r\n /** Whether the ripple effect is disabled or not. */\r\n @Input()\r\n get disableRipple(): boolean {\r\n return this._disableRipple;\r\n }\r\n\r\n set disableRipple(value: BooleanInput) {\r\n this._disableRipple = coerceBooleanProperty(value);\r\n }\r\n\r\n private _disableRipple: boolean = false;\r\n\r\n /** Theme color of the nav bar. */\r\n @Input() color: ThemePalette = 'primary';\r\n\r\n /**\r\n * Associated tab panel controlled by the nav bar. If not provided, then the nav bar\r\n * follows the ARIA link / navigation landmark pattern. If provided, it follows the\r\n * ARIA tabs design pattern.\r\n */\r\n @Input() tabPanel?: MatTabNavPanel;\r\n\r\n @ViewChild('tabListContainer', {static: true}) _tabListContainer: ElementRef;\r\n @ViewChild('tabList', {static: true}) _tabList: ElementRef;\r\n @ViewChild('tabListInner', {static: true}) _tabListInner: ElementRef;\r\n @ViewChild('nextPaginator') _nextPaginator: ElementRef;\r\n @ViewChild('previousPaginator') _previousPaginator: ElementRef;\r\n _inkBar: MatInkBar;\r\n\r\n constructor(\r\n elementRef: ElementRef,\r\n @Optional() dir: Directionality,\r\n ngZone: NgZone,\r\n changeDetectorRef: ChangeDetectorRef,\r\n viewportRuler: ViewportRuler,\r\n platform: Platform,\r\n @Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string,\r\n @Optional() @Inject(MAT_TABS_CONFIG) defaultConfig?: MatTabsConfig,\r\n ) {\r\n super(elementRef, changeDetectorRef, viewportRuler, dir, ngZone, platform, animationMode);\r\n this.disablePagination =\r\n defaultConfig && defaultConfig.disablePagination != null\r\n ? defaultConfig.disablePagination\r\n : false;\r\n this.fitInkBarToContent =\r\n defaultConfig && defaultConfig.fitInkBarToContent != null\r\n ? defaultConfig.fitInkBarToContent\r\n : false;\r\n this.stretchTabs =\r\n defaultConfig && defaultConfig.stretchTabs != null ? defaultConfig.stretchTabs : true;\r\n }\r\n\r\n protected _itemSelected() {\r\n // noop\r\n }\r\n\r\n override ngAfterContentInit() {\r\n this._inkBar = new MatInkBar(this._items);\r\n // We need this to run before the `changes` subscription in parent to ensure that the\r\n // selectedIndex is up-to-date by the time the super class starts looking for it.\r\n this._items.changes.pipe(startWith(null), takeUntil(this._destroyed)).subscribe(() => {\r\n this.updateActiveLink();\r\n });\r\n\r\n super.ngAfterContentInit();\r\n }\r\n\r\n override ngAfterViewInit() {\r\n if (!this.tabPanel && (typeof isDevMode === 'undefined' || isDevMode)) {\r\n throw new Error('A mat-tab-nav-panel must be specified via [tabPanel].');\r\n }\r\n super.ngAfterViewInit();\r\n }\r\n\r\n /** Notifies the component that the active link has been changed. */\r\n updateActiveLink() {\r\n if (!this._items) {\r\n return;\r\n }\r\n\r\n const items = this._items.toArray();\r\n\r\n for (let i = 0; i < items.length; i++) {\r\n if (items[i].active) {\r\n this.selectedIndex = i;\r\n this._changeDetectorRef.markForCheck();\r\n\r\n if (this.tabPanel) {\r\n this.tabPanel._activeTabId = items[i].id;\r\n }\r\n\r\n return;\r\n }\r\n }\r\n\r\n // The ink bar should hide itself if no items are active.\r\n this.selectedIndex = -1;\r\n this._inkBar.hide();\r\n }\r\n\r\n _getRole(): string | null {\r\n return this.tabPanel ? 'tablist' : this._elementRef.nativeElement.getAttribute('role');\r\n }\r\n}\r\n\r\n// Boilerplate for applying mixins to MatTabLink.\r\nconst _MatTabLinkMixinBase = mixinInkBarItem(\r\n mixinTabIndex(\r\n mixinDisableRipple(\r\n mixinDisabled(\r\n class {\r\n elementRef: ElementRef;\r\n },\r\n ),\r\n ),\r\n ),\r\n);\r\n\r\n/**\r\n * Link inside a `mat-tab-nav-bar`.\r\n */\r\n@Component({\r\n selector: '[mat-tab-link], [matTabLink]',\r\n exportAs: 'matTabLink',\r\n inputs: ['disabled', 'disableRipple', 'tabIndex', 'active', 'id'],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n templateUrl: 'tab-link.html',\r\n styleUrls: ['tab-link.scss'],\r\n host: {\r\n 'class': 'mdc-tab mat-mdc-tab-link mat-mdc-focus-indicator',\r\n '[attr.aria-controls]': '_getAriaControls()',\r\n '[attr.aria-current]': '_getAriaCurrent()',\r\n '[attr.aria-disabled]': 'disabled',\r\n '[attr.aria-selected]': '_getAriaSelected()',\r\n '[attr.id]': 'id',\r\n '[attr.tabIndex]': '_getTabIndex()',\r\n '[attr.role]': '_getRole()',\r\n '[class.mat-mdc-tab-disabled]': 'disabled',\r\n '[class.mdc-tab--active]': 'active',\r\n '(focus)': '_handleFocus()',\r\n '(keydown)': '_handleKeydown($event)',\r\n },\r\n})\r\nexport class MatTabLink\r\n extends _MatTabLinkMixinBase\r\n implements\r\n AfterViewInit,\r\n OnDestroy,\r\n CanDisable,\r\n CanDisableRipple,\r\n HasTabIndex,\r\n RippleTarget,\r\n FocusableOption\r\n{\r\n private readonly _destroyed = new Subject();\r\n\r\n /** Whether the tab link is active or not. */\r\n protected _isActive: boolean = false;\r\n\r\n /** Whether the link is active. */\r\n @Input()\r\n get active(): boolean {\r\n return this._isActive;\r\n }\r\n\r\n set active(value: BooleanInput) {\r\n const newValue = coerceBooleanProperty(value);\r\n\r\n if (newValue !== this._isActive) {\r\n this._isActive = newValue;\r\n this._tabNavBar.updateActiveLink();\r\n }\r\n }\r\n\r\n /**\r\n * Ripple configuration for ripples that are launched on pointer down. The ripple config\r\n * is set to the global ripple options since we don't have any configurable options for\r\n * the tab link ripples.\r\n * @docs-private\r\n */\r\n rippleConfig: RippleConfig & RippleGlobalOptions;\r\n\r\n /**\r\n * Whether ripples are disabled on interaction.\r\n * @docs-private\r\n */\r\n get rippleDisabled(): boolean {\r\n return (\r\n this.disabled ||\r\n this.disableRipple ||\r\n this._tabNavBar.disableRipple ||\r\n !!this.rippleConfig.disabled\r\n );\r\n }\r\n\r\n /** Unique id for the tab. */\r\n @Input() id = `mat-tab-link-${nextUniqueId++}`;\r\n\r\n constructor(\r\n private _tabNavBar: MatTabNav,\r\n /** @docs-private */ \r\n override elementRef: ElementRef,\r\n @Optional() @Inject(MAT_RIPPLE_GLOBAL_OPTIONS) globalRippleOptions: RippleGlobalOptions | null,\r\n @Attribute('tabindex') tabIndex: string,\r\n private _focusMonitor: FocusMonitor,\r\n @Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string,\r\n ) {\r\n super();\r\n\r\n this.rippleConfig = globalRippleOptions || {};\r\n this.tabIndex = parseInt(tabIndex) || 0;\r\n\r\n if (animationMode === 'NoopAnimations') {\r\n this.rippleConfig.animation = {enterDuration: 0, exitDuration: 0};\r\n }\r\n\r\n _tabNavBar._fitInkBarToContent\r\n .pipe(takeUntil(this._destroyed))\r\n .subscribe(fitInkBarToContent => {\r\n this.fitInkBarToContent = fitInkBarToContent;\r\n });\r\n }\r\n\r\n /** Focuses the tab link. */\r\n focus() {\r\n this.elementRef.nativeElement.focus();\r\n }\r\n\r\n ngAfterViewInit() {\r\n this._focusMonitor.monitor(this.elementRef);\r\n }\r\n\r\n override ngOnDestroy() {\r\n this._destroyed.next();\r\n this._destroyed.complete();\r\n super.ngOnDestroy();\r\n this._focusMonitor.stopMonitoring(this.elementRef);\r\n }\r\n\r\n _handleFocus() {\r\n // Since we allow navigation through tabbing in the nav bar, we\r\n // have to update the focused index whenever the link receives focus.\r\n this._tabNavBar.focusIndex = this._tabNavBar._items.toArray().indexOf(this);\r\n }\r\n\r\n _handleKeydown(event: KeyboardEvent) {\r\n if (this.disabled && (event.keyCode === SPACE || event.keyCode === ENTER)) {\r\n event.preventDefault();\r\n } else if (this._tabNavBar.tabPanel && event.keyCode === SPACE) {\r\n this.elementRef.nativeElement.click();\r\n }\r\n }\r\n\r\n _getAriaControls(): string | null {\r\n return this._tabNavBar.tabPanel\r\n ? this._tabNavBar.tabPanel?.id\r\n : this.elementRef.nativeElement.getAttribute('aria-controls');\r\n }\r\n\r\n _getAriaSelected(): string | null {\r\n if (this._tabNavBar.tabPanel) {\r\n return this.active ? 'true' : 'false';\r\n } else {\r\n return this.elementRef.nativeElement.getAttribute('aria-selected');\r\n }\r\n }\r\n\r\n _getAriaCurrent(): string | null {\r\n return this.active && !this._tabNavBar.tabPanel ? 'page' : null;\r\n }\r\n\r\n _getRole(): string | null {\r\n return this._tabNavBar.tabPanel ? 'tab' : this.elementRef.nativeElement.getAttribute('role');\r\n }\r\n\r\n _getTabIndex(): number {\r\n if (this._tabNavBar.tabPanel) {\r\n return this._isActive && !this.disabled ? 0 : -1;\r\n } else {\r\n return this.tabIndex;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Tab panel component associated with MatTabNav.\r\n */\r\n@Component({\r\n selector: 'mat-tab-nav-panel',\r\n exportAs: 'matTabNavPanel',\r\n template: '',\r\n host: {\r\n '[attr.aria-labelledby]': '_activeTabId',\r\n '[attr.id]': 'id',\r\n 'class': 'mat-mdc-tab-nav-panel',\r\n 'role': 'tabpanel',\r\n },\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class MatTabNavPanel {\r\n /** Unique id for the tab panel. */\r\n @Input() id = `mat-tab-nav-panel-${nextUniqueId++}`;\r\n\r\n /** Id of the active tab in the nav bar. */\r\n _activeTabId?: string;\r\n}\r\n", + "assetsDirs": [], + "styleUrlsData": [ + { + "data": "@use '../../core/style/variables';\r\n@use '../tabs-common';\r\n\r\n// Wraps each link in the header\r\n.mat-mdc-tab-link {\r\n // @include tabs-common.tab;\r\n\r\n // Note that we only want to target direct descendant tabs.\r\n .mat-mdc-tab-header.mat-mdc-tab-nav-bar-stretch-tabs & {\r\n flex-grow: 1;\r\n }\r\n\r\n // For the tab-link element, default inset/offset values are necessary to ensure that\r\n // the focus indicator is sufficiently contrastive and renders appropriately.\r\n &::before {\r\n margin: 5px;\r\n }\r\n}\r\n\r\n// @media (variables.$xsmall) {\r\n// .mat-mdc-tab-link {\r\n// min-width: 72px;\r\n// }\r\n// }\r\n", + "styleUrl": "tab-link.scss" + } ], + "stylesData": "", "constructorObj": { "name": "constructor", "description": "", @@ -18441,44 +28037,26 @@ "deprecationMessage": "", "args": [ { - "name": "_overlay", - "type": "Overlay", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "_elementRef", - "type": "ElementRef", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "_scrollDispatcher", - "type": "ScrollDispatcher", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "_viewContainerRef", - "type": "ViewContainerRef", + "name": "_tabNavBar", + "type": "MatTabNav", "deprecated": false, "deprecationMessage": "" }, { - "name": "_ngZone", - "type": "NgZone", + "name": "elementRef", + "type": "ElementRef", "deprecated": false, "deprecationMessage": "" }, { - "name": "platform", - "type": "Platform", + "name": "globalRippleOptions", + "type": "RippleGlobalOptions | null", "deprecated": false, "deprecationMessage": "" }, { - "name": "_ariaDescriber", - "type": "AriaDescriber", + "name": "tabIndex", + "type": "string", "deprecated": false, "deprecationMessage": "" }, @@ -18489,59 +28067,18 @@ "deprecationMessage": "" }, { - "name": "scrollStrategy", - "type": "any", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "_dir", - "type": "Directionality", + "name": "animationMode", + "type": "string", "deprecated": false, - "deprecationMessage": "" + "deprecationMessage": "", + "optional": true } ], - "line": 362, + "line": 339, "jsdoctags": [ { - "name": "_overlay", - "type": "Overlay", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "_elementRef", - "type": "ElementRef", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "_scrollDispatcher", - "type": "ScrollDispatcher", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "_viewContainerRef", - "type": "ViewContainerRef", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "_ngZone", - "type": "NgZone", + "name": "_tabNavBar", + "type": "MatTabNav", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -18549,8 +28086,8 @@ } }, { - "name": "platform", - "type": "Platform", + "name": "elementRef", + "type": "ElementRef", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -18558,8 +28095,8 @@ } }, { - "name": "_ariaDescriber", - "type": "AriaDescriber", + "name": "globalRippleOptions", + "type": "RippleGlobalOptions | null", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -18567,8 +28104,8 @@ } }, { - "name": "_focusMonitor", - "type": "FocusMonitor", + "name": "tabIndex", + "type": "string", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -18576,8 +28113,8 @@ } }, { - "name": "scrollStrategy", - "type": "any", + "name": "_focusMonitor", + "type": "FocusMonitor", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -18585,76 +28122,49 @@ } }, { - "name": "_dir", - "type": "Directionality", + "name": "animationMode", + "type": "string", "deprecated": false, "deprecationMessage": "", + "optional": true, "tagName": { "text": "param" } } ] }, + "extends": "_MatTabLinkMixinBase", + "implements": [ + "AfterViewInit", + "OnDestroy", + "CanDisable", + "CanDisableRipple", + "HasTabIndex", + "RippleTarget", + "FocusableOption" + ], "accessors": { - "position": { - "name": "position", - "setSignature": { - "name": "position", - "type": "void", - "deprecated": false, - "deprecationMessage": "", - "args": [ - { - "name": "value", - "type": "TooltipPosition", - "deprecated": false, - "deprecationMessage": "" - } - ], - "returnType": "void", - "line": 285, - "jsdoctags": [ - { - "name": "value", - "type": "TooltipPosition", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } - ] - }, - "getSignature": { - "name": "position", - "type": "", - "returnType": "TooltipPosition", - "line": 282, - "rawdescription": "\nAllows the user to define the position of the tooltip relative to the parent element", - "description": "

Allows the user to define the position of the tooltip relative to the parent element

\n" - } - }, - "disabled": { - "name": "disabled", + "active": { + "name": "active", "setSignature": { - "name": "disabled", + "name": "active", "type": "void", "deprecated": false, "deprecationMessage": "", "args": [ { "name": "value", - "type": "", + "type": "BooleanInput", "deprecated": false, "deprecationMessage": "" } ], "returnType": "void", - "line": 305, + "line": 308, "jsdoctags": [ { "name": "value", - "type": "", + "type": "BooleanInput", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -18664,661 +28174,1052 @@ ] }, "getSignature": { - "name": "disabled", + "name": "active", "type": "boolean", "returnType": "boolean", - "line": 302, - "rawdescription": "\nDisables the display of the tooltip.", - "description": "

Disables the display of the tooltip.

\n" + "line": 304, + "rawdescription": "\nWhether the link is active.", + "description": "

Whether the link is active.

\n" } }, - "message": { - "name": "message", - "setSignature": { - "name": "message", - "type": "void", - "deprecated": false, - "deprecationMessage": "", - "args": [ - { - "name": "value", - "type": "string", - "deprecated": false, - "deprecationMessage": "" - } - ], - "returnType": "void", - "line": 321, - "jsdoctags": [ - { - "name": "value", - "type": "string", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } - ] - }, + "rippleDisabled": { + "name": "rippleDisabled", "getSignature": { - "name": "message", - "type": "", - "returnType": "", - "line": 318, - "rawdescription": "\nThe message to be displayed in the tooltip", - "description": "

The message to be displayed in the tooltip

\n" - } - }, - "tooltipClass": { - "name": "tooltipClass", - "setSignature": { - "name": "tooltipClass", - "type": "void", - "deprecated": false, - "deprecationMessage": "", - "args": [ - { - "name": "value", - "type": "string | string[] | Set | literal type", - "deprecated": false, - "deprecationMessage": "" - } - ], - "returnType": "void", - "line": 346, + "name": "rippleDisabled", + "type": "boolean", + "returnType": "boolean", + "line": 329, + "rawdescription": "\n\nWhether ripples are disabled on interaction.\n", + "description": "

Whether ripples are disabled on interaction.

\n", "jsdoctags": [ { - "name": "value", - "type": "string | string[] | Set | literal type", - "deprecated": false, - "deprecationMessage": "", + "pos": 10195, + "end": 10213, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 325, "tagName": { - "text": "param" - } + "pos": 10196, + "end": 10208, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "docs-private" + }, + "comment": "" } ] - }, - "getSignature": { - "name": "tooltipClass", - "type": "", - "returnType": "", - "line": 343, - "rawdescription": "\nClasses to be passed to the tooltip. Supports the same syntax as `ngClass`.", - "description": "

Classes to be passed to the tooltip. Supports the same syntax as ngClass.

\n" } } - } - } - ], - "components": [ + }, + "templateData": "\r\n\r\n\r\n\r\n\r\n \r\n \r\n \r\n\r\n\r\n" + }, { - "name": "Checkbox", - "id": "component-Checkbox-446b701178c47a726d6ff70a1e4eb7ff0b63474a65ce26f0e330f524dce0e1d999614d7d81eb85dc9770dbde33a009e0817d1e7c98ab47ac687271d6d667b174", - "file": "ui/src/components/checkbox/checkbox.ts", - "changeDetection": "ChangeDetectionStrategy.OnPush", + "name": "MatTabNav", + "id": "component-MatTabNav-bd9c0cd29c6bf499bbb3e061668b0c5b33870adbc190c4dc86f938c085db45a7f8c16d1d8d9ef9c004e185e411d20f0eb23946a251271f43623faa6c022a6f23", + "file": "ui/src/components/tabs/tab-nav-bar/tab-nav-bar.ts", + "changeDetection": "ChangeDetectionStrategy.Default", "encapsulation": [ "ViewEncapsulation.None" ], "entryComponents": [], - "exportAs": "ouiCheckbox", + "exportAs": "matTabNavBar, matTabNav", "host": {}, "inputs": [ - "tabIndex", "color" ], "outputs": [], - "providers": [ - { - "name": "{\n provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => Checkbox), multi: true,\n}" - } - ], - "selector": "oui-checkbox", + "providers": [], + "selector": "[mat-tab-nav-bar]", "styleUrls": [ - "./checkbox.scss" + "tab-nav-bar.scss" ], "styles": [], "templateUrl": [ - "./checkbox.html" + "tab-nav-bar.html" ], "viewProviders": [], "inputsClass": [ { - "name": "aria-label", - "defaultValue": "''", + "name": "animationDuration", "deprecated": false, "deprecationMessage": "", - "line": 101, - "type": "any", + "line": 110, + "type": "string", "decorators": [] }, { - "name": "aria-labelledby", - "defaultValue": "null", + "name": "backgroundColor", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\n\nUsers can specify the `aria-labelledby` attribute which will be forwarded to the input element\n", - "description": "

Users can specify the aria-labelledby attribute which will be forwarded to the input element

\n", - "line": 107, - "type": "any | null", + "rawdescription": "\nBackground color of the tab nav.", + "description": "

Background color of the tab nav.

\n", + "line": 125, + "type": "ThemePalette", "decorators": [] }, { - "name": "checked", + "name": "color", + "defaultValue": "'primary'", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\n\nWhether the checkbox is checked.\n", - "description": "

Whether the checkbox is checked.

\n", - "line": 158, - "type": "boolean", + "rawdescription": "\nTheme color of the nav bar.", + "description": "

Theme color of the nav bar.

\n", + "line": 155, + "type": "ThemePalette", "decorators": [] }, { - "name": "disabled", + "name": "disableRipple", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\n\nWhether the checkbox is disabled. This fully overrides the implementation provided by\nmixinDisabled, but the mixin is still required because mixinTabIndex requires it.\n", - "description": "

Whether the checkbox is disabled. This fully overrides the implementation provided by\nmixinDisabled, but the mixin is still required because mixinTabIndex requires it.

\n", - "line": 176, - "type": "any", + "rawdescription": "\nWhether the ripple effect is disabled or not.", + "description": "

Whether the ripple effect is disabled or not.

\n", + "line": 144, + "type": "boolean", "decorators": [] }, { - "name": "id", - "defaultValue": "this._uniqueId", + "name": "fitInkBarToContent", "deprecated": false, "deprecationMessage": "", - "line": 116, - "type": "string", + "rawdescription": "\nWhether the ink bar should fit its width to the size of the tab label content.", + "description": "

Whether the ink bar should fit its width to the size of the tab label content.

\n", + "line": 90, + "type": "boolean", "decorators": [] }, { - "name": "labelPosition", - "defaultValue": "'after'", + "name": "mat-stretch-tabs", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nWhether the label should appear after or before the checkbox. Defaults to 'after'", - "description": "

Whether the label should appear after or before the checkbox. Defaults to 'after'

\n", - "line": 137, - "type": "\"before\" | \"after\"", + "rawdescription": "\nWhether tabs should be stretched to fill the header.", + "description": "

Whether tabs should be stretched to fill the header.

\n", + "line": 101, + "type": "boolean", "decorators": [] }, { - "name": "name", - "defaultValue": "null", + "name": "tabPanel", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nName value will be applied to the input element if present", - "description": "

Name value will be applied to the input element if present

\n", - "line": 141, - "type": "string | null", + "rawdescription": "\n\nAssociated tab panel controlled by the nav bar. If not provided, then the nav bar\nfollows the ARIA link / navigation landmark pattern. If provided, it follows the\nARIA tabs design pattern.\n", + "description": "

Associated tab panel controlled by the nav bar. If not provided, then the nav bar\nfollows the ARIA link / navigation landmark pattern. If provided, it follows the\nARIA tabs design pattern.

\n", + "line": 162, + "type": "MatTabNavPanel", "decorators": [] }, { - "name": "required", + "name": "disablePagination", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nWhether the checkbox is required.", - "description": "

Whether the checkbox is required.

\n", - "line": 125, + "rawdescription": "\n\nWhether pagination should be disabled. This can be used to avoid unnecessary\nlayout recalculations if it's known that pagination won't be required.\n", + "description": "

Whether pagination should be disabled. This can be used to avoid unnecessary\nlayout recalculations if it's known that pagination won't be required.

\n", + "line": 132, "type": "boolean", - "decorators": [] - }, + "decorators": [], + "inheritance": { + "file": "MatPaginatedTabHeader" + } + } + ], + "outputsClass": [], + "propertiesClass": [ { - "name": "value", + "name": "_animationDuration", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nThe value attribute of the native input element", - "description": "

The value attribute of the native input element

\n", - "line": 152, "type": "string", - "decorators": [] - } - ], - "outputsClass": [ + "optional": false, + "description": "", + "line": 118, + "modifierKind": [ + 121 + ] + }, { - "name": "change", - "defaultValue": "new EventEmitter()", + "name": "_backgroundColor", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nEvent emitted when the checkbox's `checked` value changes.", - "description": "

Event emitted when the checkbox's checked value changes.

\n", - "line": 145, - "type": "EventEmitter" - } - ], - "propertiesClass": [ + "type": "ThemePalette", + "optional": false, + "description": "", + "line": 140, + "modifierKind": [ + 121 + ] + }, { - "name": "_checked", + "name": "_disableRipple", "defaultValue": "false", "deprecated": false, "deprecationMessage": "", - "type": "any", + "type": "boolean", "optional": false, "description": "", - "line": 169, + "line": 152, "modifierKind": [ 121 ] }, { - "name": "_controlValueAccessorChangeFn", - "defaultValue": "() => {...}", + "name": "_fitInkBarToContent", + "defaultValue": "new BehaviorSubject(false)", "deprecated": false, "deprecationMessage": "", - "type": "function", + "type": "", "optional": false, "description": "", - "line": 332, + "line": 97 + }, + { + "name": "_inkBar", + "deprecated": false, + "deprecationMessage": "", + "type": "MatInkBar", + "optional": false, + "description": "", + "line": 169, + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_items", + "deprecated": false, + "deprecationMessage": "", + "type": "QueryList", + "optional": false, + "description": "

Query list of all tab links of the tab navigation.

\n", + "line": 121, + "rawdescription": "\nQuery list of all tab links of the tab navigation.", + "decorators": [ + { + "name": "ContentChildren", + "stringifiedArguments": "undefined, {descendants: true}" + } + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_nextPaginator", + "deprecated": false, + "deprecationMessage": "", + "type": "ElementRef", + "optional": false, + "description": "", + "line": 167, + "decorators": [ + { + "name": "ViewChild", + "stringifiedArguments": "'nextPaginator'" + } + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_previousPaginator", + "deprecated": false, + "deprecationMessage": "", + "type": "ElementRef", + "optional": false, + "description": "", + "line": 168, + "decorators": [ + { + "name": "ViewChild", + "stringifiedArguments": "'previousPaginator'" + } + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_stretchTabs", + "defaultValue": "true", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "optional": false, + "description": "", + "line": 107, "modifierKind": [ 121 ] }, { - "name": "_currentAnimationClass", - "defaultValue": "''", + "name": "_tabList", + "deprecated": false, + "deprecationMessage": "", + "type": "ElementRef", + "optional": false, + "description": "", + "line": 165, + "decorators": [ + { + "name": "ViewChild", + "stringifiedArguments": "'tabList', {static: true}" + } + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_tabListContainer", + "deprecated": false, + "deprecationMessage": "", + "type": "ElementRef", + "optional": false, + "description": "", + "line": 164, + "decorators": [ + { + "name": "ViewChild", + "stringifiedArguments": "'tabListContainer', {static: true}" + } + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_tabListInner", + "deprecated": false, + "deprecationMessage": "", + "type": "ElementRef", + "optional": false, + "description": "", + "line": 166, + "decorators": [ + { + "name": "ViewChild", + "stringifiedArguments": "'tabListInner', {static: true}" + } + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_animationMode", + "deprecated": false, + "deprecationMessage": "", + "type": "string", + "optional": true, + "description": "", + "line": 171, + "decorators": [ + { + "name": "Optional", + "stringifiedArguments": "" + }, + { + "name": "Inject", + "stringifiedArguments": "ANIMATION_MODULE_TYPE" + } + ], + "modifierKind": [ + 123 + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_currentTextContent", "deprecated": false, "deprecationMessage": "", "type": "string", "optional": false, + "description": "

Cached text content of the header.

\n", + "line": 122, + "rawdescription": "\nCached text content of the header.", + "modifierKind": [ + 121 + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_destroyed", + "defaultValue": "new Subject()", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "optional": false, + "description": "

Emits when the component is destroyed.

\n", + "line": 98, + "rawdescription": "\nEmits when the component is destroyed.", + "modifierKind": [ + 122, + 144 + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_disablePagination", + "defaultValue": "false", + "deprecated": false, + "deprecationMessage": "", + "type": "boolean", + "optional": false, "description": "", - "line": 191, + "line": 138, "modifierKind": [ 121 - ] + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_currentCheckState", - "defaultValue": "TransitionCheckState.Init", + "name": "_disableScrollAfter", + "defaultValue": "true", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "optional": false, + "description": "

Whether the tab list can be scrolled more towards the end of the tab label list.

\n", + "line": 104, + "rawdescription": "\nWhether the tab list can be scrolled more towards the end of the tab label list.", + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_disableScrollBefore", + "defaultValue": "true", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "optional": false, + "description": "

Whether the tab list can be scrolled more towards the beginning of the tab label list.

\n", + "line": 107, + "rawdescription": "\nWhether the tab list can be scrolled more towards the beginning of the tab label list.", + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_keyManager", + "deprecated": false, + "deprecationMessage": "", + "type": "FocusKeyManager", + "optional": false, + "description": "

Used to manage focus between the tabs.

\n", + "line": 119, + "rawdescription": "\nUsed to manage focus between the tabs.", + "modifierKind": [ + 121 + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_scrollDistance", + "defaultValue": "0", + "deprecated": false, + "deprecationMessage": "", + "type": "number", + "optional": false, + "description": "

The distance in pixels that the tab labels should be translated to the left.

\n", + "line": 92, + "rawdescription": "\nThe distance in pixels that the tab labels should be translated to the left.", + "modifierKind": [ + 121 + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_scrollDistanceChanged", + "deprecated": false, + "deprecationMessage": "", + "type": "boolean", + "optional": false, + "description": "

Whether the scroll distance has changed and should be applied after the view is checked.

\n", + "line": 116, + "rawdescription": "\nWhether the scroll distance has changed and should be applied after the view is checked.", + "modifierKind": [ + 121 + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_selectedIndex", + "defaultValue": "0", "deprecated": false, "deprecationMessage": "", - "type": "TransitionCheckState", + "type": "number", "optional": false, "description": "", - "line": 190, + "line": 156, "modifierKind": [ 121 - ] + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_disabled", + "name": "_selectedIndexChanged", "defaultValue": "false", "deprecated": false, "deprecationMessage": "", "type": "", "optional": false, - "description": "", - "line": 189, + "description": "

Whether the header should scroll to the selected index after the view has been checked.

\n", + "line": 95, + "rawdescription": "\nWhether the header should scroll to the selected index after the view has been checked.", "modifierKind": [ 121 - ] + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_elementRef", + "name": "_showPaginationControls", + "defaultValue": "false", "deprecated": false, "deprecationMessage": "", - "type": "ElementRef", + "type": "", "optional": false, - "description": "", - "line": 199, - "modifierKind": [ - 123 - ] + "description": "

Whether the controls for pagination should be displayed

\n", + "line": 101, + "rawdescription": "\nWhether the controls for pagination should be displayed", + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_inputElement", + "name": "_stopScrolling", + "defaultValue": "new Subject()", "deprecated": false, "deprecationMessage": "", - "type": "ElementRef", + "type": "", "optional": false, - "description": "

The native <input type="checkbox"> element

\n", - "line": 148, - "rawdescription": "\nThe native `` element", - "decorators": [ - { - "name": "ViewChild", - "stringifiedArguments": "'input'" - } - ] + "description": "

Stream that will stop the automated scrolling.

\n", + "line": 125, + "rawdescription": "\nStream that will stop the automated scrolling.", + "modifierKind": [ + 121 + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_monitorSubscription", - "defaultValue": "Subscription.EMPTY", + "name": "_tabLabelCount", "deprecated": false, "deprecationMessage": "", - "type": "Subscription", + "type": "number", "optional": false, - "description": "

A unique id for the checkbox input. If none is supplied, it will be auto-generated.

\n", + "description": "

The number of tab labels that are displayed on the header. When this changes, the header\nshould re-evaluate the scroll position.

\n", "line": 113, - "rawdescription": "\nA unique id for the checkbox input. If none is supplied, it will be auto-generated.", + "rawdescription": "\n\nThe number of tab labels that are displayed on the header. When this changes, the header\nshould re-evaluate the scroll position.\n", "modifierKind": [ 121 - ] + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_onTouched", - "defaultValue": "() => {...}", + "name": "indexFocused", + "defaultValue": "new EventEmitter()", "deprecated": false, "deprecationMessage": "", - "type": "function", + "type": "EventEmitter", "optional": false, - "description": "

Called when the checkbox is blurred. Needed to properly implement ControlValueAccessor.

\n", - "line": 330, - "rawdescription": "\n\nCalled when the checkbox is blurred. Needed to properly implement ControlValueAccessor.\n\n", - "jsdoctags": [ - { - "pos": 10191, - "end": 10209, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 325, - "tagName": { - "pos": 10192, - "end": 10204, - "flags": 4227072, - "modifierFlagsCache": 0, - "transformFlags": 0, - "kind": 79, - "escapedText": "docs-private" - }, - "comment": "" - } - ] + "description": "

Event emitted when a label is focused.

\n", + "line": 162, + "rawdescription": "\nEvent emitted when a label is focused.", + "modifierKind": [ + 144 + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_required", + "name": "selectFocusedIndex", + "defaultValue": "new EventEmitter()", "deprecated": false, "deprecationMessage": "", - "type": "boolean", + "type": "EventEmitter", "optional": false, - "description": "", - "line": 133, + "description": "

Event emitted when the option is selected.

\n", + "line": 159, + "rawdescription": "\nEvent emitted when the option is selected.", "modifierKind": [ - 121 - ] + 144 + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } + } + ], + "methodsClass": [ + { + "name": "_getRole", + "args": [], + "optional": false, + "returnType": "string | null", + "typeParameters": [], + "line": 242, + "deprecated": false, + "deprecationMessage": "" }, { - "name": "_uniqueId", - "defaultValue": "`oui-checkbox-${++nextUniqueId}`", + "name": "_itemSelected", + "args": [], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 194, "deprecated": false, "deprecationMessage": "", - "type": "any", + "modifierKind": [ + 122 + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "ngAfterContentInit", + "args": [], "optional": false, - "description": "", - "line": 109, + "returnType": "void", + "typeParameters": [], + "line": 198, + "deprecated": false, + "deprecationMessage": "", "modifierKind": [ - 121 - ] + 158 + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "color", - "defaultValue": "'primary'", + "name": "ngAfterViewInit", + "args": [], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 209, "deprecated": false, "deprecationMessage": "", - "type": "string", + "modifierKind": [ + 158 + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "updateActiveLink", + "args": [], "optional": false, - "description": "

Implemented as part of CanColor.

\n", - "line": 98, - "rawdescription": "\n\nImplemented as part of CanColor.\n" + "returnType": "void", + "typeParameters": [], + "line": 217, + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nNotifies the component that the active link has been changed.", + "description": "

Notifies the component that the active link has been changed.

\n" }, { - "name": "tabIndex", + "name": "_alignInkBarToSelectedTab", + "args": [], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 592, "deprecated": false, "deprecationMessage": "", - "type": "any", + "rawdescription": "\nTells the ink-bar to align itself to the current label wrapper", + "description": "

Tells the ink-bar to align itself to the current label wrapper

\n", + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_checkPaginationEnabled", + "args": [], "optional": false, - "description": "

Implemented as part of HasTabIndex.

\n", - "line": 196, - "rawdescription": "\n\nImplemented as part of HasTabIndex.\n" - } - ], - "methodsClass": [ + "returnType": "void", + "typeParameters": [], + "line": 539, + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\n\nEvaluate whether the pagination controls should be displayed. If the scroll width of the\ntab list is wider than the size of the header container, then the pagination controls should\nbe shown.\n\nThis is an expensive call that forces a layout reflow to compute box and scroll metrics and\nshould be called sparingly.\n", + "description": "

Evaluate whether the pagination controls should be displayed. If the scroll width of the\ntab list is wider than the size of the header container, then the pagination controls should\nbe shown.

\n

This is an expensive call that forces a layout reflow to compute box and scroll metrics and\nshould be called sparingly.

\n", + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, { - "name": "_emitChangeEvent", + "name": "_checkScrollingControls", "args": [], "optional": false, "returnType": "void", "typeParameters": [], - "line": 295, + "line": 567, "deprecated": false, "deprecationMessage": "", - "modifierKind": [ - 121 - ] + "rawdescription": "\n\nEvaluate whether the before and after controls should be enabled or disabled.\nIf the header is at the beginning of the list (scroll distance is equal to 0) then disable the\nbefore button. If the header is at the end of the list (scroll distance is equal to the\nmaximum distance we can scroll), then disable the after button.\n\nThis is an expensive call that forces a layout reflow to compute box and scroll metrics and\nshould be called sparingly.\n", + "description": "

Evaluate whether the before and after controls should be enabled or disabled.\nIf the header is at the beginning of the list (scroll distance is equal to 0) then disable the\nbefore button. If the header is at the end of the list (scroll distance is equal to the\nmaximum distance we can scroll), then disable the after button.

\n

This is an expensive call that forces a layout reflow to compute box and scroll metrics and\nshould be called sparingly.

\n", + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_getAnimationClassForCheckStateTransition", + "name": "_getLayoutDirection", + "args": [], + "optional": false, + "returnType": "Direction", + "typeParameters": [], + "line": 425, + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nThe layout direction of the containing app.", + "description": "

The layout direction of the containing app.

\n", + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_getMaxScrollDistance", + "args": [], + "optional": false, + "returnType": "number", + "typeParameters": [], + "line": 585, + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\n\nDetermines what is the maximum length in pixels that can be set for the scroll distance. This\nis equal to the difference in width between the tab list container and tab header container.\n\nThis is an expensive call that forces a layout reflow to compute box and scroll metrics and\nshould be called sparingly.\n", + "description": "

Determines what is the maximum length in pixels that can be set for the scroll distance. This\nis equal to the difference in width between the tab list container and tab header container.

\n

This is an expensive call that forces a layout reflow to compute box and scroll metrics and\nshould be called sparingly.

\n", + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_handleKeydown", "args": [ { - "name": "oldState", - "type": "TransitionCheckState", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "newState", - "type": "TransitionCheckState", + "name": "event", + "type": "KeyboardEvent", "deprecated": false, "deprecationMessage": "" } ], "optional": false, - "returnType": "string", + "returnType": "void", "typeParameters": [], - "line": 339, + "line": 318, "deprecated": false, "deprecationMessage": "", - "modifierKind": [ - 121 - ], + "rawdescription": "\nHandles keyboard events on the header.", + "description": "

Handles keyboard events on the header.

\n", "jsdoctags": [ { - "name": "oldState", - "type": "TransitionCheckState", + "name": "event", + "type": "KeyboardEvent", "deprecated": false, "deprecationMessage": "", "tagName": { "text": "param" } - }, + } + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_handlePaginatorClick", + "args": [ { - "name": "newState", - "type": "TransitionCheckState", + "name": "direction", + "type": "ScrollDirection", + "deprecated": false, + "deprecationMessage": "" + } + ], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 481, + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nHandles click events on the pagination arrows.", + "description": "

Handles click events on the pagination arrows.

\n", + "jsdoctags": [ + { + "name": "direction", + "type": "ScrollDirection", "deprecated": false, "deprecationMessage": "", "tagName": { "text": "param" } } - ] - }, - { - "name": "_getAriaChecked", - "args": [], - "optional": false, - "returnType": "\"true\" | \"false\"", - "typeParameters": [], - "line": 215, - "deprecated": false, - "deprecationMessage": "" + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_onInputClick", + "name": "_handlePaginatorPress", "args": [ { - "name": "event", - "type": "Event", + "name": "direction", + "type": "ScrollDirection", "deprecated": false, "deprecationMessage": "" + }, + { + "name": "mouseEvent", + "type": "MouseEvent", + "deprecated": false, + "deprecationMessage": "", + "optional": true } ], "optional": false, "returnType": "void", "typeParameters": [], - "line": 235, + "line": 614, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\n\nEvent handler for checkbox input element.\nToggles checked state if element is not disabled.\nDo not toggle on (change) event since IE doesn't fire change event when\n indeterminate checkbox is clicked.\n\n", - "description": "

Event handler for checkbox input element.\nToggles checked state if element is not disabled.\nDo not toggle on (change) event since IE doesn't fire change event when\n indeterminate checkbox is clicked.

\n", + "rawdescription": "\n\nHandles the user pressing down on one of the paginators.\nStarts scrolling the header after a certain amount of time.\n", + "description": "

Handles the user pressing down on one of the paginators.\nStarts scrolling the header after a certain amount of time.

\n", "jsdoctags": [ { "name": { - "pos": 6835, - "end": 6840, + "pos": 22780, + "end": 22789, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, "kind": 79, - "escapedText": "event" + "escapedText": "direction" }, - "type": "Event", + "type": "ScrollDirection", "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 6829, - "end": 6834, + "pos": 22774, + "end": 22779, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, "kind": 79, "escapedText": "param" }, - "comment": "" - } - ] - }, - { - "name": "_onInteractionEvent", - "args": [ - { - "name": "event", - "type": "Event", - "deprecated": false, - "deprecationMessage": "" - } - ], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 308, - "deprecated": false, - "deprecationMessage": "", - "jsdoctags": [ + "comment": "

In which direction the paginator should be scrolled.

\n" + }, { - "name": "event", - "type": "Event", + "name": "mouseEvent", + "type": "MouseEvent", "deprecated": false, "deprecationMessage": "", + "optional": true, "tagName": { "text": "param" } } - ] + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "_transitionCheckState", + "name": "_isValidIndex", "args": [ { - "name": "newState", - "type": "TransitionCheckState", + "name": "index", + "type": "number", "deprecated": false, "deprecationMessage": "" } ], "optional": false, - "returnType": "void", + "returnType": "boolean", "typeParameters": [], - "line": 261, + "line": 394, "deprecated": false, "deprecationMessage": "", - "modifierKind": [ - 121 - ], + "rawdescription": "\n\nDetermines if an index is valid. If the tabs are not ready yet, we assume that the user is\nproviding a valid index and return true.\n", + "description": "

Determines if an index is valid. If the tabs are not ready yet, we assume that the user is\nproviding a valid index and return true.

\n", "jsdoctags": [ { - "name": "newState", - "type": "TransitionCheckState", + "name": "index", + "type": "number", "deprecated": false, "deprecationMessage": "", "tagName": { "text": "param" } } - ] + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "focus", + "name": "_itemsResized", "args": [], "optional": false, - "returnType": "void", + "returnType": "Observable", "typeParameters": [], - "line": 220, + "line": 255, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nFocuses the checkbox.", - "description": "

Focuses the checkbox.

\n" + "rawdescription": "\nSends any changes that could affect the layout of the items.", + "description": "

Sends any changes that could affect the layout of the items.

\n", + "modifierKind": [ + 121 + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "ngOnDestroy", + "name": "_onContentChanges", "args": [], "optional": false, "returnType": "void", "typeParameters": [], - "line": 223, + "line": 344, "deprecated": false, - "deprecationMessage": "" + "deprecationMessage": "", + "rawdescription": "\n\nCallback for when the MutationObserver detects that the content has changed.\n", + "description": "

Callback for when the MutationObserver detects that the content has changed.

\n", + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "registerOnChange", + "name": "_scrollHeader", "args": [ { - "name": "fn", - "type": "function", + "name": "direction", + "type": "ScrollDirection", "deprecated": false, - "deprecationMessage": "", - "function": [ - { - "name": "value", - "type": "any", - "deprecated": false, - "deprecationMessage": "" - } - ] + "deprecationMessage": "" } ], "optional": false, - "returnType": "void", + "returnType": "{ maxScrollDistance: number; distance: number; }", "typeParameters": [], - "line": 321, + "line": 471, "deprecated": false, "deprecationMessage": "", + "rawdescription": "\n\nMoves the tab list in the 'before' or 'after' direction (towards the beginning of the list or\nthe end of the list, respectively). The distance to scroll is computed to be a third of the\nlength of the tab list view window.\n\nThis is an expensive call that forces a layout reflow to compute box and scroll metrics and\nshould be called sparingly.\n", + "description": "

Moves the tab list in the 'before' or 'after' direction (towards the beginning of the list or\nthe end of the list, respectively). The distance to scroll is computed to be a third of the\nlength of the tab list view window.

\n

This is an expensive call that forces a layout reflow to compute box and scroll metrics and\nshould be called sparingly.

\n", "jsdoctags": [ { - "name": "fn", - "type": "function", + "name": "direction", + "type": "ScrollDirection", "deprecated": false, "deprecationMessage": "", - "function": [ - { - "name": "value", - "type": "any", - "deprecated": false, - "deprecationMessage": "" - } - ], "tagName": { "text": "param" } } - ] + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "registerOnTouched", + "name": "_scrollTo", "args": [ { - "name": "fn", - "type": "any", + "name": "position", + "type": "number", + "deprecated": false, + "deprecationMessage": "" + } + ], + "optional": false, + "returnType": "{ maxScrollDistance: number; distance: number; }", + "typeParameters": [], + "line": 643, + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\n\nScrolls the header to a given position.\n", + "description": "

Scrolls the header to a given position.

\n", + "modifierKind": [ + 121 + ], + "jsdoctags": [ + { + "name": { + "pos": 23946, + "end": 23954, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "position" + }, + "type": "number", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "pos": 23940, + "end": 23945, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "param" + }, + "comment": "

Position to which to scroll.

\n" + }, + { + "tagName": { + "pos": 23991, + "end": 23998, + "flags": 4227072, + "modifierFlagsCache": 0, + "transformFlags": 0, + "kind": 79, + "escapedText": "returns" + }, + "comment": "

Information on the current scroll distance and the maximum.

\n" + } + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "_scrollToLabel", + "args": [ + { + "name": "labelIndex", + "type": "number", "deprecated": false, "deprecationMessage": "" } @@ -19326,27 +29227,32 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 335, + "line": 492, "deprecated": false, "deprecationMessage": "", + "rawdescription": "\n\nMoves the tab list such that the desired tab label (marked by index) is moved into view.\n\nThis is an expensive call that forces a layout reflow to compute box and scroll metrics and\nshould be called sparingly.\n", + "description": "

Moves the tab list such that the desired tab label (marked by index) is moved into view.

\n

This is an expensive call that forces a layout reflow to compute box and scroll metrics and\nshould be called sparingly.

\n", "jsdoctags": [ { - "name": "fn", - "type": "any", + "name": "labelIndex", + "type": "number", "deprecated": false, "deprecationMessage": "", "tagName": { "text": "param" } } - ] + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "setDisabledState", + "name": "_setTabFocus", "args": [ { - "name": "isDisabled", - "type": "boolean", + "name": "tabIndex", + "type": "number", "deprecated": false, "deprecationMessage": "" } @@ -19354,75 +29260,111 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 291, + "line": 402, "deprecated": false, "deprecationMessage": "", + "rawdescription": "\n\nSets focus on the HTML element for the label wrapper and scrolls it into the view if\nscrolling is enabled.\n", + "description": "

Sets focus on the HTML element for the label wrapper and scrolls it into the view if\nscrolling is enabled.

\n", "jsdoctags": [ { - "name": "isDisabled", - "type": "boolean", + "name": "tabIndex", + "type": "number", "deprecated": false, "deprecationMessage": "", "tagName": { "text": "param" } } - ] + ], + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "toggle", + "name": "_stopInterval", "args": [], "optional": false, "returnType": "void", "typeParameters": [], - "line": 304, + "line": 605, "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nToggles the `checked` state of the checkbox.", - "description": "

Toggles the checked state of the checkbox.

\n" + "rawdescription": "\nStops the currently-running paginator interval.", + "description": "

Stops the currently-running paginator interval.

\n", + "inheritance": { + "file": "MatPaginatedTabHeader" + } }, { - "name": "writeValue", - "args": [ - { - "name": "value", - "type": "any", - "deprecated": false, - "deprecationMessage": "" - } - ], + "name": "_updateTabScrollPosition", + "args": [], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 430, + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nPerforms the CSS transformation on the tab list that will cause the list to scroll.", + "description": "

Performs the CSS transformation on the tab list that will cause the list to scroll.

\n", + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "ngAfterContentChecked", + "args": [], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 283, + "deprecated": false, + "deprecationMessage": "", + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "ngOnDestroy", + "args": [], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 310, + "deprecated": false, + "deprecationMessage": "", + "inheritance": { + "file": "MatPaginatedTabHeader" + } + }, + { + "name": "updatePagination", + "args": [], "optional": false, "returnType": "void", "typeParameters": [], - "line": 316, + "line": 370, "deprecated": false, "deprecationMessage": "", - "jsdoctags": [ - { - "name": "value", - "type": "any", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } - ] + "rawdescription": "\n\nUpdates the view whether pagination should be enabled or not.\n\nWARNING: Calling this method can be very costly in terms of performance. It should be called\nas infrequently as possible from outside of the Tabs component as it causes a reflow of the\npage.\n", + "description": "

Updates the view whether pagination should be enabled or not.

\n

WARNING: Calling this method can be very costly in terms of performance. It should be called\nas infrequently as possible from outside of the Tabs component as it causes a reflow of the\npage.

\n", + "inheritance": { + "file": "MatPaginatedTabHeader" + } } ], "deprecated": false, "deprecationMessage": "", "hostBindings": [], "hostListeners": [], - "description": "

Supports all of the functionality of an HTML5 checkbox,\nand exposes a similar API. A MatCheckbox can be either checked, unchecked, or\ndisabled. Note that all additional accessibility attributes are taken care of by the component,\nso there is no need to provide them yourself. However, if you want to omit a label and still\nhave the checkbox be accessible, you may supply an [aria-label] input.\nSee: https://github.com/scheduleonce/once-ui/tree/master/ui/src/components/checkbox/README.md

\n", - "rawdescription": "\n\nSupports all of the functionality of an HTML5 checkbox,\nand exposes a similar API. A MatCheckbox can be either checked, unchecked, or\ndisabled. Note that all additional accessibility attributes are taken care of by the component,\nso there is no need to provide them yourself. However, if you want to omit a label and still\nhave the checkbox be accessible, you may supply an [aria-label] input.\nSee: https://github.com/scheduleonce/once-ui/tree/master/ui/src/components/checkbox/README.md\n", + "description": "

Navigation component matching the styles of the tab group header.\nProvides anchored navigation with animated ink bar.

\n", + "rawdescription": "\n\nNavigation component matching the styles of the tab group header.\nProvides anchored navigation with animated ink bar.\n", "type": "component", - "sourceCode": "import {\r\n Component,\r\n ChangeDetectionStrategy,\r\n ViewEncapsulation,\r\n Input,\r\n EventEmitter,\r\n Output,\r\n NgZone,\r\n ChangeDetectorRef,\r\n forwardRef,\r\n ElementRef,\r\n Attribute,\r\n ViewChild,\r\n OnDestroy,\r\n} from '@angular/core';\r\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\r\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\r\nimport { HasTabIndex } from '../core';\r\nimport { FocusMonitor } from '@angular/cdk/a11y';\r\nimport { mixinColor } from '../core';\r\nimport { Subscription } from 'rxjs';\r\n// Increasing integer for generating unique ids for checkbox components.\r\nlet nextUniqueId = 0;\r\n\r\n/** Change event object emitted by MatCheckbox. */\r\nexport class OuiCheckboxChange {\r\n /** The source MatCheckbox of the event. */\r\n source: Checkbox;\r\n /** The new `checked` value of the checkbox. */\r\n checked: boolean;\r\n}\r\n\r\nexport class OuiCheckboxBase {\r\n constructor(public _elementRef: ElementRef) {}\r\n}\r\n\r\nexport const OuiCheckboxMixinBase: typeof OuiCheckboxBase =\r\n mixinColor(OuiCheckboxBase);\r\n\r\n/**\r\n * Represents the different states that require custom transitions between them.\r\n *\r\n * @docs-private\r\n */\r\nexport enum TransitionCheckState {\r\n Init,\r\n /** The state representing the component when it's becoming checked. */\r\n Checked,\r\n /** The state representing the component when it's becoming unchecked. */\r\n Unchecked,\r\n}\r\n\r\n/**\r\n * Supports all of the functionality of an HTML5 checkbox,\r\n * and exposes a similar API. A MatCheckbox can be either checked, unchecked, or\r\n * disabled. Note that all additional accessibility attributes are taken care of by the component,\r\n * so there is no need to provide them yourself. However, if you want to omit a label and still\r\n * have the checkbox be accessible, you may supply an [aria-label] input.\r\n * See: https://github.com/scheduleonce/once-ui/tree/master/ui/src/components/checkbox/README.md\r\n */\r\n@Component({\r\n selector: 'oui-checkbox',\r\n templateUrl: './checkbox.html',\r\n styleUrls: ['./checkbox.scss'],\r\n exportAs: 'ouiCheckbox',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-checkbox',\r\n '[id]': 'id',\r\n '[attr.tabindex]': 'null',\r\n '[class.oui-checkbox-checked]': 'checked',\r\n '[class.oui-checkbox-disabled]': 'disabled',\r\n '[class.oui-checkbox-label-before]': 'labelPosition == \"before\"',\r\n },\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['tabIndex', 'color'],\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n providers: [\r\n {\r\n provide: NG_VALUE_ACCESSOR,\r\n useExisting: forwardRef(() => Checkbox),\r\n multi: true,\r\n },\r\n ],\r\n})\r\nexport class Checkbox\r\n extends OuiCheckboxMixinBase\r\n implements ControlValueAccessor, HasTabIndex, OnDestroy\r\n{\r\n /**\r\n * Attached to the aria-label attribute of the host element. In most cases, arial-labelledby will\r\n * take precedence so this may be omitted.\r\n */\r\n /**\r\n * Implemented as part of CanColor.\r\n */\r\n color = 'primary';\r\n\r\n @Input('aria-label')\r\n ariaLabel: any = '';\r\n\r\n /**\r\n * Users can specify the `aria-labelledby` attribute which will be forwarded to the input element\r\n */\r\n @Input('aria-labelledby')\r\n ariaLabelledby: any | null = null;\r\n\r\n private _uniqueId: any = `oui-checkbox-${++nextUniqueId}`;\r\n\r\n /** A unique id for the checkbox input. If none is supplied, it will be auto-generated. */\r\n\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n\r\n @Input()\r\n id: string = this._uniqueId;\r\n\r\n /** Returns the unique id for the visual hidden input. */\r\n get inputId(): string {\r\n return `${this.id || this._uniqueId}-input`;\r\n }\r\n\r\n /** Whether the checkbox is required. */\r\n @Input()\r\n get required(): boolean {\r\n return this._required;\r\n }\r\n\r\n set required(value: boolean) {\r\n this._required = coerceBooleanProperty(value);\r\n }\r\n\r\n private _required: boolean;\r\n\r\n /** Whether the label should appear after or before the checkbox. Defaults to 'after' */\r\n @Input()\r\n labelPosition: 'before' | 'after' = 'after';\r\n\r\n /** Name value will be applied to the input element if present */\r\n @Input()\r\n name: string | null = null;\r\n\r\n /** Event emitted when the checkbox's `checked` value changes. */\r\n @Output()\r\n readonly change: EventEmitter = new EventEmitter();\r\n\r\n /** The native `` element */\r\n @ViewChild('input') _inputElement: ElementRef;\r\n\r\n /** The value attribute of the native input element */\r\n @Input()\r\n value: string;\r\n\r\n /**\r\n * Whether the checkbox is checked.\r\n */\r\n @Input()\r\n get checked(): boolean {\r\n return this._checked;\r\n }\r\n\r\n set checked(value: boolean) {\r\n if (value !== this.checked) {\r\n this._checked = value;\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n }\r\n\r\n private _checked: any = false;\r\n\r\n /**\r\n * Whether the checkbox is disabled. This fully overrides the implementation provided by\r\n * mixinDisabled, but the mixin is still required because mixinTabIndex requires it.\r\n */\r\n @Input()\r\n get disabled() {\r\n return this._disabled;\r\n }\r\n\r\n set disabled(value: any) {\r\n const newValue = coerceBooleanProperty(value);\r\n\r\n if (newValue !== this.disabled) {\r\n this._disabled = newValue;\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n }\r\n\r\n private _disabled = false;\r\n private _currentCheckState: TransitionCheckState = TransitionCheckState.Init;\r\n private _currentAnimationClass = '';\r\n\r\n /**\r\n * Implemented as part of HasTabIndex.\r\n */\r\n tabIndex: any;\r\n constructor(\r\n private _changeDetectorRef: ChangeDetectorRef,\r\n public _elementRef: ElementRef,\r\n private _ngZone: NgZone,\r\n private _focusMonitor: FocusMonitor,\r\n @Attribute('tabindex') tabIndex: string\r\n ) {\r\n super(_elementRef);\r\n this.tabIndex = parseInt(tabIndex, 10) || 0;\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this._elementRef, true)\r\n .subscribe(() =>\r\n this._ngZone.run(() => {\r\n this._changeDetectorRef.markForCheck();\r\n })\r\n );\r\n }\r\n\r\n _getAriaChecked(): 'true' | 'false' {\r\n return this.checked ? 'true' : 'false';\r\n }\r\n\r\n /** Focuses the checkbox. */\r\n focus(): void {\r\n this._focusMonitor.focusVia(this._inputElement, 'keyboard');\r\n }\r\n ngOnDestroy() {\r\n this._focusMonitor.stopMonitoring(this._elementRef);\r\n this._monitorSubscription.unsubscribe();\r\n }\r\n /**\r\n * Event handler for checkbox input element.\r\n * Toggles checked state if element is not disabled.\r\n * Do not toggle on (change) event since IE doesn't fire change event when\r\n * indeterminate checkbox is clicked.\r\n *\r\n * @param event\r\n */\r\n _onInputClick(event: Event) {\r\n // We have to stop propagation for click events on the visual hidden input element.\r\n // By default, when a user clicks on a label element, a generated click event will be\r\n // dispatched on the associated input element. Since we are using a label element as our\r\n // root container, the click event on the `checkbox` will be executed twice.\r\n // The real click event will bubble up, and the generated click event also tries to bubble up.\r\n // This will lead to multiple click events.\r\n // Preventing bubbling for the second event will solve that issue.\r\n event.stopPropagation();\r\n\r\n // If resetIndeterminate is false, and the current state is indeterminate, do nothing on click\r\n if (!this.disabled) {\r\n this.toggle();\r\n this._transitionCheckState(\r\n this._checked\r\n ? TransitionCheckState.Checked\r\n : TransitionCheckState.Unchecked\r\n );\r\n\r\n // Emit our custom change event if the native input emitted one.\r\n // It is important to only emit it, if the native input triggered one, because\r\n // we don't want to trigger a change event, when the `checked` variable changes for example.\r\n this._emitChangeEvent();\r\n }\r\n }\r\n\r\n private _transitionCheckState(newState: TransitionCheckState) {\r\n const oldState = this._currentCheckState;\r\n const element: HTMLElement = this._elementRef.nativeElement;\r\n\r\n if (oldState === newState) {\r\n return;\r\n }\r\n if (this._currentAnimationClass.length > 0) {\r\n element.classList.remove(this._currentAnimationClass);\r\n }\r\n\r\n this._currentAnimationClass =\r\n this._getAnimationClassForCheckStateTransition(oldState, newState);\r\n this._currentCheckState = newState;\r\n\r\n if (this._currentAnimationClass.length > 0) {\r\n element.classList.add(this._currentAnimationClass);\r\n\r\n // Remove the animation class to avoid animation when the checkbox is moved between containers\r\n const animationClass = this._currentAnimationClass;\r\n\r\n this._ngZone.runOutsideAngular(() => {\r\n setTimeout(() => {\r\n element.classList.remove(animationClass);\r\n }, 1000);\r\n });\r\n }\r\n }\r\n\r\n // Implemented as part of ControlValueAccessor.\r\n setDisabledState(isDisabled: boolean) {\r\n this.disabled = isDisabled;\r\n }\r\n\r\n private _emitChangeEvent() {\r\n const event = new OuiCheckboxChange();\r\n event.source = this;\r\n event.checked = this.checked;\r\n this._controlValueAccessorChangeFn(this.checked);\r\n this.change.emit(event);\r\n }\r\n\r\n /** Toggles the `checked` state of the checkbox. */\r\n toggle(): void {\r\n this.checked = !this.checked;\r\n }\r\n\r\n _onInteractionEvent(event: Event) {\r\n // We always have to stop propagation on the change event.\r\n // Otherwise the change event, from the input element, will bubble up and\r\n // emit its event object to the `change` output.\r\n event.stopPropagation();\r\n }\r\n\r\n // Implemented as part of ControlValueAccessor.\r\n writeValue(value: any) {\r\n this.checked = !!value;\r\n }\r\n\r\n // Implemented as part of ControlValueAccessor.\r\n registerOnChange(fn: (value: any) => void) {\r\n this._controlValueAccessorChangeFn = fn;\r\n }\r\n\r\n /**\r\n * Called when the checkbox is blurred. Needed to properly implement ControlValueAccessor.\r\n *\r\n * @docs-private\r\n */\r\n _onTouched: () => any = () => {};\r\n\r\n private _controlValueAccessorChangeFn: (value: any) => void = () => {};\r\n\r\n // Implemented as part of ControlValueAccessor.\r\n registerOnTouched(fn: any) {\r\n this._onTouched = fn;\r\n }\r\n\r\n private _getAnimationClassForCheckStateTransition(\r\n oldState: TransitionCheckState,\r\n newState: TransitionCheckState\r\n ): string {\r\n let animSuffix: any = '';\r\n\r\n switch (oldState) {\r\n case TransitionCheckState.Init:\r\n // Handle edge case where user interacts with checkbox that does not have [(ngModel)] or\r\n // [checked] bound to it.\r\n if (newState === TransitionCheckState.Checked) {\r\n animSuffix = 'unchecked-checked';\r\n } else if (newState === TransitionCheckState.Unchecked) {\r\n animSuffix = 'unchecked-unchecked';\r\n } else {\r\n return '';\r\n }\r\n break;\r\n case TransitionCheckState.Unchecked:\r\n animSuffix =\r\n newState === TransitionCheckState.Checked ? 'unchecked-checked' : '';\r\n break;\r\n case TransitionCheckState.Checked:\r\n animSuffix =\r\n newState === TransitionCheckState.Unchecked\r\n ? 'checked-unchecked'\r\n : '';\r\n break;\r\n }\r\n\r\n return `oui-checkbox-anim-${animSuffix}`;\r\n }\r\n}\r\n", + "sourceCode": "import {\r\n AfterContentChecked,\r\n AfterContentInit,\r\n AfterViewInit,\r\n Attribute,\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n ContentChildren,\r\n ElementRef,\r\n forwardRef,\r\n Inject,\r\n Input,\r\n NgZone,\r\n OnDestroy,\r\n Optional,\r\n QueryList,\r\n ViewChild,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\nimport {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations';\r\nimport {\r\n CanDisable,\r\n CanDisableRipple,\r\n HasTabIndex,\r\n MAT_RIPPLE_GLOBAL_OPTIONS,\r\n mixinDisabled,\r\n mixinDisableRipple,\r\n mixinTabIndex,\r\n RippleConfig,\r\n RippleGlobalOptions,\r\n RippleTarget,\r\n ThemePalette,\r\n} from '../../core';\r\nimport {FocusableOption, FocusMonitor} from '@angular/cdk/a11y';\r\nimport {Directionality} from '@angular/cdk/bidi';\r\nimport {ViewportRuler} from '@angular/cdk/scrolling';\r\nimport {Platform} from '@angular/cdk/platform';\r\nimport {MatInkBar, mixinInkBarItem} from '../ink-bar';\r\nimport {BooleanInput, coerceBooleanProperty, NumberInput} from '@angular/cdk/coercion';\r\nimport {BehaviorSubject, Subject} from 'rxjs';\r\nimport {startWith, takeUntil} from 'rxjs/operators';\r\nimport {ENTER, SPACE} from '@angular/cdk/keycodes';\r\nimport {MAT_TABS_CONFIG, MatTabsConfig} from '../tab-config';\r\nimport {MatPaginatedTabHeader} from '../paginated-tab-header';\r\nimport { isDevMode } from '@angular/core';\r\n\r\n// Increasing integer for generating unique ids for tab nav components.\r\nlet nextUniqueId = 0;\r\n\r\n/**\r\n * Navigation component matching the styles of the tab group header.\r\n * Provides anchored navigation with animated ink bar.\r\n */\r\n@Component({\r\n selector: '[mat-tab-nav-bar]',\r\n exportAs: 'matTabNavBar, matTabNav',\r\n inputs: ['color'],\r\n templateUrl: 'tab-nav-bar.html',\r\n styleUrls: ['tab-nav-bar.scss'],\r\n host: {\r\n '[attr.role]': '_getRole()',\r\n 'class': 'mat-mdc-tab-nav-bar mat-mdc-tab-header',\r\n '[class.mat-mdc-tab-header-pagination-controls-enabled]': '_showPaginationControls',\r\n '[class.mat-mdc-tab-header-rtl]': \"_getLayoutDirection() == 'rtl'\",\r\n '[class.mat-mdc-tab-nav-bar-stretch-tabs]': 'stretchTabs',\r\n '[class.mat-primary]': 'color !== \"warn\" && color !== \"accent\"',\r\n '[class.mat-accent]': 'color === \"accent\"',\r\n '[class.mat-warn]': 'color === \"warn\"',\r\n '[class._mat-animation-noopable]': '_animationMode === \"NoopAnimations\"',\r\n '[style.--mat-tab-animation-duration]': 'animationDuration',\r\n },\r\n encapsulation: ViewEncapsulation.None,\r\n // tslint:disable-next-line:validate-decorators\r\n changeDetection: ChangeDetectionStrategy.Default,\r\n})\r\nexport class MatTabNav\r\n extends MatPaginatedTabHeader\r\n implements AfterContentChecked, AfterContentInit, OnDestroy, AfterViewInit\r\n{\r\n /** Whether the ink bar should fit its width to the size of the tab label content. */\r\n @Input()\r\n get fitInkBarToContent(): boolean {\r\n return this._fitInkBarToContent.value;\r\n }\r\n set fitInkBarToContent(v: BooleanInput) {\r\n this._fitInkBarToContent.next(coerceBooleanProperty(v));\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n _fitInkBarToContent = new BehaviorSubject(false);\r\n\r\n /** Whether tabs should be stretched to fill the header. */\r\n @Input('mat-stretch-tabs')\r\n get stretchTabs(): boolean {\r\n return this._stretchTabs;\r\n }\r\n set stretchTabs(v: BooleanInput) {\r\n this._stretchTabs = coerceBooleanProperty(v);\r\n }\r\n private _stretchTabs = true;\r\n\r\n @Input()\r\n get animationDuration(): string {\r\n return this._animationDuration;\r\n }\r\n\r\n set animationDuration(value: NumberInput) {\r\n this._animationDuration = /^\\d+$/.test(value + '') ? value + 'ms' : (value as string);\r\n }\r\n\r\n private _animationDuration: string;\r\n\r\n /** Query list of all tab links of the tab navigation. */\r\n @ContentChildren(forwardRef(() => MatTabLink), {descendants: true}) _items: QueryList;\r\n\r\n /** Background color of the tab nav. */\r\n @Input()\r\n get backgroundColor(): ThemePalette {\r\n return this._backgroundColor;\r\n }\r\n\r\n set backgroundColor(value: ThemePalette) {\r\n const classList = this._elementRef.nativeElement.classList;\r\n classList.remove('mat-tabs-with-background', `mat-background-${this.backgroundColor}`);\r\n\r\n if (value) {\r\n classList.add('mat-tabs-with-background', `mat-background-${value}`);\r\n }\r\n\r\n this._backgroundColor = value;\r\n }\r\n\r\n private _backgroundColor: ThemePalette;\r\n\r\n /** Whether the ripple effect is disabled or not. */\r\n @Input()\r\n get disableRipple(): boolean {\r\n return this._disableRipple;\r\n }\r\n\r\n set disableRipple(value: BooleanInput) {\r\n this._disableRipple = coerceBooleanProperty(value);\r\n }\r\n\r\n private _disableRipple: boolean = false;\r\n\r\n /** Theme color of the nav bar. */\r\n @Input() color: ThemePalette = 'primary';\r\n\r\n /**\r\n * Associated tab panel controlled by the nav bar. If not provided, then the nav bar\r\n * follows the ARIA link / navigation landmark pattern. If provided, it follows the\r\n * ARIA tabs design pattern.\r\n */\r\n @Input() tabPanel?: MatTabNavPanel;\r\n\r\n @ViewChild('tabListContainer', {static: true}) _tabListContainer: ElementRef;\r\n @ViewChild('tabList', {static: true}) _tabList: ElementRef;\r\n @ViewChild('tabListInner', {static: true}) _tabListInner: ElementRef;\r\n @ViewChild('nextPaginator') _nextPaginator: ElementRef;\r\n @ViewChild('previousPaginator') _previousPaginator: ElementRef;\r\n _inkBar: MatInkBar;\r\n\r\n constructor(\r\n elementRef: ElementRef,\r\n @Optional() dir: Directionality,\r\n ngZone: NgZone,\r\n changeDetectorRef: ChangeDetectorRef,\r\n viewportRuler: ViewportRuler,\r\n platform: Platform,\r\n @Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string,\r\n @Optional() @Inject(MAT_TABS_CONFIG) defaultConfig?: MatTabsConfig,\r\n ) {\r\n super(elementRef, changeDetectorRef, viewportRuler, dir, ngZone, platform, animationMode);\r\n this.disablePagination =\r\n defaultConfig && defaultConfig.disablePagination != null\r\n ? defaultConfig.disablePagination\r\n : false;\r\n this.fitInkBarToContent =\r\n defaultConfig && defaultConfig.fitInkBarToContent != null\r\n ? defaultConfig.fitInkBarToContent\r\n : false;\r\n this.stretchTabs =\r\n defaultConfig && defaultConfig.stretchTabs != null ? defaultConfig.stretchTabs : true;\r\n }\r\n\r\n protected _itemSelected() {\r\n // noop\r\n }\r\n\r\n override ngAfterContentInit() {\r\n this._inkBar = new MatInkBar(this._items);\r\n // We need this to run before the `changes` subscription in parent to ensure that the\r\n // selectedIndex is up-to-date by the time the super class starts looking for it.\r\n this._items.changes.pipe(startWith(null), takeUntil(this._destroyed)).subscribe(() => {\r\n this.updateActiveLink();\r\n });\r\n\r\n super.ngAfterContentInit();\r\n }\r\n\r\n override ngAfterViewInit() {\r\n if (!this.tabPanel && (typeof isDevMode === 'undefined' || isDevMode)) {\r\n throw new Error('A mat-tab-nav-panel must be specified via [tabPanel].');\r\n }\r\n super.ngAfterViewInit();\r\n }\r\n\r\n /** Notifies the component that the active link has been changed. */\r\n updateActiveLink() {\r\n if (!this._items) {\r\n return;\r\n }\r\n\r\n const items = this._items.toArray();\r\n\r\n for (let i = 0; i < items.length; i++) {\r\n if (items[i].active) {\r\n this.selectedIndex = i;\r\n this._changeDetectorRef.markForCheck();\r\n\r\n if (this.tabPanel) {\r\n this.tabPanel._activeTabId = items[i].id;\r\n }\r\n\r\n return;\r\n }\r\n }\r\n\r\n // The ink bar should hide itself if no items are active.\r\n this.selectedIndex = -1;\r\n this._inkBar.hide();\r\n }\r\n\r\n _getRole(): string | null {\r\n return this.tabPanel ? 'tablist' : this._elementRef.nativeElement.getAttribute('role');\r\n }\r\n}\r\n\r\n// Boilerplate for applying mixins to MatTabLink.\r\nconst _MatTabLinkMixinBase = mixinInkBarItem(\r\n mixinTabIndex(\r\n mixinDisableRipple(\r\n mixinDisabled(\r\n class {\r\n elementRef: ElementRef;\r\n },\r\n ),\r\n ),\r\n ),\r\n);\r\n\r\n/**\r\n * Link inside a `mat-tab-nav-bar`.\r\n */\r\n@Component({\r\n selector: '[mat-tab-link], [matTabLink]',\r\n exportAs: 'matTabLink',\r\n inputs: ['disabled', 'disableRipple', 'tabIndex', 'active', 'id'],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n templateUrl: 'tab-link.html',\r\n styleUrls: ['tab-link.scss'],\r\n host: {\r\n 'class': 'mdc-tab mat-mdc-tab-link mat-mdc-focus-indicator',\r\n '[attr.aria-controls]': '_getAriaControls()',\r\n '[attr.aria-current]': '_getAriaCurrent()',\r\n '[attr.aria-disabled]': 'disabled',\r\n '[attr.aria-selected]': '_getAriaSelected()',\r\n '[attr.id]': 'id',\r\n '[attr.tabIndex]': '_getTabIndex()',\r\n '[attr.role]': '_getRole()',\r\n '[class.mat-mdc-tab-disabled]': 'disabled',\r\n '[class.mdc-tab--active]': 'active',\r\n '(focus)': '_handleFocus()',\r\n '(keydown)': '_handleKeydown($event)',\r\n },\r\n})\r\nexport class MatTabLink\r\n extends _MatTabLinkMixinBase\r\n implements\r\n AfterViewInit,\r\n OnDestroy,\r\n CanDisable,\r\n CanDisableRipple,\r\n HasTabIndex,\r\n RippleTarget,\r\n FocusableOption\r\n{\r\n private readonly _destroyed = new Subject();\r\n\r\n /** Whether the tab link is active or not. */\r\n protected _isActive: boolean = false;\r\n\r\n /** Whether the link is active. */\r\n @Input()\r\n get active(): boolean {\r\n return this._isActive;\r\n }\r\n\r\n set active(value: BooleanInput) {\r\n const newValue = coerceBooleanProperty(value);\r\n\r\n if (newValue !== this._isActive) {\r\n this._isActive = newValue;\r\n this._tabNavBar.updateActiveLink();\r\n }\r\n }\r\n\r\n /**\r\n * Ripple configuration for ripples that are launched on pointer down. The ripple config\r\n * is set to the global ripple options since we don't have any configurable options for\r\n * the tab link ripples.\r\n * @docs-private\r\n */\r\n rippleConfig: RippleConfig & RippleGlobalOptions;\r\n\r\n /**\r\n * Whether ripples are disabled on interaction.\r\n * @docs-private\r\n */\r\n get rippleDisabled(): boolean {\r\n return (\r\n this.disabled ||\r\n this.disableRipple ||\r\n this._tabNavBar.disableRipple ||\r\n !!this.rippleConfig.disabled\r\n );\r\n }\r\n\r\n /** Unique id for the tab. */\r\n @Input() id = `mat-tab-link-${nextUniqueId++}`;\r\n\r\n constructor(\r\n private _tabNavBar: MatTabNav,\r\n /** @docs-private */ \r\n override elementRef: ElementRef,\r\n @Optional() @Inject(MAT_RIPPLE_GLOBAL_OPTIONS) globalRippleOptions: RippleGlobalOptions | null,\r\n @Attribute('tabindex') tabIndex: string,\r\n private _focusMonitor: FocusMonitor,\r\n @Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string,\r\n ) {\r\n super();\r\n\r\n this.rippleConfig = globalRippleOptions || {};\r\n this.tabIndex = parseInt(tabIndex) || 0;\r\n\r\n if (animationMode === 'NoopAnimations') {\r\n this.rippleConfig.animation = {enterDuration: 0, exitDuration: 0};\r\n }\r\n\r\n _tabNavBar._fitInkBarToContent\r\n .pipe(takeUntil(this._destroyed))\r\n .subscribe(fitInkBarToContent => {\r\n this.fitInkBarToContent = fitInkBarToContent;\r\n });\r\n }\r\n\r\n /** Focuses the tab link. */\r\n focus() {\r\n this.elementRef.nativeElement.focus();\r\n }\r\n\r\n ngAfterViewInit() {\r\n this._focusMonitor.monitor(this.elementRef);\r\n }\r\n\r\n override ngOnDestroy() {\r\n this._destroyed.next();\r\n this._destroyed.complete();\r\n super.ngOnDestroy();\r\n this._focusMonitor.stopMonitoring(this.elementRef);\r\n }\r\n\r\n _handleFocus() {\r\n // Since we allow navigation through tabbing in the nav bar, we\r\n // have to update the focused index whenever the link receives focus.\r\n this._tabNavBar.focusIndex = this._tabNavBar._items.toArray().indexOf(this);\r\n }\r\n\r\n _handleKeydown(event: KeyboardEvent) {\r\n if (this.disabled && (event.keyCode === SPACE || event.keyCode === ENTER)) {\r\n event.preventDefault();\r\n } else if (this._tabNavBar.tabPanel && event.keyCode === SPACE) {\r\n this.elementRef.nativeElement.click();\r\n }\r\n }\r\n\r\n _getAriaControls(): string | null {\r\n return this._tabNavBar.tabPanel\r\n ? this._tabNavBar.tabPanel?.id\r\n : this.elementRef.nativeElement.getAttribute('aria-controls');\r\n }\r\n\r\n _getAriaSelected(): string | null {\r\n if (this._tabNavBar.tabPanel) {\r\n return this.active ? 'true' : 'false';\r\n } else {\r\n return this.elementRef.nativeElement.getAttribute('aria-selected');\r\n }\r\n }\r\n\r\n _getAriaCurrent(): string | null {\r\n return this.active && !this._tabNavBar.tabPanel ? 'page' : null;\r\n }\r\n\r\n _getRole(): string | null {\r\n return this._tabNavBar.tabPanel ? 'tab' : this.elementRef.nativeElement.getAttribute('role');\r\n }\r\n\r\n _getTabIndex(): number {\r\n if (this._tabNavBar.tabPanel) {\r\n return this._isActive && !this.disabled ? 0 : -1;\r\n } else {\r\n return this.tabIndex;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Tab panel component associated with MatTabNav.\r\n */\r\n@Component({\r\n selector: 'mat-tab-nav-panel',\r\n exportAs: 'matTabNavPanel',\r\n template: '',\r\n host: {\r\n '[attr.aria-labelledby]': '_activeTabId',\r\n '[attr.id]': 'id',\r\n 'class': 'mat-mdc-tab-nav-panel',\r\n 'role': 'tabpanel',\r\n },\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class MatTabNavPanel {\r\n /** Unique id for the tab panel. */\r\n @Input() id = `mat-tab-nav-panel-${nextUniqueId++}`;\r\n\r\n /** Id of the active tab in the nav bar. */\r\n _activeTabId?: string;\r\n}\r\n", "assetsDirs": [], "styleUrlsData": [ { - "data": "@use 'sass:math';\r\n\r\n$_oui-checkbox-mark-path-length: 22.910259;\r\n$_oui-checkbox-indeterminate-checked-easing-function: cubic-bezier(\r\n 0.14,\r\n 0,\r\n 0,\r\n 1\r\n);\r\n$oui-toggle-padding: 9px !default;\r\n$oui-checkbox-size: 16px !default;\r\n$oui-checkbox-border-width: 1px;\r\n$oui-checkbox-border-enable: #9c9c9c;\r\n$oui-checkbox-stoke-enable: #333333;\r\n$oui-checkbox-border-disable: #e4e4e4;\r\n$oui-checkbox-stoke-disable: #9b9b9b;\r\n$_oui-checkbox-item-spacing: $oui-toggle-padding;\r\n$oui-checkbox-disable-background: #f9f9f9;\r\n$_oui-checkbox-mark-stroke-size: math.div(3, 15) * $oui-checkbox-size !default;\r\n$oui-linear-out-slow-in-timing-function: cubic-bezier(0, 0, 0.2, 0.1) !default;\r\n$oui-fast-out-linear-in-timing-function: cubic-bezier(0.4, 0, 1, 1) !default;\r\n$oui-checkbox-transition-duration: 90ms;\r\n@mixin oui-fill {\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n position: absolute;\r\n}\r\n@mixin cdk-visually-hidden {\r\n border: 0;\r\n clip: rect(0 0 0 0);\r\n height: 1px;\r\n margin: -1px;\r\n overflow: hidden;\r\n padding: 0;\r\n position: absolute;\r\n width: 1px;\r\n outline: 0;\r\n -webkit-appearance: none;\r\n -moz-appearance: none;\r\n}\r\n@mixin cdk-high-contrast($target: active) {\r\n @media (-ms-high-contrast: $target) {\r\n @content;\r\n }\r\n}\r\n@keyframes oui-checkbox-unchecked-checked-checkmark-path {\r\n 0%,\r\n 50% {\r\n stroke-dashoffset: $_oui-checkbox-mark-path-length;\r\n }\r\n 50% {\r\n animation-timing-function: $oui-linear-out-slow-in-timing-function;\r\n }\r\n 100% {\r\n stroke-dashoffset: 0;\r\n }\r\n}\r\n@keyframes oui-checkbox-checked-unchecked-checkmark-path {\r\n from {\r\n animation-timing-function: $oui-fast-out-linear-in-timing-function;\r\n stroke-dashoffset: 0;\r\n }\r\n to {\r\n stroke-dashoffset: $_oui-checkbox-mark-path-length * -1;\r\n }\r\n}\r\n%oui-checkbox-outer-box {\r\n @include oui-fill;\r\n border-radius: 3px;\r\n box-sizing: border-box;\r\n pointer-events: none;\r\n}\r\n.oui-checkbox {\r\n cursor: pointer;\r\n -webkit-tap-highlight-color: transparent;\r\n}\r\n.oui-checkbox-layout {\r\n cursor: inherit;\r\n align-items: baseline;\r\n vertical-align: middle;\r\n display: inline-flex;\r\n white-space: nowrap;\r\n}\r\n.oui-checkbox-inner-container {\r\n display: inline-block;\r\n height: $oui-checkbox-size;\r\n line-height: 0;\r\n margin: auto;\r\n margin-right: $_oui-checkbox-item-spacing;\r\n order: 0;\r\n position: relative;\r\n vertical-align: middle;\r\n white-space: nowrap;\r\n width: $oui-checkbox-size;\r\n flex-shrink: 0;\r\n [dir='rtl'] & {\r\n margin: {\r\n left: $_oui-checkbox-item-spacing;\r\n right: auto;\r\n }\r\n }\r\n}\r\n.oui-checkbox-inner-container-no-side-margin {\r\n margin: {\r\n left: 0;\r\n right: 0;\r\n }\r\n}\r\n.oui-checkbox-frame {\r\n @extend %oui-checkbox-outer-box;\r\n background-color: transparent;\r\n border: {\r\n width: $oui-checkbox-border-width;\r\n style: solid;\r\n color: $oui-checkbox-border-enable;\r\n }\r\n}\r\n.oui-checkbox-checkmark {\r\n @include oui-fill;\r\n width: 100%;\r\n}\r\n.oui-checkbox-checkmark-path {\r\n stroke: {\r\n dashoffset: $_oui-checkbox-mark-path-length;\r\n dasharray: $_oui-checkbox-mark-path-length;\r\n width: $_oui-checkbox-mark-stroke-size;\r\n }\r\n}\r\n.oui-checkbox-mixedmark {\r\n $height: floor($_oui-checkbox-mark-stroke-size);\r\n width: calc(100% - 6px);\r\n height: $height;\r\n opacity: 0;\r\n transform: scaleX(0) rotate(0deg);\r\n border-radius: 2px;\r\n @include cdk-high-contrast {\r\n height: 0;\r\n border-top: solid $height;\r\n margin-top: $height;\r\n }\r\n}\r\n.oui-checkbox-label-before {\r\n .oui-checkbox-inner-container {\r\n order: 1;\r\n margin: {\r\n left: $_oui-checkbox-item-spacing;\r\n right: auto;\r\n }\r\n [dir='rtl'] & {\r\n margin: {\r\n left: auto;\r\n right: $_oui-checkbox-item-spacing;\r\n }\r\n }\r\n }\r\n}\r\n.oui-checkbox-checked {\r\n .oui-checkbox-checkmark {\r\n opacity: 1;\r\n }\r\n .oui-checkbox-checkmark-path {\r\n stroke-dashoffset: 0;\r\n }\r\n .oui-checkbox-mixedmark {\r\n transform: scaleX(1) rotate(-45deg);\r\n }\r\n}\r\n.oui-checkbox-disabled {\r\n cursor: default;\r\n .oui-checkbox-frame {\r\n border: {\r\n color: $oui-checkbox-border-disable;\r\n }\r\n background: $oui-checkbox-disable-background;\r\n }\r\n .oui-checkbox-checkmark-path {\r\n stroke: $oui-checkbox-stoke-disable;\r\n }\r\n}\r\n.oui-checkbox-anim {\r\n &-unchecked-checked {\r\n .oui-checkbox-checkmark-path {\r\n animation: $oui-checkbox-transition-duration * 2 linear 0ms\r\n oui-checkbox-unchecked-checked-checkmark-path;\r\n }\r\n }\r\n &-checked-unchecked {\r\n .oui-checkbox-checkmark-path {\r\n animation: $oui-checkbox-transition-duration linear 0ms\r\n oui-checkbox-checked-unchecked-checkmark-path;\r\n }\r\n }\r\n}\r\n.oui-checkbox-input {\r\n @include cdk-visually-hidden;\r\n bottom: 0;\r\n left: 50%;\r\n}\r\n.cdk-keyboard-focused {\r\n .oui-checkbox-frame {\r\n border-width: 2px;\r\n }\r\n}\r\n", - "styleUrl": "./checkbox.scss" + "data": "// @use '../tabs-common';\r\n\r\n// @include tabs-common.structural-styles;\r\n// @include tabs-common.paginated-tab-header;\r\n\r\n// .mat-mdc-tab-links {\r\n// @include tabs-common.paginated-tab-header-item-wrapper('.mat-mdc-tab-link-container');\r\n// }\r\n\r\n// .mat-mdc-tab-link-container {\r\n// @include tabs-common.paginated-tab-header-container;\r\n// }\r\n\r\n// .mat-mdc-tab-nav-bar {\r\n// @include tabs-common.paginated-tab-header-with-background('.mat-mdc-tab-link-container',\r\n// '.mat-mdc-tab-link');\r\n// }\r\n", + "styleUrl": "tab-nav-bar.scss" } ], "stylesData": "", @@ -19433,41 +29375,61 @@ "deprecationMessage": "", "args": [ { - "name": "_changeDetectorRef", - "type": "ChangeDetectorRef", + "name": "elementRef", + "type": "ElementRef", "deprecated": false, "deprecationMessage": "" }, { - "name": "_elementRef", - "type": "ElementRef", + "name": "dir", + "type": "Directionality", "deprecated": false, "deprecationMessage": "" }, { - "name": "_ngZone", + "name": "ngZone", "type": "NgZone", "deprecated": false, "deprecationMessage": "" }, { - "name": "_focusMonitor", - "type": "FocusMonitor", + "name": "changeDetectorRef", + "type": "ChangeDetectorRef", "deprecated": false, "deprecationMessage": "" }, { - "name": "tabIndex", - "type": "string", + "name": "viewportRuler", + "type": "ViewportRuler", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "platform", + "type": "Platform", "deprecated": false, "deprecationMessage": "" + }, + { + "name": "animationMode", + "type": "string", + "deprecated": false, + "deprecationMessage": "", + "optional": true + }, + { + "name": "defaultConfig", + "type": "MatTabsConfig", + "deprecated": false, + "deprecationMessage": "", + "optional": true } ], - "line": 196, + "line": 169, "jsdoctags": [ { - "name": "_changeDetectorRef", - "type": "ChangeDetectorRef", + "name": "elementRef", + "type": "ElementRef", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -19475,8 +29437,8 @@ } }, { - "name": "_elementRef", - "type": "ElementRef", + "name": "dir", + "type": "Directionality", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -19484,7 +29446,7 @@ } }, { - "name": "_ngZone", + "name": "ngZone", "type": "NgZone", "deprecated": false, "deprecationMessage": "", @@ -19493,8 +29455,8 @@ } }, { - "name": "_focusMonitor", - "type": "FocusMonitor", + "name": "changeDetectorRef", + "type": "ChangeDetectorRef", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -19502,55 +29464,112 @@ } }, { - "name": "tabIndex", + "name": "viewportRuler", + "type": "ViewportRuler", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "platform", + "type": "Platform", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "animationMode", "type": "string", "deprecated": false, "deprecationMessage": "", + "optional": true, + "tagName": { + "text": "param" + } + }, + { + "name": "defaultConfig", + "type": "MatTabsConfig", + "deprecated": false, + "deprecationMessage": "", + "optional": true, "tagName": { "text": "param" } } ] }, - "extends": "OuiCheckboxMixinBase", + "extends": "MatPaginatedTabHeader", "implements": [ - "ControlValueAccessor", - "HasTabIndex", - "OnDestroy" + "AfterContentChecked", + "AfterContentInit", + "OnDestroy", + "AfterViewInit" ], "accessors": { - "inputId": { - "name": "inputId", + "fitInkBarToContent": { + "name": "fitInkBarToContent", + "setSignature": { + "name": "fitInkBarToContent", + "type": "void", + "deprecated": false, + "deprecationMessage": "", + "args": [ + { + "name": "v", + "type": "BooleanInput", + "deprecated": false, + "deprecationMessage": "" + } + ], + "returnType": "void", + "line": 93, + "jsdoctags": [ + { + "name": "v", + "type": "BooleanInput", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + }, "getSignature": { - "name": "inputId", - "type": "string", - "returnType": "string", - "line": 119, - "rawdescription": "\nReturns the unique id for the visual hidden input.", - "description": "

Returns the unique id for the visual hidden input.

\n" + "name": "fitInkBarToContent", + "type": "boolean", + "returnType": "boolean", + "line": 90, + "rawdescription": "\nWhether the ink bar should fit its width to the size of the tab label content.", + "description": "

Whether the ink bar should fit its width to the size of the tab label content.

\n" } }, - "required": { - "name": "required", + "stretchTabs": { + "name": "stretchTabs", "setSignature": { - "name": "required", + "name": "stretchTabs", "type": "void", "deprecated": false, "deprecationMessage": "", "args": [ { - "name": "value", - "type": "boolean", + "name": "v", + "type": "BooleanInput", "deprecated": false, "deprecationMessage": "" } ], "returnType": "void", - "line": 129, + "line": 104, "jsdoctags": [ { - "name": "value", - "type": "boolean", + "name": "v", + "type": "BooleanInput", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -19560,35 +29579,35 @@ ] }, "getSignature": { - "name": "required", + "name": "stretchTabs", "type": "boolean", "returnType": "boolean", - "line": 125, - "rawdescription": "\nWhether the checkbox is required.", - "description": "

Whether the checkbox is required.

\n" + "line": 101, + "rawdescription": "\nWhether tabs should be stretched to fill the header.", + "description": "

Whether tabs should be stretched to fill the header.

\n" } }, - "checked": { - "name": "checked", + "animationDuration": { + "name": "animationDuration", "setSignature": { - "name": "checked", + "name": "animationDuration", "type": "void", "deprecated": false, "deprecationMessage": "", "args": [ { "name": "value", - "type": "boolean", + "type": "NumberInput", "deprecated": false, "deprecationMessage": "" } ], "returnType": "void", - "line": 162, + "line": 114, "jsdoctags": [ { "name": "value", - "type": "boolean", + "type": "NumberInput", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -19598,35 +29617,33 @@ ] }, "getSignature": { - "name": "checked", - "type": "boolean", - "returnType": "boolean", - "line": 158, - "rawdescription": "\n\nWhether the checkbox is checked.\n", - "description": "

Whether the checkbox is checked.

\n" + "name": "animationDuration", + "type": "string", + "returnType": "string", + "line": 110 } }, - "disabled": { - "name": "disabled", + "backgroundColor": { + "name": "backgroundColor", "setSignature": { - "name": "disabled", + "name": "backgroundColor", "type": "void", "deprecated": false, "deprecationMessage": "", "args": [ { "name": "value", - "type": "any", + "type": "ThemePalette", "deprecated": false, "deprecationMessage": "" } ], "returnType": "void", - "line": 180, + "line": 129, "jsdoctags": [ { "name": "value", - "type": "any", + "type": "ThemePalette", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -19636,69 +29653,84 @@ ] }, "getSignature": { - "name": "disabled", + "name": "backgroundColor", "type": "", - "returnType": "", - "line": 176, - "rawdescription": "\n\nWhether the checkbox is disabled. This fully overrides the implementation provided by\nmixinDisabled, but the mixin is still required because mixinTabIndex requires it.\n", - "description": "

Whether the checkbox is disabled. This fully overrides the implementation provided by\nmixinDisabled, but the mixin is still required because mixinTabIndex requires it.

\n" + "returnType": "ThemePalette", + "line": 125, + "rawdescription": "\nBackground color of the tab nav.", + "description": "

Background color of the tab nav.

\n" + } + }, + "disableRipple": { + "name": "disableRipple", + "setSignature": { + "name": "disableRipple", + "type": "void", + "deprecated": false, + "deprecationMessage": "", + "args": [ + { + "name": "value", + "type": "BooleanInput", + "deprecated": false, + "deprecationMessage": "" + } + ], + "returnType": "void", + "line": 148, + "jsdoctags": [ + { + "name": "value", + "type": "BooleanInput", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + }, + "getSignature": { + "name": "disableRipple", + "type": "boolean", + "returnType": "boolean", + "line": 144, + "rawdescription": "\nWhether the ripple effect is disabled or not.", + "description": "

Whether the ripple effect is disabled or not.

\n" } } }, - "templateData": "\r\n" + "templateData": "\r\n\r\n\r\n\r\n\r\n\r\n\r\n" }, { - "name": "Icon", - "id": "component-Icon-a80dbf67f59027cfecbc70d7ea735f57cc200e0aa565ea565d9fdb1518a9c5111df5433e42069784a86228a97004f63e2d541f2589c97ebadf7e7007b604a6c7", - "file": "ui/src/components/icon/icon.ts", + "name": "MatTabNavPanel", + "id": "component-MatTabNavPanel-bd9c0cd29c6bf499bbb3e061668b0c5b33870adbc190c4dc86f938c085db45a7f8c16d1d8d9ef9c004e185e411d20f0eb23946a251271f43623faa6c022a6f23", + "file": "ui/src/components/tabs/tab-nav-bar/tab-nav-bar.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ "ViewEncapsulation.None" ], "entryComponents": [], - "exportAs": "ouiIcon", + "exportAs": "matTabNavPanel", "host": {}, - "inputs": [ - "color" - ], + "inputs": [], "outputs": [], "providers": [], - "selector": "oui-icon", - "styleUrls": [ - "icon.scss" - ], + "selector": "mat-tab-nav-panel", + "styleUrls": [], "styles": [], "template": "", "templateUrl": [], "viewProviders": [], "inputsClass": [ { - "name": "inline", - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\n\nWhether the icon should be inlined, automatically sizing the icon to match the font size of\nthe element the icon is contained in.\n", - "description": "

Whether the icon should be inlined, automatically sizing the icon to match the font size of\nthe element the icon is contained in.

\n", - "line": 97, - "type": "boolean", - "decorators": [] - }, - { - "name": "size", - "deprecated": false, - "deprecationMessage": "", - "rawdescription": "\nUser will be able to supply a size property for overriding the size.", - "description": "

User will be able to supply a size property for overriding the size.

\n", - "line": 116, - "type": "number", - "decorators": [] - }, - { - "name": "svgIcon", + "name": "id", + "defaultValue": "`mat-tab-nav-panel-${nextUniqueId++}`", "deprecated": false, "deprecationMessage": "", - "rawdescription": "\nName of the icon in the SVG icon set.", - "description": "

Name of the icon in the SVG icon set.

\n", - "line": 113, + "rawdescription": "\nUnique id for the tab panel.", + "description": "

Unique id for the tab panel.

\n", + "line": 445, "type": "string", "decorators": [] } @@ -19706,245 +29738,71 @@ "outputsClass": [], "propertiesClass": [ { - "name": "_elementRef", - "deprecated": false, - "deprecationMessage": "", - "type": "ElementRef", - "optional": false, - "description": "", - "line": 120, - "modifierKind": [ - 123 - ] - }, - { - "name": "_inline", - "defaultValue": "false", - "deprecated": false, - "deprecationMessage": "", - "type": "boolean", - "optional": false, - "description": "", - "line": 104, - "modifierKind": [ - 121 - ] - }, - { - "name": "color", - "deprecated": false, - "deprecationMessage": "", - "type": "any", - "optional": false, - "description": "

Implemented as part of CanColor.

\n", - "line": 109, - "rawdescription": "\n\nImplemented as part of CanColor.\n" - }, - { - "name": "titleCasePipe", - "deprecated": false, - "deprecationMessage": "", - "type": "TitleCasePipe", - "optional": false, - "description": "", - "line": 121, - "modifierKind": [ - 123 - ] - } - ], - "methodsClass": [ - { - "name": "_clearSvgElement", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 160, - "deprecated": false, - "deprecationMessage": "", - "modifierKind": [ - 121 - ] - }, - { - "name": "_setSvgElement", - "args": [ - { - "name": "svg", - "type": "SVGElement", - "deprecated": false, - "deprecationMessage": "" - } - ], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 144, + "name": "_activeTabId", "deprecated": false, "deprecationMessage": "", - "modifierKind": [ - 121 - ], - "jsdoctags": [ - { - "name": "svg", - "type": "SVGElement", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } - ] - }, - { - "name": "ngOnInit", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 132, - "deprecated": false, - "deprecationMessage": "" + "type": "string", + "optional": true, + "description": "

Id of the active tab in the nav bar.

\n", + "line": 448, + "rawdescription": "\nId of the active tab in the nav bar." } ], + "methodsClass": [], "deprecated": false, "deprecationMessage": "", "hostBindings": [], "hostListeners": [], - "description": "

oui-icon makes it easier to use vector-based icons in your app. This directive supports only SVG icons.\nTo associate a name with an icon URL, use the addSvgIcon. The methods of OuiIconRegistry. After registering an icon, it\ncan be displayed by setting the svgIcon input. For an icon in the default namespace, use the name directly.\nComponent to display an icon. It can be used in the following ways:

\n
    \n
  • Specify the svgIcon input to load an SVG icon from a URL previously registered with the addSvgIcon, addSvgIconSet.\nExamples:\n
  • \n
  • Use a font ligature as an icon by putting the ligature text in the content of the component.\nExample: home sun
  • \n
\n", - "rawdescription": "\n\noui-icon makes it easier to use vector-based icons in your app. This directive supports only SVG icons.\nTo associate a name with an icon URL, use the addSvgIcon. The methods of OuiIconRegistry. After registering an icon, it\ncan be displayed by setting the svgIcon input. For an icon in the default namespace, use the name directly.\nComponent to display an icon. It can be used in the following ways:\n- Specify the svgIcon input to load an SVG icon from a URL previously registered with the addSvgIcon, addSvgIconSet.\n Examples:\n \n- Use a font ligature as an icon by putting the ligature text in the content of the component.\n Example: home sun\n", + "description": "

Tab panel component associated with MatTabNav.

\n", + "rawdescription": "\n\nTab panel component associated with MatTabNav.\n", "type": "component", - "sourceCode": "import {\r\n Component,\r\n ViewEncapsulation,\r\n Input,\r\n OnInit,\r\n ElementRef,\r\n Attribute,\r\n ChangeDetectionStrategy,\r\n InjectionToken,\r\n inject,\r\n} from '@angular/core';\r\nimport { CanColor, CanColorCtor, mixinColor } from '../core';\r\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\r\nimport { OuiIconRegistry } from './icon-registery';\r\nimport { take } from 'rxjs/operators';\r\nimport { TitleCasePipe } from '@angular/common';\r\nimport { DOCUMENT } from '@angular/common';\r\n\r\n// Boilerplate for applying mixins to OuiButton.\r\n/** @docs-private */\r\nexport class OuiIconBase {\r\n constructor(public _elementRef: ElementRef) {}\r\n}\r\n\r\nexport const OuiIconMixinBase: CanColorCtor & typeof OuiIconBase =\r\n mixinColor(OuiIconBase);\r\n\r\n/**\r\n * Injection token used to provide the current location to `OuiIcon`.\r\n * Used to handle server-side rendering and to stub out during unit tests.\r\n *\r\n * @docs-private\r\n */\r\nexport const OUI_ICON_LOCATION = new InjectionToken(\r\n 'oui-icon-location',\r\n {\r\n providedIn: 'root',\r\n factory: OUI_ICON_LOCATION_FACTORY,\r\n }\r\n);\r\n\r\n/**\r\n * Stubbed out location for `OuiIcon`.\r\n *\r\n * @docs-private\r\n */\r\nexport interface OuiIconLocation {\r\n getPathname: () => string;\r\n}\r\n\r\n/** @docs-private */\r\nexport function OUI_ICON_LOCATION_FACTORY(): OuiIconLocation {\r\n const _document = inject(DOCUMENT);\r\n const _location = _document ? _document.location : null;\r\n\r\n return {\r\n // Note that this needs to be a function, rather than a property, because Angular\r\n // will only resolve it once, but we want the current path on each call.\r\n getPathname: () => (_location ? _location.pathname + _location.search : ''),\r\n };\r\n}\r\n\r\n/**\r\n * oui-icon makes it easier to use vector-based icons in your app. This directive supports only SVG icons.\r\n * To associate a name with an icon URL, use the addSvgIcon. The methods of OuiIconRegistry. After registering an icon, it\r\n * can be displayed by setting the svgIcon input. For an icon in the default namespace, use the name directly.\r\n * Component to display an icon. It can be used in the following ways:\r\n * - Specify the svgIcon input to load an SVG icon from a URL previously registered with the addSvgIcon, addSvgIconSet.\r\n * Examples:\r\n * \r\n * - Use a font ligature as an icon by putting the ligature text in the content of the component.\r\n * Example: home sun\r\n */\r\n@Component({\r\n template: '',\r\n selector: 'oui-icon',\r\n exportAs: 'ouiIcon',\r\n styleUrls: ['icon.scss'],\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['color'],\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n role: 'img',\r\n class: 'oui-icon',\r\n '[class.oui-icon-inline]': 'inline',\r\n '[attr.aria-label]': 'this.titleCasePipe.transform(this.svgIcon)',\r\n },\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class Icon extends OuiIconMixinBase implements OnInit, CanColor {\r\n /**\r\n * Whether the icon should be inlined, automatically sizing the icon to match the font size of\r\n * the element the icon is contained in.\r\n */\r\n @Input()\r\n get inline(): boolean {\r\n return this._inline;\r\n }\r\n set inline(inline: boolean) {\r\n this._inline = coerceBooleanProperty(inline);\r\n }\r\n // eslint-disable-next-line @typescript-eslint/no-inferrable-types\r\n private _inline: boolean = false;\r\n\r\n /**\r\n * Implemented as part of CanColor.\r\n */\r\n color: any;\r\n\r\n /** Name of the icon in the SVG icon set. */\r\n @Input()\r\n svgIcon: string;\r\n /** User will be able to supply a size property for overriding the size. */\r\n @Input()\r\n size: number;\r\n\r\n constructor(\r\n private _iconRegistry: OuiIconRegistry,\r\n public _elementRef: ElementRef,\r\n public titleCasePipe: TitleCasePipe,\r\n @Attribute('aria-hidden') ariaHidden: string\r\n ) {\r\n super(_elementRef);\r\n // If the user has not explicitly set aria-hidden, mark the icon as hidden, as this is\r\n // the right thing to do for the majority of icon use-cases.\r\n if (!ariaHidden) {\r\n _elementRef.nativeElement.setAttribute('aria-hidden', 'true');\r\n }\r\n }\r\n\r\n ngOnInit() {\r\n if (this.svgIcon) {\r\n this._iconRegistry\r\n .getNamedSvgIcon(this.svgIcon)\r\n .pipe(take(1))\r\n .subscribe(\r\n (svg) => this._setSvgElement(svg),\r\n (err: Error) => console.log(`Error retrieving icon: ${err.message}`)\r\n );\r\n }\r\n }\r\n\r\n private _setSvgElement(svg: SVGElement) {\r\n this._clearSvgElement();\r\n this._elementRef.nativeElement.appendChild(svg);\r\n let svgSize = svg.getAttribute('height');\r\n if (this.size) {\r\n //noinspection TypeScriptUnresolvedFunction\r\n const { x, y, width, height } = (svg).getBBox();\r\n svg.setAttribute('height', '100%');\r\n svg.setAttribute('width', '100%');\r\n svg.setAttribute('viewBox', `${x} ${y} ${width} ${height}`);\r\n svgSize = `${this.size}px`;\r\n }\r\n this._elementRef.nativeElement.style.height = svgSize;\r\n this._elementRef.nativeElement.style.width = svgSize;\r\n }\r\n\r\n private _clearSvgElement() {\r\n const layoutElement: HTMLElement = this._elementRef.nativeElement;\r\n let childCount = layoutElement.childNodes.length;\r\n\r\n // Remove existing non-element child nodes and SVGs, and add the new SVG element. Note that\r\n // we can't use innerHTML, because IE will throw if the element has a data binding.\r\n while (childCount--) {\r\n const child = layoutElement.childNodes[childCount];\r\n\r\n // 1 corresponds to Node.ELEMENT_NODE. We remove all non-element nodes in order to get rid\r\n // of any loose text nodes, as well as any SVG elements in order to remove any old icons.\r\n if (child.nodeType !== 1 || child.nodeName.toLowerCase() === 'svg') {\r\n layoutElement.removeChild(child);\r\n }\r\n }\r\n }\r\n}\r\n", + "sourceCode": "import {\r\n AfterContentChecked,\r\n AfterContentInit,\r\n AfterViewInit,\r\n Attribute,\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n ContentChildren,\r\n ElementRef,\r\n forwardRef,\r\n Inject,\r\n Input,\r\n NgZone,\r\n OnDestroy,\r\n Optional,\r\n QueryList,\r\n ViewChild,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\nimport {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations';\r\nimport {\r\n CanDisable,\r\n CanDisableRipple,\r\n HasTabIndex,\r\n MAT_RIPPLE_GLOBAL_OPTIONS,\r\n mixinDisabled,\r\n mixinDisableRipple,\r\n mixinTabIndex,\r\n RippleConfig,\r\n RippleGlobalOptions,\r\n RippleTarget,\r\n ThemePalette,\r\n} from '../../core';\r\nimport {FocusableOption, FocusMonitor} from '@angular/cdk/a11y';\r\nimport {Directionality} from '@angular/cdk/bidi';\r\nimport {ViewportRuler} from '@angular/cdk/scrolling';\r\nimport {Platform} from '@angular/cdk/platform';\r\nimport {MatInkBar, mixinInkBarItem} from '../ink-bar';\r\nimport {BooleanInput, coerceBooleanProperty, NumberInput} from '@angular/cdk/coercion';\r\nimport {BehaviorSubject, Subject} from 'rxjs';\r\nimport {startWith, takeUntil} from 'rxjs/operators';\r\nimport {ENTER, SPACE} from '@angular/cdk/keycodes';\r\nimport {MAT_TABS_CONFIG, MatTabsConfig} from '../tab-config';\r\nimport {MatPaginatedTabHeader} from '../paginated-tab-header';\r\nimport { isDevMode } from '@angular/core';\r\n\r\n// Increasing integer for generating unique ids for tab nav components.\r\nlet nextUniqueId = 0;\r\n\r\n/**\r\n * Navigation component matching the styles of the tab group header.\r\n * Provides anchored navigation with animated ink bar.\r\n */\r\n@Component({\r\n selector: '[mat-tab-nav-bar]',\r\n exportAs: 'matTabNavBar, matTabNav',\r\n inputs: ['color'],\r\n templateUrl: 'tab-nav-bar.html',\r\n styleUrls: ['tab-nav-bar.scss'],\r\n host: {\r\n '[attr.role]': '_getRole()',\r\n 'class': 'mat-mdc-tab-nav-bar mat-mdc-tab-header',\r\n '[class.mat-mdc-tab-header-pagination-controls-enabled]': '_showPaginationControls',\r\n '[class.mat-mdc-tab-header-rtl]': \"_getLayoutDirection() == 'rtl'\",\r\n '[class.mat-mdc-tab-nav-bar-stretch-tabs]': 'stretchTabs',\r\n '[class.mat-primary]': 'color !== \"warn\" && color !== \"accent\"',\r\n '[class.mat-accent]': 'color === \"accent\"',\r\n '[class.mat-warn]': 'color === \"warn\"',\r\n '[class._mat-animation-noopable]': '_animationMode === \"NoopAnimations\"',\r\n '[style.--mat-tab-animation-duration]': 'animationDuration',\r\n },\r\n encapsulation: ViewEncapsulation.None,\r\n // tslint:disable-next-line:validate-decorators\r\n changeDetection: ChangeDetectionStrategy.Default,\r\n})\r\nexport class MatTabNav\r\n extends MatPaginatedTabHeader\r\n implements AfterContentChecked, AfterContentInit, OnDestroy, AfterViewInit\r\n{\r\n /** Whether the ink bar should fit its width to the size of the tab label content. */\r\n @Input()\r\n get fitInkBarToContent(): boolean {\r\n return this._fitInkBarToContent.value;\r\n }\r\n set fitInkBarToContent(v: BooleanInput) {\r\n this._fitInkBarToContent.next(coerceBooleanProperty(v));\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n _fitInkBarToContent = new BehaviorSubject(false);\r\n\r\n /** Whether tabs should be stretched to fill the header. */\r\n @Input('mat-stretch-tabs')\r\n get stretchTabs(): boolean {\r\n return this._stretchTabs;\r\n }\r\n set stretchTabs(v: BooleanInput) {\r\n this._stretchTabs = coerceBooleanProperty(v);\r\n }\r\n private _stretchTabs = true;\r\n\r\n @Input()\r\n get animationDuration(): string {\r\n return this._animationDuration;\r\n }\r\n\r\n set animationDuration(value: NumberInput) {\r\n this._animationDuration = /^\\d+$/.test(value + '') ? value + 'ms' : (value as string);\r\n }\r\n\r\n private _animationDuration: string;\r\n\r\n /** Query list of all tab links of the tab navigation. */\r\n @ContentChildren(forwardRef(() => MatTabLink), {descendants: true}) _items: QueryList;\r\n\r\n /** Background color of the tab nav. */\r\n @Input()\r\n get backgroundColor(): ThemePalette {\r\n return this._backgroundColor;\r\n }\r\n\r\n set backgroundColor(value: ThemePalette) {\r\n const classList = this._elementRef.nativeElement.classList;\r\n classList.remove('mat-tabs-with-background', `mat-background-${this.backgroundColor}`);\r\n\r\n if (value) {\r\n classList.add('mat-tabs-with-background', `mat-background-${value}`);\r\n }\r\n\r\n this._backgroundColor = value;\r\n }\r\n\r\n private _backgroundColor: ThemePalette;\r\n\r\n /** Whether the ripple effect is disabled or not. */\r\n @Input()\r\n get disableRipple(): boolean {\r\n return this._disableRipple;\r\n }\r\n\r\n set disableRipple(value: BooleanInput) {\r\n this._disableRipple = coerceBooleanProperty(value);\r\n }\r\n\r\n private _disableRipple: boolean = false;\r\n\r\n /** Theme color of the nav bar. */\r\n @Input() color: ThemePalette = 'primary';\r\n\r\n /**\r\n * Associated tab panel controlled by the nav bar. If not provided, then the nav bar\r\n * follows the ARIA link / navigation landmark pattern. If provided, it follows the\r\n * ARIA tabs design pattern.\r\n */\r\n @Input() tabPanel?: MatTabNavPanel;\r\n\r\n @ViewChild('tabListContainer', {static: true}) _tabListContainer: ElementRef;\r\n @ViewChild('tabList', {static: true}) _tabList: ElementRef;\r\n @ViewChild('tabListInner', {static: true}) _tabListInner: ElementRef;\r\n @ViewChild('nextPaginator') _nextPaginator: ElementRef;\r\n @ViewChild('previousPaginator') _previousPaginator: ElementRef;\r\n _inkBar: MatInkBar;\r\n\r\n constructor(\r\n elementRef: ElementRef,\r\n @Optional() dir: Directionality,\r\n ngZone: NgZone,\r\n changeDetectorRef: ChangeDetectorRef,\r\n viewportRuler: ViewportRuler,\r\n platform: Platform,\r\n @Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string,\r\n @Optional() @Inject(MAT_TABS_CONFIG) defaultConfig?: MatTabsConfig,\r\n ) {\r\n super(elementRef, changeDetectorRef, viewportRuler, dir, ngZone, platform, animationMode);\r\n this.disablePagination =\r\n defaultConfig && defaultConfig.disablePagination != null\r\n ? defaultConfig.disablePagination\r\n : false;\r\n this.fitInkBarToContent =\r\n defaultConfig && defaultConfig.fitInkBarToContent != null\r\n ? defaultConfig.fitInkBarToContent\r\n : false;\r\n this.stretchTabs =\r\n defaultConfig && defaultConfig.stretchTabs != null ? defaultConfig.stretchTabs : true;\r\n }\r\n\r\n protected _itemSelected() {\r\n // noop\r\n }\r\n\r\n override ngAfterContentInit() {\r\n this._inkBar = new MatInkBar(this._items);\r\n // We need this to run before the `changes` subscription in parent to ensure that the\r\n // selectedIndex is up-to-date by the time the super class starts looking for it.\r\n this._items.changes.pipe(startWith(null), takeUntil(this._destroyed)).subscribe(() => {\r\n this.updateActiveLink();\r\n });\r\n\r\n super.ngAfterContentInit();\r\n }\r\n\r\n override ngAfterViewInit() {\r\n if (!this.tabPanel && (typeof isDevMode === 'undefined' || isDevMode)) {\r\n throw new Error('A mat-tab-nav-panel must be specified via [tabPanel].');\r\n }\r\n super.ngAfterViewInit();\r\n }\r\n\r\n /** Notifies the component that the active link has been changed. */\r\n updateActiveLink() {\r\n if (!this._items) {\r\n return;\r\n }\r\n\r\n const items = this._items.toArray();\r\n\r\n for (let i = 0; i < items.length; i++) {\r\n if (items[i].active) {\r\n this.selectedIndex = i;\r\n this._changeDetectorRef.markForCheck();\r\n\r\n if (this.tabPanel) {\r\n this.tabPanel._activeTabId = items[i].id;\r\n }\r\n\r\n return;\r\n }\r\n }\r\n\r\n // The ink bar should hide itself if no items are active.\r\n this.selectedIndex = -1;\r\n this._inkBar.hide();\r\n }\r\n\r\n _getRole(): string | null {\r\n return this.tabPanel ? 'tablist' : this._elementRef.nativeElement.getAttribute('role');\r\n }\r\n}\r\n\r\n// Boilerplate for applying mixins to MatTabLink.\r\nconst _MatTabLinkMixinBase = mixinInkBarItem(\r\n mixinTabIndex(\r\n mixinDisableRipple(\r\n mixinDisabled(\r\n class {\r\n elementRef: ElementRef;\r\n },\r\n ),\r\n ),\r\n ),\r\n);\r\n\r\n/**\r\n * Link inside a `mat-tab-nav-bar`.\r\n */\r\n@Component({\r\n selector: '[mat-tab-link], [matTabLink]',\r\n exportAs: 'matTabLink',\r\n inputs: ['disabled', 'disableRipple', 'tabIndex', 'active', 'id'],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n templateUrl: 'tab-link.html',\r\n styleUrls: ['tab-link.scss'],\r\n host: {\r\n 'class': 'mdc-tab mat-mdc-tab-link mat-mdc-focus-indicator',\r\n '[attr.aria-controls]': '_getAriaControls()',\r\n '[attr.aria-current]': '_getAriaCurrent()',\r\n '[attr.aria-disabled]': 'disabled',\r\n '[attr.aria-selected]': '_getAriaSelected()',\r\n '[attr.id]': 'id',\r\n '[attr.tabIndex]': '_getTabIndex()',\r\n '[attr.role]': '_getRole()',\r\n '[class.mat-mdc-tab-disabled]': 'disabled',\r\n '[class.mdc-tab--active]': 'active',\r\n '(focus)': '_handleFocus()',\r\n '(keydown)': '_handleKeydown($event)',\r\n },\r\n})\r\nexport class MatTabLink\r\n extends _MatTabLinkMixinBase\r\n implements\r\n AfterViewInit,\r\n OnDestroy,\r\n CanDisable,\r\n CanDisableRipple,\r\n HasTabIndex,\r\n RippleTarget,\r\n FocusableOption\r\n{\r\n private readonly _destroyed = new Subject();\r\n\r\n /** Whether the tab link is active or not. */\r\n protected _isActive: boolean = false;\r\n\r\n /** Whether the link is active. */\r\n @Input()\r\n get active(): boolean {\r\n return this._isActive;\r\n }\r\n\r\n set active(value: BooleanInput) {\r\n const newValue = coerceBooleanProperty(value);\r\n\r\n if (newValue !== this._isActive) {\r\n this._isActive = newValue;\r\n this._tabNavBar.updateActiveLink();\r\n }\r\n }\r\n\r\n /**\r\n * Ripple configuration for ripples that are launched on pointer down. The ripple config\r\n * is set to the global ripple options since we don't have any configurable options for\r\n * the tab link ripples.\r\n * @docs-private\r\n */\r\n rippleConfig: RippleConfig & RippleGlobalOptions;\r\n\r\n /**\r\n * Whether ripples are disabled on interaction.\r\n * @docs-private\r\n */\r\n get rippleDisabled(): boolean {\r\n return (\r\n this.disabled ||\r\n this.disableRipple ||\r\n this._tabNavBar.disableRipple ||\r\n !!this.rippleConfig.disabled\r\n );\r\n }\r\n\r\n /** Unique id for the tab. */\r\n @Input() id = `mat-tab-link-${nextUniqueId++}`;\r\n\r\n constructor(\r\n private _tabNavBar: MatTabNav,\r\n /** @docs-private */ \r\n override elementRef: ElementRef,\r\n @Optional() @Inject(MAT_RIPPLE_GLOBAL_OPTIONS) globalRippleOptions: RippleGlobalOptions | null,\r\n @Attribute('tabindex') tabIndex: string,\r\n private _focusMonitor: FocusMonitor,\r\n @Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string,\r\n ) {\r\n super();\r\n\r\n this.rippleConfig = globalRippleOptions || {};\r\n this.tabIndex = parseInt(tabIndex) || 0;\r\n\r\n if (animationMode === 'NoopAnimations') {\r\n this.rippleConfig.animation = {enterDuration: 0, exitDuration: 0};\r\n }\r\n\r\n _tabNavBar._fitInkBarToContent\r\n .pipe(takeUntil(this._destroyed))\r\n .subscribe(fitInkBarToContent => {\r\n this.fitInkBarToContent = fitInkBarToContent;\r\n });\r\n }\r\n\r\n /** Focuses the tab link. */\r\n focus() {\r\n this.elementRef.nativeElement.focus();\r\n }\r\n\r\n ngAfterViewInit() {\r\n this._focusMonitor.monitor(this.elementRef);\r\n }\r\n\r\n override ngOnDestroy() {\r\n this._destroyed.next();\r\n this._destroyed.complete();\r\n super.ngOnDestroy();\r\n this._focusMonitor.stopMonitoring(this.elementRef);\r\n }\r\n\r\n _handleFocus() {\r\n // Since we allow navigation through tabbing in the nav bar, we\r\n // have to update the focused index whenever the link receives focus.\r\n this._tabNavBar.focusIndex = this._tabNavBar._items.toArray().indexOf(this);\r\n }\r\n\r\n _handleKeydown(event: KeyboardEvent) {\r\n if (this.disabled && (event.keyCode === SPACE || event.keyCode === ENTER)) {\r\n event.preventDefault();\r\n } else if (this._tabNavBar.tabPanel && event.keyCode === SPACE) {\r\n this.elementRef.nativeElement.click();\r\n }\r\n }\r\n\r\n _getAriaControls(): string | null {\r\n return this._tabNavBar.tabPanel\r\n ? this._tabNavBar.tabPanel?.id\r\n : this.elementRef.nativeElement.getAttribute('aria-controls');\r\n }\r\n\r\n _getAriaSelected(): string | null {\r\n if (this._tabNavBar.tabPanel) {\r\n return this.active ? 'true' : 'false';\r\n } else {\r\n return this.elementRef.nativeElement.getAttribute('aria-selected');\r\n }\r\n }\r\n\r\n _getAriaCurrent(): string | null {\r\n return this.active && !this._tabNavBar.tabPanel ? 'page' : null;\r\n }\r\n\r\n _getRole(): string | null {\r\n return this._tabNavBar.tabPanel ? 'tab' : this.elementRef.nativeElement.getAttribute('role');\r\n }\r\n\r\n _getTabIndex(): number {\r\n if (this._tabNavBar.tabPanel) {\r\n return this._isActive && !this.disabled ? 0 : -1;\r\n } else {\r\n return this.tabIndex;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Tab panel component associated with MatTabNav.\r\n */\r\n@Component({\r\n selector: 'mat-tab-nav-panel',\r\n exportAs: 'matTabNavPanel',\r\n template: '',\r\n host: {\r\n '[attr.aria-labelledby]': '_activeTabId',\r\n '[attr.id]': 'id',\r\n 'class': 'mat-mdc-tab-nav-panel',\r\n 'role': 'tabpanel',\r\n },\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class MatTabNavPanel {\r\n /** Unique id for the tab panel. */\r\n @Input() id = `mat-tab-nav-panel-${nextUniqueId++}`;\r\n\r\n /** Id of the active tab in the nav bar. */\r\n _activeTabId?: string;\r\n}\r\n", "assetsDirs": [], - "styleUrlsData": [ - { - "data": "// The width/height of the icon element.\r\n.oui-icon {\r\n background-repeat: no-repeat;\r\n display: inline-block;\r\n fill: currentColor;\r\n\r\n &.oui-icon-inline {\r\n font-size: inherit;\r\n height: inherit;\r\n line-height: inherit;\r\n width: inherit;\r\n }\r\n\r\n svg {\r\n pointer-events: none;\r\n }\r\n}\r\n\r\n.oui-icon[ouitooltip] {\r\n padding: 2px !important;\r\n &.cdk-focused.cdk-keyboard-focused {\r\n background: rgba(200, 200, 200, 0.4);\r\n border-radius: 2px;\r\n transition: opacity 0.2s cubic-bezier(0.35, 0, 0.25, 1),\r\n background-color 0.2s cubic-bezier(0.35, 0, 0.25, 1);\r\n padding: 2px;\r\n outline: 0 none;\r\n }\r\n}\r\n", - "styleUrl": "icon.scss" - } - ], + "styleUrlsData": "", + "stylesData": "" + }, + { + "name": "MatTabStorybook", + "id": "component-MatTabStorybook-366473549002fc9f8261ab25f2780c89294f0eca174dd76a71f1bc54a395e661c1aa3bff09f0030bbfe6b33c264bc2936dfd37782691a8066be5ab5a6d9f0341", + "file": "ui/src/stories/tabs/tabs.component.ts", + "encapsulation": [], + "entryComponents": [], + "inputs": [], + "outputs": [], + "providers": [], + "selector": "mat-tab-storybook", + "styleUrls": [], + "styles": [], + "template": "\n AAAAAAAAAAAAAAAAAAAA\">\n BBBBBBBBBBBBBBBBBBBB\">\n CCCCCCCCCCCCCCCCCCCC\">\n\n", + "templateUrl": [], + "viewProviders": [], + "inputsClass": [], + "outputsClass": [], + "propertiesClass": [], + "methodsClass": [], + "deprecated": false, + "deprecationMessage": "", + "hostBindings": [], + "hostListeners": [], + "description": "", + "rawdescription": "\n", + "type": "component", + "sourceCode": "import { Component } from '@angular/core';\r\n\r\n@Component({\r\n selector: 'mat-tab-storybook',\r\n template: `\r\n \r\n AAAAAAAAAAAAAAAAAAAA\">\r\n BBBBBBBBBBBBBBBBBBBB\">\r\n CCCCCCCCCCCCCCCCCCCC\">\r\n \r\n `,\r\n})\r\nexport class MatTabStorybook {\r\n constructor() {}\r\n}", + "assetsDirs": [], + "styleUrlsData": "", "stylesData": "", "constructorObj": { "name": "constructor", "description": "", "deprecated": false, "deprecationMessage": "", - "args": [ - { - "name": "_iconRegistry", - "type": "OuiIconRegistry", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "_elementRef", - "type": "ElementRef", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "titleCasePipe", - "type": "TitleCasePipe", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "ariaHidden", - "type": "string", - "deprecated": false, - "deprecationMessage": "" - } - ], - "line": 116, - "jsdoctags": [ - { - "name": "_iconRegistry", - "type": "OuiIconRegistry", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "_elementRef", - "type": "ElementRef", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "titleCasePipe", - "type": "TitleCasePipe", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "ariaHidden", - "type": "string", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } - ] - }, - "extends": "OuiIconMixinBase", - "implements": [ - "OnInit", - "CanColor" - ], - "accessors": { - "inline": { - "name": "inline", - "setSignature": { - "name": "inline", - "type": "void", - "deprecated": false, - "deprecationMessage": "", - "args": [ - { - "name": "inline", - "type": "boolean", - "deprecated": false, - "deprecationMessage": "" - } - ], - "returnType": "void", - "line": 100, - "jsdoctags": [ - { - "name": "inline", - "type": "boolean", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } - ] - }, - "getSignature": { - "name": "inline", - "type": "boolean", - "returnType": "boolean", - "line": 97, - "rawdescription": "\n\nWhether the icon should be inlined, automatically sizing the icon to match the font size of\nthe element the icon is contained in.\n", - "description": "

Whether the icon should be inlined, automatically sizing the icon to match the font size of\nthe element the icon is contained in.

\n" - } - } + "args": [], + "line": 13 } }, { "name": "OuiAnchor", - "id": "component-OuiAnchor-309b291ac11361b1deae57302d81829f29747ee89fc32835b9bd6812870953cd80a029eb0f793ec52f1765efd49f818af249c9f1edddc674785953707c8f3580", + "id": "component-OuiAnchor-0d7d7fa85ca575eff44ea16216e0b4e33534845e4ea90e975cbffa14d598d4392f2620b0e45183fa30e446ad5dfa4283ef721d2475ff2f880c1047553f88955f", "file": "ui/src/components/button/button.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -20143,11 +30001,11 @@ "description": "

Once UI anchor.

\n", "rawdescription": "\n\nOnce UI anchor.\n", "type": "component", - "sourceCode": "import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n ElementRef,\r\n ViewEncapsulation,\r\n OnDestroy,\r\n ChangeDetectorRef,\r\n NgZone,\r\n Input,\r\n} from '@angular/core';\r\nimport {\r\n CanDisable,\r\n CanColor,\r\n CanDisableCtor,\r\n CanColorCtor,\r\n mixinColor,\r\n mixinDisabled,\r\n} from '../core';\r\n\r\nimport { CanProgress, CanProgressCtor, mixinProgress } from './progress';\r\nimport { FocusMonitor } from '@angular/cdk/a11y';\r\nimport { Subscription } from 'rxjs';\r\n/**\r\n * List of classes to add to Button instances based on host attributes to\r\n * style as different variants.\r\n */\r\nconst BUTTON_HOST_ATTRIBUTES = [\r\n 'oui-button',\r\n 'oui-ghost-button',\r\n 'oui-link-button',\r\n 'oui-icon-button',\r\n 'oui-icon-text-button',\r\n];\r\n\r\n/** Default color palette for round buttons (oui-fab and oui-mini-fab) */\r\nconst DEFAULT_COLOR = 'primary';\r\n\r\n// Boilerplate for applying mixins to OuiButton.\r\n/** @docs-private */\r\nexport class OuiButtonBase {\r\n constructor(public _elementRef: ElementRef, public _cdr: ChangeDetectorRef) {}\r\n}\r\n\r\nexport const OuiButtonMixinBase: CanDisableCtor &\r\n CanColorCtor &\r\n CanProgressCtor &\r\n typeof OuiButtonBase = mixinProgress(\r\n mixinColor(mixinDisabled(OuiButtonBase))\r\n);\r\n\r\n/**\r\n * Once Ui button.\r\n */\r\n@Component({\r\n // eslint-disable-next-line @angular-eslint/component-selector\r\n selector: `button[oui-button], button[oui-ghost-button], button[oui-link-button], button[oui-icon-button],\r\n button[oui-icon-text-button]`,\r\n exportAs: 'ouiButton',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n '[disabled]': 'disabled || null',\r\n },\r\n templateUrl: 'button.html',\r\n styleUrls: ['button.scss'],\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['disabled', 'color', 'progress', 'tabIndex'],\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class OuiButton\r\n extends OuiButtonMixinBase\r\n implements OnDestroy, CanDisable, CanColor, CanProgress\r\n{\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n constructor(\r\n protected elementRef: ElementRef,\r\n private _focusMonitor: FocusMonitor,\r\n public _cdr: ChangeDetectorRef,\r\n private _ngZone: NgZone\r\n ) {\r\n super(elementRef, _cdr);\r\n this.addClass();\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this.elementRef, true)\r\n .subscribe(() =>\r\n this._ngZone.run(() => {\r\n this._cdr.markForCheck();\r\n })\r\n );\r\n }\r\n\r\n protected addClass() {\r\n for (const attr of BUTTON_HOST_ATTRIBUTES) {\r\n if (this.hasHostAttributes(attr)) {\r\n (this.elementRef.nativeElement as HTMLElement).classList.add(attr);\r\n }\r\n }\r\n if (!this.color) {\r\n this.color = DEFAULT_COLOR;\r\n }\r\n }\r\n\r\n ngOnDestroy() {\r\n this._focusMonitor.stopMonitoring(this.elementRef);\r\n this._monitorSubscription.unsubscribe();\r\n }\r\n\r\n /** Focuses the button. */\r\n focus(): void {\r\n this.getHostElement().focus();\r\n }\r\n\r\n getHostElement() {\r\n return this.elementRef.nativeElement;\r\n }\r\n /** Gets whether the button has one of the given attributes. */\r\n hasHostAttributes(...attributes: string[]) {\r\n return attributes.some((attribute) =>\r\n this.getHostElement().hasAttribute(attribute)\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Once UI anchor.\r\n */\r\n@Component({\r\n // eslint-disable-next-line @angular-eslint/component-selector\r\n selector: `a[oui-button], a[oui-ghost-button], a[oui-link-button], a[oui-icon-button],\r\n a[oui-icon-text-button]`,\r\n exportAs: 'ouiButton, ouiAnchor',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n '[attr.tabindex]': 'disabled ? -1 : (tabIndex || 0)',\r\n '[attr.disabled]': 'disabled || null',\r\n '[attr.aria-disabled]': 'disabled.toString()',\r\n '(click)': '_haltDisabledEvents($event)',\r\n },\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['disabled', 'color'],\r\n templateUrl: 'button.html',\r\n styleUrls: ['button.scss'],\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class OuiAnchor extends OuiButton {\r\n /** Tabindex of the button. */\r\n @Input() tabIndex: number;\r\n constructor(\r\n elementRef: ElementRef,\r\n focusMonitor: FocusMonitor,\r\n _cdr: ChangeDetectorRef,\r\n _ngZone: NgZone\r\n ) {\r\n super(elementRef, focusMonitor, _cdr, _ngZone);\r\n }\r\n\r\n _haltDisabledEvents(event: Event) {\r\n // A disabled button shouldn't apply any actions\r\n if (this.disabled) {\r\n event.preventDefault();\r\n event.stopImmediatePropagation();\r\n }\r\n }\r\n}\r\n", + "sourceCode": "import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n ViewEncapsulation,\n OnDestroy,\n ChangeDetectorRef,\n NgZone,\n Input,\n} from '@angular/core';\nimport {\n CanDisable,\n CanColor,\n CanDisableCtor,\n CanColorCtor,\n mixinColor,\n mixinDisabled,\n} from '../core';\n\nimport { CanProgress, CanProgressCtor, mixinProgress } from './progress';\nimport { FocusMonitor } from '@angular/cdk/a11y';\nimport { Subscription } from 'rxjs';\n/**\n * List of classes to add to Button instances based on host attributes to\n * style as different variants.\n */\nconst BUTTON_HOST_ATTRIBUTES = [\n 'oui-button',\n 'oui-ghost-button',\n 'oui-link-button',\n 'oui-icon-button',\n 'oui-icon-text-button',\n];\n\n/** Default color palette for round buttons (oui-fab and oui-mini-fab) */\nconst DEFAULT_COLOR = 'primary';\n\n// Boilerplate for applying mixins to OuiButton.\n/** @docs-private */\nexport class OuiButtonBase {\n constructor(public _elementRef: ElementRef, public _cdr: ChangeDetectorRef) {}\n}\n\nexport const OuiButtonMixinBase: CanDisableCtor &\n CanColorCtor &\n CanProgressCtor &\n typeof OuiButtonBase = mixinProgress(\n mixinColor(mixinDisabled(OuiButtonBase))\n);\n\n/**\n * Once Ui button.\n */\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: `button[oui-button], button[oui-ghost-button], button[oui-link-button], button[oui-icon-button],\n button[oui-icon-text-button]`,\n exportAs: 'ouiButton',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n '[disabled]': 'disabled || null',\n },\n templateUrl: 'button.html',\n styleUrls: ['button.scss'],\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\n inputs: ['disabled', 'color', 'progress', 'tabIndex'],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class OuiButton\n extends OuiButtonMixinBase\n implements OnDestroy, CanDisable, CanColor, CanProgress\n{\n private _monitorSubscription: Subscription = Subscription.EMPTY;\n constructor(\n protected elementRef: ElementRef,\n private _focusMonitor: FocusMonitor,\n public _cdr: ChangeDetectorRef,\n private _ngZone: NgZone\n ) {\n super(elementRef, _cdr);\n this.addClass();\n this._monitorSubscription = this._focusMonitor\n .monitor(this.elementRef, true)\n .subscribe(() =>\n this._ngZone.run(() => {\n this._cdr.markForCheck();\n })\n );\n }\n\n protected addClass() {\n for (const attr of BUTTON_HOST_ATTRIBUTES) {\n if (this.hasHostAttributes(attr)) {\n (this.elementRef.nativeElement as HTMLElement).classList.add(attr);\n }\n }\n if (!this.color) {\n this.color = DEFAULT_COLOR;\n }\n }\n\n ngOnDestroy() {\n this._focusMonitor.stopMonitoring(this.elementRef);\n this._monitorSubscription.unsubscribe();\n }\n\n /** Focuses the button. */\n focus(): void {\n this.getHostElement().focus();\n }\n\n getHostElement() {\n return this.elementRef.nativeElement;\n }\n /** Gets whether the button has one of the given attributes. */\n hasHostAttributes(...attributes: string[]) {\n return attributes.some((attribute) =>\n this.getHostElement().hasAttribute(attribute)\n );\n }\n}\n\n/**\n * Once UI anchor.\n */\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: `a[oui-button], a[oui-ghost-button], a[oui-link-button], a[oui-icon-button],\n a[oui-icon-text-button]`,\n exportAs: 'ouiButton, ouiAnchor',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n '[attr.tabindex]': 'disabled ? -1 : (tabIndex || 0)',\n '[attr.disabled]': 'disabled || null',\n '[attr.aria-disabled]': 'disabled.toString()',\n '(click)': '_haltDisabledEvents($event)',\n },\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\n inputs: ['disabled', 'color'],\n templateUrl: 'button.html',\n styleUrls: ['button.scss'],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class OuiAnchor extends OuiButton {\n /** Tabindex of the button. */\n @Input() tabIndex: number;\n constructor(\n elementRef: ElementRef,\n focusMonitor: FocusMonitor,\n _cdr: ChangeDetectorRef,\n _ngZone: NgZone\n ) {\n super(elementRef, focusMonitor, _cdr, _ngZone);\n }\n\n _haltDisabledEvents(event: Event) {\n // A disabled button shouldn't apply any actions\n if (this.disabled) {\n event.preventDefault();\n event.stopImmediatePropagation();\n }\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": "@import 'button-theme';\r\n@import 'button-icon';\r\n@include oui-icon-text();\r\n\r\n$border-radius: 100px;\r\n$font-size: 14px;\r\n$letter-spacing: 0.2px;\r\n$padding: 6px 20px;\r\n$remove-padding: 0px;\r\n$progress-icon-padding: 0px 0px 0px 23px;\r\n$line-height: 19px;\r\n$min-height: 35px;\r\n$min-width: 70px;\r\n$font-normal: normal;\r\n$font-semi-bold: 600;\r\n$font-bold: 700;\r\n$cursor: pointer;\r\n$disabled-cursor: default;\r\n$ghost-button-background: #fff;\r\n$border: 1px solid transparent;\r\n$text-decoration: none;\r\n$text-align-center: center;\r\n$text-align-left: left;\r\n$green-tick-icon: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTgiIGhlaWdodD0iMTMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTcuMzUgMTAuMTVMMTYuNTgyLjk0OGwxLjQxNCAxLjQxNEw3LjM0NiAxMi45OCA1LjkzIDExLjU2NWwuMDAyLS4wMDItNC45MjItNC45NEwyLjQyNiA1LjIxbDQuOTI0IDQuOTQyeiIgZmlsbD0iIzAwOUI0OCIgZmlsbC1ydWxlPSJldmVub2RkIi8+PC9zdmc+')\r\n left center no-repeat;\r\n@mixin oui-button-core() {\r\n .oui-button,\r\n .oui-ghost-button,\r\n .oui-icon-text-button,\r\n .oui-link-button,\r\n .oui-progress-button,\r\n .oui-progress-ghost-button,\r\n .oui-progress-link-button {\r\n @include oui-button-reset();\r\n @include oui-button-base();\r\n }\r\n\r\n .oui-button {\r\n @include oui-progress-button-stages();\r\n &.cdk-keyboard-focused {\r\n text-decoration: underline;\r\n }\r\n }\r\n .oui-link-button {\r\n @include oui-progress-link-button-stages();\r\n }\r\n .oui-ghost-button {\r\n background: $ghost-button-background;\r\n @include oui-progress-ghost-button-stages();\r\n &.cdk-keyboard-focused {\r\n text-decoration: underline;\r\n }\r\n }\r\n .oui-icon-button {\r\n @include oui-button-reset();\r\n }\r\n}\r\n.oui-icon-button {\r\n &.cdk-keyboard-focused {\r\n background: rgba(200, 200, 200, 0.4);\r\n border-radius: $oui-icon-spacing;\r\n transition: opacity 0.2s cubic-bezier(0.35, 0, 0.25, 1),\r\n background-color 0.2s cubic-bezier(0.35, 0, 0.25, 1);\r\n }\r\n}\r\n/*common properties in all buttons*/\r\n@mixin oui-button-base {\r\n font-size: $font-size;\r\n border-radius: $border-radius;\r\n letter-spacing: $letter-spacing;\r\n padding: $padding;\r\n line-height: $line-height;\r\n min-height: $min-height;\r\n min-width: $min-width;\r\n font-weight: $font-semi-bold;\r\n cursor: $cursor;\r\n border: $border;\r\n background: transparent;\r\n text-decoration: $text-decoration;\r\n text-align: $text-align-center;\r\n &[disabled] {\r\n cursor: $disabled-cursor;\r\n }\r\n}\r\n/*reset the browser default properties*/\r\n@mixin oui-button-reset {\r\n border: 0 none;\r\n background: none;\r\n font-family: 'Open Sans', Helvetica, Arial, sans-serif;\r\n &:focus {\r\n outline: 0 none;\r\n }\r\n // The `outline: none` from above works on all browsers, however Firefox also\r\n // adds a special `focus-inner` which we have to disable explicitly. See:\r\n // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Firefox\r\n &::-moz-focus-inner {\r\n border: 0;\r\n }\r\n}\r\n\r\n.oui-link-button {\r\n padding: 6px 0px 6px 0px !important;\r\n min-width: auto !important;\r\n font-weight: $font-normal !important;\r\n &:hover {\r\n text-decoration: underline;\r\n }\r\n &[disabled]:hover {\r\n text-decoration: none;\r\n }\r\n &:focus {\r\n outline: 0 none;\r\n }\r\n &.cdk-keyboard-focused {\r\n outline: solid 1px #006bb1 !important;\r\n text-decoration: underline;\r\n border-radius: 5px;\r\n }\r\n}\r\n\r\n.oui-ghost-button {\r\n &[disabled],\r\n &.oui-stage-progress {\r\n background: transparent !important;\r\n cursor: $disabled-cursor !important;\r\n }\r\n}\r\n\r\n@mixin oui-progress-button-stages {\r\n &.oui-stage-progress {\r\n background: transparent !important;\r\n text-align: $text-align-left !important;\r\n padding: $remove-padding !important;\r\n font-weight: $font-bold !important;\r\n cursor: $disabled-cursor !important;\r\n }\r\n &.oui-stage-done {\r\n background: $green-tick-icon !important;\r\n text-align: $text-align-left !important;\r\n padding: $progress-icon-padding !important;\r\n font-weight: $font-bold !important;\r\n cursor: $disabled-cursor !important;\r\n }\r\n}\r\n@mixin oui-progress-link-button-stages {\r\n &.oui-stage-progress {\r\n background: transparent !important;\r\n text-align: $text-align-left !important;\r\n padding: $remove-padding !important;\r\n font-weight: $font-bold !important;\r\n cursor: $disabled-cursor !important;\r\n text-decoration: $text-decoration !important;\r\n }\r\n &.oui-stage-done {\r\n background: $green-tick-icon !important;\r\n text-align: $text-align-left !important;\r\n padding: $progress-icon-padding !important;\r\n font-weight: $font-bold !important;\r\n cursor: $disabled-cursor !important;\r\n text-decoration: $text-decoration !important;\r\n }\r\n}\r\n@mixin oui-progress-ghost-button-stages {\r\n &.oui-stage-progress {\r\n background: transparent !important;\r\n text-align: $text-align-left !important;\r\n padding: $remove-padding !important;\r\n font-weight: $font-bold !important;\r\n cursor: $disabled-cursor !important;\r\n border: $border !important;\r\n }\r\n &.oui-stage-done {\r\n background: $green-tick-icon !important;\r\n text-align: $text-align-left !important;\r\n padding: $progress-icon-padding !important;\r\n font-weight: $font-bold !important;\r\n cursor: $disabled-cursor !important;\r\n border: $border !important;\r\n }\r\n}\r\n\r\n@include oui-button-core();\r\n", + "data": "@import 'button-theme';\n@import 'button-icon';\n@include oui-icon-text();\n\n$border-radius: 100px;\n$font-size: 14px;\n$letter-spacing: 0.2px;\n$padding: 6px 20px;\n$remove-padding: 0px;\n$progress-icon-padding: 0px 0px 0px 23px;\n$line-height: 19px;\n$min-height: 35px;\n$min-width: 70px;\n$font-normal: normal;\n$font-semi-bold: 600;\n$font-bold: 700;\n$cursor: pointer;\n$disabled-cursor: default;\n$ghost-button-background: #fff;\n$border: 1px solid transparent;\n$text-decoration: none;\n$text-align-center: center;\n$text-align-left: left;\n$green-tick-icon: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTgiIGhlaWdodD0iMTMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTcuMzUgMTAuMTVMMTYuNTgyLjk0OGwxLjQxNCAxLjQxNEw3LjM0NiAxMi45OCA1LjkzIDExLjU2NWwuMDAyLS4wMDItNC45MjItNC45NEwyLjQyNiA1LjIxbDQuOTI0IDQuOTQyeiIgZmlsbD0iIzAwOUI0OCIgZmlsbC1ydWxlPSJldmVub2RkIi8+PC9zdmc+')\n left center no-repeat;\n@mixin oui-button-core() {\n .oui-button,\n .oui-ghost-button,\n .oui-icon-text-button,\n .oui-link-button,\n .oui-progress-button,\n .oui-progress-ghost-button,\n .oui-progress-link-button {\n @include oui-button-reset();\n @include oui-button-base();\n }\n\n .oui-button {\n @include oui-progress-button-stages();\n &.cdk-keyboard-focused {\n text-decoration: underline;\n }\n }\n .oui-link-button {\n @include oui-progress-link-button-stages();\n }\n .oui-ghost-button {\n background: $ghost-button-background;\n @include oui-progress-ghost-button-stages();\n &.cdk-keyboard-focused {\n text-decoration: underline;\n }\n }\n .oui-icon-button {\n @include oui-button-reset();\n }\n}\n.oui-icon-button {\n &.cdk-keyboard-focused,\n &.cdk-program-focused {\n background: rgba(200, 200, 200, 0.4);\n border-radius: $oui-icon-spacing;\n transition: opacity 0.2s cubic-bezier(0.35, 0, 0.25, 1),\n background-color 0.2s cubic-bezier(0.35, 0, 0.25, 1);\n }\n}\n/*common properties in all buttons*/\n@mixin oui-button-base {\n font-size: $font-size;\n border-radius: $border-radius;\n letter-spacing: $letter-spacing;\n padding: $padding;\n line-height: $line-height;\n min-height: $min-height;\n min-width: $min-width;\n font-weight: $font-semi-bold;\n cursor: $cursor;\n border: $border;\n background: transparent;\n text-decoration: $text-decoration;\n text-align: $text-align-center;\n &[disabled] {\n cursor: $disabled-cursor;\n }\n}\n/*reset the browser default properties*/\n@mixin oui-button-reset {\n border: 0 none;\n background: none;\n font-family: 'Open Sans', Helvetica, Arial, sans-serif;\n &:focus {\n outline: 0 none;\n }\n // The `outline: none` from above works on all browsers, however Firefox also\n // adds a special `focus-inner` which we have to disable explicitly. See:\n // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Firefox\n &::-moz-focus-inner {\n border: 0;\n }\n}\n\n.oui-link-button {\n padding: 6px 0px 6px 0px !important;\n min-width: auto !important;\n font-weight: $font-normal !important;\n &:hover {\n text-decoration: underline;\n }\n &[disabled]:hover {\n text-decoration: none;\n }\n &:focus {\n outline: 0 none;\n }\n &.cdk-keyboard-focused {\n outline: solid 1px #006bb1 !important;\n text-decoration: underline;\n border-radius: 5px;\n }\n}\n\n.oui-ghost-button {\n &[disabled],\n &.oui-stage-progress {\n background: transparent !important;\n cursor: $disabled-cursor !important;\n }\n}\n\n@mixin oui-progress-button-stages {\n &.oui-stage-progress {\n background: transparent !important;\n text-align: $text-align-left !important;\n padding: $remove-padding !important;\n font-weight: $font-bold !important;\n cursor: $disabled-cursor !important;\n }\n &.oui-stage-done {\n background: $green-tick-icon !important;\n text-align: $text-align-left !important;\n padding: $progress-icon-padding !important;\n font-weight: $font-bold !important;\n cursor: $disabled-cursor !important;\n }\n}\n@mixin oui-progress-link-button-stages {\n &.oui-stage-progress {\n background: transparent !important;\n text-align: $text-align-left !important;\n padding: $remove-padding !important;\n font-weight: $font-bold !important;\n cursor: $disabled-cursor !important;\n text-decoration: $text-decoration !important;\n }\n &.oui-stage-done {\n background: $green-tick-icon !important;\n text-align: $text-align-left !important;\n padding: $progress-icon-padding !important;\n font-weight: $font-bold !important;\n cursor: $disabled-cursor !important;\n text-decoration: $text-decoration !important;\n }\n}\n@mixin oui-progress-ghost-button-stages {\n &.oui-stage-progress {\n background: transparent !important;\n text-align: $text-align-left !important;\n padding: $remove-padding !important;\n font-weight: $font-bold !important;\n cursor: $disabled-cursor !important;\n border: $border !important;\n }\n &.oui-stage-done {\n background: $green-tick-icon !important;\n text-align: $text-align-left !important;\n padding: $progress-icon-padding !important;\n font-weight: $font-bold !important;\n cursor: $disabled-cursor !important;\n border: $border !important;\n }\n}\n\n@include oui-button-core();\n", "styleUrl": "button.scss" } ], @@ -20224,11 +30082,11 @@ ] }, "extends": "OuiButton", - "templateData": "\r\n" + "templateData": "\n" }, { "name": "OuiAutocomplete", - "id": "component-OuiAutocomplete-c607b652cd94fad28c222b66bfeb805d55ac0fd90c4c02d765153588a8bfd4e575d459422da8db39a96be2938e02de82f3b62ff100d3d4263353dc3bab51be45", + "id": "component-OuiAutocomplete-563a5b390d5e1a49f6ed1f1630db0496ed667cecdaa7432f6eae6c51f0d0792f51cc22606b8bb1b88c453a1837d7dd3a58c2f0e08518a7a0dee4ed6e1831b625", "file": "ui/src/components/autocomplete/autocomplete.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -20271,7 +30129,7 @@ "deprecationMessage": "", "rawdescription": "\n\nTakes classes set on the host oui-autocomplete element and applies them to the panel\ninside the overlay container to allow for easy styling.\n", "description": "

Takes classes set on the host oui-autocomplete element and applies them to the panel\ninside the overlay container to allow for easy styling.

\n", - "line": 148, + "line": 149, "type": "string", "decorators": [] }, @@ -20305,7 +30163,7 @@ "deprecationMessage": "", "rawdescription": "\nEvent that is emitted when the autocomplete panel is closed.", "description": "

Event that is emitted when the autocomplete panel is closed.

\n", - "line": 141, + "line": 142, "type": "EventEmitter" }, { @@ -20315,7 +30173,7 @@ "deprecationMessage": "", "rawdescription": "\nEvent that is emitted when the autocomplete panel is opened.", "description": "

Event that is emitted when the autocomplete panel is opened.

\n", - "line": 137, + "line": 138, "type": "EventEmitter" }, { @@ -20350,7 +30208,7 @@ "type": "literal type", "optional": false, "description": "", - "line": 156 + "line": 157 }, { "name": "_isOpen", @@ -20380,7 +30238,7 @@ "type": "string", "optional": false, "description": "

Unique ID to be used by autocomplete trigger's "aria-owns" property.

\n", - "line": 160, + "line": 161, "rawdescription": "\nUnique ID to be used by autocomplete trigger's \"aria-owns\" property." }, { @@ -20400,15 +30258,15 @@ ], "jsdoctags": [ { - "pos": 3213, - "end": 3227, + "pos": 3111, + "end": 3125, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, "kind": 325, "tagName": { - "pos": 3214, - "end": 3226, + "pos": 3112, + "end": 3124, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -20436,15 +30294,15 @@ ], "jsdoctags": [ { - "pos": 3099, - "end": 3113, + "pos": 3001, + "end": 3015, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, "kind": 325, "tagName": { - "pos": 3100, - "end": 3112, + "pos": 3002, + "end": 3014, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -20499,15 +30357,15 @@ ], "jsdoctags": [ { - "pos": 2899, - "end": 2913, + "pos": 2809, + "end": 2823, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, "kind": 325, "tagName": { - "pos": 2900, - "end": 2912, + "pos": 2810, + "end": 2822, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -20533,7 +30391,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 203, + "line": 204, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nEmits the `select` event.", @@ -20556,7 +30414,7 @@ "optional": false, "returnType": "number", "typeParameters": [], - "line": 190, + "line": 191, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nReturns the panel's scrollTop.", @@ -20575,7 +30433,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 183, + "line": 184, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nSets the panel scrollTop. This allows us to manually scroll to display options\nabove or below the fold, as they are not actually being focused when active.\n", @@ -20598,7 +30456,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 195, + "line": 196, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nPanel should hide itself when the option list is empty.", @@ -20610,7 +30468,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 171, + "line": 172, "deprecated": false, "deprecationMessage": "" } @@ -20622,11 +30480,11 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import { ActiveDescendantKeyManager } from '@angular/cdk/a11y';\r\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\r\nimport {\r\n AfterContentInit,\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n ContentChildren,\r\n ElementRef,\r\n EventEmitter,\r\n Inject,\r\n InjectionToken,\r\n Input,\r\n Output,\r\n QueryList,\r\n TemplateRef,\r\n ViewChild,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\n\r\nimport { OUI_OPTION_PARENT_COMPONENT, OuiOption } from '../core/option/option';\r\nimport { OuiOptgroup } from '../core/option/optgroup';\r\n\r\n/**\r\n * Autocomplete IDs need to be unique across components, so this counter exists outside of\r\n * the component definition.\r\n */\r\nlet _uniqueAutocompleteIdCounter = 0;\r\n\r\n/** Event object that is emitted when an autocomplete option is selected. */\r\nexport class OuiAutocompleteSelectedEvent {\r\n constructor(\r\n /** Reference to the autocomplete panel that emitted the event. */\r\n public source: OuiAutocomplete,\r\n /** Option that was selected. */\r\n public option: OuiOption\r\n ) {}\r\n}\r\n\r\n/** Default `oui-autocomplete` options that can be overridden. */\r\nexport interface OuiAutocompleteDefaultOptions {\r\n /** Whether the first option should be highlighted when an autocomplete panel is opened. */\r\n autoActiveFirstOption?: boolean;\r\n}\r\n\r\n/** Injection token to be used to override the default options for `oui-autocomplete`. */\r\nexport const OUI_AUTOCOMPLETE_DEFAULT_OPTIONS =\r\n new InjectionToken(\r\n 'oui-autocomplete-default-options',\r\n {\r\n providedIn: 'root',\r\n factory: OUI_AUTOCOMPLETE_DEFAULT_OPTIONS_FACTORY,\r\n }\r\n );\r\n\r\n/** @docs-private */\r\nexport function OUI_AUTOCOMPLETE_DEFAULT_OPTIONS_FACTORY(): OuiAutocompleteDefaultOptions {\r\n return { autoActiveFirstOption: false };\r\n}\r\n\r\n@Component({\r\n selector: 'oui-autocomplete',\r\n templateUrl: 'autocomplete.html',\r\n styleUrls: ['autocomplete.scss'],\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n exportAs: 'ouiAutocomplete',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-autocomplete',\r\n },\r\n providers: [\r\n { provide: OUI_OPTION_PARENT_COMPONENT, useExisting: OuiAutocomplete },\r\n ],\r\n})\r\nexport class OuiAutocomplete implements AfterContentInit {\r\n /** Manages active item in option list based on key events. */\r\n _keyManager: ActiveDescendantKeyManager;\r\n\r\n /** Whether the autocomplete panel should be visible, depending on option length. */\r\n // eslint-disable-next-line @typescript-eslint/no-inferrable-types\r\n showPanel: boolean = false;\r\n\r\n /** Whether the autocomplete panel is open. */\r\n get isOpen(): boolean {\r\n return this._isOpen && this.showPanel;\r\n }\r\n // eslint-disable-next-line @typescript-eslint/no-inferrable-types\r\n _isOpen: boolean = false;\r\n\r\n /** @docs-private */\r\n @ViewChild(TemplateRef)\r\n template: TemplateRef;\r\n\r\n /** Element for the panel containing the autocomplete options. */\r\n @ViewChild('panel')\r\n panel: ElementRef;\r\n\r\n /** @docs-private */\r\n @ContentChildren(OuiOption, { descendants: true })\r\n options: QueryList;\r\n\r\n /** @docs-private */\r\n @ContentChildren(OuiOptgroup)\r\n optionGroups: QueryList;\r\n\r\n /** Function that maps an option's control value to its display value in the trigger. */\r\n @Input()\r\n displayWith: ((value: any) => string) | null = null;\r\n\r\n /**\r\n * Whether the first option should be highlighted when the autocomplete panel is opened.\r\n * Can be configured globally through the `OUI_AUTOCOMPLETE_DEFAULT_OPTIONS` token.\r\n */\r\n @Input()\r\n get autoActiveFirstOption(): boolean {\r\n return this._autoActiveFirstOption;\r\n }\r\n set autoActiveFirstOption(value: boolean) {\r\n this._autoActiveFirstOption = coerceBooleanProperty(value);\r\n }\r\n private _autoActiveFirstOption: boolean;\r\n\r\n /**\r\n * Specify the width of the autocomplete panel. Can be any CSS sizing value, otherwise it will\r\n * match the width of its host.\r\n */\r\n @Input()\r\n panelWidth: string | number;\r\n\r\n /** Event that is emitted whenever an option from the list is selected. */\r\n @Output()\r\n readonly optionSelected: EventEmitter = new EventEmitter();\r\n\r\n /** Event that is emitted when the autocomplete panel is opened. */\r\n @Output()\r\n readonly opened: EventEmitter = new EventEmitter();\r\n\r\n /** Event that is emitted when the autocomplete panel is closed. */\r\n @Output()\r\n readonly closed: EventEmitter = new EventEmitter();\r\n\r\n /**\r\n * Takes classes set on the host oui-autocomplete element and applies them to the panel\r\n * inside the overlay container to allow for easy styling.\r\n */\r\n @Input('class')\r\n set classList(value: string) {\r\n if (value && value.length) {\r\n value\r\n .split(' ')\r\n .forEach((className) => (this._classList[className.trim()] = true));\r\n this._elementRef.nativeElement.className = '';\r\n }\r\n }\r\n _classList: { [key: string]: boolean } = {};\r\n\r\n /** Unique ID to be used by autocomplete trigger's \"aria-owns\" property. */\r\n // eslint-disable-next-line @typescript-eslint/no-inferrable-types\r\n id: string = `oui-autocomplete-${_uniqueAutocompleteIdCounter++}`;\r\n\r\n constructor(\r\n private _changeDetectorRef: ChangeDetectorRef,\r\n private _elementRef: ElementRef,\r\n @Inject(OUI_AUTOCOMPLETE_DEFAULT_OPTIONS)\r\n defaults: OuiAutocompleteDefaultOptions\r\n ) {\r\n this._autoActiveFirstOption = !!defaults.autoActiveFirstOption;\r\n }\r\n\r\n ngAfterContentInit() {\r\n this._keyManager = new ActiveDescendantKeyManager(\r\n this.options\r\n ).withWrap();\r\n // Set the initial visibility state.\r\n this._setVisibility();\r\n }\r\n\r\n /**\r\n * Sets the panel scrollTop. This allows us to manually scroll to display options\r\n * above or below the fold, as they are not actually being focused when active.\r\n */\r\n _setScrollTop(scrollTop: number): void {\r\n if (this.panel) {\r\n this.panel.nativeElement.scrollTop = scrollTop;\r\n }\r\n }\r\n\r\n /** Returns the panel's scrollTop. */\r\n _getScrollTop(): number {\r\n return this.panel ? this.panel.nativeElement.scrollTop : 0;\r\n }\r\n\r\n /** Panel should hide itself when the option list is empty. */\r\n _setVisibility() {\r\n this.showPanel = !!this.options.length;\r\n this._classList['oui-autocomplete-visible'] = this.showPanel;\r\n this._classList['oui-autocomplete-hidden'] = !this.showPanel;\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n\r\n /** Emits the `select` event. */\r\n _emitSelectEvent(option: OuiOption): void {\r\n const event = new OuiAutocompleteSelectedEvent(this, option);\r\n this.optionSelected.emit(event);\r\n }\r\n}\r\n", + "sourceCode": "import { ActiveDescendantKeyManager } from '@angular/cdk/a11y';\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport {\n AfterContentInit,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n ContentChildren,\n ElementRef,\n EventEmitter,\n Inject,\n InjectionToken,\n Input,\n Output,\n QueryList,\n TemplateRef,\n ViewChild,\n ViewEncapsulation,\n} from '@angular/core';\n\nimport { OUI_OPTION_PARENT_COMPONENT, OuiOption } from '../core/option/option';\nimport { OuiOptgroup } from '../core/option/optgroup';\n\n/**\n * Autocomplete IDs need to be unique across components, so this counter exists outside of\n * the component definition.\n */\nlet _uniqueAutocompleteIdCounter = 0;\n\n/** Event object that is emitted when an autocomplete option is selected. */\nexport class OuiAutocompleteSelectedEvent {\n constructor(\n /** Reference to the autocomplete panel that emitted the event. */\n public source: OuiAutocomplete,\n /** Option that was selected. */\n public option: OuiOption\n ) {}\n}\n\n/** Default `oui-autocomplete` options that can be overridden. */\nexport interface OuiAutocompleteDefaultOptions {\n /** Whether the first option should be highlighted when an autocomplete panel is opened. */\n autoActiveFirstOption?: boolean;\n}\n\n/** Injection token to be used to override the default options for `oui-autocomplete`. */\nexport const OUI_AUTOCOMPLETE_DEFAULT_OPTIONS =\n new InjectionToken(\n 'oui-autocomplete-default-options',\n {\n providedIn: 'root',\n factory: OUI_AUTOCOMPLETE_DEFAULT_OPTIONS_FACTORY,\n }\n );\n\n/** @docs-private */\nexport function OUI_AUTOCOMPLETE_DEFAULT_OPTIONS_FACTORY(): OuiAutocompleteDefaultOptions {\n return { autoActiveFirstOption: false };\n}\n\n@Component({\n selector: 'oui-autocomplete',\n templateUrl: 'autocomplete.html',\n styleUrls: ['autocomplete.scss'],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n exportAs: 'ouiAutocomplete',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-autocomplete',\n },\n providers: [\n { provide: OUI_OPTION_PARENT_COMPONENT, useExisting: OuiAutocomplete },\n ],\n})\nexport class OuiAutocomplete implements AfterContentInit {\n /** Manages active item in option list based on key events. */\n _keyManager: ActiveDescendantKeyManager;\n\n /** Whether the autocomplete panel should be visible, depending on option length. */\n // eslint-disable-next-line @typescript-eslint/no-inferrable-types\n showPanel: boolean = false;\n\n /** Whether the autocomplete panel is open. */\n get isOpen(): boolean {\n return this._isOpen && this.showPanel;\n }\n // eslint-disable-next-line @typescript-eslint/no-inferrable-types\n _isOpen: boolean = false;\n\n /** @docs-private */\n @ViewChild(TemplateRef)\n template: TemplateRef;\n\n /** Element for the panel containing the autocomplete options. */\n @ViewChild('panel')\n panel: ElementRef;\n\n /** @docs-private */\n @ContentChildren(OuiOption, { descendants: true })\n options: QueryList;\n\n /** @docs-private */\n @ContentChildren(OuiOptgroup)\n optionGroups: QueryList;\n\n /** Function that maps an option's control value to its display value in the trigger. */\n @Input()\n displayWith: ((value: any) => string) | null = null;\n\n /**\n * Whether the first option should be highlighted when the autocomplete panel is opened.\n * Can be configured globally through the `OUI_AUTOCOMPLETE_DEFAULT_OPTIONS` token.\n */\n @Input()\n get autoActiveFirstOption(): boolean {\n return this._autoActiveFirstOption;\n }\n set autoActiveFirstOption(value: boolean) {\n this._autoActiveFirstOption = coerceBooleanProperty(value);\n }\n private _autoActiveFirstOption: boolean;\n\n /**\n * Specify the width of the autocomplete panel. Can be any CSS sizing value, otherwise it will\n * match the width of its host.\n */\n @Input()\n panelWidth: string | number;\n\n /** Event that is emitted whenever an option from the list is selected. */\n @Output()\n readonly optionSelected: EventEmitter =\n new EventEmitter();\n\n /** Event that is emitted when the autocomplete panel is opened. */\n @Output()\n readonly opened: EventEmitter = new EventEmitter();\n\n /** Event that is emitted when the autocomplete panel is closed. */\n @Output()\n readonly closed: EventEmitter = new EventEmitter();\n\n /**\n * Takes classes set on the host oui-autocomplete element and applies them to the panel\n * inside the overlay container to allow for easy styling.\n */\n @Input('class')\n set classList(value: string) {\n if (value && value.length) {\n value\n .split(' ')\n .forEach((className) => (this._classList[className.trim()] = true));\n this._elementRef.nativeElement.className = '';\n }\n }\n _classList: { [key: string]: boolean } = {};\n\n /** Unique ID to be used by autocomplete trigger's \"aria-owns\" property. */\n // eslint-disable-next-line @typescript-eslint/no-inferrable-types\n id: string = `oui-autocomplete-${_uniqueAutocompleteIdCounter++}`;\n\n constructor(\n private _changeDetectorRef: ChangeDetectorRef,\n private _elementRef: ElementRef,\n @Inject(OUI_AUTOCOMPLETE_DEFAULT_OPTIONS)\n defaults: OuiAutocompleteDefaultOptions\n ) {\n this._autoActiveFirstOption = !!defaults.autoActiveFirstOption;\n }\n\n ngAfterContentInit() {\n this._keyManager = new ActiveDescendantKeyManager(\n this.options\n ).withWrap();\n // Set the initial visibility state.\n this._setVisibility();\n }\n\n /**\n * Sets the panel scrollTop. This allows us to manually scroll to display options\n * above or below the fold, as they are not actually being focused when active.\n */\n _setScrollTop(scrollTop: number): void {\n if (this.panel) {\n this.panel.nativeElement.scrollTop = scrollTop;\n }\n }\n\n /** Returns the panel's scrollTop. */\n _getScrollTop(): number {\n return this.panel ? this.panel.nativeElement.scrollTop : 0;\n }\n\n /** Panel should hide itself when the option list is empty. */\n _setVisibility() {\n this.showPanel = !!this.options.length;\n this._classList['oui-autocomplete-visible'] = this.showPanel;\n this._classList['oui-autocomplete-hidden'] = !this.showPanel;\n this._changeDetectorRef.markForCheck();\n }\n\n /** Emits the `select` event. */\n _emitSelectEvent(option: OuiOption): void {\n const event = new OuiAutocompleteSelectedEvent(this, option);\n this.optionSelected.emit(event);\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": "/**\r\n * The max-height of the panel, currently matching oui-select value.\r\n */\r\n$oui-autocomplete-panel-max-height: 316px !default;\r\n$oui-autocomplete-panel-border-radius: 4px !default;\r\n\r\n.oui-autocomplete-panel {\r\n font-family: 'Open Sans', Helvetica, Arial, sans-serif;\r\n min-width: 112px;\r\n max-width: none;\r\n overflow: auto;\r\n -webkit-overflow-scrolling: touch;\r\n max-height: $oui-autocomplete-panel-max-height;\r\n position: relative;\r\n width: 100%;\r\n box-shadow: 0px 1px 3px 0px #4a4a4a;\r\n background: #fff;\r\n color: rgba(0, 0, 0, 0.87);\r\n &.oui-autocomplete-visible {\r\n visibility: visible;\r\n }\r\n\r\n &.oui-autocomplete-hidden {\r\n visibility: hidden;\r\n }\r\n\r\n .oui-autocomplete-panel-above & {\r\n border-radius: 0;\r\n }\r\n\r\n // We need to offset horizontal dividers by their height, because\r\n // they throw off the keyboard navigation inside the panel.\r\n .oui-divider-horizontal {\r\n margin-top: -1px;\r\n }\r\n\r\n .oui-option {\r\n white-space: nowrap;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n line-height: 48px;\r\n height: 38px;\r\n padding: 0 12px;\r\n text-align: left;\r\n text-decoration: none;\r\n max-width: 100%;\r\n position: relative;\r\n cursor: pointer;\r\n outline: 0;\r\n display: flex;\r\n flex-direction: row;\r\n box-sizing: border-box;\r\n align-items: center;\r\n -webkit-tap-highlight-color: transparent;\r\n font-size: 14px;\r\n color: #333;\r\n .oui-option-text {\r\n text-overflow: ellipsis;\r\n overflow: hidden;\r\n }\r\n }\r\n}\r\n\r\n.oui-option {\r\n color: rgba(0, 0, 0, 0.87);\r\n &:focus:not(.oui-option-disabled),\r\n &:hover:not(.oui-option-disabled),\r\n &.oui-selected:not(.oui-option-multiple):not(.oui-option-disabled) {\r\n background: #eee;\r\n }\r\n &.oui-active {\r\n background: #eee;\r\n color: #333;\r\n }\r\n &.oui-option-disabled {\r\n color: rgba(0, 0, 0, 0.38);\r\n }\r\n}\r\n\r\n.oui-autocomplete-panel {\r\n background: #fff;\r\n color: rgba(0, 0, 0, 0.87);\r\n border: 1px solid #c8c8c8;\r\n border-top: 0;\r\n padding: 10px 0;\r\n .oui-option.oui-selected:not(.oui-active):not(:hover) {\r\n background: #fff;\r\n &:not(.oui-option-disabled) {\r\n color: rgba(0, 0, 0, 0.87);\r\n }\r\n }\r\n .oui-optgroup-label {\r\n padding: 0 12px;\r\n color: #333;\r\n font-weight: bold;\r\n line-height: 20px;\r\n margin-bottom: 7px;\r\n display: inline-block;\r\n margin-top: 7px;\r\n font-size: 14px;\r\n }\r\n &.autocomplete-group {\r\n padding: 5px 0;\r\n }\r\n}\r\n.oui-autocomplete-panel-above {\r\n .oui-autocomplete-panel {\r\n border-top: 1px solid #c8c8c8;\r\n }\r\n}\r\n", + "data": "/**\n * The max-height of the panel, currently matching oui-select value.\n */\n$oui-autocomplete-panel-max-height: 316px !default;\n$oui-autocomplete-panel-border-radius: 4px !default;\n\n.oui-autocomplete-panel {\n font-family: 'Open Sans', Helvetica, Arial, sans-serif;\n min-width: 112px;\n max-width: none;\n overflow: auto;\n -webkit-overflow-scrolling: touch;\n max-height: $oui-autocomplete-panel-max-height;\n position: relative;\n width: 100%;\n box-shadow: 0px 1px 3px 0px #4a4a4a;\n background: #fff;\n color: rgba(0, 0, 0, 0.87);\n &.oui-autocomplete-visible {\n visibility: visible;\n }\n\n &.oui-autocomplete-hidden {\n visibility: hidden;\n }\n\n .oui-autocomplete-panel-above & {\n border-radius: 0;\n }\n\n // We need to offset horizontal dividers by their height, because\n // they throw off the keyboard navigation inside the panel.\n .oui-divider-horizontal {\n margin-top: -1px;\n }\n\n .oui-option {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n line-height: 48px;\n height: 38px;\n padding: 0 12px;\n text-align: left;\n text-decoration: none;\n max-width: 100%;\n position: relative;\n cursor: pointer;\n outline: 0;\n display: flex;\n flex-direction: row;\n box-sizing: border-box;\n align-items: center;\n -webkit-tap-highlight-color: transparent;\n font-size: 14px;\n color: #333;\n .oui-option-text {\n text-overflow: ellipsis;\n overflow: hidden;\n }\n }\n}\n\n.oui-option {\n color: rgba(0, 0, 0, 0.87);\n &:focus:not(.oui-option-disabled),\n &:hover:not(.oui-option-disabled),\n &.oui-selected:not(.oui-option-multiple):not(.oui-option-disabled) {\n background: #eee;\n }\n &.oui-active {\n background: #eee;\n color: #333;\n }\n &.oui-option-disabled {\n color: rgba(0, 0, 0, 0.38);\n }\n}\n\n.oui-autocomplete-panel {\n background: #fff;\n color: rgba(0, 0, 0, 0.87);\n border: 1px solid #c8c8c8;\n border-top: 0;\n padding: 10px 0;\n .oui-option.oui-selected:not(.oui-active):not(:hover) {\n background: #fff;\n &:not(.oui-option-disabled) {\n color: rgba(0, 0, 0, 0.87);\n }\n }\n .oui-optgroup-label {\n padding: 0 12px;\n color: #333;\n font-weight: bold;\n line-height: 20px;\n margin-bottom: 7px;\n display: inline-block;\n margin-top: 7px;\n font-size: 14px;\n }\n &.autocomplete-group {\n padding: 5px 0;\n }\n}\n.oui-autocomplete-panel-above {\n .oui-autocomplete-panel {\n border-top: 1px solid #c8c8c8;\n }\n}\n", "styleUrl": "autocomplete.scss" } ], @@ -20656,7 +30514,7 @@ "deprecationMessage": "" } ], - "line": 160, + "line": 161, "jsdoctags": [ { "name": "_changeDetectorRef", @@ -20756,7 +30614,7 @@ } ], "returnType": "void", - "line": 148, + "line": 149, "rawdescription": "\n\nTakes classes set on the host oui-autocomplete element and applies them to the panel\ninside the overlay container to allow for easy styling.\n", "description": "

Takes classes set on the host oui-autocomplete element and applies them to the panel\ninside the overlay container to allow for easy styling.

\n", "jsdoctags": [ @@ -20773,11 +30631,11 @@ } } }, - "templateData": "\r\n \r\n \r\n \r\n\r\n" + "templateData": "\n \n \n \n\n" }, { "name": "OuiAutocompleteGroupStorybook", - "id": "component-OuiAutocompleteGroupStorybook-21c965928e1d23d24394a8fd423e0a89093c844653536a90ebfd5fd510b98151019b2bd3a01049fc7c27d2ec4fae237b3858db7453f5d495547274b26da23b37", + "id": "component-OuiAutocompleteGroupStorybook-262c0b4fa18a2e94de5cc158226c2e5fcf96d15646485bfb03de1e640a925df3a241edfc746c16db21e03366517543083bcb228a82f8a5cf66c956fa813e5dec", "file": "ui/src/stories/autocomplete/autocomplete.component.ts", "encapsulation": [], "entryComponents": [], @@ -20795,7 +30653,7 @@ "name": "disabled", "deprecated": false, "deprecationMessage": "", - "line": 107, + "line": 111, "type": "boolean", "decorators": [] }, @@ -20803,7 +30661,7 @@ "name": "stateGroups", "deprecated": false, "deprecationMessage": "", - "line": 105, + "line": 109, "type": "StateGroup[]", "decorators": [] } @@ -20812,13 +30670,13 @@ "propertiesClass": [ { "name": "stateForm", - "defaultValue": "this.fb.group({\r\n stateGroup: '',\r\n })", + "defaultValue": "this.fb.group({\n stateGroup: '',\n })", "deprecated": false, "deprecationMessage": "", "type": "UntypedFormGroup", "optional": false, "description": "", - "line": 114 + "line": 118 }, { "name": "stateGroupOptions", @@ -20827,7 +30685,7 @@ "type": "Observable", "optional": false, "description": "", - "line": 117 + "line": 121 } ], "methodsClass": [ @@ -20844,7 +30702,7 @@ "optional": false, "returnType": "StateGroup[]", "typeParameters": [], - "line": 130, + "line": 134, "deprecated": false, "deprecationMessage": "", "modifierKind": [ @@ -20868,7 +30726,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 120, + "line": 124, "deprecated": false, "deprecationMessage": "" } @@ -20880,7 +30738,7 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import { Input, Component, OnInit } from '@angular/core';\r\nimport { Observable } from 'rxjs';\r\nimport { UntypedFormControl, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';\r\nimport { startWith, map } from 'rxjs/operators';\r\n\r\nexport interface StateGroup {\r\n letter: string;\r\n names: string[];\r\n}\r\nexport const _filter = (opt: string[], value: string): string[] => {\r\n const filterValue = value.toLowerCase();\r\n\r\n return opt.filter((item) => item.toLowerCase().indexOf(filterValue) === 0);\r\n};\r\n\r\n@Component({\r\n selector: 'oui-autocomplete-storybook',\r\n template: `\r\n \r\n \r\n \r\n \r\n \r\n {{ option }}\r\n \r\n \r\n `,\r\n})\r\nexport class OuiAutocompleteStorybook implements OnInit {\r\n filteredOptions: Observable;\r\n myControl = new UntypedFormControl();\r\n @Input() options: any[];\r\n @Input()\r\n set disabled(value: boolean) {\r\n if (value) {\r\n this.myControl.disable();\r\n } else {\r\n this.myControl.enable();\r\n }\r\n }\r\n ngOnInit() {\r\n this.filteredOptions = this.myControl.valueChanges.pipe(\r\n startWith(''),\r\n map((value) => (typeof value === 'string' ? value : value)),\r\n map((option) => (option ? this._filter(option) : this.options.slice()))\r\n );\r\n }\r\n private _filter(option): string[] {\r\n const filterValue = option.toLowerCase();\r\n\r\n return this.options.filter(\r\n // eslint-disable-next-line no-shadow\r\n (option) => option.toLowerCase().indexOf(filterValue) === 0\r\n );\r\n }\r\n}\r\n\r\n@Component({\r\n selector: 'oui-autocomplete-group-storybook',\r\n template: `\r\n \r\n \r\n \r\n \r\n \r\n {{ name }}\r\n \r\n \r\n \r\n \r\n `,\r\n})\r\nexport class OuiAutocompleteGroupStorybook implements OnInit {\r\n @Input() stateGroups: StateGroup[];\r\n @Input()\r\n set disabled(value: boolean) {\r\n if (value) {\r\n this.stateForm.get('stateGroup')!.disable();\r\n } else {\r\n this.stateForm.get('stateGroup')!.enable();\r\n }\r\n }\r\n stateForm: UntypedFormGroup = this.fb.group({\r\n stateGroup: '',\r\n });\r\n stateGroupOptions: Observable;\r\n\r\n constructor(private fb: UntypedFormBuilder) {}\r\n ngOnInit() {\r\n console.log(this.stateGroups);\r\n this.stateGroupOptions = this.stateForm\r\n .get('stateGroup')!\r\n .valueChanges.pipe(\r\n startWith(''),\r\n map((value: string) => this._filterGroup(value))\r\n );\r\n }\r\n\r\n private _filterGroup(value: string): StateGroup[] {\r\n if (value) {\r\n return this.stateGroups\r\n .map((group) => ({\r\n letter: group.letter,\r\n names: _filter(group.names, value),\r\n }))\r\n .filter((group) => group.names.length > 0);\r\n }\r\n\r\n return this.stateGroups;\r\n }\r\n}\r\n", + "sourceCode": "import { Input, Component, OnInit } from '@angular/core';\nimport { Observable } from 'rxjs';\nimport {\n UntypedFormControl,\n UntypedFormBuilder,\n UntypedFormGroup,\n} from '@angular/forms';\nimport { startWith, map } from 'rxjs/operators';\n\nexport interface StateGroup {\n letter: string;\n names: string[];\n}\nexport const _filter = (opt: string[], value: string): string[] => {\n const filterValue = value.toLowerCase();\n\n return opt.filter((item) => item.toLowerCase().indexOf(filterValue) === 0);\n};\n\n@Component({\n selector: 'oui-autocomplete-storybook',\n template: `\n \n \n \n \n \n {{ option }}\n \n \n `,\n})\nexport class OuiAutocompleteStorybook implements OnInit {\n filteredOptions: Observable;\n myControl = new UntypedFormControl();\n @Input() options: any[];\n @Input()\n set disabled(value: boolean) {\n if (value) {\n this.myControl.disable();\n } else {\n this.myControl.enable();\n }\n }\n ngOnInit() {\n this.filteredOptions = this.myControl.valueChanges.pipe(\n startWith(''),\n map((value) => (typeof value === 'string' ? value : value)),\n map((option) => (option ? this._filter(option) : this.options.slice()))\n );\n }\n private _filter(option): string[] {\n const filterValue = option.toLowerCase();\n\n return this.options.filter(\n // eslint-disable-next-line no-shadow\n (option) => option.toLowerCase().indexOf(filterValue) === 0\n );\n }\n}\n\n@Component({\n selector: 'oui-autocomplete-group-storybook',\n template: `\n \n \n \n \n \n {{ name }}\n \n \n \n \n `,\n})\nexport class OuiAutocompleteGroupStorybook implements OnInit {\n @Input() stateGroups: StateGroup[];\n @Input()\n set disabled(value: boolean) {\n if (value) {\n this.stateForm.get('stateGroup')!.disable();\n } else {\n this.stateForm.get('stateGroup')!.enable();\n }\n }\n stateForm: UntypedFormGroup = this.fb.group({\n stateGroup: '',\n });\n stateGroupOptions: Observable;\n\n constructor(private fb: UntypedFormBuilder) {}\n ngOnInit() {\n console.log(this.stateGroups);\n this.stateGroupOptions = this.stateForm\n .get('stateGroup')!\n .valueChanges.pipe(\n startWith(''),\n map((value: string) => this._filterGroup(value))\n );\n }\n\n private _filterGroup(value: string): StateGroup[] {\n if (value) {\n return this.stateGroups\n .map((group) => ({\n letter: group.letter,\n names: _filter(group.names, value),\n }))\n .filter((group) => group.names.length > 0);\n }\n\n return this.stateGroups;\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -20897,7 +30755,7 @@ "deprecationMessage": "" } ], - "line": 117, + "line": 121, "jsdoctags": [ { "name": "fb", @@ -20930,7 +30788,7 @@ } ], "returnType": "void", - "line": 107, + "line": 111, "jsdoctags": [ { "name": "value", @@ -20948,7 +30806,7 @@ }, { "name": "OuiAutocompleteStorybook", - "id": "component-OuiAutocompleteStorybook-21c965928e1d23d24394a8fd423e0a89093c844653536a90ebfd5fd510b98151019b2bd3a01049fc7c27d2ec4fae237b3858db7453f5d495547274b26da23b37", + "id": "component-OuiAutocompleteStorybook-262c0b4fa18a2e94de5cc158226c2e5fcf96d15646485bfb03de1e640a925df3a241edfc746c16db21e03366517543083bcb228a82f8a5cf66c956fa813e5dec", "file": "ui/src/stories/autocomplete/autocomplete.component.ts", "encapsulation": [], "entryComponents": [], @@ -20966,7 +30824,7 @@ "name": "disabled", "deprecated": false, "deprecationMessage": "", - "line": 46, + "line": 50, "type": "boolean", "decorators": [] }, @@ -20974,7 +30832,7 @@ "name": "options", "deprecated": false, "deprecationMessage": "", - "line": 44, + "line": 48, "type": "any[]", "decorators": [] } @@ -20988,7 +30846,7 @@ "type": "Observable", "optional": false, "description": "", - "line": 42 + "line": 46 }, { "name": "myControl", @@ -20998,7 +30856,7 @@ "type": "", "optional": false, "description": "", - "line": 43 + "line": 47 } ], "methodsClass": [ @@ -21015,7 +30873,7 @@ "optional": false, "returnType": "string[]", "typeParameters": [], - "line": 60, + "line": 64, "deprecated": false, "deprecationMessage": "", "modifierKind": [ @@ -21039,7 +30897,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 53, + "line": 57, "deprecated": false, "deprecationMessage": "" } @@ -21051,7 +30909,7 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import { Input, Component, OnInit } from '@angular/core';\r\nimport { Observable } from 'rxjs';\r\nimport { UntypedFormControl, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';\r\nimport { startWith, map } from 'rxjs/operators';\r\n\r\nexport interface StateGroup {\r\n letter: string;\r\n names: string[];\r\n}\r\nexport const _filter = (opt: string[], value: string): string[] => {\r\n const filterValue = value.toLowerCase();\r\n\r\n return opt.filter((item) => item.toLowerCase().indexOf(filterValue) === 0);\r\n};\r\n\r\n@Component({\r\n selector: 'oui-autocomplete-storybook',\r\n template: `\r\n \r\n \r\n \r\n \r\n \r\n {{ option }}\r\n \r\n \r\n `,\r\n})\r\nexport class OuiAutocompleteStorybook implements OnInit {\r\n filteredOptions: Observable;\r\n myControl = new UntypedFormControl();\r\n @Input() options: any[];\r\n @Input()\r\n set disabled(value: boolean) {\r\n if (value) {\r\n this.myControl.disable();\r\n } else {\r\n this.myControl.enable();\r\n }\r\n }\r\n ngOnInit() {\r\n this.filteredOptions = this.myControl.valueChanges.pipe(\r\n startWith(''),\r\n map((value) => (typeof value === 'string' ? value : value)),\r\n map((option) => (option ? this._filter(option) : this.options.slice()))\r\n );\r\n }\r\n private _filter(option): string[] {\r\n const filterValue = option.toLowerCase();\r\n\r\n return this.options.filter(\r\n // eslint-disable-next-line no-shadow\r\n (option) => option.toLowerCase().indexOf(filterValue) === 0\r\n );\r\n }\r\n}\r\n\r\n@Component({\r\n selector: 'oui-autocomplete-group-storybook',\r\n template: `\r\n \r\n \r\n \r\n \r\n \r\n {{ name }}\r\n \r\n \r\n \r\n \r\n `,\r\n})\r\nexport class OuiAutocompleteGroupStorybook implements OnInit {\r\n @Input() stateGroups: StateGroup[];\r\n @Input()\r\n set disabled(value: boolean) {\r\n if (value) {\r\n this.stateForm.get('stateGroup')!.disable();\r\n } else {\r\n this.stateForm.get('stateGroup')!.enable();\r\n }\r\n }\r\n stateForm: UntypedFormGroup = this.fb.group({\r\n stateGroup: '',\r\n });\r\n stateGroupOptions: Observable;\r\n\r\n constructor(private fb: UntypedFormBuilder) {}\r\n ngOnInit() {\r\n console.log(this.stateGroups);\r\n this.stateGroupOptions = this.stateForm\r\n .get('stateGroup')!\r\n .valueChanges.pipe(\r\n startWith(''),\r\n map((value: string) => this._filterGroup(value))\r\n );\r\n }\r\n\r\n private _filterGroup(value: string): StateGroup[] {\r\n if (value) {\r\n return this.stateGroups\r\n .map((group) => ({\r\n letter: group.letter,\r\n names: _filter(group.names, value),\r\n }))\r\n .filter((group) => group.names.length > 0);\r\n }\r\n\r\n return this.stateGroups;\r\n }\r\n}\r\n", + "sourceCode": "import { Input, Component, OnInit } from '@angular/core';\nimport { Observable } from 'rxjs';\nimport {\n UntypedFormControl,\n UntypedFormBuilder,\n UntypedFormGroup,\n} from '@angular/forms';\nimport { startWith, map } from 'rxjs/operators';\n\nexport interface StateGroup {\n letter: string;\n names: string[];\n}\nexport const _filter = (opt: string[], value: string): string[] => {\n const filterValue = value.toLowerCase();\n\n return opt.filter((item) => item.toLowerCase().indexOf(filterValue) === 0);\n};\n\n@Component({\n selector: 'oui-autocomplete-storybook',\n template: `\n \n \n \n \n \n {{ option }}\n \n \n `,\n})\nexport class OuiAutocompleteStorybook implements OnInit {\n filteredOptions: Observable;\n myControl = new UntypedFormControl();\n @Input() options: any[];\n @Input()\n set disabled(value: boolean) {\n if (value) {\n this.myControl.disable();\n } else {\n this.myControl.enable();\n }\n }\n ngOnInit() {\n this.filteredOptions = this.myControl.valueChanges.pipe(\n startWith(''),\n map((value) => (typeof value === 'string' ? value : value)),\n map((option) => (option ? this._filter(option) : this.options.slice()))\n );\n }\n private _filter(option): string[] {\n const filterValue = option.toLowerCase();\n\n return this.options.filter(\n // eslint-disable-next-line no-shadow\n (option) => option.toLowerCase().indexOf(filterValue) === 0\n );\n }\n}\n\n@Component({\n selector: 'oui-autocomplete-group-storybook',\n template: `\n \n \n \n \n \n {{ name }}\n \n \n \n \n `,\n})\nexport class OuiAutocompleteGroupStorybook implements OnInit {\n @Input() stateGroups: StateGroup[];\n @Input()\n set disabled(value: boolean) {\n if (value) {\n this.stateForm.get('stateGroup')!.disable();\n } else {\n this.stateForm.get('stateGroup')!.enable();\n }\n }\n stateForm: UntypedFormGroup = this.fb.group({\n stateGroup: '',\n });\n stateGroupOptions: Observable;\n\n constructor(private fb: UntypedFormBuilder) {}\n ngOnInit() {\n console.log(this.stateGroups);\n this.stateGroupOptions = this.stateForm\n .get('stateGroup')!\n .valueChanges.pipe(\n startWith(''),\n map((value: string) => this._filterGroup(value))\n );\n }\n\n private _filterGroup(value: string): StateGroup[] {\n if (value) {\n return this.stateGroups\n .map((group) => ({\n letter: group.letter,\n names: _filter(group.names, value),\n }))\n .filter((group) => group.names.length > 0);\n }\n\n return this.stateGroups;\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -21075,7 +30933,7 @@ } ], "returnType": "void", - "line": 46, + "line": 50, "jsdoctags": [ { "name": "value", @@ -21093,7 +30951,7 @@ }, { "name": "OuiButton", - "id": "component-OuiButton-309b291ac11361b1deae57302d81829f29747ee89fc32835b9bd6812870953cd80a029eb0f793ec52f1765efd49f818af249c9f1edddc674785953707c8f3580", + "id": "component-OuiButton-0d7d7fa85ca575eff44ea16216e0b4e33534845e4ea90e975cbffa14d598d4392f2620b0e45183fa30e446ad5dfa4283ef721d2475ff2f880c1047553f88955f", "file": "ui/src/components/button/button.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -21234,11 +31092,11 @@ "description": "

Once Ui button.

\n", "rawdescription": "\n\nOnce Ui button.\n", "type": "component", - "sourceCode": "import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n ElementRef,\r\n ViewEncapsulation,\r\n OnDestroy,\r\n ChangeDetectorRef,\r\n NgZone,\r\n Input,\r\n} from '@angular/core';\r\nimport {\r\n CanDisable,\r\n CanColor,\r\n CanDisableCtor,\r\n CanColorCtor,\r\n mixinColor,\r\n mixinDisabled,\r\n} from '../core';\r\n\r\nimport { CanProgress, CanProgressCtor, mixinProgress } from './progress';\r\nimport { FocusMonitor } from '@angular/cdk/a11y';\r\nimport { Subscription } from 'rxjs';\r\n/**\r\n * List of classes to add to Button instances based on host attributes to\r\n * style as different variants.\r\n */\r\nconst BUTTON_HOST_ATTRIBUTES = [\r\n 'oui-button',\r\n 'oui-ghost-button',\r\n 'oui-link-button',\r\n 'oui-icon-button',\r\n 'oui-icon-text-button',\r\n];\r\n\r\n/** Default color palette for round buttons (oui-fab and oui-mini-fab) */\r\nconst DEFAULT_COLOR = 'primary';\r\n\r\n// Boilerplate for applying mixins to OuiButton.\r\n/** @docs-private */\r\nexport class OuiButtonBase {\r\n constructor(public _elementRef: ElementRef, public _cdr: ChangeDetectorRef) {}\r\n}\r\n\r\nexport const OuiButtonMixinBase: CanDisableCtor &\r\n CanColorCtor &\r\n CanProgressCtor &\r\n typeof OuiButtonBase = mixinProgress(\r\n mixinColor(mixinDisabled(OuiButtonBase))\r\n);\r\n\r\n/**\r\n * Once Ui button.\r\n */\r\n@Component({\r\n // eslint-disable-next-line @angular-eslint/component-selector\r\n selector: `button[oui-button], button[oui-ghost-button], button[oui-link-button], button[oui-icon-button],\r\n button[oui-icon-text-button]`,\r\n exportAs: 'ouiButton',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n '[disabled]': 'disabled || null',\r\n },\r\n templateUrl: 'button.html',\r\n styleUrls: ['button.scss'],\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['disabled', 'color', 'progress', 'tabIndex'],\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class OuiButton\r\n extends OuiButtonMixinBase\r\n implements OnDestroy, CanDisable, CanColor, CanProgress\r\n{\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n constructor(\r\n protected elementRef: ElementRef,\r\n private _focusMonitor: FocusMonitor,\r\n public _cdr: ChangeDetectorRef,\r\n private _ngZone: NgZone\r\n ) {\r\n super(elementRef, _cdr);\r\n this.addClass();\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this.elementRef, true)\r\n .subscribe(() =>\r\n this._ngZone.run(() => {\r\n this._cdr.markForCheck();\r\n })\r\n );\r\n }\r\n\r\n protected addClass() {\r\n for (const attr of BUTTON_HOST_ATTRIBUTES) {\r\n if (this.hasHostAttributes(attr)) {\r\n (this.elementRef.nativeElement as HTMLElement).classList.add(attr);\r\n }\r\n }\r\n if (!this.color) {\r\n this.color = DEFAULT_COLOR;\r\n }\r\n }\r\n\r\n ngOnDestroy() {\r\n this._focusMonitor.stopMonitoring(this.elementRef);\r\n this._monitorSubscription.unsubscribe();\r\n }\r\n\r\n /** Focuses the button. */\r\n focus(): void {\r\n this.getHostElement().focus();\r\n }\r\n\r\n getHostElement() {\r\n return this.elementRef.nativeElement;\r\n }\r\n /** Gets whether the button has one of the given attributes. */\r\n hasHostAttributes(...attributes: string[]) {\r\n return attributes.some((attribute) =>\r\n this.getHostElement().hasAttribute(attribute)\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Once UI anchor.\r\n */\r\n@Component({\r\n // eslint-disable-next-line @angular-eslint/component-selector\r\n selector: `a[oui-button], a[oui-ghost-button], a[oui-link-button], a[oui-icon-button],\r\n a[oui-icon-text-button]`,\r\n exportAs: 'ouiButton, ouiAnchor',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n '[attr.tabindex]': 'disabled ? -1 : (tabIndex || 0)',\r\n '[attr.disabled]': 'disabled || null',\r\n '[attr.aria-disabled]': 'disabled.toString()',\r\n '(click)': '_haltDisabledEvents($event)',\r\n },\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['disabled', 'color'],\r\n templateUrl: 'button.html',\r\n styleUrls: ['button.scss'],\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class OuiAnchor extends OuiButton {\r\n /** Tabindex of the button. */\r\n @Input() tabIndex: number;\r\n constructor(\r\n elementRef: ElementRef,\r\n focusMonitor: FocusMonitor,\r\n _cdr: ChangeDetectorRef,\r\n _ngZone: NgZone\r\n ) {\r\n super(elementRef, focusMonitor, _cdr, _ngZone);\r\n }\r\n\r\n _haltDisabledEvents(event: Event) {\r\n // A disabled button shouldn't apply any actions\r\n if (this.disabled) {\r\n event.preventDefault();\r\n event.stopImmediatePropagation();\r\n }\r\n }\r\n}\r\n", + "sourceCode": "import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n ViewEncapsulation,\n OnDestroy,\n ChangeDetectorRef,\n NgZone,\n Input,\n} from '@angular/core';\nimport {\n CanDisable,\n CanColor,\n CanDisableCtor,\n CanColorCtor,\n mixinColor,\n mixinDisabled,\n} from '../core';\n\nimport { CanProgress, CanProgressCtor, mixinProgress } from './progress';\nimport { FocusMonitor } from '@angular/cdk/a11y';\nimport { Subscription } from 'rxjs';\n/**\n * List of classes to add to Button instances based on host attributes to\n * style as different variants.\n */\nconst BUTTON_HOST_ATTRIBUTES = [\n 'oui-button',\n 'oui-ghost-button',\n 'oui-link-button',\n 'oui-icon-button',\n 'oui-icon-text-button',\n];\n\n/** Default color palette for round buttons (oui-fab and oui-mini-fab) */\nconst DEFAULT_COLOR = 'primary';\n\n// Boilerplate for applying mixins to OuiButton.\n/** @docs-private */\nexport class OuiButtonBase {\n constructor(public _elementRef: ElementRef, public _cdr: ChangeDetectorRef) {}\n}\n\nexport const OuiButtonMixinBase: CanDisableCtor &\n CanColorCtor &\n CanProgressCtor &\n typeof OuiButtonBase = mixinProgress(\n mixinColor(mixinDisabled(OuiButtonBase))\n);\n\n/**\n * Once Ui button.\n */\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: `button[oui-button], button[oui-ghost-button], button[oui-link-button], button[oui-icon-button],\n button[oui-icon-text-button]`,\n exportAs: 'ouiButton',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n '[disabled]': 'disabled || null',\n },\n templateUrl: 'button.html',\n styleUrls: ['button.scss'],\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\n inputs: ['disabled', 'color', 'progress', 'tabIndex'],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class OuiButton\n extends OuiButtonMixinBase\n implements OnDestroy, CanDisable, CanColor, CanProgress\n{\n private _monitorSubscription: Subscription = Subscription.EMPTY;\n constructor(\n protected elementRef: ElementRef,\n private _focusMonitor: FocusMonitor,\n public _cdr: ChangeDetectorRef,\n private _ngZone: NgZone\n ) {\n super(elementRef, _cdr);\n this.addClass();\n this._monitorSubscription = this._focusMonitor\n .monitor(this.elementRef, true)\n .subscribe(() =>\n this._ngZone.run(() => {\n this._cdr.markForCheck();\n })\n );\n }\n\n protected addClass() {\n for (const attr of BUTTON_HOST_ATTRIBUTES) {\n if (this.hasHostAttributes(attr)) {\n (this.elementRef.nativeElement as HTMLElement).classList.add(attr);\n }\n }\n if (!this.color) {\n this.color = DEFAULT_COLOR;\n }\n }\n\n ngOnDestroy() {\n this._focusMonitor.stopMonitoring(this.elementRef);\n this._monitorSubscription.unsubscribe();\n }\n\n /** Focuses the button. */\n focus(): void {\n this.getHostElement().focus();\n }\n\n getHostElement() {\n return this.elementRef.nativeElement;\n }\n /** Gets whether the button has one of the given attributes. */\n hasHostAttributes(...attributes: string[]) {\n return attributes.some((attribute) =>\n this.getHostElement().hasAttribute(attribute)\n );\n }\n}\n\n/**\n * Once UI anchor.\n */\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: `a[oui-button], a[oui-ghost-button], a[oui-link-button], a[oui-icon-button],\n a[oui-icon-text-button]`,\n exportAs: 'ouiButton, ouiAnchor',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n '[attr.tabindex]': 'disabled ? -1 : (tabIndex || 0)',\n '[attr.disabled]': 'disabled || null',\n '[attr.aria-disabled]': 'disabled.toString()',\n '(click)': '_haltDisabledEvents($event)',\n },\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\n inputs: ['disabled', 'color'],\n templateUrl: 'button.html',\n styleUrls: ['button.scss'],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class OuiAnchor extends OuiButton {\n /** Tabindex of the button. */\n @Input() tabIndex: number;\n constructor(\n elementRef: ElementRef,\n focusMonitor: FocusMonitor,\n _cdr: ChangeDetectorRef,\n _ngZone: NgZone\n ) {\n super(elementRef, focusMonitor, _cdr, _ngZone);\n }\n\n _haltDisabledEvents(event: Event) {\n // A disabled button shouldn't apply any actions\n if (this.disabled) {\n event.preventDefault();\n event.stopImmediatePropagation();\n }\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": "@import 'button-theme';\r\n@import 'button-icon';\r\n@include oui-icon-text();\r\n\r\n$border-radius: 100px;\r\n$font-size: 14px;\r\n$letter-spacing: 0.2px;\r\n$padding: 6px 20px;\r\n$remove-padding: 0px;\r\n$progress-icon-padding: 0px 0px 0px 23px;\r\n$line-height: 19px;\r\n$min-height: 35px;\r\n$min-width: 70px;\r\n$font-normal: normal;\r\n$font-semi-bold: 600;\r\n$font-bold: 700;\r\n$cursor: pointer;\r\n$disabled-cursor: default;\r\n$ghost-button-background: #fff;\r\n$border: 1px solid transparent;\r\n$text-decoration: none;\r\n$text-align-center: center;\r\n$text-align-left: left;\r\n$green-tick-icon: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTgiIGhlaWdodD0iMTMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTcuMzUgMTAuMTVMMTYuNTgyLjk0OGwxLjQxNCAxLjQxNEw3LjM0NiAxMi45OCA1LjkzIDExLjU2NWwuMDAyLS4wMDItNC45MjItNC45NEwyLjQyNiA1LjIxbDQuOTI0IDQuOTQyeiIgZmlsbD0iIzAwOUI0OCIgZmlsbC1ydWxlPSJldmVub2RkIi8+PC9zdmc+')\r\n left center no-repeat;\r\n@mixin oui-button-core() {\r\n .oui-button,\r\n .oui-ghost-button,\r\n .oui-icon-text-button,\r\n .oui-link-button,\r\n .oui-progress-button,\r\n .oui-progress-ghost-button,\r\n .oui-progress-link-button {\r\n @include oui-button-reset();\r\n @include oui-button-base();\r\n }\r\n\r\n .oui-button {\r\n @include oui-progress-button-stages();\r\n &.cdk-keyboard-focused {\r\n text-decoration: underline;\r\n }\r\n }\r\n .oui-link-button {\r\n @include oui-progress-link-button-stages();\r\n }\r\n .oui-ghost-button {\r\n background: $ghost-button-background;\r\n @include oui-progress-ghost-button-stages();\r\n &.cdk-keyboard-focused {\r\n text-decoration: underline;\r\n }\r\n }\r\n .oui-icon-button {\r\n @include oui-button-reset();\r\n }\r\n}\r\n.oui-icon-button {\r\n &.cdk-keyboard-focused {\r\n background: rgba(200, 200, 200, 0.4);\r\n border-radius: $oui-icon-spacing;\r\n transition: opacity 0.2s cubic-bezier(0.35, 0, 0.25, 1),\r\n background-color 0.2s cubic-bezier(0.35, 0, 0.25, 1);\r\n }\r\n}\r\n/*common properties in all buttons*/\r\n@mixin oui-button-base {\r\n font-size: $font-size;\r\n border-radius: $border-radius;\r\n letter-spacing: $letter-spacing;\r\n padding: $padding;\r\n line-height: $line-height;\r\n min-height: $min-height;\r\n min-width: $min-width;\r\n font-weight: $font-semi-bold;\r\n cursor: $cursor;\r\n border: $border;\r\n background: transparent;\r\n text-decoration: $text-decoration;\r\n text-align: $text-align-center;\r\n &[disabled] {\r\n cursor: $disabled-cursor;\r\n }\r\n}\r\n/*reset the browser default properties*/\r\n@mixin oui-button-reset {\r\n border: 0 none;\r\n background: none;\r\n font-family: 'Open Sans', Helvetica, Arial, sans-serif;\r\n &:focus {\r\n outline: 0 none;\r\n }\r\n // The `outline: none` from above works on all browsers, however Firefox also\r\n // adds a special `focus-inner` which we have to disable explicitly. See:\r\n // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Firefox\r\n &::-moz-focus-inner {\r\n border: 0;\r\n }\r\n}\r\n\r\n.oui-link-button {\r\n padding: 6px 0px 6px 0px !important;\r\n min-width: auto !important;\r\n font-weight: $font-normal !important;\r\n &:hover {\r\n text-decoration: underline;\r\n }\r\n &[disabled]:hover {\r\n text-decoration: none;\r\n }\r\n &:focus {\r\n outline: 0 none;\r\n }\r\n &.cdk-keyboard-focused {\r\n outline: solid 1px #006bb1 !important;\r\n text-decoration: underline;\r\n border-radius: 5px;\r\n }\r\n}\r\n\r\n.oui-ghost-button {\r\n &[disabled],\r\n &.oui-stage-progress {\r\n background: transparent !important;\r\n cursor: $disabled-cursor !important;\r\n }\r\n}\r\n\r\n@mixin oui-progress-button-stages {\r\n &.oui-stage-progress {\r\n background: transparent !important;\r\n text-align: $text-align-left !important;\r\n padding: $remove-padding !important;\r\n font-weight: $font-bold !important;\r\n cursor: $disabled-cursor !important;\r\n }\r\n &.oui-stage-done {\r\n background: $green-tick-icon !important;\r\n text-align: $text-align-left !important;\r\n padding: $progress-icon-padding !important;\r\n font-weight: $font-bold !important;\r\n cursor: $disabled-cursor !important;\r\n }\r\n}\r\n@mixin oui-progress-link-button-stages {\r\n &.oui-stage-progress {\r\n background: transparent !important;\r\n text-align: $text-align-left !important;\r\n padding: $remove-padding !important;\r\n font-weight: $font-bold !important;\r\n cursor: $disabled-cursor !important;\r\n text-decoration: $text-decoration !important;\r\n }\r\n &.oui-stage-done {\r\n background: $green-tick-icon !important;\r\n text-align: $text-align-left !important;\r\n padding: $progress-icon-padding !important;\r\n font-weight: $font-bold !important;\r\n cursor: $disabled-cursor !important;\r\n text-decoration: $text-decoration !important;\r\n }\r\n}\r\n@mixin oui-progress-ghost-button-stages {\r\n &.oui-stage-progress {\r\n background: transparent !important;\r\n text-align: $text-align-left !important;\r\n padding: $remove-padding !important;\r\n font-weight: $font-bold !important;\r\n cursor: $disabled-cursor !important;\r\n border: $border !important;\r\n }\r\n &.oui-stage-done {\r\n background: $green-tick-icon !important;\r\n text-align: $text-align-left !important;\r\n padding: $progress-icon-padding !important;\r\n font-weight: $font-bold !important;\r\n cursor: $disabled-cursor !important;\r\n border: $border !important;\r\n }\r\n}\r\n\r\n@include oui-button-core();\r\n", + "data": "@import 'button-theme';\n@import 'button-icon';\n@include oui-icon-text();\n\n$border-radius: 100px;\n$font-size: 14px;\n$letter-spacing: 0.2px;\n$padding: 6px 20px;\n$remove-padding: 0px;\n$progress-icon-padding: 0px 0px 0px 23px;\n$line-height: 19px;\n$min-height: 35px;\n$min-width: 70px;\n$font-normal: normal;\n$font-semi-bold: 600;\n$font-bold: 700;\n$cursor: pointer;\n$disabled-cursor: default;\n$ghost-button-background: #fff;\n$border: 1px solid transparent;\n$text-decoration: none;\n$text-align-center: center;\n$text-align-left: left;\n$green-tick-icon: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTgiIGhlaWdodD0iMTMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTcuMzUgMTAuMTVMMTYuNTgyLjk0OGwxLjQxNCAxLjQxNEw3LjM0NiAxMi45OCA1LjkzIDExLjU2NWwuMDAyLS4wMDItNC45MjItNC45NEwyLjQyNiA1LjIxbDQuOTI0IDQuOTQyeiIgZmlsbD0iIzAwOUI0OCIgZmlsbC1ydWxlPSJldmVub2RkIi8+PC9zdmc+')\n left center no-repeat;\n@mixin oui-button-core() {\n .oui-button,\n .oui-ghost-button,\n .oui-icon-text-button,\n .oui-link-button,\n .oui-progress-button,\n .oui-progress-ghost-button,\n .oui-progress-link-button {\n @include oui-button-reset();\n @include oui-button-base();\n }\n\n .oui-button {\n @include oui-progress-button-stages();\n &.cdk-keyboard-focused {\n text-decoration: underline;\n }\n }\n .oui-link-button {\n @include oui-progress-link-button-stages();\n }\n .oui-ghost-button {\n background: $ghost-button-background;\n @include oui-progress-ghost-button-stages();\n &.cdk-keyboard-focused {\n text-decoration: underline;\n }\n }\n .oui-icon-button {\n @include oui-button-reset();\n }\n}\n.oui-icon-button {\n &.cdk-keyboard-focused,\n &.cdk-program-focused {\n background: rgba(200, 200, 200, 0.4);\n border-radius: $oui-icon-spacing;\n transition: opacity 0.2s cubic-bezier(0.35, 0, 0.25, 1),\n background-color 0.2s cubic-bezier(0.35, 0, 0.25, 1);\n }\n}\n/*common properties in all buttons*/\n@mixin oui-button-base {\n font-size: $font-size;\n border-radius: $border-radius;\n letter-spacing: $letter-spacing;\n padding: $padding;\n line-height: $line-height;\n min-height: $min-height;\n min-width: $min-width;\n font-weight: $font-semi-bold;\n cursor: $cursor;\n border: $border;\n background: transparent;\n text-decoration: $text-decoration;\n text-align: $text-align-center;\n &[disabled] {\n cursor: $disabled-cursor;\n }\n}\n/*reset the browser default properties*/\n@mixin oui-button-reset {\n border: 0 none;\n background: none;\n font-family: 'Open Sans', Helvetica, Arial, sans-serif;\n &:focus {\n outline: 0 none;\n }\n // The `outline: none` from above works on all browsers, however Firefox also\n // adds a special `focus-inner` which we have to disable explicitly. See:\n // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Firefox\n &::-moz-focus-inner {\n border: 0;\n }\n}\n\n.oui-link-button {\n padding: 6px 0px 6px 0px !important;\n min-width: auto !important;\n font-weight: $font-normal !important;\n &:hover {\n text-decoration: underline;\n }\n &[disabled]:hover {\n text-decoration: none;\n }\n &:focus {\n outline: 0 none;\n }\n &.cdk-keyboard-focused {\n outline: solid 1px #006bb1 !important;\n text-decoration: underline;\n border-radius: 5px;\n }\n}\n\n.oui-ghost-button {\n &[disabled],\n &.oui-stage-progress {\n background: transparent !important;\n cursor: $disabled-cursor !important;\n }\n}\n\n@mixin oui-progress-button-stages {\n &.oui-stage-progress {\n background: transparent !important;\n text-align: $text-align-left !important;\n padding: $remove-padding !important;\n font-weight: $font-bold !important;\n cursor: $disabled-cursor !important;\n }\n &.oui-stage-done {\n background: $green-tick-icon !important;\n text-align: $text-align-left !important;\n padding: $progress-icon-padding !important;\n font-weight: $font-bold !important;\n cursor: $disabled-cursor !important;\n }\n}\n@mixin oui-progress-link-button-stages {\n &.oui-stage-progress {\n background: transparent !important;\n text-align: $text-align-left !important;\n padding: $remove-padding !important;\n font-weight: $font-bold !important;\n cursor: $disabled-cursor !important;\n text-decoration: $text-decoration !important;\n }\n &.oui-stage-done {\n background: $green-tick-icon !important;\n text-align: $text-align-left !important;\n padding: $progress-icon-padding !important;\n font-weight: $font-bold !important;\n cursor: $disabled-cursor !important;\n text-decoration: $text-decoration !important;\n }\n}\n@mixin oui-progress-ghost-button-stages {\n &.oui-stage-progress {\n background: transparent !important;\n text-align: $text-align-left !important;\n padding: $remove-padding !important;\n font-weight: $font-bold !important;\n cursor: $disabled-cursor !important;\n border: $border !important;\n }\n &.oui-stage-done {\n background: $green-tick-icon !important;\n text-align: $text-align-left !important;\n padding: $progress-icon-padding !important;\n font-weight: $font-bold !important;\n cursor: $disabled-cursor !important;\n border: $border !important;\n }\n}\n\n@include oui-button-core();\n", "styleUrl": "button.scss" } ], @@ -21321,11 +31179,11 @@ "CanColor", "CanProgress" ], - "templateData": "\r\n" + "templateData": "\n" }, { "name": "OuiCalendar", - "id": "component-OuiCalendar-8f354118592827c1783232402ad1e8f7d0e2a62c504accf4daf43ab307d740eee08a679ef62cb14f156e051ef835d1a81db3c60d5888ffe84155bcd8a769911d", + "id": "component-OuiCalendar-e8009240f3a519e7433edf1e6572fe9bd6f7f784a717c93166ba842a1125b01455109a31792fcd91db5d1de096039f310a9f6310f626e8f6829a34ebc5a09670", "file": "ui/src/components/datepicker/calendar.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -21710,8 +31568,8 @@ "jsdoctags": [ { "name": { - "pos": 8643, - "end": 8646, + "pos": 8352, + "end": 8355, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -21722,8 +31580,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 8637, - "end": 8642, + "pos": 8346, + "end": 8351, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -21734,8 +31592,8 @@ }, { "tagName": { - "pos": 8675, - "end": 8682, + "pos": 8383, + "end": 8390, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -21949,11 +31807,11 @@ "description": "

A calendar that is used as part of the datepicker.

\n", "rawdescription": "\n\nA calendar that is used as part of the datepicker.\n", "type": "component", - "sourceCode": "import { ComponentPortal, ComponentType, Portal } from '@angular/cdk/portal';\r\nimport {\r\n AfterContentInit,\r\n AfterViewChecked,\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n EventEmitter,\r\n forwardRef,\r\n Inject,\r\n Input,\r\n OnChanges,\r\n OnDestroy,\r\n Optional,\r\n Output,\r\n SimpleChanges,\r\n ViewChild,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\nimport { Subject, Subscription } from 'rxjs';\r\nimport { createMissingDateImplError } from './datepicker-errors';\r\nimport { OuiDatepickerIntl } from './datepicker-intl';\r\nimport { OuiMonthView } from './month-view';\r\nimport { OuiMultiYearView, yearsPerPage } from './multi-year-view';\r\nimport { OuiYearView } from './year-view';\r\nimport { OuiCalendarCellCssClasses } from './calendar-body';\r\nimport { DateAdapter } from './date-adapter';\r\nimport { OuiDateFormats, OUI_DATE_FORMATS } from './date-formats';\r\nimport { OuiIconRegistry } from '../icon/icon-registery';\r\nimport { DomSanitizer } from '@angular/platform-browser';\r\nimport { ICONS } from '../core/shared/icons';\r\n\r\n/**\r\n * Possible views for the calendar.\r\n */\r\nexport type OuiCalendarView = 'month' | 'year' | 'multi-year';\r\n\r\n/**\r\n * A calendar that is used as part of the datepicker.\r\n */\r\n@Component({\r\n selector: 'oui-calendar',\r\n templateUrl: 'calendar.html',\r\n styleUrls: ['calendar.scss'],\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-calendar',\r\n },\r\n exportAs: 'ouiCalendar',\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class OuiCalendar\r\n implements AfterContentInit, AfterViewChecked, OnDestroy, OnChanges\r\n{\r\n /** An input indicating the type of the header component, if set. */\r\n @Input() headerComponent: ComponentType;\r\n\r\n /** A portal containing the header component type for this calendar. */\r\n _calendarHeaderPortal: Portal;\r\n\r\n private _intlChanges: Subscription;\r\n\r\n /**\r\n * Used for scheduling that focus should be moved to the active cell on the next tick.\r\n * We need to schedule it, rather than do it immediately, because we have to wait\r\n * for Angular to re-evaluate the view children.\r\n */\r\n private _moveFocusOnNextTick = false;\r\n\r\n /** A date representing the period (month or year) to start the calendar in. */\r\n @Input()\r\n get startAt(): D | null {\r\n return this._startAt;\r\n }\r\n set startAt(value: D | null) {\r\n this._startAt = this._getValidDateOrNull(\r\n this._dateAdapter.deserialize(value)\r\n );\r\n }\r\n private _startAt: D | null;\r\n\r\n /** Whether the calendar should be started in month or year view. */\r\n @Input() startView: OuiCalendarView = 'month';\r\n\r\n /** The currently selected date. */\r\n @Input()\r\n get selected(): D | null {\r\n return this._selected;\r\n }\r\n set selected(value: D | null) {\r\n this._selected = this._getValidDateOrNull(\r\n this._dateAdapter.deserialize(value)\r\n );\r\n }\r\n private _selected: D | null;\r\n\r\n /** The minimum selectable date. */\r\n @Input()\r\n get minDate(): D | null {\r\n return this._minDate;\r\n }\r\n set minDate(value: D | null) {\r\n this._minDate = this._getValidDateOrNull(\r\n this._dateAdapter.deserialize(value)\r\n );\r\n }\r\n private _minDate: D | null;\r\n\r\n /** The maximum selectable date. */\r\n @Input()\r\n get maxDate(): D | null {\r\n return this._maxDate;\r\n }\r\n set maxDate(value: D | null) {\r\n this._maxDate = this._getValidDateOrNull(\r\n this._dateAdapter.deserialize(value)\r\n );\r\n }\r\n private _maxDate: D | null;\r\n\r\n /** Function used to filter which dates are selectable. */\r\n @Input() dateFilter: (date: D) => boolean;\r\n\r\n /** Function that can be used to add custom CSS classes to dates. */\r\n @Input() dateClass: (date: D) => OuiCalendarCellCssClasses;\r\n\r\n /** Emits when the currently selected date changes. */\r\n @Output() readonly selectedChange: EventEmitter = new EventEmitter();\r\n\r\n /**\r\n * Emits the year chosen in multiyear view.\r\n * This doesn't imply a change on the selected date.\r\n */\r\n @Output() readonly yearSelected: EventEmitter = new EventEmitter();\r\n\r\n /**\r\n * Emits the month chosen in year view.\r\n * This doesn't imply a change on the selected date.\r\n */\r\n @Output() readonly monthSelected: EventEmitter = new EventEmitter();\r\n\r\n /** Emits when any date is selected. */\r\n @Output()\r\n readonly _userSelection: EventEmitter = new EventEmitter();\r\n\r\n /** Reference to the current month view component. */\r\n @ViewChild(OuiMonthView) monthView: OuiMonthView;\r\n\r\n /** Reference to the current year view component. */\r\n @ViewChild(OuiYearView) yearView: OuiYearView;\r\n\r\n /** Reference to the current multi-year view component. */\r\n @ViewChild(OuiMultiYearView)\r\n multiYearView: OuiMultiYearView;\r\n\r\n /**\r\n * The current active date. This determines which time period is shown and which date is\r\n * highlighted when using keyboard navigation.\r\n */\r\n get activeDate(): D {\r\n return this._clampedActiveDate;\r\n }\r\n set activeDate(value: D) {\r\n this._clampedActiveDate = this._dateAdapter.clampDate(\r\n value,\r\n this.minDate,\r\n this.maxDate\r\n );\r\n this.stateChanges.next();\r\n }\r\n private _clampedActiveDate: D;\r\n\r\n /** Whether the calendar is in month view. */\r\n get currentView(): OuiCalendarView {\r\n return this._currentView;\r\n }\r\n set currentView(value: OuiCalendarView) {\r\n this._currentView = value;\r\n this._moveFocusOnNextTick = true;\r\n }\r\n private _currentView: OuiCalendarView;\r\n\r\n /**\r\n * Emits whenever there is a state change that the header may need to respond to.\r\n */\r\n stateChanges = new Subject();\r\n\r\n constructor(\r\n _intl: OuiDatepickerIntl,\r\n @Optional() private _dateAdapter: DateAdapter,\r\n @Optional() @Inject(OUI_DATE_FORMATS) private _dateFormats: OuiDateFormats,\r\n private _changeDetectorRef: ChangeDetectorRef\r\n ) {\r\n if (!this._dateAdapter) {\r\n throw createMissingDateImplError('DateAdapter');\r\n }\r\n\r\n if (!this._dateFormats) {\r\n throw createMissingDateImplError('OuiDATE_FORMATS');\r\n }\r\n\r\n this._intlChanges = _intl.changes.subscribe(() => {\r\n _changeDetectorRef.markForCheck();\r\n this.stateChanges.next();\r\n });\r\n }\r\n\r\n ngAfterContentInit() {\r\n this._calendarHeaderPortal = new ComponentPortal(\r\n this.headerComponent || OuiCalendarHeader\r\n );\r\n this.activeDate = this.startAt || this._dateAdapter.today();\r\n\r\n // Assign to the private property since we don't want to move focus on init.\r\n this._currentView = this.startView;\r\n }\r\n\r\n ngAfterViewChecked() {\r\n if (this._moveFocusOnNextTick) {\r\n this._moveFocusOnNextTick = false;\r\n this.focusActiveCell();\r\n }\r\n }\r\n\r\n ngOnDestroy() {\r\n this._intlChanges.unsubscribe();\r\n this.stateChanges.complete();\r\n }\r\n\r\n ngOnChanges(changes: SimpleChanges) {\r\n const change = changes.minDate || changes.maxDate || changes.dateFilter;\r\n\r\n if (change && !change.firstChange) {\r\n const view = this._getCurrentViewComponent();\r\n\r\n if (view) {\r\n // We need to `detectChanges` manually here, because the `minDate`, `maxDate` etc. are\r\n // passed down to the view via data bindings which won't be up-to-date when we call `_init`.\r\n this._changeDetectorRef.detectChanges();\r\n view._init();\r\n }\r\n }\r\n\r\n this.stateChanges.next();\r\n }\r\n\r\n focusActiveCell() {\r\n this._getCurrentViewComponent()._focusActiveCell();\r\n }\r\n\r\n /** Updates today's date after an update of the active date */\r\n updateTodaysDate() {\r\n const view =\r\n this.currentView === 'month'\r\n ? this.monthView\r\n : this.currentView === 'year'\r\n ? this.yearView\r\n : this.multiYearView;\r\n\r\n view.ngAfterContentInit();\r\n }\r\n\r\n /** Handles date selection in the month view. */\r\n _dateSelected(date: D): void {\r\n if (!this._dateAdapter.sameDate(date, this.selected)) {\r\n this.selectedChange.emit(date);\r\n }\r\n }\r\n\r\n /** Handles year selection in the multiyear view. */\r\n _yearSelectedInMultiYearView(normalizedYear: D) {\r\n this.yearSelected.emit(normalizedYear);\r\n }\r\n\r\n /** Handles month selection in the year view. */\r\n _monthSelectedInYearView(normalizedMonth: D) {\r\n this.monthSelected.emit(normalizedMonth);\r\n }\r\n\r\n _userSelected(): void {\r\n this._userSelection.emit();\r\n }\r\n\r\n /** Handles year/month selection in the multi-year/year views. */\r\n _goToDateInView(date: D, view: 'month' | 'year' | 'multi-year'): void {\r\n this.activeDate = date;\r\n this.currentView = view;\r\n }\r\n\r\n /**\r\n * @param obj The object to check.\r\n * @returns The given object if it is both a date instance and valid, otherwise null.\r\n */\r\n private _getValidDateOrNull(obj: any): D | null {\r\n return this._dateAdapter.isDateInstance(obj) &&\r\n this._dateAdapter.isValid(obj as any as D)\r\n ? obj\r\n : null;\r\n }\r\n\r\n /** Returns the component instance that corresponds to the current calendar view. */\r\n private _getCurrentViewComponent() {\r\n return this.monthView || this.yearView || this.multiYearView;\r\n }\r\n}\r\n\r\n/** Default header for OuiCalendar */\r\n@Component({\r\n selector: 'oui-calendar-header',\r\n templateUrl: 'calendar-header.html',\r\n exportAs: 'ouiCalendarHeader',\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class OuiCalendarHeader {\r\n constructor(\r\n private _intl: OuiDatepickerIntl,\r\n @Inject(forwardRef(() => OuiCalendar)) public calendar: OuiCalendar,\r\n @Optional() private _dateAdapter: DateAdapter,\r\n changeDetectorRef: ChangeDetectorRef,\r\n private ouiIconRegistry: OuiIconRegistry,\r\n private domSanitizer: DomSanitizer\r\n ) {\r\n this.calendar.stateChanges.subscribe(() =>\r\n changeDetectorRef.markForCheck()\r\n );\r\n this.ouiIconRegistry.addSvgIconLiteral(\r\n `arrow-icon`,\r\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.ARROW_ICON)\r\n );\r\n }\r\n\r\n /** The label for the current calendar view. */\r\n get periodButtonText(): string {\r\n if (this.calendar.currentView === 'month') {\r\n return (\r\n this._dateAdapter.getMonthNames('short')[\r\n this._dateAdapter.getMonth(this.calendar.activeDate)\r\n ] +\r\n ' ' +\r\n this._dateAdapter.getYearName(this.calendar.activeDate)\r\n );\r\n }\r\n if (this.calendar.currentView === 'year') {\r\n return this._dateAdapter.getYearName(this.calendar.activeDate);\r\n }\r\n const activeYear = this._dateAdapter.getYear(this.calendar.activeDate);\r\n const firstYearInView = this._dateAdapter.getYearName(\r\n this._dateAdapter.createDate(activeYear - (activeYear % 24), 0, 1)\r\n );\r\n const lastYearInView = this._dateAdapter.getYearName(\r\n this._dateAdapter.createDate(\r\n activeYear + yearsPerPage - 1 - (activeYear % 24),\r\n 0,\r\n 1\r\n )\r\n );\r\n return `${firstYearInView} \\u2013 ${lastYearInView}`;\r\n }\r\n\r\n get periodButtonLabel(): string {\r\n return this.calendar.currentView === 'month'\r\n ? this._intl.switchToMultiYearViewLabel\r\n : this._intl.switchToMonthViewLabel;\r\n }\r\n\r\n /** The label for the the previous button. */\r\n get prevButtonLabel(): string {\r\n return {\r\n month: this._intl.prevMonthLabel,\r\n year: this._intl.prevYearLabel,\r\n 'multi-year': this._intl.prevMultiYearLabel,\r\n }[this.calendar.currentView];\r\n }\r\n\r\n /** The label for the the next button. */\r\n get nextButtonLabel(): string {\r\n return {\r\n month: this._intl.nextMonthLabel,\r\n year: this._intl.nextYearLabel,\r\n 'multi-year': this._intl.nextMultiYearLabel,\r\n }[this.calendar.currentView];\r\n }\r\n\r\n /** Handles user clicks on the period label. */\r\n currentPeriodClicked(): void {\r\n this.calendar.currentView =\r\n this.calendar.currentView === 'month' ? 'multi-year' : 'month';\r\n }\r\n\r\n /** Handles user clicks on the previous button. */\r\n previousClicked(): void {\r\n this.calendar.activeDate =\r\n this.calendar.currentView === 'month'\r\n ? this._dateAdapter.addCalendarMonths(this.calendar.activeDate, -1)\r\n : this._dateAdapter.addCalendarYears(\r\n this.calendar.activeDate,\r\n this.calendar.currentView === 'year' ? -1 : -yearsPerPage\r\n );\r\n }\r\n\r\n /** Handles user clicks on the next button. */\r\n nextClicked(): void {\r\n this.calendar.activeDate =\r\n this.calendar.currentView === 'month'\r\n ? this._dateAdapter.addCalendarMonths(this.calendar.activeDate, 1)\r\n : this._dateAdapter.addCalendarYears(\r\n this.calendar.activeDate,\r\n this.calendar.currentView === 'year' ? 1 : yearsPerPage\r\n );\r\n }\r\n\r\n /** Whether the previous period button is enabled. */\r\n previousEnabled(): boolean {\r\n if (!this.calendar.minDate) {\r\n return true;\r\n }\r\n return (\r\n !this.calendar.minDate ||\r\n !this._isSameView(this.calendar.activeDate, this.calendar.minDate)\r\n );\r\n }\r\n\r\n /** Whether the next period button is enabled. */\r\n nextEnabled(): boolean {\r\n return (\r\n !this.calendar.maxDate ||\r\n !this._isSameView(this.calendar.activeDate, this.calendar.maxDate)\r\n );\r\n }\r\n\r\n /** Whether the two dates represent the same view in the current view mode (month or year). */\r\n private _isSameView(date1: D, date2: D): boolean {\r\n if (this.calendar.currentView === 'month') {\r\n return (\r\n this._dateAdapter.getYear(date1) === this._dateAdapter.getYear(date2) &&\r\n this._dateAdapter.getMonth(date1) === this._dateAdapter.getMonth(date2)\r\n );\r\n }\r\n if (this.calendar.currentView === 'year') {\r\n return (\r\n this._dateAdapter.getYear(date1) === this._dateAdapter.getYear(date2)\r\n );\r\n }\r\n // Otherwise we are in 'multi-year' view.\r\n return (\r\n Math.floor(this._dateAdapter.getYear(date1) / yearsPerPage) ===\r\n Math.floor(this._dateAdapter.getYear(date2) / yearsPerPage)\r\n );\r\n }\r\n}\r\n", + "sourceCode": "import { ComponentPortal, ComponentType, Portal } from '@angular/cdk/portal';\nimport {\n AfterContentInit,\n AfterViewChecked,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n EventEmitter,\n forwardRef,\n Inject,\n Input,\n OnChanges,\n OnDestroy,\n Optional,\n Output,\n SimpleChanges,\n ViewChild,\n ViewEncapsulation,\n} from '@angular/core';\nimport { Subject, Subscription } from 'rxjs';\nimport { createMissingDateImplError } from './datepicker-errors';\nimport { OuiDatepickerIntl } from './datepicker-intl';\nimport { OuiMonthView } from './month-view';\nimport { OuiMultiYearView, yearsPerPage } from './multi-year-view';\nimport { OuiYearView } from './year-view';\nimport { OuiCalendarCellCssClasses } from './calendar-body';\nimport { DateAdapter } from './date-adapter';\nimport { OuiDateFormats, OUI_DATE_FORMATS } from './date-formats';\nimport { OuiIconRegistry } from '../icon/icon-registery';\nimport { DomSanitizer } from '@angular/platform-browser';\nimport { ICONS } from '../core/shared/icons';\n\n/**\n * Possible views for the calendar.\n */\nexport type OuiCalendarView = 'month' | 'year' | 'multi-year';\n\n/**\n * A calendar that is used as part of the datepicker.\n */\n@Component({\n selector: 'oui-calendar',\n templateUrl: 'calendar.html',\n styleUrls: ['calendar.scss'],\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-calendar',\n },\n exportAs: 'ouiCalendar',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class OuiCalendar\n implements AfterContentInit, AfterViewChecked, OnDestroy, OnChanges\n{\n /** An input indicating the type of the header component, if set. */\n @Input() headerComponent: ComponentType;\n\n /** A portal containing the header component type for this calendar. */\n _calendarHeaderPortal: Portal;\n\n private _intlChanges: Subscription;\n\n /**\n * Used for scheduling that focus should be moved to the active cell on the next tick.\n * We need to schedule it, rather than do it immediately, because we have to wait\n * for Angular to re-evaluate the view children.\n */\n private _moveFocusOnNextTick = false;\n\n /** A date representing the period (month or year) to start the calendar in. */\n @Input()\n get startAt(): D | null {\n return this._startAt;\n }\n set startAt(value: D | null) {\n this._startAt = this._getValidDateOrNull(\n this._dateAdapter.deserialize(value)\n );\n }\n private _startAt: D | null;\n\n /** Whether the calendar should be started in month or year view. */\n @Input() startView: OuiCalendarView = 'month';\n\n /** The currently selected date. */\n @Input()\n get selected(): D | null {\n return this._selected;\n }\n set selected(value: D | null) {\n this._selected = this._getValidDateOrNull(\n this._dateAdapter.deserialize(value)\n );\n }\n private _selected: D | null;\n\n /** The minimum selectable date. */\n @Input()\n get minDate(): D | null {\n return this._minDate;\n }\n set minDate(value: D | null) {\n this._minDate = this._getValidDateOrNull(\n this._dateAdapter.deserialize(value)\n );\n }\n private _minDate: D | null;\n\n /** The maximum selectable date. */\n @Input()\n get maxDate(): D | null {\n return this._maxDate;\n }\n set maxDate(value: D | null) {\n this._maxDate = this._getValidDateOrNull(\n this._dateAdapter.deserialize(value)\n );\n }\n private _maxDate: D | null;\n\n /** Function used to filter which dates are selectable. */\n @Input() dateFilter: (date: D) => boolean;\n\n /** Function that can be used to add custom CSS classes to dates. */\n @Input() dateClass: (date: D) => OuiCalendarCellCssClasses;\n\n /** Emits when the currently selected date changes. */\n @Output() readonly selectedChange: EventEmitter = new EventEmitter();\n\n /**\n * Emits the year chosen in multiyear view.\n * This doesn't imply a change on the selected date.\n */\n @Output() readonly yearSelected: EventEmitter = new EventEmitter();\n\n /**\n * Emits the month chosen in year view.\n * This doesn't imply a change on the selected date.\n */\n @Output() readonly monthSelected: EventEmitter = new EventEmitter();\n\n /** Emits when any date is selected. */\n @Output()\n readonly _userSelection: EventEmitter = new EventEmitter();\n\n /** Reference to the current month view component. */\n @ViewChild(OuiMonthView) monthView: OuiMonthView;\n\n /** Reference to the current year view component. */\n @ViewChild(OuiYearView) yearView: OuiYearView;\n\n /** Reference to the current multi-year view component. */\n @ViewChild(OuiMultiYearView)\n multiYearView: OuiMultiYearView;\n\n /**\n * The current active date. This determines which time period is shown and which date is\n * highlighted when using keyboard navigation.\n */\n get activeDate(): D {\n return this._clampedActiveDate;\n }\n set activeDate(value: D) {\n this._clampedActiveDate = this._dateAdapter.clampDate(\n value,\n this.minDate,\n this.maxDate\n );\n this.stateChanges.next();\n }\n private _clampedActiveDate: D;\n\n /** Whether the calendar is in month view. */\n get currentView(): OuiCalendarView {\n return this._currentView;\n }\n set currentView(value: OuiCalendarView) {\n this._currentView = value;\n this._moveFocusOnNextTick = true;\n }\n private _currentView: OuiCalendarView;\n\n /**\n * Emits whenever there is a state change that the header may need to respond to.\n */\n stateChanges = new Subject();\n\n constructor(\n _intl: OuiDatepickerIntl,\n @Optional() private _dateAdapter: DateAdapter,\n @Optional() @Inject(OUI_DATE_FORMATS) private _dateFormats: OuiDateFormats,\n private _changeDetectorRef: ChangeDetectorRef\n ) {\n if (!this._dateAdapter) {\n throw createMissingDateImplError('DateAdapter');\n }\n\n if (!this._dateFormats) {\n throw createMissingDateImplError('OuiDATE_FORMATS');\n }\n\n this._intlChanges = _intl.changes.subscribe(() => {\n _changeDetectorRef.markForCheck();\n this.stateChanges.next();\n });\n }\n\n ngAfterContentInit() {\n this._calendarHeaderPortal = new ComponentPortal(\n this.headerComponent || OuiCalendarHeader\n );\n this.activeDate = this.startAt || this._dateAdapter.today();\n\n // Assign to the private property since we don't want to move focus on init.\n this._currentView = this.startView;\n }\n\n ngAfterViewChecked() {\n if (this._moveFocusOnNextTick) {\n this._moveFocusOnNextTick = false;\n this.focusActiveCell();\n }\n }\n\n ngOnDestroy() {\n this._intlChanges.unsubscribe();\n this.stateChanges.complete();\n }\n\n ngOnChanges(changes: SimpleChanges) {\n const change = changes.minDate || changes.maxDate || changes.dateFilter;\n\n if (change && !change.firstChange) {\n const view = this._getCurrentViewComponent();\n\n if (view) {\n // We need to `detectChanges` manually here, because the `minDate`, `maxDate` etc. are\n // passed down to the view via data bindings which won't be up-to-date when we call `_init`.\n this._changeDetectorRef.detectChanges();\n view._init();\n }\n }\n\n this.stateChanges.next();\n }\n\n focusActiveCell() {\n this._getCurrentViewComponent()._focusActiveCell();\n }\n\n /** Updates today's date after an update of the active date */\n updateTodaysDate() {\n const view =\n this.currentView === 'month'\n ? this.monthView\n : this.currentView === 'year'\n ? this.yearView\n : this.multiYearView;\n\n view.ngAfterContentInit();\n }\n\n /** Handles date selection in the month view. */\n _dateSelected(date: D): void {\n if (!this._dateAdapter.sameDate(date, this.selected)) {\n this.selectedChange.emit(date);\n }\n }\n\n /** Handles year selection in the multiyear view. */\n _yearSelectedInMultiYearView(normalizedYear: D) {\n this.yearSelected.emit(normalizedYear);\n }\n\n /** Handles month selection in the year view. */\n _monthSelectedInYearView(normalizedMonth: D) {\n this.monthSelected.emit(normalizedMonth);\n }\n\n _userSelected(): void {\n this._userSelection.emit();\n }\n\n /** Handles year/month selection in the multi-year/year views. */\n _goToDateInView(date: D, view: 'month' | 'year' | 'multi-year'): void {\n this.activeDate = date;\n this.currentView = view;\n }\n\n /**\n * @param obj The object to check.\n * @returns The given object if it is both a date instance and valid, otherwise null.\n */\n private _getValidDateOrNull(obj: any): D | null {\n return this._dateAdapter.isDateInstance(obj) &&\n this._dateAdapter.isValid(obj as any as D)\n ? obj\n : null;\n }\n\n /** Returns the component instance that corresponds to the current calendar view. */\n private _getCurrentViewComponent() {\n return this.monthView || this.yearView || this.multiYearView;\n }\n}\n\n/** Default header for OuiCalendar */\n@Component({\n selector: 'oui-calendar-header',\n templateUrl: 'calendar-header.html',\n exportAs: 'ouiCalendarHeader',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class OuiCalendarHeader {\n constructor(\n private _intl: OuiDatepickerIntl,\n @Inject(forwardRef(() => OuiCalendar)) public calendar: OuiCalendar,\n @Optional() private _dateAdapter: DateAdapter,\n changeDetectorRef: ChangeDetectorRef,\n private ouiIconRegistry: OuiIconRegistry,\n private domSanitizer: DomSanitizer\n ) {\n this.calendar.stateChanges.subscribe(() =>\n changeDetectorRef.markForCheck()\n );\n this.ouiIconRegistry.addSvgIconLiteral(\n `arrow-icon`,\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.ARROW_ICON)\n );\n }\n\n /** The label for the current calendar view. */\n get periodButtonText(): string {\n if (this.calendar.currentView === 'month') {\n return (\n this._dateAdapter.getMonthNames('short')[\n this._dateAdapter.getMonth(this.calendar.activeDate)\n ] +\n ' ' +\n this._dateAdapter.getYearName(this.calendar.activeDate)\n );\n }\n if (this.calendar.currentView === 'year') {\n return this._dateAdapter.getYearName(this.calendar.activeDate);\n }\n const activeYear = this._dateAdapter.getYear(this.calendar.activeDate);\n const firstYearInView = this._dateAdapter.getYearName(\n this._dateAdapter.createDate(activeYear - (activeYear % 24), 0, 1)\n );\n const lastYearInView = this._dateAdapter.getYearName(\n this._dateAdapter.createDate(\n activeYear + yearsPerPage - 1 - (activeYear % 24),\n 0,\n 1\n )\n );\n return `${firstYearInView} \\u2013 ${lastYearInView}`;\n }\n\n get periodButtonLabel(): string {\n return this.calendar.currentView === 'month'\n ? this._intl.switchToMultiYearViewLabel\n : this._intl.switchToMonthViewLabel;\n }\n\n /** The label for the the previous button. */\n get prevButtonLabel(): string {\n return {\n month: this._intl.prevMonthLabel,\n year: this._intl.prevYearLabel,\n 'multi-year': this._intl.prevMultiYearLabel,\n }[this.calendar.currentView];\n }\n\n /** The label for the the next button. */\n get nextButtonLabel(): string {\n return {\n month: this._intl.nextMonthLabel,\n year: this._intl.nextYearLabel,\n 'multi-year': this._intl.nextMultiYearLabel,\n }[this.calendar.currentView];\n }\n\n /** Handles user clicks on the period label. */\n currentPeriodClicked(): void {\n this.calendar.currentView =\n this.calendar.currentView === 'month' ? 'multi-year' : 'month';\n }\n\n /** Handles user clicks on the previous button. */\n previousClicked(): void {\n this.calendar.activeDate =\n this.calendar.currentView === 'month'\n ? this._dateAdapter.addCalendarMonths(this.calendar.activeDate, -1)\n : this._dateAdapter.addCalendarYears(\n this.calendar.activeDate,\n this.calendar.currentView === 'year' ? -1 : -yearsPerPage\n );\n }\n\n /** Handles user clicks on the next button. */\n nextClicked(): void {\n this.calendar.activeDate =\n this.calendar.currentView === 'month'\n ? this._dateAdapter.addCalendarMonths(this.calendar.activeDate, 1)\n : this._dateAdapter.addCalendarYears(\n this.calendar.activeDate,\n this.calendar.currentView === 'year' ? 1 : yearsPerPage\n );\n }\n\n /** Whether the previous period button is enabled. */\n previousEnabled(): boolean {\n if (!this.calendar.minDate) {\n return true;\n }\n return (\n !this.calendar.minDate ||\n !this._isSameView(this.calendar.activeDate, this.calendar.minDate)\n );\n }\n\n /** Whether the next period button is enabled. */\n nextEnabled(): boolean {\n return (\n !this.calendar.maxDate ||\n !this._isSameView(this.calendar.activeDate, this.calendar.maxDate)\n );\n }\n\n /** Whether the two dates represent the same view in the current view mode (month or year). */\n private _isSameView(date1: D, date2: D): boolean {\n if (this.calendar.currentView === 'month') {\n return (\n this._dateAdapter.getYear(date1) === this._dateAdapter.getYear(date2) &&\n this._dateAdapter.getMonth(date1) === this._dateAdapter.getMonth(date2)\n );\n }\n if (this.calendar.currentView === 'year') {\n return (\n this._dateAdapter.getYear(date1) === this._dateAdapter.getYear(date2)\n );\n }\n // Otherwise we are in 'multi-year' view.\n return (\n Math.floor(this._dateAdapter.getYear(date1) / yearsPerPage) ===\n Math.floor(this._dateAdapter.getYear(date2) / yearsPerPage)\n );\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": "// @import '../core/style/layout-common';\r\n\r\n$oui-calendar-padding: 8px !default;\r\n$oui-calendar-header-divider-width: 1px !default;\r\n$oui-calendar-controls-vertical-padding: 5%;\r\n// We use the same padding as the month / year label, but subtract 16px since there is padding\r\n// between the edge of the button and the text. This ensures that the button text lines up with\r\n// the month / year label text.\r\n$oui-calendar-controls-side-margin: calc(33% / 7 - 13px);\r\n\r\n$oui-calendar-arrow-size: 5px !default;\r\n$oui-calendar-arrow-disabled-opacity: 0.5 !default;\r\n\r\n// Values chosen to approximate https://material.io/icons/#ic_navigate_before and\r\n// https://material.io/icons/#ic_navigate_next as closely as possible.\r\n$oui-calendar-prev-next-icon-border-width: 2px;\r\n$oui-calendar-prev-next-icon-margin: 15.5px;\r\n$oui-calendar-prev-icon-transform: translateX(2px) rotate(-45deg);\r\n$oui-calendar-next-icon-transform: translateX(-2px) rotate(45deg);\r\n$oui-focused-background: #e8e8e8;\r\n\r\n.oui-calendar {\r\n display: block;\r\n}\r\n\r\n.oui-calendar-header {\r\n padding: $oui-calendar-padding $oui-calendar-padding 0 $oui-calendar-padding;\r\n}\r\n\r\n.oui-calendar-content {\r\n padding: 0 $oui-calendar-padding $oui-calendar-padding $oui-calendar-padding;\r\n outline: none;\r\n}\r\n\r\n.oui-calendar-controls {\r\n display: flex;\r\n margin: $oui-calendar-controls-vertical-padding\r\n $oui-calendar-controls-side-margin;\r\n .oui-icon-button {\r\n border-radius: 50%;\r\n &.cdk-keyboard-focused {\r\n background: $oui-focused-background;\r\n }\r\n }\r\n}\r\n.oui-calendar-body-active.oui-calendar-body-cell-content {\r\n border-radius: 2px;\r\n}\r\n.cdk-keyboard-focused\r\n .oui-calendar-body-active\r\n > .oui-calendar-body-cell-content:not(.oui-calendar-body-selected) {\r\n background-color: $oui-focused-background;\r\n}\r\n.oui-selected-focus {\r\n position: absolute;\r\n width: 90%;\r\n height: 90%;\r\n background: $oui-focused-background;\r\n left: 5%;\r\n top: 5%;\r\n border-radius: 2px;\r\n display: none;\r\n}\r\n.cdk-keyboard-focused .oui-calendar-body-active > .oui-selected-focus {\r\n display: block;\r\n}\r\n.oui-calendar-controls svg {\r\n height: 20px;\r\n width: 20px;\r\n margin: auto;\r\n}\r\n\r\n.oui-calendar-spacer {\r\n flex: 1 1 auto;\r\n}\r\n\r\n.oui-calendar-period-button {\r\n min-width: 0;\r\n color: inherit !important;\r\n padding-left: 15px;\r\n}\r\n.oui-calendar-period-button {\r\n background: none !important;\r\n border-radius: 2px !important;\r\n padding: 6px 8px 6px 13px !important;\r\n}\r\n.oui-calendar-period-button.cdk-keyboard-focused {\r\n background: $oui-focused-background !important;\r\n}\r\n\r\n.oui-calendar-arrow {\r\n display: inline-block;\r\n width: 20px;\r\n height: 20px;\r\n vertical-align: bottom;\r\n -webkit-transform: rotate(90deg);\r\n transform: rotate(90deg);\r\n\r\n &.oui-calendar-invert {\r\n transform: rotate(270deg);\r\n }\r\n\r\n [dir='rtl'] & {\r\n margin: 0 $oui-calendar-arrow-size 0 0;\r\n }\r\n}\r\n\r\n.oui-calendar-previous-button,\r\n.oui-calendar-next-button {\r\n padding: 0px;\r\n width: 36px;\r\n height: 36px;\r\n}\r\n.oui-calendar-table {\r\n border-spacing: 0;\r\n border-collapse: collapse;\r\n width: 100%;\r\n}\r\n\r\n.oui-calendar-table-header th {\r\n text-align: center;\r\n padding: 0 0 $oui-calendar-padding 0;\r\n}\r\n\r\n.oui-calendar-table-header-divider {\r\n position: relative;\r\n height: $oui-calendar-header-divider-width;\r\n\r\n // We use an absolutely positioned pseudo-element as the divider line for the table header so we\r\n // can extend it all the way to the edge of the calendar.\r\n &::after {\r\n content: '';\r\n position: absolute;\r\n top: 0;\r\n left: -$oui-calendar-padding;\r\n right: -$oui-calendar-padding;\r\n height: $oui-calendar-header-divider-width;\r\n }\r\n}\r\n", + "data": "// @import '../core/style/layout-common';\n\n$oui-calendar-padding: 8px !default;\n$oui-calendar-header-divider-width: 1px !default;\n$oui-calendar-controls-vertical-padding: 5%;\n// We use the same padding as the month / year label, but subtract 16px since there is padding\n// between the edge of the button and the text. This ensures that the button text lines up with\n// the month / year label text.\n$oui-calendar-controls-side-margin: calc(33% / 7 - 13px);\n\n$oui-calendar-arrow-size: 5px !default;\n$oui-calendar-arrow-disabled-opacity: 0.5 !default;\n\n// Values chosen to approximate https://material.io/icons/#ic_navigate_before and\n// https://material.io/icons/#ic_navigate_next as closely as possible.\n$oui-calendar-prev-next-icon-border-width: 2px;\n$oui-calendar-prev-next-icon-margin: 15.5px;\n$oui-calendar-prev-icon-transform: translateX(2px) rotate(-45deg);\n$oui-calendar-next-icon-transform: translateX(-2px) rotate(45deg);\n$oui-focused-background: #e8e8e8;\n\n.oui-calendar {\n display: block;\n}\n\n.oui-calendar-header {\n padding: $oui-calendar-padding $oui-calendar-padding 0 $oui-calendar-padding;\n}\n\n.oui-calendar-content {\n padding: 0 $oui-calendar-padding $oui-calendar-padding $oui-calendar-padding;\n outline: none;\n}\n\n.oui-calendar-controls {\n display: flex;\n margin: $oui-calendar-controls-vertical-padding\n $oui-calendar-controls-side-margin;\n .oui-icon-button {\n border-radius: 50%;\n &.cdk-keyboard-focused {\n background: $oui-focused-background;\n }\n }\n}\n.oui-calendar-body-active.oui-calendar-body-cell-content {\n border-radius: 2px;\n}\n.cdk-keyboard-focused\n .oui-calendar-body-active\n > .oui-calendar-body-cell-content:not(.oui-calendar-body-selected) {\n background-color: $oui-focused-background;\n}\n.oui-selected-focus {\n position: absolute;\n width: 90%;\n height: 90%;\n background: $oui-focused-background;\n left: 5%;\n top: 5%;\n border-radius: 2px;\n display: none;\n}\n.cdk-keyboard-focused .oui-calendar-body-active > .oui-selected-focus {\n display: block;\n}\n.oui-calendar-controls svg {\n height: 20px;\n width: 20px;\n margin: auto;\n}\n\n.oui-calendar-spacer {\n flex: 1 1 auto;\n}\n\n.oui-calendar-period-button {\n min-width: 0;\n color: inherit !important;\n padding-left: 15px;\n}\n.oui-calendar-period-button {\n background: none !important;\n border-radius: 2px !important;\n padding: 6px 8px 6px 13px !important;\n}\n.oui-calendar-period-button.cdk-keyboard-focused {\n background: $oui-focused-background !important;\n}\n\n.oui-calendar-arrow {\n display: inline-block;\n width: 20px;\n height: 20px;\n vertical-align: bottom;\n -webkit-transform: rotate(90deg);\n transform: rotate(90deg);\n\n &.oui-calendar-invert {\n transform: rotate(270deg);\n }\n\n [dir='rtl'] & {\n margin: 0 $oui-calendar-arrow-size 0 0;\n }\n}\n\n.oui-calendar-previous-button,\n.oui-calendar-next-button {\n padding: 0px;\n width: 36px;\n height: 36px;\n}\n.oui-calendar-table {\n border-spacing: 0;\n border-collapse: collapse;\n width: 100%;\n}\n\n.oui-calendar-table-header th {\n text-align: center;\n padding: 0 0 $oui-calendar-padding 0;\n}\n\n.oui-calendar-table-header-divider {\n position: relative;\n height: $oui-calendar-header-divider-width;\n\n // We use an absolutely positioned pseudo-element as the divider line for the table header so we\n // can extend it all the way to the edge of the calendar.\n &::after {\n content: '';\n position: absolute;\n top: 0;\n left: -$oui-calendar-padding;\n right: -$oui-calendar-padding;\n height: $oui-calendar-header-divider-width;\n }\n}\n", "styleUrl": "calendar.scss" } ], @@ -22265,11 +32123,11 @@ } } }, - "templateData": "\r\n\r\n\r\n \r\n \r\n\r\n \r\n \r\n\r\n \r\n \r\n\r\n" + "templateData": "\n\n\n \n \n\n \n \n\n \n \n\n" }, { "name": "OuiCalendarBody", - "id": "component-OuiCalendarBody-a86dcfa449c2b65edb14320b6d201cab30ccc12fdf58e5afa7555c9b5d3e35ad1a24a692e79a7c30e7ef3bc0b4780c08378ae95a5fe7c6aec5f7590fc0039454", + "id": "component-OuiCalendarBody-cbd01f04bc36230b9d48b75c996e1fe97f6732d38737e2367731d3bbff87f8b788215d2a0b2392fb5afc25bb3e49614443e100cfad1c2893c6cafee06228eea7", "file": "ui/src/components/datepicker/calendar-body.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -22395,7 +32253,7 @@ "type": "string", "optional": false, "description": "

Padding for the individual date cells.

\n", - "line": 91, + "line": 92, "rawdescription": "\nPadding for the individual date cells." }, { @@ -22405,7 +32263,7 @@ "type": "string", "optional": false, "description": "

Width of an individual cell.

\n", - "line": 94, + "line": 95, "rawdescription": "\nWidth of an individual cell." }, { @@ -22415,7 +32273,7 @@ "type": "number", "optional": false, "description": "

The number of blank cells to put at the beginning for the first row.

\n", - "line": 88, + "line": 89, "rawdescription": "\nThe number of blank cells to put at the beginning for the first row." } ], @@ -22433,7 +32291,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 101, + "line": 102, "deprecated": false, "deprecationMessage": "", "jsdoctags": [ @@ -22454,7 +32312,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 137, + "line": 138, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nFocuses the active cell after the microtask queue is empty.", @@ -22479,7 +32337,7 @@ "optional": false, "returnType": "boolean", "typeParameters": [], - "line": 125, + "line": 126, "deprecated": false, "deprecationMessage": "", "jsdoctags": [ @@ -22516,7 +32374,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 107, + "line": 108, "deprecated": false, "deprecationMessage": "", "jsdoctags": [ @@ -22539,11 +32397,11 @@ "description": "

An internal component used to display calendar data in a table.

\n", "rawdescription": "\n\nAn internal component used to display calendar data in a table.\n", "type": "component", - "sourceCode": "import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n ElementRef,\r\n EventEmitter,\r\n Input,\r\n Output,\r\n ViewEncapsulation,\r\n NgZone,\r\n OnChanges,\r\n SimpleChanges,\r\n} from '@angular/core';\r\nimport { take } from 'rxjs/operators';\r\n\r\n/**\r\n * Extra CSS classes that can be associated with a calendar cell.\r\n */\r\nexport type OuiCalendarCellCssClasses =\r\n | string\r\n | string[]\r\n | Set\r\n | { [key: string]: any };\r\n\r\n/**\r\n * An internal class that represents the data corresponding to a single calendar cell.\r\n */\r\nexport class OuiCalendarCell {\r\n constructor(\r\n public value: number,\r\n public displayValue: string,\r\n public ariaLabel: string,\r\n public enabled: boolean,\r\n public cssClasses?: OuiCalendarCellCssClasses\r\n ) {}\r\n}\r\n\r\n/**\r\n * An internal component used to display calendar data in a table.\r\n */\r\n@Component({\r\n // eslint-disable-next-line @angular-eslint/component-selector\r\n selector: '[oui-calendar-body]',\r\n templateUrl: 'calendar-body.html',\r\n styleUrls: ['calendar-body.scss'],\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-calendar-body',\r\n role: 'grid',\r\n 'aria-readonly': 'true',\r\n },\r\n exportAs: 'ouiCalendarBody',\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class OuiCalendarBody implements OnChanges {\r\n /** The label for the table. (e.g. \"Jan 2017\"). */\r\n @Input() label: string;\r\n\r\n /** The cells to display in the table. */\r\n @Input() rows: OuiCalendarCell[][];\r\n\r\n /** The value in the table that corresponds to today. */\r\n @Input() todayValue: number;\r\n\r\n /** The value in the table that is currently selected. */\r\n @Input() selectedValue: number;\r\n\r\n /** The minimum number of free cells needed to fit the label in the first row. */\r\n @Input() labelMinRequiredCells: number;\r\n\r\n /** The number of columns in the table. */\r\n @Input() numCols = 7;\r\n\r\n /** The cell number of the active cell in the table. */\r\n @Input() activeCell = 0;\r\n\r\n /**\r\n * The aspect ratio (width / height) to use for the cells in the table. This aspect ratio will be\r\n * maintained even as the table resizes.\r\n */\r\n @Input() cellAspectRatio = 1;\r\n\r\n /** Emits when a new value is selected. */\r\n @Output()\r\n readonly selectedValueChange: EventEmitter = new EventEmitter();\r\n\r\n /** The number of blank cells to put at the beginning for the first row. */\r\n _firstRowOffset: number;\r\n\r\n /** Padding for the individual date cells. */\r\n _cellPadding: string;\r\n\r\n /** Width of an individual cell. */\r\n _cellWidth: string;\r\n\r\n constructor(\r\n private _elementRef: ElementRef,\r\n private _ngZone: NgZone\r\n ) {}\r\n\r\n _cellClicked(cell: OuiCalendarCell): void {\r\n if (cell.enabled) {\r\n this.selectedValueChange.emit(cell.value);\r\n }\r\n }\r\n\r\n ngOnChanges(changes: SimpleChanges) {\r\n const columnChanges = changes.numCols;\r\n const { rows, numCols } = this;\r\n\r\n if (changes.rows || columnChanges) {\r\n this._firstRowOffset =\r\n rows && rows.length && rows[0].length ? numCols - rows[0].length : 0;\r\n }\r\n\r\n if (changes.cellAspectRatio || columnChanges || !this._cellPadding) {\r\n this._cellPadding = `${(50 * this.cellAspectRatio) / numCols}%`;\r\n }\r\n\r\n if (columnChanges || !this._cellWidth) {\r\n this._cellWidth = `${100 / numCols}%`;\r\n }\r\n }\r\n\r\n _isActiveCell(rowIndex: number, colIndex: number): boolean {\r\n let cellNumber = rowIndex * this.numCols + colIndex;\r\n\r\n // Account for the fact that the first row may not have as many cells.\r\n if (rowIndex) {\r\n cellNumber -= this._firstRowOffset;\r\n }\r\n\r\n return cellNumber === this.activeCell;\r\n }\r\n\r\n /** Focuses the active cell after the microtask queue is empty. */\r\n _focusActiveCell() {\r\n this._ngZone.runOutsideAngular(() => {\r\n this._ngZone.onStable\r\n .asObservable()\r\n .pipe(take(1))\r\n .subscribe(() => {\r\n const activeCell: HTMLElement | null =\r\n this._elementRef.nativeElement.querySelector(\r\n '.oui-calendar-body-active'\r\n );\r\n\r\n if (activeCell) {\r\n activeCell.focus();\r\n }\r\n });\r\n });\r\n }\r\n}\r\n", + "sourceCode": "import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n EventEmitter,\n Input,\n Output,\n ViewEncapsulation,\n NgZone,\n OnChanges,\n SimpleChanges,\n} from '@angular/core';\nimport { take } from 'rxjs/operators';\n\n/**\n * Extra CSS classes that can be associated with a calendar cell.\n */\nexport type OuiCalendarCellCssClasses =\n | string\n | string[]\n | Set\n | { [key: string]: any };\n\n/**\n * An internal class that represents the data corresponding to a single calendar cell.\n */\nexport class OuiCalendarCell {\n constructor(\n public value: number,\n public displayValue: string,\n public ariaLabel: string,\n public enabled: boolean,\n public cssClasses?: OuiCalendarCellCssClasses\n ) {}\n}\n\n/**\n * An internal component used to display calendar data in a table.\n */\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: '[oui-calendar-body]',\n templateUrl: 'calendar-body.html',\n styleUrls: ['calendar-body.scss'],\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-calendar-body',\n role: 'grid',\n 'aria-readonly': 'true',\n },\n exportAs: 'ouiCalendarBody',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class OuiCalendarBody implements OnChanges {\n /** The label for the table. (e.g. \"Jan 2017\"). */\n @Input() label: string;\n\n /** The cells to display in the table. */\n @Input() rows: OuiCalendarCell[][];\n\n /** The value in the table that corresponds to today. */\n @Input() todayValue: number;\n\n /** The value in the table that is currently selected. */\n @Input() selectedValue: number;\n\n /** The minimum number of free cells needed to fit the label in the first row. */\n @Input() labelMinRequiredCells: number;\n\n /** The number of columns in the table. */\n @Input() numCols = 7;\n\n /** The cell number of the active cell in the table. */\n @Input() activeCell = 0;\n\n /**\n * The aspect ratio (width / height) to use for the cells in the table. This aspect ratio will be\n * maintained even as the table resizes.\n */\n @Input() cellAspectRatio = 1;\n\n /** Emits when a new value is selected. */\n @Output()\n readonly selectedValueChange: EventEmitter =\n new EventEmitter();\n\n /** The number of blank cells to put at the beginning for the first row. */\n _firstRowOffset: number;\n\n /** Padding for the individual date cells. */\n _cellPadding: string;\n\n /** Width of an individual cell. */\n _cellWidth: string;\n\n constructor(\n private _elementRef: ElementRef,\n private _ngZone: NgZone\n ) {}\n\n _cellClicked(cell: OuiCalendarCell): void {\n if (cell.enabled) {\n this.selectedValueChange.emit(cell.value);\n }\n }\n\n ngOnChanges(changes: SimpleChanges) {\n const columnChanges = changes.numCols;\n const { rows, numCols } = this;\n\n if (changes.rows || columnChanges) {\n this._firstRowOffset =\n rows && rows.length && rows[0].length ? numCols - rows[0].length : 0;\n }\n\n if (changes.cellAspectRatio || columnChanges || !this._cellPadding) {\n this._cellPadding = `${(50 * this.cellAspectRatio) / numCols}%`;\n }\n\n if (columnChanges || !this._cellWidth) {\n this._cellWidth = `${100 / numCols}%`;\n }\n }\n\n _isActiveCell(rowIndex: number, colIndex: number): boolean {\n let cellNumber = rowIndex * this.numCols + colIndex;\n\n // Account for the fact that the first row may not have as many cells.\n if (rowIndex) {\n cellNumber -= this._firstRowOffset;\n }\n\n return cellNumber === this.activeCell;\n }\n\n /** Focuses the active cell after the microtask queue is empty. */\n _focusActiveCell() {\n this._ngZone.runOutsideAngular(() => {\n this._ngZone.onStable\n .asObservable()\n .pipe(take(1))\n .subscribe(() => {\n const activeCell: HTMLElement | null =\n this._elementRef.nativeElement.querySelector(\n '.oui-calendar-body-active'\n );\n\n if (activeCell) {\n activeCell.focus();\n }\n });\n });\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": "// @import '../../cdk/a11y/a11y';\r\n@use 'sass:math';\r\n\r\n$oui-calendar-body-label-padding-start: 5% !default;\r\n// We don't want the label to jump around when we switch between month and year views, so we use\r\n// the same amount of padding regardless of the number of columns. We align the header label with\r\n// the one third mark of the first cell, this was chosen somewhat arbitrarily to make it look\r\n// roughly like the mock. Half way is too far since the cell text is center aligned.\r\n$oui-calendar-body-label-side-padding: math.div(33%, 7) !default;\r\n$oui-calendar-body-cell-min-size: 32px !default;\r\n$oui-calendar-body-cell-content-margin: 5% !default;\r\n$oui-calendar-body-cell-content-border-width: 1px !default;\r\n$body-font-size: 13px;\r\n$oui-calendar-body-min-size: 7 * $oui-calendar-body-cell-min-size !default;\r\n$oui-calendar-body-cell-content-size: 100% -\r\n $oui-calendar-body-cell-content-margin * 2 !default;\r\n\r\n.oui-calendar-body {\r\n min-width: $oui-calendar-body-min-size;\r\n font-size: $body-font-size;\r\n}\r\n\r\n.oui-calendar-body-label {\r\n height: 0;\r\n line-height: 0;\r\n text-align: left;\r\n padding-left: $oui-calendar-body-label-side-padding;\r\n padding-right: $oui-calendar-body-label-side-padding;\r\n}\r\n\r\n.oui-calendar-body-cell {\r\n position: relative;\r\n height: 0;\r\n line-height: 0;\r\n text-align: center;\r\n outline: none;\r\n cursor: pointer;\r\n}\r\n\r\n.oui-calendar-body-disabled {\r\n cursor: default;\r\n}\r\n\r\n.oui-calendar-body-cell-content {\r\n position: absolute;\r\n top: $oui-calendar-body-cell-content-margin;\r\n left: $oui-calendar-body-cell-content-margin;\r\n\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n\r\n box-sizing: border-box;\r\n width: $oui-calendar-body-cell-content-size;\r\n height: $oui-calendar-body-cell-content-size;\r\n\r\n // Prevents text being off-center on Android.\r\n line-height: 1;\r\n\r\n border-width: $oui-calendar-body-cell-content-border-width;\r\n border-style: solid;\r\n\r\n // Choosing a value clearly larger than the height ensures we get the correct capsule shape.\r\n border-radius: 999px;\r\n}\r\n\r\n[dir='rtl'] {\r\n .oui-calendar-body-label {\r\n text-align: right;\r\n }\r\n}\r\n", + "data": "// @import '../../cdk/a11y/a11y';\n@use 'sass:math';\n\n$oui-calendar-body-label-padding-start: 5% !default;\n// We don't want the label to jump around when we switch between month and year views, so we use\n// the same amount of padding regardless of the number of columns. We align the header label with\n// the one third mark of the first cell, this was chosen somewhat arbitrarily to make it look\n// roughly like the mock. Half way is too far since the cell text is center aligned.\n$oui-calendar-body-label-side-padding: math.div(33%, 7) !default;\n$oui-calendar-body-cell-min-size: 32px !default;\n$oui-calendar-body-cell-content-margin: 5% !default;\n$oui-calendar-body-cell-content-border-width: 1px !default;\n$body-font-size: 13px;\n$oui-calendar-body-min-size: 7 * $oui-calendar-body-cell-min-size !default;\n$oui-calendar-body-cell-content-size: 100% -\n $oui-calendar-body-cell-content-margin * 2 !default;\n\n.oui-calendar-body {\n min-width: $oui-calendar-body-min-size;\n font-size: $body-font-size;\n}\n\n.oui-calendar-body-label {\n height: 0;\n line-height: 0;\n text-align: left;\n padding-left: $oui-calendar-body-label-side-padding;\n padding-right: $oui-calendar-body-label-side-padding;\n}\n\n.oui-calendar-body-cell {\n position: relative;\n height: 0;\n line-height: 0;\n text-align: center;\n outline: none;\n cursor: pointer;\n}\n\n.oui-calendar-body-disabled {\n cursor: default;\n}\n\n.oui-calendar-body-cell-content {\n position: absolute;\n top: $oui-calendar-body-cell-content-margin;\n left: $oui-calendar-body-cell-content-margin;\n\n display: flex;\n align-items: center;\n justify-content: center;\n\n box-sizing: border-box;\n width: $oui-calendar-body-cell-content-size;\n height: $oui-calendar-body-cell-content-size;\n\n // Prevents text being off-center on Android.\n line-height: 1;\n\n border-width: $oui-calendar-body-cell-content-border-width;\n border-style: solid;\n\n // Choosing a value clearly larger than the height ensures we get the correct capsule shape.\n border-radius: 999px;\n}\n\n[dir='rtl'] {\n .oui-calendar-body-label {\n text-align: right;\n }\n}\n", "styleUrl": "calendar-body.scss" } ], @@ -22567,7 +32425,7 @@ "deprecationMessage": "" } ], - "line": 94, + "line": 95, "jsdoctags": [ { "name": "_elementRef", @@ -22592,11 +32450,11 @@ "implements": [ "OnChanges" ], - "templateData": "\r\n\r\n \r\n \r\n \r\n \r\n \r\n {{item.displayValue}}\r\n \r\n \r\n\r\n" + "templateData": "\n\n \n \n \n \n \n {{item.displayValue}}\n \n \n\n" }, { "name": "OuiCalendarHeader", - "id": "component-OuiCalendarHeader-8f354118592827c1783232402ad1e8f7d0e2a62c504accf4daf43ab307d740eee08a679ef62cb14f156e051ef835d1a81db3c60d5888ffe84155bcd8a769911d", + "id": "component-OuiCalendarHeader-e8009240f3a519e7433edf1e6572fe9bd6f7f784a717c93166ba842a1125b01455109a31792fcd91db5d1de096039f310a9f6310f626e8f6829a34ebc5a09670", "file": "ui/src/components/datepicker/calendar.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -22753,7 +32611,7 @@ "description": "

Default header for OuiCalendar

\n", "rawdescription": "\nDefault header for OuiCalendar", "type": "component", - "sourceCode": "import { ComponentPortal, ComponentType, Portal } from '@angular/cdk/portal';\r\nimport {\r\n AfterContentInit,\r\n AfterViewChecked,\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n EventEmitter,\r\n forwardRef,\r\n Inject,\r\n Input,\r\n OnChanges,\r\n OnDestroy,\r\n Optional,\r\n Output,\r\n SimpleChanges,\r\n ViewChild,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\nimport { Subject, Subscription } from 'rxjs';\r\nimport { createMissingDateImplError } from './datepicker-errors';\r\nimport { OuiDatepickerIntl } from './datepicker-intl';\r\nimport { OuiMonthView } from './month-view';\r\nimport { OuiMultiYearView, yearsPerPage } from './multi-year-view';\r\nimport { OuiYearView } from './year-view';\r\nimport { OuiCalendarCellCssClasses } from './calendar-body';\r\nimport { DateAdapter } from './date-adapter';\r\nimport { OuiDateFormats, OUI_DATE_FORMATS } from './date-formats';\r\nimport { OuiIconRegistry } from '../icon/icon-registery';\r\nimport { DomSanitizer } from '@angular/platform-browser';\r\nimport { ICONS } from '../core/shared/icons';\r\n\r\n/**\r\n * Possible views for the calendar.\r\n */\r\nexport type OuiCalendarView = 'month' | 'year' | 'multi-year';\r\n\r\n/**\r\n * A calendar that is used as part of the datepicker.\r\n */\r\n@Component({\r\n selector: 'oui-calendar',\r\n templateUrl: 'calendar.html',\r\n styleUrls: ['calendar.scss'],\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-calendar',\r\n },\r\n exportAs: 'ouiCalendar',\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class OuiCalendar\r\n implements AfterContentInit, AfterViewChecked, OnDestroy, OnChanges\r\n{\r\n /** An input indicating the type of the header component, if set. */\r\n @Input() headerComponent: ComponentType;\r\n\r\n /** A portal containing the header component type for this calendar. */\r\n _calendarHeaderPortal: Portal;\r\n\r\n private _intlChanges: Subscription;\r\n\r\n /**\r\n * Used for scheduling that focus should be moved to the active cell on the next tick.\r\n * We need to schedule it, rather than do it immediately, because we have to wait\r\n * for Angular to re-evaluate the view children.\r\n */\r\n private _moveFocusOnNextTick = false;\r\n\r\n /** A date representing the period (month or year) to start the calendar in. */\r\n @Input()\r\n get startAt(): D | null {\r\n return this._startAt;\r\n }\r\n set startAt(value: D | null) {\r\n this._startAt = this._getValidDateOrNull(\r\n this._dateAdapter.deserialize(value)\r\n );\r\n }\r\n private _startAt: D | null;\r\n\r\n /** Whether the calendar should be started in month or year view. */\r\n @Input() startView: OuiCalendarView = 'month';\r\n\r\n /** The currently selected date. */\r\n @Input()\r\n get selected(): D | null {\r\n return this._selected;\r\n }\r\n set selected(value: D | null) {\r\n this._selected = this._getValidDateOrNull(\r\n this._dateAdapter.deserialize(value)\r\n );\r\n }\r\n private _selected: D | null;\r\n\r\n /** The minimum selectable date. */\r\n @Input()\r\n get minDate(): D | null {\r\n return this._minDate;\r\n }\r\n set minDate(value: D | null) {\r\n this._minDate = this._getValidDateOrNull(\r\n this._dateAdapter.deserialize(value)\r\n );\r\n }\r\n private _minDate: D | null;\r\n\r\n /** The maximum selectable date. */\r\n @Input()\r\n get maxDate(): D | null {\r\n return this._maxDate;\r\n }\r\n set maxDate(value: D | null) {\r\n this._maxDate = this._getValidDateOrNull(\r\n this._dateAdapter.deserialize(value)\r\n );\r\n }\r\n private _maxDate: D | null;\r\n\r\n /** Function used to filter which dates are selectable. */\r\n @Input() dateFilter: (date: D) => boolean;\r\n\r\n /** Function that can be used to add custom CSS classes to dates. */\r\n @Input() dateClass: (date: D) => OuiCalendarCellCssClasses;\r\n\r\n /** Emits when the currently selected date changes. */\r\n @Output() readonly selectedChange: EventEmitter = new EventEmitter();\r\n\r\n /**\r\n * Emits the year chosen in multiyear view.\r\n * This doesn't imply a change on the selected date.\r\n */\r\n @Output() readonly yearSelected: EventEmitter = new EventEmitter();\r\n\r\n /**\r\n * Emits the month chosen in year view.\r\n * This doesn't imply a change on the selected date.\r\n */\r\n @Output() readonly monthSelected: EventEmitter = new EventEmitter();\r\n\r\n /** Emits when any date is selected. */\r\n @Output()\r\n readonly _userSelection: EventEmitter = new EventEmitter();\r\n\r\n /** Reference to the current month view component. */\r\n @ViewChild(OuiMonthView) monthView: OuiMonthView;\r\n\r\n /** Reference to the current year view component. */\r\n @ViewChild(OuiYearView) yearView: OuiYearView;\r\n\r\n /** Reference to the current multi-year view component. */\r\n @ViewChild(OuiMultiYearView)\r\n multiYearView: OuiMultiYearView;\r\n\r\n /**\r\n * The current active date. This determines which time period is shown and which date is\r\n * highlighted when using keyboard navigation.\r\n */\r\n get activeDate(): D {\r\n return this._clampedActiveDate;\r\n }\r\n set activeDate(value: D) {\r\n this._clampedActiveDate = this._dateAdapter.clampDate(\r\n value,\r\n this.minDate,\r\n this.maxDate\r\n );\r\n this.stateChanges.next();\r\n }\r\n private _clampedActiveDate: D;\r\n\r\n /** Whether the calendar is in month view. */\r\n get currentView(): OuiCalendarView {\r\n return this._currentView;\r\n }\r\n set currentView(value: OuiCalendarView) {\r\n this._currentView = value;\r\n this._moveFocusOnNextTick = true;\r\n }\r\n private _currentView: OuiCalendarView;\r\n\r\n /**\r\n * Emits whenever there is a state change that the header may need to respond to.\r\n */\r\n stateChanges = new Subject();\r\n\r\n constructor(\r\n _intl: OuiDatepickerIntl,\r\n @Optional() private _dateAdapter: DateAdapter,\r\n @Optional() @Inject(OUI_DATE_FORMATS) private _dateFormats: OuiDateFormats,\r\n private _changeDetectorRef: ChangeDetectorRef\r\n ) {\r\n if (!this._dateAdapter) {\r\n throw createMissingDateImplError('DateAdapter');\r\n }\r\n\r\n if (!this._dateFormats) {\r\n throw createMissingDateImplError('OuiDATE_FORMATS');\r\n }\r\n\r\n this._intlChanges = _intl.changes.subscribe(() => {\r\n _changeDetectorRef.markForCheck();\r\n this.stateChanges.next();\r\n });\r\n }\r\n\r\n ngAfterContentInit() {\r\n this._calendarHeaderPortal = new ComponentPortal(\r\n this.headerComponent || OuiCalendarHeader\r\n );\r\n this.activeDate = this.startAt || this._dateAdapter.today();\r\n\r\n // Assign to the private property since we don't want to move focus on init.\r\n this._currentView = this.startView;\r\n }\r\n\r\n ngAfterViewChecked() {\r\n if (this._moveFocusOnNextTick) {\r\n this._moveFocusOnNextTick = false;\r\n this.focusActiveCell();\r\n }\r\n }\r\n\r\n ngOnDestroy() {\r\n this._intlChanges.unsubscribe();\r\n this.stateChanges.complete();\r\n }\r\n\r\n ngOnChanges(changes: SimpleChanges) {\r\n const change = changes.minDate || changes.maxDate || changes.dateFilter;\r\n\r\n if (change && !change.firstChange) {\r\n const view = this._getCurrentViewComponent();\r\n\r\n if (view) {\r\n // We need to `detectChanges` manually here, because the `minDate`, `maxDate` etc. are\r\n // passed down to the view via data bindings which won't be up-to-date when we call `_init`.\r\n this._changeDetectorRef.detectChanges();\r\n view._init();\r\n }\r\n }\r\n\r\n this.stateChanges.next();\r\n }\r\n\r\n focusActiveCell() {\r\n this._getCurrentViewComponent()._focusActiveCell();\r\n }\r\n\r\n /** Updates today's date after an update of the active date */\r\n updateTodaysDate() {\r\n const view =\r\n this.currentView === 'month'\r\n ? this.monthView\r\n : this.currentView === 'year'\r\n ? this.yearView\r\n : this.multiYearView;\r\n\r\n view.ngAfterContentInit();\r\n }\r\n\r\n /** Handles date selection in the month view. */\r\n _dateSelected(date: D): void {\r\n if (!this._dateAdapter.sameDate(date, this.selected)) {\r\n this.selectedChange.emit(date);\r\n }\r\n }\r\n\r\n /** Handles year selection in the multiyear view. */\r\n _yearSelectedInMultiYearView(normalizedYear: D) {\r\n this.yearSelected.emit(normalizedYear);\r\n }\r\n\r\n /** Handles month selection in the year view. */\r\n _monthSelectedInYearView(normalizedMonth: D) {\r\n this.monthSelected.emit(normalizedMonth);\r\n }\r\n\r\n _userSelected(): void {\r\n this._userSelection.emit();\r\n }\r\n\r\n /** Handles year/month selection in the multi-year/year views. */\r\n _goToDateInView(date: D, view: 'month' | 'year' | 'multi-year'): void {\r\n this.activeDate = date;\r\n this.currentView = view;\r\n }\r\n\r\n /**\r\n * @param obj The object to check.\r\n * @returns The given object if it is both a date instance and valid, otherwise null.\r\n */\r\n private _getValidDateOrNull(obj: any): D | null {\r\n return this._dateAdapter.isDateInstance(obj) &&\r\n this._dateAdapter.isValid(obj as any as D)\r\n ? obj\r\n : null;\r\n }\r\n\r\n /** Returns the component instance that corresponds to the current calendar view. */\r\n private _getCurrentViewComponent() {\r\n return this.monthView || this.yearView || this.multiYearView;\r\n }\r\n}\r\n\r\n/** Default header for OuiCalendar */\r\n@Component({\r\n selector: 'oui-calendar-header',\r\n templateUrl: 'calendar-header.html',\r\n exportAs: 'ouiCalendarHeader',\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class OuiCalendarHeader {\r\n constructor(\r\n private _intl: OuiDatepickerIntl,\r\n @Inject(forwardRef(() => OuiCalendar)) public calendar: OuiCalendar,\r\n @Optional() private _dateAdapter: DateAdapter,\r\n changeDetectorRef: ChangeDetectorRef,\r\n private ouiIconRegistry: OuiIconRegistry,\r\n private domSanitizer: DomSanitizer\r\n ) {\r\n this.calendar.stateChanges.subscribe(() =>\r\n changeDetectorRef.markForCheck()\r\n );\r\n this.ouiIconRegistry.addSvgIconLiteral(\r\n `arrow-icon`,\r\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.ARROW_ICON)\r\n );\r\n }\r\n\r\n /** The label for the current calendar view. */\r\n get periodButtonText(): string {\r\n if (this.calendar.currentView === 'month') {\r\n return (\r\n this._dateAdapter.getMonthNames('short')[\r\n this._dateAdapter.getMonth(this.calendar.activeDate)\r\n ] +\r\n ' ' +\r\n this._dateAdapter.getYearName(this.calendar.activeDate)\r\n );\r\n }\r\n if (this.calendar.currentView === 'year') {\r\n return this._dateAdapter.getYearName(this.calendar.activeDate);\r\n }\r\n const activeYear = this._dateAdapter.getYear(this.calendar.activeDate);\r\n const firstYearInView = this._dateAdapter.getYearName(\r\n this._dateAdapter.createDate(activeYear - (activeYear % 24), 0, 1)\r\n );\r\n const lastYearInView = this._dateAdapter.getYearName(\r\n this._dateAdapter.createDate(\r\n activeYear + yearsPerPage - 1 - (activeYear % 24),\r\n 0,\r\n 1\r\n )\r\n );\r\n return `${firstYearInView} \\u2013 ${lastYearInView}`;\r\n }\r\n\r\n get periodButtonLabel(): string {\r\n return this.calendar.currentView === 'month'\r\n ? this._intl.switchToMultiYearViewLabel\r\n : this._intl.switchToMonthViewLabel;\r\n }\r\n\r\n /** The label for the the previous button. */\r\n get prevButtonLabel(): string {\r\n return {\r\n month: this._intl.prevMonthLabel,\r\n year: this._intl.prevYearLabel,\r\n 'multi-year': this._intl.prevMultiYearLabel,\r\n }[this.calendar.currentView];\r\n }\r\n\r\n /** The label for the the next button. */\r\n get nextButtonLabel(): string {\r\n return {\r\n month: this._intl.nextMonthLabel,\r\n year: this._intl.nextYearLabel,\r\n 'multi-year': this._intl.nextMultiYearLabel,\r\n }[this.calendar.currentView];\r\n }\r\n\r\n /** Handles user clicks on the period label. */\r\n currentPeriodClicked(): void {\r\n this.calendar.currentView =\r\n this.calendar.currentView === 'month' ? 'multi-year' : 'month';\r\n }\r\n\r\n /** Handles user clicks on the previous button. */\r\n previousClicked(): void {\r\n this.calendar.activeDate =\r\n this.calendar.currentView === 'month'\r\n ? this._dateAdapter.addCalendarMonths(this.calendar.activeDate, -1)\r\n : this._dateAdapter.addCalendarYears(\r\n this.calendar.activeDate,\r\n this.calendar.currentView === 'year' ? -1 : -yearsPerPage\r\n );\r\n }\r\n\r\n /** Handles user clicks on the next button. */\r\n nextClicked(): void {\r\n this.calendar.activeDate =\r\n this.calendar.currentView === 'month'\r\n ? this._dateAdapter.addCalendarMonths(this.calendar.activeDate, 1)\r\n : this._dateAdapter.addCalendarYears(\r\n this.calendar.activeDate,\r\n this.calendar.currentView === 'year' ? 1 : yearsPerPage\r\n );\r\n }\r\n\r\n /** Whether the previous period button is enabled. */\r\n previousEnabled(): boolean {\r\n if (!this.calendar.minDate) {\r\n return true;\r\n }\r\n return (\r\n !this.calendar.minDate ||\r\n !this._isSameView(this.calendar.activeDate, this.calendar.minDate)\r\n );\r\n }\r\n\r\n /** Whether the next period button is enabled. */\r\n nextEnabled(): boolean {\r\n return (\r\n !this.calendar.maxDate ||\r\n !this._isSameView(this.calendar.activeDate, this.calendar.maxDate)\r\n );\r\n }\r\n\r\n /** Whether the two dates represent the same view in the current view mode (month or year). */\r\n private _isSameView(date1: D, date2: D): boolean {\r\n if (this.calendar.currentView === 'month') {\r\n return (\r\n this._dateAdapter.getYear(date1) === this._dateAdapter.getYear(date2) &&\r\n this._dateAdapter.getMonth(date1) === this._dateAdapter.getMonth(date2)\r\n );\r\n }\r\n if (this.calendar.currentView === 'year') {\r\n return (\r\n this._dateAdapter.getYear(date1) === this._dateAdapter.getYear(date2)\r\n );\r\n }\r\n // Otherwise we are in 'multi-year' view.\r\n return (\r\n Math.floor(this._dateAdapter.getYear(date1) / yearsPerPage) ===\r\n Math.floor(this._dateAdapter.getYear(date2) / yearsPerPage)\r\n );\r\n }\r\n}\r\n", + "sourceCode": "import { ComponentPortal, ComponentType, Portal } from '@angular/cdk/portal';\nimport {\n AfterContentInit,\n AfterViewChecked,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n EventEmitter,\n forwardRef,\n Inject,\n Input,\n OnChanges,\n OnDestroy,\n Optional,\n Output,\n SimpleChanges,\n ViewChild,\n ViewEncapsulation,\n} from '@angular/core';\nimport { Subject, Subscription } from 'rxjs';\nimport { createMissingDateImplError } from './datepicker-errors';\nimport { OuiDatepickerIntl } from './datepicker-intl';\nimport { OuiMonthView } from './month-view';\nimport { OuiMultiYearView, yearsPerPage } from './multi-year-view';\nimport { OuiYearView } from './year-view';\nimport { OuiCalendarCellCssClasses } from './calendar-body';\nimport { DateAdapter } from './date-adapter';\nimport { OuiDateFormats, OUI_DATE_FORMATS } from './date-formats';\nimport { OuiIconRegistry } from '../icon/icon-registery';\nimport { DomSanitizer } from '@angular/platform-browser';\nimport { ICONS } from '../core/shared/icons';\n\n/**\n * Possible views for the calendar.\n */\nexport type OuiCalendarView = 'month' | 'year' | 'multi-year';\n\n/**\n * A calendar that is used as part of the datepicker.\n */\n@Component({\n selector: 'oui-calendar',\n templateUrl: 'calendar.html',\n styleUrls: ['calendar.scss'],\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-calendar',\n },\n exportAs: 'ouiCalendar',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class OuiCalendar\n implements AfterContentInit, AfterViewChecked, OnDestroy, OnChanges\n{\n /** An input indicating the type of the header component, if set. */\n @Input() headerComponent: ComponentType;\n\n /** A portal containing the header component type for this calendar. */\n _calendarHeaderPortal: Portal;\n\n private _intlChanges: Subscription;\n\n /**\n * Used for scheduling that focus should be moved to the active cell on the next tick.\n * We need to schedule it, rather than do it immediately, because we have to wait\n * for Angular to re-evaluate the view children.\n */\n private _moveFocusOnNextTick = false;\n\n /** A date representing the period (month or year) to start the calendar in. */\n @Input()\n get startAt(): D | null {\n return this._startAt;\n }\n set startAt(value: D | null) {\n this._startAt = this._getValidDateOrNull(\n this._dateAdapter.deserialize(value)\n );\n }\n private _startAt: D | null;\n\n /** Whether the calendar should be started in month or year view. */\n @Input() startView: OuiCalendarView = 'month';\n\n /** The currently selected date. */\n @Input()\n get selected(): D | null {\n return this._selected;\n }\n set selected(value: D | null) {\n this._selected = this._getValidDateOrNull(\n this._dateAdapter.deserialize(value)\n );\n }\n private _selected: D | null;\n\n /** The minimum selectable date. */\n @Input()\n get minDate(): D | null {\n return this._minDate;\n }\n set minDate(value: D | null) {\n this._minDate = this._getValidDateOrNull(\n this._dateAdapter.deserialize(value)\n );\n }\n private _minDate: D | null;\n\n /** The maximum selectable date. */\n @Input()\n get maxDate(): D | null {\n return this._maxDate;\n }\n set maxDate(value: D | null) {\n this._maxDate = this._getValidDateOrNull(\n this._dateAdapter.deserialize(value)\n );\n }\n private _maxDate: D | null;\n\n /** Function used to filter which dates are selectable. */\n @Input() dateFilter: (date: D) => boolean;\n\n /** Function that can be used to add custom CSS classes to dates. */\n @Input() dateClass: (date: D) => OuiCalendarCellCssClasses;\n\n /** Emits when the currently selected date changes. */\n @Output() readonly selectedChange: EventEmitter = new EventEmitter();\n\n /**\n * Emits the year chosen in multiyear view.\n * This doesn't imply a change on the selected date.\n */\n @Output() readonly yearSelected: EventEmitter = new EventEmitter();\n\n /**\n * Emits the month chosen in year view.\n * This doesn't imply a change on the selected date.\n */\n @Output() readonly monthSelected: EventEmitter = new EventEmitter();\n\n /** Emits when any date is selected. */\n @Output()\n readonly _userSelection: EventEmitter = new EventEmitter();\n\n /** Reference to the current month view component. */\n @ViewChild(OuiMonthView) monthView: OuiMonthView;\n\n /** Reference to the current year view component. */\n @ViewChild(OuiYearView) yearView: OuiYearView;\n\n /** Reference to the current multi-year view component. */\n @ViewChild(OuiMultiYearView)\n multiYearView: OuiMultiYearView;\n\n /**\n * The current active date. This determines which time period is shown and which date is\n * highlighted when using keyboard navigation.\n */\n get activeDate(): D {\n return this._clampedActiveDate;\n }\n set activeDate(value: D) {\n this._clampedActiveDate = this._dateAdapter.clampDate(\n value,\n this.minDate,\n this.maxDate\n );\n this.stateChanges.next();\n }\n private _clampedActiveDate: D;\n\n /** Whether the calendar is in month view. */\n get currentView(): OuiCalendarView {\n return this._currentView;\n }\n set currentView(value: OuiCalendarView) {\n this._currentView = value;\n this._moveFocusOnNextTick = true;\n }\n private _currentView: OuiCalendarView;\n\n /**\n * Emits whenever there is a state change that the header may need to respond to.\n */\n stateChanges = new Subject();\n\n constructor(\n _intl: OuiDatepickerIntl,\n @Optional() private _dateAdapter: DateAdapter,\n @Optional() @Inject(OUI_DATE_FORMATS) private _dateFormats: OuiDateFormats,\n private _changeDetectorRef: ChangeDetectorRef\n ) {\n if (!this._dateAdapter) {\n throw createMissingDateImplError('DateAdapter');\n }\n\n if (!this._dateFormats) {\n throw createMissingDateImplError('OuiDATE_FORMATS');\n }\n\n this._intlChanges = _intl.changes.subscribe(() => {\n _changeDetectorRef.markForCheck();\n this.stateChanges.next();\n });\n }\n\n ngAfterContentInit() {\n this._calendarHeaderPortal = new ComponentPortal(\n this.headerComponent || OuiCalendarHeader\n );\n this.activeDate = this.startAt || this._dateAdapter.today();\n\n // Assign to the private property since we don't want to move focus on init.\n this._currentView = this.startView;\n }\n\n ngAfterViewChecked() {\n if (this._moveFocusOnNextTick) {\n this._moveFocusOnNextTick = false;\n this.focusActiveCell();\n }\n }\n\n ngOnDestroy() {\n this._intlChanges.unsubscribe();\n this.stateChanges.complete();\n }\n\n ngOnChanges(changes: SimpleChanges) {\n const change = changes.minDate || changes.maxDate || changes.dateFilter;\n\n if (change && !change.firstChange) {\n const view = this._getCurrentViewComponent();\n\n if (view) {\n // We need to `detectChanges` manually here, because the `minDate`, `maxDate` etc. are\n // passed down to the view via data bindings which won't be up-to-date when we call `_init`.\n this._changeDetectorRef.detectChanges();\n view._init();\n }\n }\n\n this.stateChanges.next();\n }\n\n focusActiveCell() {\n this._getCurrentViewComponent()._focusActiveCell();\n }\n\n /** Updates today's date after an update of the active date */\n updateTodaysDate() {\n const view =\n this.currentView === 'month'\n ? this.monthView\n : this.currentView === 'year'\n ? this.yearView\n : this.multiYearView;\n\n view.ngAfterContentInit();\n }\n\n /** Handles date selection in the month view. */\n _dateSelected(date: D): void {\n if (!this._dateAdapter.sameDate(date, this.selected)) {\n this.selectedChange.emit(date);\n }\n }\n\n /** Handles year selection in the multiyear view. */\n _yearSelectedInMultiYearView(normalizedYear: D) {\n this.yearSelected.emit(normalizedYear);\n }\n\n /** Handles month selection in the year view. */\n _monthSelectedInYearView(normalizedMonth: D) {\n this.monthSelected.emit(normalizedMonth);\n }\n\n _userSelected(): void {\n this._userSelection.emit();\n }\n\n /** Handles year/month selection in the multi-year/year views. */\n _goToDateInView(date: D, view: 'month' | 'year' | 'multi-year'): void {\n this.activeDate = date;\n this.currentView = view;\n }\n\n /**\n * @param obj The object to check.\n * @returns The given object if it is both a date instance and valid, otherwise null.\n */\n private _getValidDateOrNull(obj: any): D | null {\n return this._dateAdapter.isDateInstance(obj) &&\n this._dateAdapter.isValid(obj as any as D)\n ? obj\n : null;\n }\n\n /** Returns the component instance that corresponds to the current calendar view. */\n private _getCurrentViewComponent() {\n return this.monthView || this.yearView || this.multiYearView;\n }\n}\n\n/** Default header for OuiCalendar */\n@Component({\n selector: 'oui-calendar-header',\n templateUrl: 'calendar-header.html',\n exportAs: 'ouiCalendarHeader',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class OuiCalendarHeader {\n constructor(\n private _intl: OuiDatepickerIntl,\n @Inject(forwardRef(() => OuiCalendar)) public calendar: OuiCalendar,\n @Optional() private _dateAdapter: DateAdapter,\n changeDetectorRef: ChangeDetectorRef,\n private ouiIconRegistry: OuiIconRegistry,\n private domSanitizer: DomSanitizer\n ) {\n this.calendar.stateChanges.subscribe(() =>\n changeDetectorRef.markForCheck()\n );\n this.ouiIconRegistry.addSvgIconLiteral(\n `arrow-icon`,\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.ARROW_ICON)\n );\n }\n\n /** The label for the current calendar view. */\n get periodButtonText(): string {\n if (this.calendar.currentView === 'month') {\n return (\n this._dateAdapter.getMonthNames('short')[\n this._dateAdapter.getMonth(this.calendar.activeDate)\n ] +\n ' ' +\n this._dateAdapter.getYearName(this.calendar.activeDate)\n );\n }\n if (this.calendar.currentView === 'year') {\n return this._dateAdapter.getYearName(this.calendar.activeDate);\n }\n const activeYear = this._dateAdapter.getYear(this.calendar.activeDate);\n const firstYearInView = this._dateAdapter.getYearName(\n this._dateAdapter.createDate(activeYear - (activeYear % 24), 0, 1)\n );\n const lastYearInView = this._dateAdapter.getYearName(\n this._dateAdapter.createDate(\n activeYear + yearsPerPage - 1 - (activeYear % 24),\n 0,\n 1\n )\n );\n return `${firstYearInView} \\u2013 ${lastYearInView}`;\n }\n\n get periodButtonLabel(): string {\n return this.calendar.currentView === 'month'\n ? this._intl.switchToMultiYearViewLabel\n : this._intl.switchToMonthViewLabel;\n }\n\n /** The label for the the previous button. */\n get prevButtonLabel(): string {\n return {\n month: this._intl.prevMonthLabel,\n year: this._intl.prevYearLabel,\n 'multi-year': this._intl.prevMultiYearLabel,\n }[this.calendar.currentView];\n }\n\n /** The label for the the next button. */\n get nextButtonLabel(): string {\n return {\n month: this._intl.nextMonthLabel,\n year: this._intl.nextYearLabel,\n 'multi-year': this._intl.nextMultiYearLabel,\n }[this.calendar.currentView];\n }\n\n /** Handles user clicks on the period label. */\n currentPeriodClicked(): void {\n this.calendar.currentView =\n this.calendar.currentView === 'month' ? 'multi-year' : 'month';\n }\n\n /** Handles user clicks on the previous button. */\n previousClicked(): void {\n this.calendar.activeDate =\n this.calendar.currentView === 'month'\n ? this._dateAdapter.addCalendarMonths(this.calendar.activeDate, -1)\n : this._dateAdapter.addCalendarYears(\n this.calendar.activeDate,\n this.calendar.currentView === 'year' ? -1 : -yearsPerPage\n );\n }\n\n /** Handles user clicks on the next button. */\n nextClicked(): void {\n this.calendar.activeDate =\n this.calendar.currentView === 'month'\n ? this._dateAdapter.addCalendarMonths(this.calendar.activeDate, 1)\n : this._dateAdapter.addCalendarYears(\n this.calendar.activeDate,\n this.calendar.currentView === 'year' ? 1 : yearsPerPage\n );\n }\n\n /** Whether the previous period button is enabled. */\n previousEnabled(): boolean {\n if (!this.calendar.minDate) {\n return true;\n }\n return (\n !this.calendar.minDate ||\n !this._isSameView(this.calendar.activeDate, this.calendar.minDate)\n );\n }\n\n /** Whether the next period button is enabled. */\n nextEnabled(): boolean {\n return (\n !this.calendar.maxDate ||\n !this._isSameView(this.calendar.activeDate, this.calendar.maxDate)\n );\n }\n\n /** Whether the two dates represent the same view in the current view mode (month or year). */\n private _isSameView(date1: D, date2: D): boolean {\n if (this.calendar.currentView === 'month') {\n return (\n this._dateAdapter.getYear(date1) === this._dateAdapter.getYear(date2) &&\n this._dateAdapter.getMonth(date1) === this._dateAdapter.getMonth(date2)\n );\n }\n if (this.calendar.currentView === 'year') {\n return (\n this._dateAdapter.getYear(date1) === this._dateAdapter.getYear(date2)\n );\n }\n // Otherwise we are in 'multi-year' view.\n return (\n Math.floor(this._dateAdapter.getYear(date1) / yearsPerPage) ===\n Math.floor(this._dateAdapter.getYear(date2) / yearsPerPage)\n );\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -22902,11 +32760,11 @@ } } }, - "templateData": "
\r\n
\r\n \r\n {{ periodButtonText }}\r\n\r\n \r\n \r\n\r\n
\r\n\r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n
\r\n
\r\n" + "templateData": "
\n
\n \n {{ periodButtonText }}\n\n \n \n\n
\n\n \n\n \n \n \n \n \n\n \n \n \n \n \n
\n
\n" }, { "name": "OuiDatepicker", - "id": "component-OuiDatepicker-458b78a894cb4d88101a90002d3b7edc97b300510a4999d795203320d180aee30d56532fe76c2af4852ef6b87a0541032cec5768225b8729dfe1aaf5772456eb", + "id": "component-OuiDatepicker-ef2f443418fcda96544206a6e7f04650559ef230ba716a2fcd192fcccff22a5183bd00b300ca1bd0840b2bdab9f680685ecce9b42c0d3c890966f004ff76d557", "file": "ui/src/components/datepicker/datepicker.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -23340,8 +33198,8 @@ "jsdoctags": [ { "name": { - "pos": 17806, - "end": 17809, + "pos": 17260, + "end": 17263, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -23352,8 +33210,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 17800, - "end": 17805, + "pos": 17254, + "end": 17259, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -23364,8 +33222,8 @@ }, { "tagName": { - "pos": 17838, - "end": 17845, + "pos": 17291, + "end": 17298, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -23427,8 +33285,8 @@ "jsdoctags": [ { "name": { - "pos": 11677, - "end": 11682, + "pos": 11329, + "end": 11334, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -23439,8 +33297,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 11671, - "end": 11676, + "pos": 11323, + "end": 11328, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -23598,7 +33456,7 @@ "description": "

Component responsible for managing the datepicker popup/dialog.

\n", "rawdescription": "\nComponent responsible for managing the datepicker popup/dialog.", "type": "component", - "sourceCode": "import { Directionality } from '@angular/cdk/bidi';\r\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\r\nimport { ESCAPE, UP_ARROW } from '@angular/cdk/keycodes';\r\nimport {\r\n Overlay,\r\n OverlayConfig,\r\n OverlayRef,\r\n PositionStrategy,\r\n ScrollStrategy,\r\n} from '@angular/cdk/overlay';\r\nimport { ComponentPortal, ComponentType } from '@angular/cdk/portal';\r\nimport { DOCUMENT } from '@angular/common';\r\nimport {\r\n AfterViewInit,\r\n ChangeDetectionStrategy,\r\n Component,\r\n ComponentRef,\r\n ElementRef,\r\n EventEmitter,\r\n Inject,\r\n InjectionToken,\r\n Input,\r\n NgZone,\r\n OnDestroy,\r\n Optional,\r\n Output,\r\n ViewChild,\r\n ViewContainerRef,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\nimport { FocusMonitor } from '@angular/cdk/a11y';\r\nimport { OuiDialog, OuiDialogRef } from '../dialog/public-api';\r\nimport { merge, Subject, Subscription } from 'rxjs';\r\nimport { filter, take } from 'rxjs/operators';\r\nimport { OuiCalendar } from './calendar';\r\nimport { ouiDatepickerAnimations } from './datepicker-animations';\r\nimport { createMissingDateImplError } from './datepicker-errors';\r\nimport { OuiDatepickerInput } from './datepicker-input';\r\nimport { OuiCalendarCellCssClasses } from './calendar-body';\r\nimport { CanColorCtor, mixinColor, CanColor, ThemePalette } from '../core';\r\nimport { DateAdapter } from './date-adapter';\r\n\r\n/** Used to generate a unique ID for each datepicker instance. */\r\nlet datepickerUid = 0;\r\n\r\n/** Injection token that determines the scroll handling while the calendar is open. */\r\nexport const OUI_DATEPICKER_SCROLL_STRATEGY = new InjectionToken<\r\n () => ScrollStrategy\r\n>('oui-datepicker-scroll-strategy');\r\n\r\nexport function OUI_DATEPICKER_SCROLL_STRATEGY_FACTORY(\r\n overlay: Overlay\r\n): () => ScrollStrategy {\r\n return () => overlay.scrollStrategies.reposition();\r\n}\r\n\r\nexport const OUI_DATEPICKER_SCROLL_STRATEGY_FACTORY_PROVIDER = {\r\n provide: OUI_DATEPICKER_SCROLL_STRATEGY,\r\n deps: [Overlay],\r\n useFactory: OUI_DATEPICKER_SCROLL_STRATEGY_FACTORY,\r\n};\r\n\r\n// Boilerplate for applying mixins to OuiDatepickerContent.\r\nexport class OuiDatepickerContentBase {\r\n constructor(public _elementRef: ElementRef) {}\r\n}\r\nexport const _OuiDatepickerContentMixinBase: CanColorCtor &\r\n typeof OuiDatepickerContentBase = mixinColor(OuiDatepickerContentBase);\r\n\r\n/**\r\n * Component used as the content for the datepicker dialog and popup. We use this instead of using\r\n * OuiCalendar directly as the content so we can control the initial focus. This also gives us a\r\n * place to put additional features of the popup that are not part of the calendar itself in the\r\n * future. (e.g. confirmation buttons).\r\n */\r\n@Component({\r\n selector: 'oui-datepicker-content',\r\n templateUrl: 'datepicker-content.html',\r\n styleUrls: ['datepicker-content.scss'],\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-datepicker-content',\r\n '[@transformPanel]': '\"enter\"',\r\n '[class.oui-datepicker-content-touch]': 'datepicker.touchUi',\r\n },\r\n animations: [\r\n ouiDatepickerAnimations.transformPanel,\r\n ouiDatepickerAnimations.fadeInCalendar,\r\n ],\r\n exportAs: 'ouiDatepickerContent',\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['color'],\r\n})\r\nexport class OuiDatepickerContent\r\n extends _OuiDatepickerContentMixinBase\r\n implements AfterViewInit, CanColor\r\n{\r\n /** Reference to the internal calendar component. */\r\n @ViewChild(OuiCalendar) _calendar: OuiCalendar;\r\n\r\n /** Reference to the datepicker that created the overlay. */\r\n datepicker: OuiDatepicker;\r\n\r\n /** Whether the datepicker is above or below the input. */\r\n _isAbove: boolean;\r\n\r\n constructor(elementRef: ElementRef) {\r\n super(elementRef);\r\n }\r\n\r\n ngAfterViewInit() {\r\n this._calendar.focusActiveCell();\r\n }\r\n}\r\n\r\n// TODO(mmalerba): We use a component instead of a directive here so the user can use implicit\r\n// template reference variables (e.g. #d vs #d=\"ouiDatepicker\"). We can change this to a directive\r\n// if angular adds support for `exportAs: '$implicit'` on directives.\r\n/** Component responsible for managing the datepicker popup/dialog. */\r\n@Component({\r\n selector: 'oui-datepicker',\r\n template: '',\r\n exportAs: 'ouiDatepicker',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n '[class.oui-datepicker-disabled]': 'disabled',\r\n },\r\n})\r\nexport class OuiDatepicker implements OnDestroy, CanColor {\r\n private _scrollStrategy: () => ScrollStrategy;\r\n\r\n /** An input indicating the type of the custom header component for the calendar, if set. */\r\n @Input() calendarHeaderComponent: ComponentType;\r\n\r\n /** The date to open the calendar to initially. */\r\n @Input()\r\n get startAt(): D | null {\r\n // If an explicit startAt is set we start there, otherwise we start at whatever the currently\r\n // selected value is.\r\n return (\r\n this._startAt ||\r\n (this._datepickerInput ? this._datepickerInput.value : null)\r\n );\r\n }\r\n set startAt(value: D | null) {\r\n this._startAt = this._getValidDateOrNull(\r\n this._dateAdapter.deserialize(value)\r\n );\r\n }\r\n private _startAt: D | null;\r\n\r\n /** The view that the calendar should start in. */\r\n @Input() startView: 'month' | 'year' | 'multi-year' = 'month';\r\n\r\n /** Color palette to use on the datepicker's calendar. */\r\n @Input()\r\n get color(): ThemePalette {\r\n return (\r\n this._color ||\r\n (this._datepickerInput\r\n ? this._datepickerInput._getThemePalette()\r\n : undefined)\r\n );\r\n }\r\n set color(value: ThemePalette) {\r\n this._color = value;\r\n }\r\n _color: ThemePalette;\r\n\r\n /**\r\n * Whether the calendar UI is in touch mode. In touch mode the calendar opens in a dialog rather\r\n * than a popup and elements have more padding to allow for bigger touch targets.\r\n */\r\n @Input()\r\n get touchUi(): boolean {\r\n return this._touchUi;\r\n }\r\n set touchUi(value: boolean) {\r\n this._touchUi = coerceBooleanProperty(value);\r\n }\r\n private _touchUi = false;\r\n\r\n /** Whether the datepicker pop-up should be disabled. */\r\n @Input()\r\n get disabled(): boolean {\r\n return this._disabled === undefined && this._datepickerInput\r\n ? this._datepickerInput.disabled\r\n : !!this._disabled;\r\n }\r\n set disabled(value: boolean) {\r\n const newValue = coerceBooleanProperty(value);\r\n\r\n if (newValue !== this._disabled) {\r\n this._disabled = newValue;\r\n this._disabledChange.next(newValue);\r\n this._datepickerInput._datepickerDisabled = newValue;\r\n }\r\n }\r\n private _disabled = false;\r\n\r\n /**\r\n * Emits selected year in multiyear view.\r\n * This doesn't imply a change on the selected date.\r\n */\r\n @Output() readonly yearSelected: EventEmitter = new EventEmitter();\r\n\r\n /**\r\n * Emits selected month in year view.\r\n * This doesn't imply a change on the selected date.\r\n */\r\n @Output() readonly monthSelected: EventEmitter = new EventEmitter();\r\n\r\n /** Classes to be passed to the date picker panel. Supports the same syntax as `ngClass`. */\r\n @Input() panelClass: string | string[];\r\n\r\n /** Function that can be used to add custom CSS classes to dates. */\r\n @Input() dateClass: (date: D) => OuiCalendarCellCssClasses;\r\n\r\n /** Emits when the datepicker has been opened. */\r\n // eslint-disable-next-line @angular-eslint/no-output-rename\r\n @Output('opened') openedStream: EventEmitter = new EventEmitter();\r\n\r\n /** Emits when the datepicker has been closed. */\r\n // eslint-disable-next-line @angular-eslint/no-output-rename\r\n @Output('closed') closedStream: EventEmitter = new EventEmitter();\r\n\r\n /** Whether the calendar is open. */\r\n @Input()\r\n get opened(): boolean {\r\n return this._opened;\r\n }\r\n set opened(value: boolean) {\r\n value ? this.open() : this.close();\r\n }\r\n private _opened = false;\r\n\r\n /** The id for the datepicker calendar. */\r\n id = `oui-datepicker-${datepickerUid++}`;\r\n\r\n /** The currently selected date. */\r\n get _selected(): D | null {\r\n return this._validSelected;\r\n }\r\n set _selected(value: D | null) {\r\n this._validSelected = value;\r\n }\r\n private _validSelected: D | null = null;\r\n\r\n /** The minimum selectable date. */\r\n get _minDate(): D | null {\r\n return this._datepickerInput && this._datepickerInput.min;\r\n }\r\n\r\n /** The maximum selectable date. */\r\n get _maxDate(): D | null {\r\n return this._datepickerInput && this._datepickerInput.max;\r\n }\r\n\r\n get _dateFilter(): (date: D | null) => boolean {\r\n return this._datepickerInput && this._datepickerInput._dateFilter;\r\n }\r\n\r\n /** A reference to the overlay when the calendar is opened as a popup. */\r\n _popupRef: OverlayRef;\r\n\r\n /** A reference to the dialog when the calendar is opened as a dialog. */\r\n private _dialogRef: OuiDialogRef> | null;\r\n\r\n /** A portal containing the calendar for this datepicker. */\r\n private _calendarPortal: ComponentPortal>;\r\n\r\n /** Reference to the component instantiated in popup mode. */\r\n private _popupComponentRef: ComponentRef> | null;\r\n\r\n /** The element that was focused before the datepicker was opened. */\r\n private _focusedElementBeforeOpen: HTMLElement | null = null;\r\n\r\n /** Subscription to value changes in the associated input element. */\r\n private _inputSubscription = Subscription.EMPTY;\r\n\r\n /** The input element this datepicker is associated with. */\r\n _datepickerInput: OuiDatepickerInput;\r\n\r\n /** Emits when the datepicker is disabled. */\r\n readonly _disabledChange = new Subject();\r\n\r\n /** Emits new selected date when selected date changes. */\r\n readonly _selectedChanged = new Subject();\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n constructor(\r\n private _dialog: OuiDialog,\r\n private _overlay: Overlay,\r\n private _ngZone: NgZone,\r\n protected elementRef: ElementRef,\r\n private _focusMonitor: FocusMonitor,\r\n private _viewContainerRef: ViewContainerRef,\r\n @Inject(OUI_DATEPICKER_SCROLL_STRATEGY) scrollStrategy: any,\r\n @Optional() private _dateAdapter: DateAdapter,\r\n @Optional() private _dir: Directionality,\r\n @Optional() @Inject(DOCUMENT) private _document: any\r\n ) {\r\n if (!this._dateAdapter) {\r\n throw createMissingDateImplError('DateAdapter');\r\n }\r\n this._scrollStrategy = scrollStrategy;\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this.elementRef, true)\r\n .subscribe(() => this._ngZone.run(() => {}));\r\n }\r\n\r\n ngOnDestroy() {\r\n this.close();\r\n this._inputSubscription.unsubscribe();\r\n this._disabledChange.complete();\r\n this._monitorSubscription.unsubscribe();\r\n\r\n if (this._popupRef) {\r\n this._popupRef.dispose();\r\n this._popupComponentRef = null;\r\n }\r\n }\r\n\r\n /** Selects the given date */\r\n select(date: D): void {\r\n const oldValue = this._selected;\r\n this._selected = date;\r\n if (!this._dateAdapter.sameDate(oldValue, this._selected)) {\r\n this._selectedChanged.next(date);\r\n }\r\n }\r\n\r\n /** Emits the selected year in multiyear view */\r\n _selectYear(normalizedYear: D): void {\r\n this.yearSelected.emit(normalizedYear);\r\n }\r\n\r\n /** Emits selected month in year view */\r\n _selectMonth(normalizedMonth: D): void {\r\n this.monthSelected.emit(normalizedMonth);\r\n }\r\n\r\n /**\r\n * Register an input with this datepicker.\r\n *\r\n * @param input The datepicker input to register with this datepicker.\r\n */\r\n _registerInput(input: OuiDatepickerInput): void {\r\n if (this._datepickerInput) {\r\n throw Error(\r\n 'A OuiDatepicker can only be associated with a single input.'\r\n );\r\n }\r\n this._datepickerInput = input;\r\n this._inputSubscription = this._datepickerInput._valueChange.subscribe(\r\n (value: D | null) => (this._selected = value)\r\n );\r\n }\r\n\r\n /** Open the calendar. */\r\n open(): void {\r\n if (this._opened || this.disabled) {\r\n return;\r\n }\r\n if (!this._datepickerInput) {\r\n throw Error(\r\n 'Attempted to open an OuiDatepicker with no associated input.'\r\n );\r\n }\r\n if (this._document) {\r\n this._focusedElementBeforeOpen = this._document.activeElement;\r\n }\r\n\r\n this.touchUi ? this._openAsDialog() : this._openAsPopup();\r\n this._opened = true;\r\n // add input focus here\r\n this._datepickerInput.focus();\r\n this.openedStream.emit();\r\n }\r\n\r\n /** Close the calendar. */\r\n close(): void {\r\n if (!this._opened) {\r\n return;\r\n }\r\n if (this._popupRef && this._popupRef.hasAttached()) {\r\n this._popupRef.detach();\r\n }\r\n if (this._dialogRef) {\r\n this._dialogRef.close();\r\n this._dialogRef = null;\r\n }\r\n if (this._calendarPortal && this._calendarPortal.isAttached) {\r\n this._calendarPortal.detach();\r\n }\r\n\r\n const completeClose = () => {\r\n // The `_opened` could've been reset already if\r\n // we got two events in quick succession.\r\n if (this._opened) {\r\n this._opened = false;\r\n this.closedStream.emit();\r\n this._focusedElementBeforeOpen = null;\r\n this._datepickerInput.blur();\r\n }\r\n };\r\n\r\n if (\r\n this._focusedElementBeforeOpen &&\r\n typeof this._focusedElementBeforeOpen.focus === 'function'\r\n ) {\r\n // Because IE moves focus asynchronously, we can't count on it being restored before we've\r\n // marked the datepicker as closed. If the event fires out of sequence and the element that\r\n // we're refocusing opens the datepicker on focus, the user could be stuck with not being\r\n // able to close the calendar at all. We work around it by making the logic, that marks\r\n // the datepicker as closed, async as well.\r\n this._focusedElementBeforeOpen.focus();\r\n setTimeout(completeClose);\r\n } else {\r\n completeClose();\r\n }\r\n }\r\n\r\n /** Open the calendar as a dialog. */\r\n private _openAsDialog(): void {\r\n // Usually this would be handled by `open` which ensures that we can only have one overlay\r\n // open at a time, however since we reset the variables in async handlers some overlays\r\n // may slip through if the user opens and closes multiple times in quick succession (e.g.\r\n // by holding down the enter key).\r\n if (this._dialogRef) {\r\n this._dialogRef.close();\r\n }\r\n\r\n this._dialogRef = this._dialog.open>(\r\n OuiDatepickerContent,\r\n {\r\n direction: this._dir ? this._dir.value : 'ltr',\r\n viewContainerRef: this._viewContainerRef,\r\n panelClass: 'oui-datepicker-dialog',\r\n }\r\n );\r\n\r\n this._dialogRef.afterClosed().subscribe(() => this.close());\r\n this._dialogRef.componentInstance.datepicker = this;\r\n this._setColor();\r\n }\r\n\r\n /** Open the calendar as a popup. */\r\n private _openAsPopup(): void {\r\n if (!this._calendarPortal) {\r\n this._calendarPortal = new ComponentPortal>(\r\n OuiDatepickerContent,\r\n this._viewContainerRef\r\n );\r\n }\r\n\r\n if (!this._popupRef) {\r\n this._createPopup();\r\n }\r\n\r\n if (!this._popupRef.hasAttached()) {\r\n this._popupComponentRef = this._popupRef.attach(this._calendarPortal);\r\n this._popupComponentRef.instance.datepicker = this;\r\n this._setColor();\r\n\r\n // Update the position once the calendar has rendered.\r\n this._ngZone.onStable\r\n .asObservable()\r\n .pipe(take(1))\r\n .subscribe(() => {\r\n this._popupRef.updatePosition();\r\n });\r\n }\r\n }\r\n\r\n /** Create the popup. */\r\n private _createPopup(): void {\r\n const overlayConfig = new OverlayConfig({\r\n positionStrategy: this._createPopupPositionStrategy(),\r\n hasBackdrop: true,\r\n backdropClass: 'oui-overlay-transparent-backdrop',\r\n direction: this._dir,\r\n scrollStrategy: this._scrollStrategy(),\r\n panelClass: 'oui-datepicker-popup',\r\n });\r\n\r\n this._popupRef = this._overlay.create(overlayConfig);\r\n this._popupRef.overlayElement.setAttribute('role', 'dialog');\r\n\r\n merge(\r\n this._popupRef.backdropClick(),\r\n this._popupRef.detachments(),\r\n this._popupRef.keydownEvents().pipe(\r\n filter(\r\n (event) =>\r\n // Closing on alt + up is only valid when there's an input associated with the datepicker.\r\n event.keyCode === ESCAPE ||\r\n (this._datepickerInput &&\r\n event.altKey &&\r\n event.keyCode === UP_ARROW)\r\n )\r\n )\r\n ).subscribe(() => this.close());\r\n }\r\n\r\n /** Create the popup PositionStrategy. */\r\n private _createPopupPositionStrategy(): PositionStrategy {\r\n return this._overlay\r\n .position()\r\n .flexibleConnectedTo(this._datepickerInput.getConnectedOverlayOrigin())\r\n .withTransformOriginOn('.oui-datepicker-content')\r\n .withFlexibleDimensions(false)\r\n .withViewportMargin(8)\r\n .withLockedPosition()\r\n .withPositions([\r\n {\r\n originX: 'start',\r\n originY: 'bottom',\r\n overlayX: 'start',\r\n overlayY: 'top',\r\n },\r\n {\r\n originX: 'start',\r\n originY: 'top',\r\n overlayX: 'start',\r\n overlayY: 'bottom',\r\n },\r\n {\r\n originX: 'end',\r\n originY: 'bottom',\r\n overlayX: 'end',\r\n overlayY: 'top',\r\n },\r\n {\r\n originX: 'end',\r\n originY: 'top',\r\n overlayX: 'end',\r\n overlayY: 'bottom',\r\n },\r\n ]);\r\n }\r\n\r\n /**\r\n * @param obj The object to check.\r\n * @returns The given object if it is both a date instance and valid, otherwise null.\r\n */\r\n private _getValidDateOrNull(obj: any): D | null {\r\n return this._dateAdapter.isDateInstance(obj) &&\r\n this._dateAdapter.isValid(obj as any as D)\r\n ? obj\r\n : null;\r\n }\r\n\r\n /** Passes the current theme color along to the calendar overlay. */\r\n private _setColor(): void {\r\n const color = this.color;\r\n if (this._popupComponentRef) {\r\n this._popupComponentRef.instance.color = color;\r\n }\r\n if (this._dialogRef) {\r\n this._dialogRef.componentInstance.color = color;\r\n }\r\n }\r\n}\r\n", + "sourceCode": "import { Directionality } from '@angular/cdk/bidi';\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { ESCAPE, UP_ARROW } from '@angular/cdk/keycodes';\nimport {\n Overlay,\n OverlayConfig,\n OverlayRef,\n PositionStrategy,\n ScrollStrategy,\n} from '@angular/cdk/overlay';\nimport { ComponentPortal, ComponentType } from '@angular/cdk/portal';\nimport { DOCUMENT } from '@angular/common';\nimport {\n AfterViewInit,\n ChangeDetectionStrategy,\n Component,\n ComponentRef,\n ElementRef,\n EventEmitter,\n Inject,\n InjectionToken,\n Input,\n NgZone,\n OnDestroy,\n Optional,\n Output,\n ViewChild,\n ViewContainerRef,\n ViewEncapsulation,\n} from '@angular/core';\nimport { FocusMonitor } from '@angular/cdk/a11y';\nimport { OuiDialog, OuiDialogRef } from '../dialog/public-api';\nimport { merge, Subject, Subscription } from 'rxjs';\nimport { filter, take } from 'rxjs/operators';\nimport { OuiCalendar } from './calendar';\nimport { ouiDatepickerAnimations } from './datepicker-animations';\nimport { createMissingDateImplError } from './datepicker-errors';\nimport { OuiDatepickerInput } from './datepicker-input';\nimport { OuiCalendarCellCssClasses } from './calendar-body';\nimport { CanColorCtor, mixinColor, CanColor, ThemePalette } from '../core';\nimport { DateAdapter } from './date-adapter';\n\n/** Used to generate a unique ID for each datepicker instance. */\nlet datepickerUid = 0;\n\n/** Injection token that determines the scroll handling while the calendar is open. */\nexport const OUI_DATEPICKER_SCROLL_STRATEGY = new InjectionToken<\n () => ScrollStrategy\n>('oui-datepicker-scroll-strategy');\n\nexport function OUI_DATEPICKER_SCROLL_STRATEGY_FACTORY(\n overlay: Overlay\n): () => ScrollStrategy {\n return () => overlay.scrollStrategies.reposition();\n}\n\nexport const OUI_DATEPICKER_SCROLL_STRATEGY_FACTORY_PROVIDER = {\n provide: OUI_DATEPICKER_SCROLL_STRATEGY,\n deps: [Overlay],\n useFactory: OUI_DATEPICKER_SCROLL_STRATEGY_FACTORY,\n};\n\n// Boilerplate for applying mixins to OuiDatepickerContent.\nexport class OuiDatepickerContentBase {\n constructor(public _elementRef: ElementRef) {}\n}\nexport const _OuiDatepickerContentMixinBase: CanColorCtor &\n typeof OuiDatepickerContentBase = mixinColor(OuiDatepickerContentBase);\n\n/**\n * Component used as the content for the datepicker dialog and popup. We use this instead of using\n * OuiCalendar directly as the content so we can control the initial focus. This also gives us a\n * place to put additional features of the popup that are not part of the calendar itself in the\n * future. (e.g. confirmation buttons).\n */\n@Component({\n selector: 'oui-datepicker-content',\n templateUrl: 'datepicker-content.html',\n styleUrls: ['datepicker-content.scss'],\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-datepicker-content',\n '[@transformPanel]': '\"enter\"',\n '[class.oui-datepicker-content-touch]': 'datepicker.touchUi',\n },\n animations: [\n ouiDatepickerAnimations.transformPanel,\n ouiDatepickerAnimations.fadeInCalendar,\n ],\n exportAs: 'ouiDatepickerContent',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\n inputs: ['color'],\n})\nexport class OuiDatepickerContent\n extends _OuiDatepickerContentMixinBase\n implements AfterViewInit, CanColor\n{\n /** Reference to the internal calendar component. */\n @ViewChild(OuiCalendar) _calendar: OuiCalendar;\n\n /** Reference to the datepicker that created the overlay. */\n datepicker: OuiDatepicker;\n\n /** Whether the datepicker is above or below the input. */\n _isAbove: boolean;\n\n constructor(elementRef: ElementRef) {\n super(elementRef);\n }\n\n ngAfterViewInit() {\n this._calendar.focusActiveCell();\n }\n}\n\n// TODO(mmalerba): We use a component instead of a directive here so the user can use implicit\n// template reference variables (e.g. #d vs #d=\"ouiDatepicker\"). We can change this to a directive\n// if angular adds support for `exportAs: '$implicit'` on directives.\n/** Component responsible for managing the datepicker popup/dialog. */\n@Component({\n selector: 'oui-datepicker',\n template: '',\n exportAs: 'ouiDatepicker',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n '[class.oui-datepicker-disabled]': 'disabled',\n },\n})\nexport class OuiDatepicker implements OnDestroy, CanColor {\n private _scrollStrategy: () => ScrollStrategy;\n\n /** An input indicating the type of the custom header component for the calendar, if set. */\n @Input() calendarHeaderComponent: ComponentType;\n\n /** The date to open the calendar to initially. */\n @Input()\n get startAt(): D | null {\n // If an explicit startAt is set we start there, otherwise we start at whatever the currently\n // selected value is.\n return (\n this._startAt ||\n (this._datepickerInput ? this._datepickerInput.value : null)\n );\n }\n set startAt(value: D | null) {\n this._startAt = this._getValidDateOrNull(\n this._dateAdapter.deserialize(value)\n );\n }\n private _startAt: D | null;\n\n /** The view that the calendar should start in. */\n @Input() startView: 'month' | 'year' | 'multi-year' = 'month';\n\n /** Color palette to use on the datepicker's calendar. */\n @Input()\n get color(): ThemePalette {\n return (\n this._color ||\n (this._datepickerInput\n ? this._datepickerInput._getThemePalette()\n : undefined)\n );\n }\n set color(value: ThemePalette) {\n this._color = value;\n }\n _color: ThemePalette;\n\n /**\n * Whether the calendar UI is in touch mode. In touch mode the calendar opens in a dialog rather\n * than a popup and elements have more padding to allow for bigger touch targets.\n */\n @Input()\n get touchUi(): boolean {\n return this._touchUi;\n }\n set touchUi(value: boolean) {\n this._touchUi = coerceBooleanProperty(value);\n }\n private _touchUi = false;\n\n /** Whether the datepicker pop-up should be disabled. */\n @Input()\n get disabled(): boolean {\n return this._disabled === undefined && this._datepickerInput\n ? this._datepickerInput.disabled\n : !!this._disabled;\n }\n set disabled(value: boolean) {\n const newValue = coerceBooleanProperty(value);\n\n if (newValue !== this._disabled) {\n this._disabled = newValue;\n this._disabledChange.next(newValue);\n this._datepickerInput._datepickerDisabled = newValue;\n }\n }\n private _disabled = false;\n\n /**\n * Emits selected year in multiyear view.\n * This doesn't imply a change on the selected date.\n */\n @Output() readonly yearSelected: EventEmitter = new EventEmitter();\n\n /**\n * Emits selected month in year view.\n * This doesn't imply a change on the selected date.\n */\n @Output() readonly monthSelected: EventEmitter = new EventEmitter();\n\n /** Classes to be passed to the date picker panel. Supports the same syntax as `ngClass`. */\n @Input() panelClass: string | string[];\n\n /** Function that can be used to add custom CSS classes to dates. */\n @Input() dateClass: (date: D) => OuiCalendarCellCssClasses;\n\n /** Emits when the datepicker has been opened. */\n // eslint-disable-next-line @angular-eslint/no-output-rename\n @Output('opened') openedStream: EventEmitter = new EventEmitter();\n\n /** Emits when the datepicker has been closed. */\n // eslint-disable-next-line @angular-eslint/no-output-rename\n @Output('closed') closedStream: EventEmitter = new EventEmitter();\n\n /** Whether the calendar is open. */\n @Input()\n get opened(): boolean {\n return this._opened;\n }\n set opened(value: boolean) {\n value ? this.open() : this.close();\n }\n private _opened = false;\n\n /** The id for the datepicker calendar. */\n id = `oui-datepicker-${datepickerUid++}`;\n\n /** The currently selected date. */\n get _selected(): D | null {\n return this._validSelected;\n }\n set _selected(value: D | null) {\n this._validSelected = value;\n }\n private _validSelected: D | null = null;\n\n /** The minimum selectable date. */\n get _minDate(): D | null {\n return this._datepickerInput && this._datepickerInput.min;\n }\n\n /** The maximum selectable date. */\n get _maxDate(): D | null {\n return this._datepickerInput && this._datepickerInput.max;\n }\n\n get _dateFilter(): (date: D | null) => boolean {\n return this._datepickerInput && this._datepickerInput._dateFilter;\n }\n\n /** A reference to the overlay when the calendar is opened as a popup. */\n _popupRef: OverlayRef;\n\n /** A reference to the dialog when the calendar is opened as a dialog. */\n private _dialogRef: OuiDialogRef> | null;\n\n /** A portal containing the calendar for this datepicker. */\n private _calendarPortal: ComponentPortal>;\n\n /** Reference to the component instantiated in popup mode. */\n private _popupComponentRef: ComponentRef> | null;\n\n /** The element that was focused before the datepicker was opened. */\n private _focusedElementBeforeOpen: HTMLElement | null = null;\n\n /** Subscription to value changes in the associated input element. */\n private _inputSubscription = Subscription.EMPTY;\n\n /** The input element this datepicker is associated with. */\n _datepickerInput: OuiDatepickerInput;\n\n /** Emits when the datepicker is disabled. */\n readonly _disabledChange = new Subject();\n\n /** Emits new selected date when selected date changes. */\n readonly _selectedChanged = new Subject();\n private _monitorSubscription: Subscription = Subscription.EMPTY;\n constructor(\n private _dialog: OuiDialog,\n private _overlay: Overlay,\n private _ngZone: NgZone,\n protected elementRef: ElementRef,\n private _focusMonitor: FocusMonitor,\n private _viewContainerRef: ViewContainerRef,\n @Inject(OUI_DATEPICKER_SCROLL_STRATEGY) scrollStrategy: any,\n @Optional() private _dateAdapter: DateAdapter,\n @Optional() private _dir: Directionality,\n @Optional() @Inject(DOCUMENT) private _document: any\n ) {\n if (!this._dateAdapter) {\n throw createMissingDateImplError('DateAdapter');\n }\n this._scrollStrategy = scrollStrategy;\n this._monitorSubscription = this._focusMonitor\n .monitor(this.elementRef, true)\n .subscribe(() => this._ngZone.run(() => {}));\n }\n\n ngOnDestroy() {\n this.close();\n this._inputSubscription.unsubscribe();\n this._disabledChange.complete();\n this._monitorSubscription.unsubscribe();\n\n if (this._popupRef) {\n this._popupRef.dispose();\n this._popupComponentRef = null;\n }\n }\n\n /** Selects the given date */\n select(date: D): void {\n const oldValue = this._selected;\n this._selected = date;\n if (!this._dateAdapter.sameDate(oldValue, this._selected)) {\n this._selectedChanged.next(date);\n }\n }\n\n /** Emits the selected year in multiyear view */\n _selectYear(normalizedYear: D): void {\n this.yearSelected.emit(normalizedYear);\n }\n\n /** Emits selected month in year view */\n _selectMonth(normalizedMonth: D): void {\n this.monthSelected.emit(normalizedMonth);\n }\n\n /**\n * Register an input with this datepicker.\n *\n * @param input The datepicker input to register with this datepicker.\n */\n _registerInput(input: OuiDatepickerInput): void {\n if (this._datepickerInput) {\n throw Error(\n 'A OuiDatepicker can only be associated with a single input.'\n );\n }\n this._datepickerInput = input;\n this._inputSubscription = this._datepickerInput._valueChange.subscribe(\n (value: D | null) => (this._selected = value)\n );\n }\n\n /** Open the calendar. */\n open(): void {\n if (this._opened || this.disabled) {\n return;\n }\n if (!this._datepickerInput) {\n throw Error(\n 'Attempted to open an OuiDatepicker with no associated input.'\n );\n }\n if (this._document) {\n this._focusedElementBeforeOpen = this._document.activeElement;\n }\n\n this.touchUi ? this._openAsDialog() : this._openAsPopup();\n this._opened = true;\n // add input focus here\n this._datepickerInput.focus();\n this.openedStream.emit();\n }\n\n /** Close the calendar. */\n close(): void {\n if (!this._opened) {\n return;\n }\n if (this._popupRef && this._popupRef.hasAttached()) {\n this._popupRef.detach();\n }\n if (this._dialogRef) {\n this._dialogRef.close();\n this._dialogRef = null;\n }\n if (this._calendarPortal && this._calendarPortal.isAttached) {\n this._calendarPortal.detach();\n }\n\n const completeClose = () => {\n // The `_opened` could've been reset already if\n // we got two events in quick succession.\n if (this._opened) {\n this._opened = false;\n this.closedStream.emit();\n this._focusedElementBeforeOpen = null;\n this._datepickerInput.blur();\n }\n };\n\n if (\n this._focusedElementBeforeOpen &&\n typeof this._focusedElementBeforeOpen.focus === 'function'\n ) {\n // Because IE moves focus asynchronously, we can't count on it being restored before we've\n // marked the datepicker as closed. If the event fires out of sequence and the element that\n // we're refocusing opens the datepicker on focus, the user could be stuck with not being\n // able to close the calendar at all. We work around it by making the logic, that marks\n // the datepicker as closed, async as well.\n this._focusedElementBeforeOpen.focus();\n setTimeout(completeClose);\n } else {\n completeClose();\n }\n }\n\n /** Open the calendar as a dialog. */\n private _openAsDialog(): void {\n // Usually this would be handled by `open` which ensures that we can only have one overlay\n // open at a time, however since we reset the variables in async handlers some overlays\n // may slip through if the user opens and closes multiple times in quick succession (e.g.\n // by holding down the enter key).\n if (this._dialogRef) {\n this._dialogRef.close();\n }\n\n this._dialogRef = this._dialog.open>(\n OuiDatepickerContent,\n {\n direction: this._dir ? this._dir.value : 'ltr',\n viewContainerRef: this._viewContainerRef,\n panelClass: 'oui-datepicker-dialog',\n }\n );\n\n this._dialogRef.afterClosed().subscribe(() => this.close());\n this._dialogRef.componentInstance.datepicker = this;\n this._setColor();\n }\n\n /** Open the calendar as a popup. */\n private _openAsPopup(): void {\n if (!this._calendarPortal) {\n this._calendarPortal = new ComponentPortal>(\n OuiDatepickerContent,\n this._viewContainerRef\n );\n }\n\n if (!this._popupRef) {\n this._createPopup();\n }\n\n if (!this._popupRef.hasAttached()) {\n this._popupComponentRef = this._popupRef.attach(this._calendarPortal);\n this._popupComponentRef.instance.datepicker = this;\n this._setColor();\n\n // Update the position once the calendar has rendered.\n this._ngZone.onStable\n .asObservable()\n .pipe(take(1))\n .subscribe(() => {\n this._popupRef.updatePosition();\n });\n }\n }\n\n /** Create the popup. */\n private _createPopup(): void {\n const overlayConfig = new OverlayConfig({\n positionStrategy: this._createPopupPositionStrategy(),\n hasBackdrop: true,\n backdropClass: 'oui-overlay-transparent-backdrop',\n direction: this._dir,\n scrollStrategy: this._scrollStrategy(),\n panelClass: 'oui-datepicker-popup',\n });\n\n this._popupRef = this._overlay.create(overlayConfig);\n this._popupRef.overlayElement.setAttribute('role', 'dialog');\n\n merge(\n this._popupRef.backdropClick(),\n this._popupRef.detachments(),\n this._popupRef.keydownEvents().pipe(\n filter(\n (event) =>\n // Closing on alt + up is only valid when there's an input associated with the datepicker.\n event.keyCode === ESCAPE ||\n (this._datepickerInput &&\n event.altKey &&\n event.keyCode === UP_ARROW)\n )\n )\n ).subscribe(() => this.close());\n }\n\n /** Create the popup PositionStrategy. */\n private _createPopupPositionStrategy(): PositionStrategy {\n return this._overlay\n .position()\n .flexibleConnectedTo(this._datepickerInput.getConnectedOverlayOrigin())\n .withTransformOriginOn('.oui-datepicker-content')\n .withFlexibleDimensions(false)\n .withViewportMargin(8)\n .withLockedPosition()\n .withPositions([\n {\n originX: 'start',\n originY: 'bottom',\n overlayX: 'start',\n overlayY: 'top',\n },\n {\n originX: 'start',\n originY: 'top',\n overlayX: 'start',\n overlayY: 'bottom',\n },\n {\n originX: 'end',\n originY: 'bottom',\n overlayX: 'end',\n overlayY: 'top',\n },\n {\n originX: 'end',\n originY: 'top',\n overlayX: 'end',\n overlayY: 'bottom',\n },\n ]);\n }\n\n /**\n * @param obj The object to check.\n * @returns The given object if it is both a date instance and valid, otherwise null.\n */\n private _getValidDateOrNull(obj: any): D | null {\n return this._dateAdapter.isDateInstance(obj) &&\n this._dateAdapter.isValid(obj as any as D)\n ? obj\n : null;\n }\n\n /** Passes the current theme color along to the calendar overlay. */\n private _setColor(): void {\n const color = this.color;\n if (this._popupComponentRef) {\n this._popupComponentRef.instance.color = color;\n }\n if (this._dialogRef) {\n this._dialogRef.componentInstance.color = color;\n }\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -24031,7 +33889,7 @@ }, { "name": "OuiDatepickerContent", - "id": "component-OuiDatepickerContent-458b78a894cb4d88101a90002d3b7edc97b300510a4999d795203320d180aee30d56532fe76c2af4852ef6b87a0541032cec5768225b8729dfe1aaf5772456eb", + "id": "component-OuiDatepickerContent-ef2f443418fcda96544206a6e7f04650559ef230ba716a2fcd192fcccff22a5183bd00b300ca1bd0840b2bdab9f680685ecce9b42c0d3c890966f004ff76d557", "file": "ui/src/components/datepicker/datepicker.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -24113,11 +33971,11 @@ "description": "

Component used as the content for the datepicker dialog and popup. We use this instead of using\nOuiCalendar directly as the content so we can control the initial focus. This also gives us a\nplace to put additional features of the popup that are not part of the calendar itself in the\nfuture. (e.g. confirmation buttons).

\n", "rawdescription": "\n\nComponent used as the content for the datepicker dialog and popup. We use this instead of using\nOuiCalendar directly as the content so we can control the initial focus. This also gives us a\nplace to put additional features of the popup that are not part of the calendar itself in the\nfuture. (e.g. confirmation buttons).\n", "type": "component", - "sourceCode": "import { Directionality } from '@angular/cdk/bidi';\r\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\r\nimport { ESCAPE, UP_ARROW } from '@angular/cdk/keycodes';\r\nimport {\r\n Overlay,\r\n OverlayConfig,\r\n OverlayRef,\r\n PositionStrategy,\r\n ScrollStrategy,\r\n} from '@angular/cdk/overlay';\r\nimport { ComponentPortal, ComponentType } from '@angular/cdk/portal';\r\nimport { DOCUMENT } from '@angular/common';\r\nimport {\r\n AfterViewInit,\r\n ChangeDetectionStrategy,\r\n Component,\r\n ComponentRef,\r\n ElementRef,\r\n EventEmitter,\r\n Inject,\r\n InjectionToken,\r\n Input,\r\n NgZone,\r\n OnDestroy,\r\n Optional,\r\n Output,\r\n ViewChild,\r\n ViewContainerRef,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\nimport { FocusMonitor } from '@angular/cdk/a11y';\r\nimport { OuiDialog, OuiDialogRef } from '../dialog/public-api';\r\nimport { merge, Subject, Subscription } from 'rxjs';\r\nimport { filter, take } from 'rxjs/operators';\r\nimport { OuiCalendar } from './calendar';\r\nimport { ouiDatepickerAnimations } from './datepicker-animations';\r\nimport { createMissingDateImplError } from './datepicker-errors';\r\nimport { OuiDatepickerInput } from './datepicker-input';\r\nimport { OuiCalendarCellCssClasses } from './calendar-body';\r\nimport { CanColorCtor, mixinColor, CanColor, ThemePalette } from '../core';\r\nimport { DateAdapter } from './date-adapter';\r\n\r\n/** Used to generate a unique ID for each datepicker instance. */\r\nlet datepickerUid = 0;\r\n\r\n/** Injection token that determines the scroll handling while the calendar is open. */\r\nexport const OUI_DATEPICKER_SCROLL_STRATEGY = new InjectionToken<\r\n () => ScrollStrategy\r\n>('oui-datepicker-scroll-strategy');\r\n\r\nexport function OUI_DATEPICKER_SCROLL_STRATEGY_FACTORY(\r\n overlay: Overlay\r\n): () => ScrollStrategy {\r\n return () => overlay.scrollStrategies.reposition();\r\n}\r\n\r\nexport const OUI_DATEPICKER_SCROLL_STRATEGY_FACTORY_PROVIDER = {\r\n provide: OUI_DATEPICKER_SCROLL_STRATEGY,\r\n deps: [Overlay],\r\n useFactory: OUI_DATEPICKER_SCROLL_STRATEGY_FACTORY,\r\n};\r\n\r\n// Boilerplate for applying mixins to OuiDatepickerContent.\r\nexport class OuiDatepickerContentBase {\r\n constructor(public _elementRef: ElementRef) {}\r\n}\r\nexport const _OuiDatepickerContentMixinBase: CanColorCtor &\r\n typeof OuiDatepickerContentBase = mixinColor(OuiDatepickerContentBase);\r\n\r\n/**\r\n * Component used as the content for the datepicker dialog and popup. We use this instead of using\r\n * OuiCalendar directly as the content so we can control the initial focus. This also gives us a\r\n * place to put additional features of the popup that are not part of the calendar itself in the\r\n * future. (e.g. confirmation buttons).\r\n */\r\n@Component({\r\n selector: 'oui-datepicker-content',\r\n templateUrl: 'datepicker-content.html',\r\n styleUrls: ['datepicker-content.scss'],\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-datepicker-content',\r\n '[@transformPanel]': '\"enter\"',\r\n '[class.oui-datepicker-content-touch]': 'datepicker.touchUi',\r\n },\r\n animations: [\r\n ouiDatepickerAnimations.transformPanel,\r\n ouiDatepickerAnimations.fadeInCalendar,\r\n ],\r\n exportAs: 'ouiDatepickerContent',\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['color'],\r\n})\r\nexport class OuiDatepickerContent\r\n extends _OuiDatepickerContentMixinBase\r\n implements AfterViewInit, CanColor\r\n{\r\n /** Reference to the internal calendar component. */\r\n @ViewChild(OuiCalendar) _calendar: OuiCalendar;\r\n\r\n /** Reference to the datepicker that created the overlay. */\r\n datepicker: OuiDatepicker;\r\n\r\n /** Whether the datepicker is above or below the input. */\r\n _isAbove: boolean;\r\n\r\n constructor(elementRef: ElementRef) {\r\n super(elementRef);\r\n }\r\n\r\n ngAfterViewInit() {\r\n this._calendar.focusActiveCell();\r\n }\r\n}\r\n\r\n// TODO(mmalerba): We use a component instead of a directive here so the user can use implicit\r\n// template reference variables (e.g. #d vs #d=\"ouiDatepicker\"). We can change this to a directive\r\n// if angular adds support for `exportAs: '$implicit'` on directives.\r\n/** Component responsible for managing the datepicker popup/dialog. */\r\n@Component({\r\n selector: 'oui-datepicker',\r\n template: '',\r\n exportAs: 'ouiDatepicker',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n '[class.oui-datepicker-disabled]': 'disabled',\r\n },\r\n})\r\nexport class OuiDatepicker implements OnDestroy, CanColor {\r\n private _scrollStrategy: () => ScrollStrategy;\r\n\r\n /** An input indicating the type of the custom header component for the calendar, if set. */\r\n @Input() calendarHeaderComponent: ComponentType;\r\n\r\n /** The date to open the calendar to initially. */\r\n @Input()\r\n get startAt(): D | null {\r\n // If an explicit startAt is set we start there, otherwise we start at whatever the currently\r\n // selected value is.\r\n return (\r\n this._startAt ||\r\n (this._datepickerInput ? this._datepickerInput.value : null)\r\n );\r\n }\r\n set startAt(value: D | null) {\r\n this._startAt = this._getValidDateOrNull(\r\n this._dateAdapter.deserialize(value)\r\n );\r\n }\r\n private _startAt: D | null;\r\n\r\n /** The view that the calendar should start in. */\r\n @Input() startView: 'month' | 'year' | 'multi-year' = 'month';\r\n\r\n /** Color palette to use on the datepicker's calendar. */\r\n @Input()\r\n get color(): ThemePalette {\r\n return (\r\n this._color ||\r\n (this._datepickerInput\r\n ? this._datepickerInput._getThemePalette()\r\n : undefined)\r\n );\r\n }\r\n set color(value: ThemePalette) {\r\n this._color = value;\r\n }\r\n _color: ThemePalette;\r\n\r\n /**\r\n * Whether the calendar UI is in touch mode. In touch mode the calendar opens in a dialog rather\r\n * than a popup and elements have more padding to allow for bigger touch targets.\r\n */\r\n @Input()\r\n get touchUi(): boolean {\r\n return this._touchUi;\r\n }\r\n set touchUi(value: boolean) {\r\n this._touchUi = coerceBooleanProperty(value);\r\n }\r\n private _touchUi = false;\r\n\r\n /** Whether the datepicker pop-up should be disabled. */\r\n @Input()\r\n get disabled(): boolean {\r\n return this._disabled === undefined && this._datepickerInput\r\n ? this._datepickerInput.disabled\r\n : !!this._disabled;\r\n }\r\n set disabled(value: boolean) {\r\n const newValue = coerceBooleanProperty(value);\r\n\r\n if (newValue !== this._disabled) {\r\n this._disabled = newValue;\r\n this._disabledChange.next(newValue);\r\n this._datepickerInput._datepickerDisabled = newValue;\r\n }\r\n }\r\n private _disabled = false;\r\n\r\n /**\r\n * Emits selected year in multiyear view.\r\n * This doesn't imply a change on the selected date.\r\n */\r\n @Output() readonly yearSelected: EventEmitter = new EventEmitter();\r\n\r\n /**\r\n * Emits selected month in year view.\r\n * This doesn't imply a change on the selected date.\r\n */\r\n @Output() readonly monthSelected: EventEmitter = new EventEmitter();\r\n\r\n /** Classes to be passed to the date picker panel. Supports the same syntax as `ngClass`. */\r\n @Input() panelClass: string | string[];\r\n\r\n /** Function that can be used to add custom CSS classes to dates. */\r\n @Input() dateClass: (date: D) => OuiCalendarCellCssClasses;\r\n\r\n /** Emits when the datepicker has been opened. */\r\n // eslint-disable-next-line @angular-eslint/no-output-rename\r\n @Output('opened') openedStream: EventEmitter = new EventEmitter();\r\n\r\n /** Emits when the datepicker has been closed. */\r\n // eslint-disable-next-line @angular-eslint/no-output-rename\r\n @Output('closed') closedStream: EventEmitter = new EventEmitter();\r\n\r\n /** Whether the calendar is open. */\r\n @Input()\r\n get opened(): boolean {\r\n return this._opened;\r\n }\r\n set opened(value: boolean) {\r\n value ? this.open() : this.close();\r\n }\r\n private _opened = false;\r\n\r\n /** The id for the datepicker calendar. */\r\n id = `oui-datepicker-${datepickerUid++}`;\r\n\r\n /** The currently selected date. */\r\n get _selected(): D | null {\r\n return this._validSelected;\r\n }\r\n set _selected(value: D | null) {\r\n this._validSelected = value;\r\n }\r\n private _validSelected: D | null = null;\r\n\r\n /** The minimum selectable date. */\r\n get _minDate(): D | null {\r\n return this._datepickerInput && this._datepickerInput.min;\r\n }\r\n\r\n /** The maximum selectable date. */\r\n get _maxDate(): D | null {\r\n return this._datepickerInput && this._datepickerInput.max;\r\n }\r\n\r\n get _dateFilter(): (date: D | null) => boolean {\r\n return this._datepickerInput && this._datepickerInput._dateFilter;\r\n }\r\n\r\n /** A reference to the overlay when the calendar is opened as a popup. */\r\n _popupRef: OverlayRef;\r\n\r\n /** A reference to the dialog when the calendar is opened as a dialog. */\r\n private _dialogRef: OuiDialogRef> | null;\r\n\r\n /** A portal containing the calendar for this datepicker. */\r\n private _calendarPortal: ComponentPortal>;\r\n\r\n /** Reference to the component instantiated in popup mode. */\r\n private _popupComponentRef: ComponentRef> | null;\r\n\r\n /** The element that was focused before the datepicker was opened. */\r\n private _focusedElementBeforeOpen: HTMLElement | null = null;\r\n\r\n /** Subscription to value changes in the associated input element. */\r\n private _inputSubscription = Subscription.EMPTY;\r\n\r\n /** The input element this datepicker is associated with. */\r\n _datepickerInput: OuiDatepickerInput;\r\n\r\n /** Emits when the datepicker is disabled. */\r\n readonly _disabledChange = new Subject();\r\n\r\n /** Emits new selected date when selected date changes. */\r\n readonly _selectedChanged = new Subject();\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n constructor(\r\n private _dialog: OuiDialog,\r\n private _overlay: Overlay,\r\n private _ngZone: NgZone,\r\n protected elementRef: ElementRef,\r\n private _focusMonitor: FocusMonitor,\r\n private _viewContainerRef: ViewContainerRef,\r\n @Inject(OUI_DATEPICKER_SCROLL_STRATEGY) scrollStrategy: any,\r\n @Optional() private _dateAdapter: DateAdapter,\r\n @Optional() private _dir: Directionality,\r\n @Optional() @Inject(DOCUMENT) private _document: any\r\n ) {\r\n if (!this._dateAdapter) {\r\n throw createMissingDateImplError('DateAdapter');\r\n }\r\n this._scrollStrategy = scrollStrategy;\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this.elementRef, true)\r\n .subscribe(() => this._ngZone.run(() => {}));\r\n }\r\n\r\n ngOnDestroy() {\r\n this.close();\r\n this._inputSubscription.unsubscribe();\r\n this._disabledChange.complete();\r\n this._monitorSubscription.unsubscribe();\r\n\r\n if (this._popupRef) {\r\n this._popupRef.dispose();\r\n this._popupComponentRef = null;\r\n }\r\n }\r\n\r\n /** Selects the given date */\r\n select(date: D): void {\r\n const oldValue = this._selected;\r\n this._selected = date;\r\n if (!this._dateAdapter.sameDate(oldValue, this._selected)) {\r\n this._selectedChanged.next(date);\r\n }\r\n }\r\n\r\n /** Emits the selected year in multiyear view */\r\n _selectYear(normalizedYear: D): void {\r\n this.yearSelected.emit(normalizedYear);\r\n }\r\n\r\n /** Emits selected month in year view */\r\n _selectMonth(normalizedMonth: D): void {\r\n this.monthSelected.emit(normalizedMonth);\r\n }\r\n\r\n /**\r\n * Register an input with this datepicker.\r\n *\r\n * @param input The datepicker input to register with this datepicker.\r\n */\r\n _registerInput(input: OuiDatepickerInput): void {\r\n if (this._datepickerInput) {\r\n throw Error(\r\n 'A OuiDatepicker can only be associated with a single input.'\r\n );\r\n }\r\n this._datepickerInput = input;\r\n this._inputSubscription = this._datepickerInput._valueChange.subscribe(\r\n (value: D | null) => (this._selected = value)\r\n );\r\n }\r\n\r\n /** Open the calendar. */\r\n open(): void {\r\n if (this._opened || this.disabled) {\r\n return;\r\n }\r\n if (!this._datepickerInput) {\r\n throw Error(\r\n 'Attempted to open an OuiDatepicker with no associated input.'\r\n );\r\n }\r\n if (this._document) {\r\n this._focusedElementBeforeOpen = this._document.activeElement;\r\n }\r\n\r\n this.touchUi ? this._openAsDialog() : this._openAsPopup();\r\n this._opened = true;\r\n // add input focus here\r\n this._datepickerInput.focus();\r\n this.openedStream.emit();\r\n }\r\n\r\n /** Close the calendar. */\r\n close(): void {\r\n if (!this._opened) {\r\n return;\r\n }\r\n if (this._popupRef && this._popupRef.hasAttached()) {\r\n this._popupRef.detach();\r\n }\r\n if (this._dialogRef) {\r\n this._dialogRef.close();\r\n this._dialogRef = null;\r\n }\r\n if (this._calendarPortal && this._calendarPortal.isAttached) {\r\n this._calendarPortal.detach();\r\n }\r\n\r\n const completeClose = () => {\r\n // The `_opened` could've been reset already if\r\n // we got two events in quick succession.\r\n if (this._opened) {\r\n this._opened = false;\r\n this.closedStream.emit();\r\n this._focusedElementBeforeOpen = null;\r\n this._datepickerInput.blur();\r\n }\r\n };\r\n\r\n if (\r\n this._focusedElementBeforeOpen &&\r\n typeof this._focusedElementBeforeOpen.focus === 'function'\r\n ) {\r\n // Because IE moves focus asynchronously, we can't count on it being restored before we've\r\n // marked the datepicker as closed. If the event fires out of sequence and the element that\r\n // we're refocusing opens the datepicker on focus, the user could be stuck with not being\r\n // able to close the calendar at all. We work around it by making the logic, that marks\r\n // the datepicker as closed, async as well.\r\n this._focusedElementBeforeOpen.focus();\r\n setTimeout(completeClose);\r\n } else {\r\n completeClose();\r\n }\r\n }\r\n\r\n /** Open the calendar as a dialog. */\r\n private _openAsDialog(): void {\r\n // Usually this would be handled by `open` which ensures that we can only have one overlay\r\n // open at a time, however since we reset the variables in async handlers some overlays\r\n // may slip through if the user opens and closes multiple times in quick succession (e.g.\r\n // by holding down the enter key).\r\n if (this._dialogRef) {\r\n this._dialogRef.close();\r\n }\r\n\r\n this._dialogRef = this._dialog.open>(\r\n OuiDatepickerContent,\r\n {\r\n direction: this._dir ? this._dir.value : 'ltr',\r\n viewContainerRef: this._viewContainerRef,\r\n panelClass: 'oui-datepicker-dialog',\r\n }\r\n );\r\n\r\n this._dialogRef.afterClosed().subscribe(() => this.close());\r\n this._dialogRef.componentInstance.datepicker = this;\r\n this._setColor();\r\n }\r\n\r\n /** Open the calendar as a popup. */\r\n private _openAsPopup(): void {\r\n if (!this._calendarPortal) {\r\n this._calendarPortal = new ComponentPortal>(\r\n OuiDatepickerContent,\r\n this._viewContainerRef\r\n );\r\n }\r\n\r\n if (!this._popupRef) {\r\n this._createPopup();\r\n }\r\n\r\n if (!this._popupRef.hasAttached()) {\r\n this._popupComponentRef = this._popupRef.attach(this._calendarPortal);\r\n this._popupComponentRef.instance.datepicker = this;\r\n this._setColor();\r\n\r\n // Update the position once the calendar has rendered.\r\n this._ngZone.onStable\r\n .asObservable()\r\n .pipe(take(1))\r\n .subscribe(() => {\r\n this._popupRef.updatePosition();\r\n });\r\n }\r\n }\r\n\r\n /** Create the popup. */\r\n private _createPopup(): void {\r\n const overlayConfig = new OverlayConfig({\r\n positionStrategy: this._createPopupPositionStrategy(),\r\n hasBackdrop: true,\r\n backdropClass: 'oui-overlay-transparent-backdrop',\r\n direction: this._dir,\r\n scrollStrategy: this._scrollStrategy(),\r\n panelClass: 'oui-datepicker-popup',\r\n });\r\n\r\n this._popupRef = this._overlay.create(overlayConfig);\r\n this._popupRef.overlayElement.setAttribute('role', 'dialog');\r\n\r\n merge(\r\n this._popupRef.backdropClick(),\r\n this._popupRef.detachments(),\r\n this._popupRef.keydownEvents().pipe(\r\n filter(\r\n (event) =>\r\n // Closing on alt + up is only valid when there's an input associated with the datepicker.\r\n event.keyCode === ESCAPE ||\r\n (this._datepickerInput &&\r\n event.altKey &&\r\n event.keyCode === UP_ARROW)\r\n )\r\n )\r\n ).subscribe(() => this.close());\r\n }\r\n\r\n /** Create the popup PositionStrategy. */\r\n private _createPopupPositionStrategy(): PositionStrategy {\r\n return this._overlay\r\n .position()\r\n .flexibleConnectedTo(this._datepickerInput.getConnectedOverlayOrigin())\r\n .withTransformOriginOn('.oui-datepicker-content')\r\n .withFlexibleDimensions(false)\r\n .withViewportMargin(8)\r\n .withLockedPosition()\r\n .withPositions([\r\n {\r\n originX: 'start',\r\n originY: 'bottom',\r\n overlayX: 'start',\r\n overlayY: 'top',\r\n },\r\n {\r\n originX: 'start',\r\n originY: 'top',\r\n overlayX: 'start',\r\n overlayY: 'bottom',\r\n },\r\n {\r\n originX: 'end',\r\n originY: 'bottom',\r\n overlayX: 'end',\r\n overlayY: 'top',\r\n },\r\n {\r\n originX: 'end',\r\n originY: 'top',\r\n overlayX: 'end',\r\n overlayY: 'bottom',\r\n },\r\n ]);\r\n }\r\n\r\n /**\r\n * @param obj The object to check.\r\n * @returns The given object if it is both a date instance and valid, otherwise null.\r\n */\r\n private _getValidDateOrNull(obj: any): D | null {\r\n return this._dateAdapter.isDateInstance(obj) &&\r\n this._dateAdapter.isValid(obj as any as D)\r\n ? obj\r\n : null;\r\n }\r\n\r\n /** Passes the current theme color along to the calendar overlay. */\r\n private _setColor(): void {\r\n const color = this.color;\r\n if (this._popupComponentRef) {\r\n this._popupComponentRef.instance.color = color;\r\n }\r\n if (this._dialogRef) {\r\n this._dialogRef.componentInstance.color = color;\r\n }\r\n }\r\n}\r\n", + "sourceCode": "import { Directionality } from '@angular/cdk/bidi';\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { ESCAPE, UP_ARROW } from '@angular/cdk/keycodes';\nimport {\n Overlay,\n OverlayConfig,\n OverlayRef,\n PositionStrategy,\n ScrollStrategy,\n} from '@angular/cdk/overlay';\nimport { ComponentPortal, ComponentType } from '@angular/cdk/portal';\nimport { DOCUMENT } from '@angular/common';\nimport {\n AfterViewInit,\n ChangeDetectionStrategy,\n Component,\n ComponentRef,\n ElementRef,\n EventEmitter,\n Inject,\n InjectionToken,\n Input,\n NgZone,\n OnDestroy,\n Optional,\n Output,\n ViewChild,\n ViewContainerRef,\n ViewEncapsulation,\n} from '@angular/core';\nimport { FocusMonitor } from '@angular/cdk/a11y';\nimport { OuiDialog, OuiDialogRef } from '../dialog/public-api';\nimport { merge, Subject, Subscription } from 'rxjs';\nimport { filter, take } from 'rxjs/operators';\nimport { OuiCalendar } from './calendar';\nimport { ouiDatepickerAnimations } from './datepicker-animations';\nimport { createMissingDateImplError } from './datepicker-errors';\nimport { OuiDatepickerInput } from './datepicker-input';\nimport { OuiCalendarCellCssClasses } from './calendar-body';\nimport { CanColorCtor, mixinColor, CanColor, ThemePalette } from '../core';\nimport { DateAdapter } from './date-adapter';\n\n/** Used to generate a unique ID for each datepicker instance. */\nlet datepickerUid = 0;\n\n/** Injection token that determines the scroll handling while the calendar is open. */\nexport const OUI_DATEPICKER_SCROLL_STRATEGY = new InjectionToken<\n () => ScrollStrategy\n>('oui-datepicker-scroll-strategy');\n\nexport function OUI_DATEPICKER_SCROLL_STRATEGY_FACTORY(\n overlay: Overlay\n): () => ScrollStrategy {\n return () => overlay.scrollStrategies.reposition();\n}\n\nexport const OUI_DATEPICKER_SCROLL_STRATEGY_FACTORY_PROVIDER = {\n provide: OUI_DATEPICKER_SCROLL_STRATEGY,\n deps: [Overlay],\n useFactory: OUI_DATEPICKER_SCROLL_STRATEGY_FACTORY,\n};\n\n// Boilerplate for applying mixins to OuiDatepickerContent.\nexport class OuiDatepickerContentBase {\n constructor(public _elementRef: ElementRef) {}\n}\nexport const _OuiDatepickerContentMixinBase: CanColorCtor &\n typeof OuiDatepickerContentBase = mixinColor(OuiDatepickerContentBase);\n\n/**\n * Component used as the content for the datepicker dialog and popup. We use this instead of using\n * OuiCalendar directly as the content so we can control the initial focus. This also gives us a\n * place to put additional features of the popup that are not part of the calendar itself in the\n * future. (e.g. confirmation buttons).\n */\n@Component({\n selector: 'oui-datepicker-content',\n templateUrl: 'datepicker-content.html',\n styleUrls: ['datepicker-content.scss'],\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-datepicker-content',\n '[@transformPanel]': '\"enter\"',\n '[class.oui-datepicker-content-touch]': 'datepicker.touchUi',\n },\n animations: [\n ouiDatepickerAnimations.transformPanel,\n ouiDatepickerAnimations.fadeInCalendar,\n ],\n exportAs: 'ouiDatepickerContent',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\n inputs: ['color'],\n})\nexport class OuiDatepickerContent\n extends _OuiDatepickerContentMixinBase\n implements AfterViewInit, CanColor\n{\n /** Reference to the internal calendar component. */\n @ViewChild(OuiCalendar) _calendar: OuiCalendar;\n\n /** Reference to the datepicker that created the overlay. */\n datepicker: OuiDatepicker;\n\n /** Whether the datepicker is above or below the input. */\n _isAbove: boolean;\n\n constructor(elementRef: ElementRef) {\n super(elementRef);\n }\n\n ngAfterViewInit() {\n this._calendar.focusActiveCell();\n }\n}\n\n// TODO(mmalerba): We use a component instead of a directive here so the user can use implicit\n// template reference variables (e.g. #d vs #d=\"ouiDatepicker\"). We can change this to a directive\n// if angular adds support for `exportAs: '$implicit'` on directives.\n/** Component responsible for managing the datepicker popup/dialog. */\n@Component({\n selector: 'oui-datepicker',\n template: '',\n exportAs: 'ouiDatepicker',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n '[class.oui-datepicker-disabled]': 'disabled',\n },\n})\nexport class OuiDatepicker implements OnDestroy, CanColor {\n private _scrollStrategy: () => ScrollStrategy;\n\n /** An input indicating the type of the custom header component for the calendar, if set. */\n @Input() calendarHeaderComponent: ComponentType;\n\n /** The date to open the calendar to initially. */\n @Input()\n get startAt(): D | null {\n // If an explicit startAt is set we start there, otherwise we start at whatever the currently\n // selected value is.\n return (\n this._startAt ||\n (this._datepickerInput ? this._datepickerInput.value : null)\n );\n }\n set startAt(value: D | null) {\n this._startAt = this._getValidDateOrNull(\n this._dateAdapter.deserialize(value)\n );\n }\n private _startAt: D | null;\n\n /** The view that the calendar should start in. */\n @Input() startView: 'month' | 'year' | 'multi-year' = 'month';\n\n /** Color palette to use on the datepicker's calendar. */\n @Input()\n get color(): ThemePalette {\n return (\n this._color ||\n (this._datepickerInput\n ? this._datepickerInput._getThemePalette()\n : undefined)\n );\n }\n set color(value: ThemePalette) {\n this._color = value;\n }\n _color: ThemePalette;\n\n /**\n * Whether the calendar UI is in touch mode. In touch mode the calendar opens in a dialog rather\n * than a popup and elements have more padding to allow for bigger touch targets.\n */\n @Input()\n get touchUi(): boolean {\n return this._touchUi;\n }\n set touchUi(value: boolean) {\n this._touchUi = coerceBooleanProperty(value);\n }\n private _touchUi = false;\n\n /** Whether the datepicker pop-up should be disabled. */\n @Input()\n get disabled(): boolean {\n return this._disabled === undefined && this._datepickerInput\n ? this._datepickerInput.disabled\n : !!this._disabled;\n }\n set disabled(value: boolean) {\n const newValue = coerceBooleanProperty(value);\n\n if (newValue !== this._disabled) {\n this._disabled = newValue;\n this._disabledChange.next(newValue);\n this._datepickerInput._datepickerDisabled = newValue;\n }\n }\n private _disabled = false;\n\n /**\n * Emits selected year in multiyear view.\n * This doesn't imply a change on the selected date.\n */\n @Output() readonly yearSelected: EventEmitter = new EventEmitter();\n\n /**\n * Emits selected month in year view.\n * This doesn't imply a change on the selected date.\n */\n @Output() readonly monthSelected: EventEmitter = new EventEmitter();\n\n /** Classes to be passed to the date picker panel. Supports the same syntax as `ngClass`. */\n @Input() panelClass: string | string[];\n\n /** Function that can be used to add custom CSS classes to dates. */\n @Input() dateClass: (date: D) => OuiCalendarCellCssClasses;\n\n /** Emits when the datepicker has been opened. */\n // eslint-disable-next-line @angular-eslint/no-output-rename\n @Output('opened') openedStream: EventEmitter = new EventEmitter();\n\n /** Emits when the datepicker has been closed. */\n // eslint-disable-next-line @angular-eslint/no-output-rename\n @Output('closed') closedStream: EventEmitter = new EventEmitter();\n\n /** Whether the calendar is open. */\n @Input()\n get opened(): boolean {\n return this._opened;\n }\n set opened(value: boolean) {\n value ? this.open() : this.close();\n }\n private _opened = false;\n\n /** The id for the datepicker calendar. */\n id = `oui-datepicker-${datepickerUid++}`;\n\n /** The currently selected date. */\n get _selected(): D | null {\n return this._validSelected;\n }\n set _selected(value: D | null) {\n this._validSelected = value;\n }\n private _validSelected: D | null = null;\n\n /** The minimum selectable date. */\n get _minDate(): D | null {\n return this._datepickerInput && this._datepickerInput.min;\n }\n\n /** The maximum selectable date. */\n get _maxDate(): D | null {\n return this._datepickerInput && this._datepickerInput.max;\n }\n\n get _dateFilter(): (date: D | null) => boolean {\n return this._datepickerInput && this._datepickerInput._dateFilter;\n }\n\n /** A reference to the overlay when the calendar is opened as a popup. */\n _popupRef: OverlayRef;\n\n /** A reference to the dialog when the calendar is opened as a dialog. */\n private _dialogRef: OuiDialogRef> | null;\n\n /** A portal containing the calendar for this datepicker. */\n private _calendarPortal: ComponentPortal>;\n\n /** Reference to the component instantiated in popup mode. */\n private _popupComponentRef: ComponentRef> | null;\n\n /** The element that was focused before the datepicker was opened. */\n private _focusedElementBeforeOpen: HTMLElement | null = null;\n\n /** Subscription to value changes in the associated input element. */\n private _inputSubscription = Subscription.EMPTY;\n\n /** The input element this datepicker is associated with. */\n _datepickerInput: OuiDatepickerInput;\n\n /** Emits when the datepicker is disabled. */\n readonly _disabledChange = new Subject();\n\n /** Emits new selected date when selected date changes. */\n readonly _selectedChanged = new Subject();\n private _monitorSubscription: Subscription = Subscription.EMPTY;\n constructor(\n private _dialog: OuiDialog,\n private _overlay: Overlay,\n private _ngZone: NgZone,\n protected elementRef: ElementRef,\n private _focusMonitor: FocusMonitor,\n private _viewContainerRef: ViewContainerRef,\n @Inject(OUI_DATEPICKER_SCROLL_STRATEGY) scrollStrategy: any,\n @Optional() private _dateAdapter: DateAdapter,\n @Optional() private _dir: Directionality,\n @Optional() @Inject(DOCUMENT) private _document: any\n ) {\n if (!this._dateAdapter) {\n throw createMissingDateImplError('DateAdapter');\n }\n this._scrollStrategy = scrollStrategy;\n this._monitorSubscription = this._focusMonitor\n .monitor(this.elementRef, true)\n .subscribe(() => this._ngZone.run(() => {}));\n }\n\n ngOnDestroy() {\n this.close();\n this._inputSubscription.unsubscribe();\n this._disabledChange.complete();\n this._monitorSubscription.unsubscribe();\n\n if (this._popupRef) {\n this._popupRef.dispose();\n this._popupComponentRef = null;\n }\n }\n\n /** Selects the given date */\n select(date: D): void {\n const oldValue = this._selected;\n this._selected = date;\n if (!this._dateAdapter.sameDate(oldValue, this._selected)) {\n this._selectedChanged.next(date);\n }\n }\n\n /** Emits the selected year in multiyear view */\n _selectYear(normalizedYear: D): void {\n this.yearSelected.emit(normalizedYear);\n }\n\n /** Emits selected month in year view */\n _selectMonth(normalizedMonth: D): void {\n this.monthSelected.emit(normalizedMonth);\n }\n\n /**\n * Register an input with this datepicker.\n *\n * @param input The datepicker input to register with this datepicker.\n */\n _registerInput(input: OuiDatepickerInput): void {\n if (this._datepickerInput) {\n throw Error(\n 'A OuiDatepicker can only be associated with a single input.'\n );\n }\n this._datepickerInput = input;\n this._inputSubscription = this._datepickerInput._valueChange.subscribe(\n (value: D | null) => (this._selected = value)\n );\n }\n\n /** Open the calendar. */\n open(): void {\n if (this._opened || this.disabled) {\n return;\n }\n if (!this._datepickerInput) {\n throw Error(\n 'Attempted to open an OuiDatepicker with no associated input.'\n );\n }\n if (this._document) {\n this._focusedElementBeforeOpen = this._document.activeElement;\n }\n\n this.touchUi ? this._openAsDialog() : this._openAsPopup();\n this._opened = true;\n // add input focus here\n this._datepickerInput.focus();\n this.openedStream.emit();\n }\n\n /** Close the calendar. */\n close(): void {\n if (!this._opened) {\n return;\n }\n if (this._popupRef && this._popupRef.hasAttached()) {\n this._popupRef.detach();\n }\n if (this._dialogRef) {\n this._dialogRef.close();\n this._dialogRef = null;\n }\n if (this._calendarPortal && this._calendarPortal.isAttached) {\n this._calendarPortal.detach();\n }\n\n const completeClose = () => {\n // The `_opened` could've been reset already if\n // we got two events in quick succession.\n if (this._opened) {\n this._opened = false;\n this.closedStream.emit();\n this._focusedElementBeforeOpen = null;\n this._datepickerInput.blur();\n }\n };\n\n if (\n this._focusedElementBeforeOpen &&\n typeof this._focusedElementBeforeOpen.focus === 'function'\n ) {\n // Because IE moves focus asynchronously, we can't count on it being restored before we've\n // marked the datepicker as closed. If the event fires out of sequence and the element that\n // we're refocusing opens the datepicker on focus, the user could be stuck with not being\n // able to close the calendar at all. We work around it by making the logic, that marks\n // the datepicker as closed, async as well.\n this._focusedElementBeforeOpen.focus();\n setTimeout(completeClose);\n } else {\n completeClose();\n }\n }\n\n /** Open the calendar as a dialog. */\n private _openAsDialog(): void {\n // Usually this would be handled by `open` which ensures that we can only have one overlay\n // open at a time, however since we reset the variables in async handlers some overlays\n // may slip through if the user opens and closes multiple times in quick succession (e.g.\n // by holding down the enter key).\n if (this._dialogRef) {\n this._dialogRef.close();\n }\n\n this._dialogRef = this._dialog.open>(\n OuiDatepickerContent,\n {\n direction: this._dir ? this._dir.value : 'ltr',\n viewContainerRef: this._viewContainerRef,\n panelClass: 'oui-datepicker-dialog',\n }\n );\n\n this._dialogRef.afterClosed().subscribe(() => this.close());\n this._dialogRef.componentInstance.datepicker = this;\n this._setColor();\n }\n\n /** Open the calendar as a popup. */\n private _openAsPopup(): void {\n if (!this._calendarPortal) {\n this._calendarPortal = new ComponentPortal>(\n OuiDatepickerContent,\n this._viewContainerRef\n );\n }\n\n if (!this._popupRef) {\n this._createPopup();\n }\n\n if (!this._popupRef.hasAttached()) {\n this._popupComponentRef = this._popupRef.attach(this._calendarPortal);\n this._popupComponentRef.instance.datepicker = this;\n this._setColor();\n\n // Update the position once the calendar has rendered.\n this._ngZone.onStable\n .asObservable()\n .pipe(take(1))\n .subscribe(() => {\n this._popupRef.updatePosition();\n });\n }\n }\n\n /** Create the popup. */\n private _createPopup(): void {\n const overlayConfig = new OverlayConfig({\n positionStrategy: this._createPopupPositionStrategy(),\n hasBackdrop: true,\n backdropClass: 'oui-overlay-transparent-backdrop',\n direction: this._dir,\n scrollStrategy: this._scrollStrategy(),\n panelClass: 'oui-datepicker-popup',\n });\n\n this._popupRef = this._overlay.create(overlayConfig);\n this._popupRef.overlayElement.setAttribute('role', 'dialog');\n\n merge(\n this._popupRef.backdropClick(),\n this._popupRef.detachments(),\n this._popupRef.keydownEvents().pipe(\n filter(\n (event) =>\n // Closing on alt + up is only valid when there's an input associated with the datepicker.\n event.keyCode === ESCAPE ||\n (this._datepickerInput &&\n event.altKey &&\n event.keyCode === UP_ARROW)\n )\n )\n ).subscribe(() => this.close());\n }\n\n /** Create the popup PositionStrategy. */\n private _createPopupPositionStrategy(): PositionStrategy {\n return this._overlay\n .position()\n .flexibleConnectedTo(this._datepickerInput.getConnectedOverlayOrigin())\n .withTransformOriginOn('.oui-datepicker-content')\n .withFlexibleDimensions(false)\n .withViewportMargin(8)\n .withLockedPosition()\n .withPositions([\n {\n originX: 'start',\n originY: 'bottom',\n overlayX: 'start',\n overlayY: 'top',\n },\n {\n originX: 'start',\n originY: 'top',\n overlayX: 'start',\n overlayY: 'bottom',\n },\n {\n originX: 'end',\n originY: 'bottom',\n overlayX: 'end',\n overlayY: 'top',\n },\n {\n originX: 'end',\n originY: 'top',\n overlayX: 'end',\n overlayY: 'bottom',\n },\n ]);\n }\n\n /**\n * @param obj The object to check.\n * @returns The given object if it is both a date instance and valid, otherwise null.\n */\n private _getValidDateOrNull(obj: any): D | null {\n return this._dateAdapter.isDateInstance(obj) &&\n this._dateAdapter.isValid(obj as any as D)\n ? obj\n : null;\n }\n\n /** Passes the current theme color along to the calendar overlay. */\n private _setColor(): void {\n const color = this.color;\n if (this._popupComponentRef) {\n this._popupComponentRef.instance.color = color;\n }\n if (this._dialogRef) {\n this._dialogRef.componentInstance.color = color;\n }\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": "$oui-datepicker-calendar-padding: 8px;\r\n$oui-datepicker-non-touch-calendar-cell-size: 40px;\r\n$oui-datepicker-non-touch-calendar-width: $oui-datepicker-non-touch-calendar-cell-size *\r\n 7 + $oui-datepicker-calendar-padding * 2;\r\n// Based on the natural height of the calendar in a month with 6 rows of dates\r\n// (largest the calendar will get).\r\n$oui-datepicker-non-touch-calendar-height: 354px;\r\n\r\n// Ideally the calendar would have a constant aspect ratio, no matter its size, and we would base\r\n// these measurements off the aspect ratio. Unfortunately, the aspect ratio does change a little as\r\n// the calendar grows, since some of the elements have pixel-based sizes. These numbers have been\r\n// chosen to minimize extra whitespace at larger sizes, while still ensuring we won't need\r\n// scrollbars at smaller sizes.\r\n$oui-datepicker-touch-landscape-width: 64vh;\r\n$oui-datepicker-touch-landscape-height: 80vh;\r\n$oui-datepicker-touch-portrait-width: 80vw;\r\n$oui-datepicker-touch-portrait-height: 100vw;\r\n$oui-datepicker-touch-min-width: 250px;\r\n$oui-datepicker-touch-min-height: 312px;\r\n$oui-datepicker-touch-max-width: 750px;\r\n$oui-datepicker-touch-max-height: 788px;\r\n\r\n.oui-datepicker-content {\r\n display: block;\r\n background-color: #ffffff;\r\n color: #000;\r\n box-shadow: 0px 1px 3px 0px #4a4a4a;\r\n border: 1px solid #c8c8c8;\r\n .oui-calendar {\r\n width: $oui-datepicker-non-touch-calendar-width;\r\n height: $oui-datepicker-non-touch-calendar-height;\r\n }\r\n}\r\n\r\n.oui-datepicker-content-touch {\r\n display: block;\r\n // make sure the dialog scrolls rather than being cropped on ludicrously small screens\r\n max-height: 80vh;\r\n overflow: auto;\r\n\r\n // TODO(mmalerba): hack to offset the padding of the dialog. Can be removed when we switch away\r\n // from using dialog.\r\n margin: -24px;\r\n\r\n .oui-calendar {\r\n min-width: $oui-datepicker-touch-min-width;\r\n min-height: $oui-datepicker-touch-min-height;\r\n max-width: $oui-datepicker-touch-max-width;\r\n max-height: $oui-datepicker-touch-max-height;\r\n }\r\n}\r\n\r\n@media all and (orientation: landscape) {\r\n .oui-datepicker-content-touch .oui-calendar {\r\n width: $oui-datepicker-touch-landscape-width;\r\n height: $oui-datepicker-touch-landscape-height;\r\n }\r\n}\r\n\r\n@media all and (orientation: portrait) {\r\n .oui-datepicker-content-touch .oui-calendar {\r\n width: $oui-datepicker-touch-portrait-width;\r\n height: $oui-datepicker-touch-portrait-height;\r\n }\r\n}\r\n", + "data": "$oui-datepicker-calendar-padding: 8px;\n$oui-datepicker-non-touch-calendar-cell-size: 40px;\n$oui-datepicker-non-touch-calendar-width: $oui-datepicker-non-touch-calendar-cell-size *\n 7 + $oui-datepicker-calendar-padding * 2;\n// Based on the natural height of the calendar in a month with 6 rows of dates\n// (largest the calendar will get).\n$oui-datepicker-non-touch-calendar-height: 354px;\n\n// Ideally the calendar would have a constant aspect ratio, no matter its size, and we would base\n// these measurements off the aspect ratio. Unfortunately, the aspect ratio does change a little as\n// the calendar grows, since some of the elements have pixel-based sizes. These numbers have been\n// chosen to minimize extra whitespace at larger sizes, while still ensuring we won't need\n// scrollbars at smaller sizes.\n$oui-datepicker-touch-landscape-width: 64vh;\n$oui-datepicker-touch-landscape-height: 80vh;\n$oui-datepicker-touch-portrait-width: 80vw;\n$oui-datepicker-touch-portrait-height: 100vw;\n$oui-datepicker-touch-min-width: 250px;\n$oui-datepicker-touch-min-height: 312px;\n$oui-datepicker-touch-max-width: 750px;\n$oui-datepicker-touch-max-height: 788px;\n\n.oui-datepicker-content {\n display: block;\n background-color: #ffffff;\n color: #000;\n box-shadow: 0px 1px 3px 0px #4a4a4a;\n border: 1px solid #c8c8c8;\n .oui-calendar {\n width: $oui-datepicker-non-touch-calendar-width;\n height: $oui-datepicker-non-touch-calendar-height;\n }\n}\n\n.oui-datepicker-content-touch {\n display: block;\n // make sure the dialog scrolls rather than being cropped on ludicrously small screens\n max-height: 80vh;\n overflow: auto;\n\n // TODO(mmalerba): hack to offset the padding of the dialog. Can be removed when we switch away\n // from using dialog.\n margin: -24px;\n\n .oui-calendar {\n min-width: $oui-datepicker-touch-min-width;\n min-height: $oui-datepicker-touch-min-height;\n max-width: $oui-datepicker-touch-max-width;\n max-height: $oui-datepicker-touch-max-height;\n }\n}\n\n@media all and (orientation: landscape) {\n .oui-datepicker-content-touch .oui-calendar {\n width: $oui-datepicker-touch-landscape-width;\n height: $oui-datepicker-touch-landscape-height;\n }\n}\n\n@media all and (orientation: portrait) {\n .oui-datepicker-content-touch .oui-calendar {\n width: $oui-datepicker-touch-portrait-width;\n height: $oui-datepicker-touch-portrait-height;\n }\n}\n", "styleUrl": "datepicker-content.scss" } ], @@ -24153,11 +34011,11 @@ "AfterViewInit", "CanColor" ], - "templateData": "\r\n\r\n" + "templateData": "\n\n" }, { "name": "OuiDatepickerCustomStorybook", - "id": "component-OuiDatepickerCustomStorybook-e4c53c638e6ede616808c9cc4a8740cb9557c0da4945cbc3e3427e5a572b2fde2c05a3ea15bede5a2b62c9b9a18fbb4b1c0b4f208802b6c79f24bb1332af13cf", + "id": "component-OuiDatepickerCustomStorybook-c9a90e05e9b508008603b4e1a0926da24f4560c1236a614a980077e61ed15818b92e3cec2458de749a4bcf2476affeffacc8bf666d4ac4bf5e12b34f09554afc", "file": "ui/src/stories/datepicker/datepicker.component.ts", "encapsulation": [], "entryComponents": [], @@ -24491,7 +34349,7 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import {\r\n Component,\r\n Output,\r\n EventEmitter,\r\n Input,\r\n OnChanges,\r\n} from '@angular/core';\r\nimport { OuiDatepickerInputEvent } from '../../components/datepicker';\r\nimport { OuiDateFormats, OUI_DATE_FORMATS } from '../../components';\r\n\r\nexport const OUI_CUSTOM_DATE_FORMATS: OuiDateFormats = {\r\n parse: {\r\n dateInput: null,\r\n },\r\n display: {\r\n dateInput: {\r\n year: 'numeric',\r\n day: '2-digit',\r\n month: '2-digit',\r\n },\r\n monthYearLabel: { year: 'numeric', month: 'short' },\r\n dateA11yLabel: { year: 'numeric', month: 'long', day: 'numeric' },\r\n monthYearA11yLabel: { year: 'numeric', month: 'long' },\r\n },\r\n};\r\n\r\n@Component({\r\n selector: 'oui-datepicker-storybook',\r\n template: `\r\n
\r\n \r\n \r\n \r\n \r\n \r\n
\r\n `,\r\n})\r\nexport class OuiDatepickerStorybook implements OnChanges {\r\n @Input() appearance = 'standard';\r\n @Input() color = 'primary';\r\n @Input() startView = ['month', 'year', 'multi-year'];\r\n @Input() opened = false;\r\n @Input() disabled = false;\r\n @Input() mindate: Date = new Date();\r\n minDate: Date = new Date();\r\n @Input() value: Date = new Date();\r\n _value: Date = new Date();\r\n @Input() maxdate: Date = new Date();\r\n maxDate: Date = new Date();\r\n @Output()\r\n readonly _closed: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _monthSelected: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _opened: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _yearSelected: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _dateChange: EventEmitter = new EventEmitter();\r\n constructor() {}\r\n ngOnChanges() {\r\n this.minDate = new Date(this.mindate);\r\n this.maxDate = new Date(this.maxdate);\r\n this._value = new Date(this.value);\r\n if (this.opened) {\r\n (document.querySelector('.oui-datepicker-toggle') as HTMLElement).focus();\r\n }\r\n }\r\n\r\n closed(e?: string) {\r\n this._closed.emit(e);\r\n }\r\n monthSelected(e?: string) {\r\n this._monthSelected.emit(e);\r\n }\r\n datepickeropened(e?: string) {\r\n this._opened.emit(e);\r\n }\r\n yearSelected(e?: string) {\r\n this._yearSelected.emit(e);\r\n }\r\n dateChange(e?: string) {\r\n this._dateChange.emit(e);\r\n }\r\n}\r\n\r\n@Component({\r\n selector: 'oui-datepicker-custom-storybook',\r\n template: `\r\n
\r\n \r\n \r\n \r\n \r\n \r\n
\r\n `,\r\n providers: [{ provide: OUI_DATE_FORMATS, useValue: OUI_CUSTOM_DATE_FORMATS }],\r\n})\r\nexport class OuiDatepickerCustomStorybook implements OnChanges {\r\n @Input() appearance = 'standard';\r\n @Input() color = 'primary';\r\n @Input() startView = ['month', 'year', 'multi-year'];\r\n @Input() opened = false;\r\n @Input() disabled = false;\r\n @Input() mindate: Date = new Date();\r\n minDate: Date = new Date();\r\n @Input() value: Date = new Date();\r\n _value: Date = new Date();\r\n @Input() maxdate: Date = new Date();\r\n maxDate: Date = new Date();\r\n @Output()\r\n readonly _closed: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _monthSelected: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _opened: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _yearSelected: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _dateChange: EventEmitter = new EventEmitter();\r\n constructor() {}\r\n ngOnChanges() {\r\n this.minDate = new Date(this.mindate);\r\n this.maxDate = new Date(this.maxdate);\r\n this._value = new Date(this.value);\r\n if (this.opened) {\r\n (document.querySelector('.oui-datepicker-toggle') as HTMLElement).focus();\r\n }\r\n }\r\n closed(e?: string) {\r\n this._closed.emit(e);\r\n }\r\n monthSelected(e?: string) {\r\n this._monthSelected.emit(e);\r\n }\r\n datepickeropened(e?: string) {\r\n this._opened.emit(e);\r\n }\r\n yearSelected(e?: string) {\r\n this._yearSelected.emit(e);\r\n }\r\n dateChange(e?: string) {\r\n this._dateChange.emit(e);\r\n }\r\n}\r\n\r\n@Component({\r\n selector: 'oui-daterangepicker-storybook',\r\n template: `\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n \r\n
\r\n
\r\n \r\n \r\n \r\n \r\n \r\n
\r\n
\r\n `,\r\n})\r\nexport class OuiDaterangepickerStorybook implements OnChanges {\r\n @Input() appearance = 'standard';\r\n @Input() color = 'primary';\r\n @Input() startView = 'primary';\r\n @Input() opened = false;\r\n @Input() disabled = false;\r\n @Input() mindate: Date;\r\n minDate: Date = new Date();\r\n minRangeDate: Date;\r\n @Input() maxdate: Date;\r\n maxRangeDate: Date;\r\n maxDate: Date;\r\n @Output()\r\n readonly _dateChange: EventEmitter<{}> = new EventEmitter<{}>();\r\n constructor() {}\r\n ngOnChanges() {\r\n this.minRangeDate = new Date(this.mindate);\r\n this.maxRangeDate = new Date(this.maxdate);\r\n }\r\n mindateChange(e: OuiDatepickerInputEvent) {\r\n this.minDate = new Date(e.value);\r\n console.log(this.minDate);\r\n if (this.maxDate) {\r\n this._dateChange.emit({\r\n min: this.minDate,\r\n max: this.maxDate,\r\n });\r\n }\r\n // this._dateChange.emit(e);\r\n }\r\n maxdateChange(e: OuiDatepickerInputEvent) {\r\n this.maxDate = new Date(e.value);\r\n console.log(this.maxDate);\r\n if (this.minDate) {\r\n this._dateChange.emit({\r\n min: this.minDate,\r\n max: this.maxDate,\r\n });\r\n }\r\n // this._dateChange.emit(e);\r\n }\r\n}\r\n", + "sourceCode": "import {\n Component,\n Output,\n EventEmitter,\n Input,\n OnChanges,\n} from '@angular/core';\nimport { OuiDatepickerInputEvent } from '../../components/datepicker';\nimport { OuiDateFormats, OUI_DATE_FORMATS } from '../../components';\n\nexport const OUI_CUSTOM_DATE_FORMATS: OuiDateFormats = {\n parse: {\n dateInput: null,\n },\n display: {\n dateInput: {\n year: 'numeric',\n day: '2-digit',\n month: '2-digit',\n },\n monthYearLabel: { year: 'numeric', month: 'short' },\n dateA11yLabel: { year: 'numeric', month: 'long', day: 'numeric' },\n monthYearA11yLabel: { year: 'numeric', month: 'long' },\n },\n};\n\n@Component({\n selector: 'oui-datepicker-storybook',\n template: `\n
\n \n \n \n \n \n
\n `,\n})\nexport class OuiDatepickerStorybook implements OnChanges {\n @Input() appearance = 'standard';\n @Input() color = 'primary';\n @Input() startView = ['month', 'year', 'multi-year'];\n @Input() opened = false;\n @Input() disabled = false;\n @Input() mindate: Date = new Date();\n minDate: Date = new Date();\n @Input() value: Date = new Date();\n _value: Date = new Date();\n @Input() maxdate: Date = new Date();\n maxDate: Date = new Date();\n @Output()\n readonly _closed: EventEmitter = new EventEmitter();\n @Output()\n readonly _monthSelected: EventEmitter = new EventEmitter();\n @Output()\n readonly _opened: EventEmitter = new EventEmitter();\n @Output()\n readonly _yearSelected: EventEmitter = new EventEmitter();\n @Output()\n readonly _dateChange: EventEmitter = new EventEmitter();\n constructor() {}\n ngOnChanges() {\n this.minDate = new Date(this.mindate);\n this.maxDate = new Date(this.maxdate);\n this._value = new Date(this.value);\n if (this.opened) {\n (document.querySelector('.oui-datepicker-toggle') as HTMLElement).focus();\n }\n }\n\n closed(e?: string) {\n this._closed.emit(e);\n }\n monthSelected(e?: string) {\n this._monthSelected.emit(e);\n }\n datepickeropened(e?: string) {\n this._opened.emit(e);\n }\n yearSelected(e?: string) {\n this._yearSelected.emit(e);\n }\n dateChange(e?: string) {\n this._dateChange.emit(e);\n }\n}\n\n@Component({\n selector: 'oui-datepicker-custom-storybook',\n template: `\n
\n \n \n \n \n \n
\n `,\n providers: [{ provide: OUI_DATE_FORMATS, useValue: OUI_CUSTOM_DATE_FORMATS }],\n})\nexport class OuiDatepickerCustomStorybook implements OnChanges {\n @Input() appearance = 'standard';\n @Input() color = 'primary';\n @Input() startView = ['month', 'year', 'multi-year'];\n @Input() opened = false;\n @Input() disabled = false;\n @Input() mindate: Date = new Date();\n minDate: Date = new Date();\n @Input() value: Date = new Date();\n _value: Date = new Date();\n @Input() maxdate: Date = new Date();\n maxDate: Date = new Date();\n @Output()\n readonly _closed: EventEmitter = new EventEmitter();\n @Output()\n readonly _monthSelected: EventEmitter = new EventEmitter();\n @Output()\n readonly _opened: EventEmitter = new EventEmitter();\n @Output()\n readonly _yearSelected: EventEmitter = new EventEmitter();\n @Output()\n readonly _dateChange: EventEmitter = new EventEmitter();\n constructor() {}\n ngOnChanges() {\n this.minDate = new Date(this.mindate);\n this.maxDate = new Date(this.maxdate);\n this._value = new Date(this.value);\n if (this.opened) {\n (document.querySelector('.oui-datepicker-toggle') as HTMLElement).focus();\n }\n }\n closed(e?: string) {\n this._closed.emit(e);\n }\n monthSelected(e?: string) {\n this._monthSelected.emit(e);\n }\n datepickeropened(e?: string) {\n this._opened.emit(e);\n }\n yearSelected(e?: string) {\n this._yearSelected.emit(e);\n }\n dateChange(e?: string) {\n this._dateChange.emit(e);\n }\n}\n\n@Component({\n selector: 'oui-daterangepicker-storybook',\n template: `\n
\n
\n \n \n \n \n \n
\n
\n \n \n \n \n \n
\n
\n `,\n})\nexport class OuiDaterangepickerStorybook implements OnChanges {\n @Input() appearance = 'standard';\n @Input() color = 'primary';\n @Input() startView = 'primary';\n @Input() opened = false;\n @Input() disabled = false;\n @Input() mindate: Date;\n minDate: Date = new Date();\n minRangeDate: Date;\n @Input() maxdate: Date;\n maxRangeDate: Date;\n maxDate: Date;\n @Output()\n readonly _dateChange: EventEmitter<{}> = new EventEmitter<{}>();\n constructor() {}\n ngOnChanges() {\n this.minRangeDate = new Date(this.mindate);\n this.maxRangeDate = new Date(this.maxdate);\n }\n mindateChange(e: OuiDatepickerInputEvent) {\n this.minDate = new Date(e.value);\n console.log(this.minDate);\n if (this.maxDate) {\n this._dateChange.emit({\n min: this.minDate,\n max: this.maxDate,\n });\n }\n // this._dateChange.emit(e);\n }\n maxdateChange(e: OuiDatepickerInputEvent) {\n this.maxDate = new Date(e.value);\n console.log(this.maxDate);\n if (this.minDate) {\n this._dateChange.emit({\n min: this.minDate,\n max: this.maxDate,\n });\n }\n // this._dateChange.emit(e);\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -24509,7 +34367,7 @@ }, { "name": "OuiDatepickerStorybook", - "id": "component-OuiDatepickerStorybook-e4c53c638e6ede616808c9cc4a8740cb9557c0da4945cbc3e3427e5a572b2fde2c05a3ea15bede5a2b62c9b9a18fbb4b1c0b4f208802b6c79f24bb1332af13cf", + "id": "component-OuiDatepickerStorybook-c9a90e05e9b508008603b4e1a0926da24f4560c1236a614a980077e61ed15818b92e3cec2458de749a4bcf2476affeffacc8bf666d4ac4bf5e12b34f09554afc", "file": "ui/src/stories/datepicker/datepicker.component.ts", "encapsulation": [], "entryComponents": [], @@ -24839,7 +34697,7 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import {\r\n Component,\r\n Output,\r\n EventEmitter,\r\n Input,\r\n OnChanges,\r\n} from '@angular/core';\r\nimport { OuiDatepickerInputEvent } from '../../components/datepicker';\r\nimport { OuiDateFormats, OUI_DATE_FORMATS } from '../../components';\r\n\r\nexport const OUI_CUSTOM_DATE_FORMATS: OuiDateFormats = {\r\n parse: {\r\n dateInput: null,\r\n },\r\n display: {\r\n dateInput: {\r\n year: 'numeric',\r\n day: '2-digit',\r\n month: '2-digit',\r\n },\r\n monthYearLabel: { year: 'numeric', month: 'short' },\r\n dateA11yLabel: { year: 'numeric', month: 'long', day: 'numeric' },\r\n monthYearA11yLabel: { year: 'numeric', month: 'long' },\r\n },\r\n};\r\n\r\n@Component({\r\n selector: 'oui-datepicker-storybook',\r\n template: `\r\n
\r\n \r\n \r\n \r\n \r\n \r\n
\r\n `,\r\n})\r\nexport class OuiDatepickerStorybook implements OnChanges {\r\n @Input() appearance = 'standard';\r\n @Input() color = 'primary';\r\n @Input() startView = ['month', 'year', 'multi-year'];\r\n @Input() opened = false;\r\n @Input() disabled = false;\r\n @Input() mindate: Date = new Date();\r\n minDate: Date = new Date();\r\n @Input() value: Date = new Date();\r\n _value: Date = new Date();\r\n @Input() maxdate: Date = new Date();\r\n maxDate: Date = new Date();\r\n @Output()\r\n readonly _closed: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _monthSelected: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _opened: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _yearSelected: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _dateChange: EventEmitter = new EventEmitter();\r\n constructor() {}\r\n ngOnChanges() {\r\n this.minDate = new Date(this.mindate);\r\n this.maxDate = new Date(this.maxdate);\r\n this._value = new Date(this.value);\r\n if (this.opened) {\r\n (document.querySelector('.oui-datepicker-toggle') as HTMLElement).focus();\r\n }\r\n }\r\n\r\n closed(e?: string) {\r\n this._closed.emit(e);\r\n }\r\n monthSelected(e?: string) {\r\n this._monthSelected.emit(e);\r\n }\r\n datepickeropened(e?: string) {\r\n this._opened.emit(e);\r\n }\r\n yearSelected(e?: string) {\r\n this._yearSelected.emit(e);\r\n }\r\n dateChange(e?: string) {\r\n this._dateChange.emit(e);\r\n }\r\n}\r\n\r\n@Component({\r\n selector: 'oui-datepicker-custom-storybook',\r\n template: `\r\n
\r\n \r\n \r\n \r\n \r\n \r\n
\r\n `,\r\n providers: [{ provide: OUI_DATE_FORMATS, useValue: OUI_CUSTOM_DATE_FORMATS }],\r\n})\r\nexport class OuiDatepickerCustomStorybook implements OnChanges {\r\n @Input() appearance = 'standard';\r\n @Input() color = 'primary';\r\n @Input() startView = ['month', 'year', 'multi-year'];\r\n @Input() opened = false;\r\n @Input() disabled = false;\r\n @Input() mindate: Date = new Date();\r\n minDate: Date = new Date();\r\n @Input() value: Date = new Date();\r\n _value: Date = new Date();\r\n @Input() maxdate: Date = new Date();\r\n maxDate: Date = new Date();\r\n @Output()\r\n readonly _closed: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _monthSelected: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _opened: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _yearSelected: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _dateChange: EventEmitter = new EventEmitter();\r\n constructor() {}\r\n ngOnChanges() {\r\n this.minDate = new Date(this.mindate);\r\n this.maxDate = new Date(this.maxdate);\r\n this._value = new Date(this.value);\r\n if (this.opened) {\r\n (document.querySelector('.oui-datepicker-toggle') as HTMLElement).focus();\r\n }\r\n }\r\n closed(e?: string) {\r\n this._closed.emit(e);\r\n }\r\n monthSelected(e?: string) {\r\n this._monthSelected.emit(e);\r\n }\r\n datepickeropened(e?: string) {\r\n this._opened.emit(e);\r\n }\r\n yearSelected(e?: string) {\r\n this._yearSelected.emit(e);\r\n }\r\n dateChange(e?: string) {\r\n this._dateChange.emit(e);\r\n }\r\n}\r\n\r\n@Component({\r\n selector: 'oui-daterangepicker-storybook',\r\n template: `\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n \r\n
\r\n
\r\n \r\n \r\n \r\n \r\n \r\n
\r\n
\r\n `,\r\n})\r\nexport class OuiDaterangepickerStorybook implements OnChanges {\r\n @Input() appearance = 'standard';\r\n @Input() color = 'primary';\r\n @Input() startView = 'primary';\r\n @Input() opened = false;\r\n @Input() disabled = false;\r\n @Input() mindate: Date;\r\n minDate: Date = new Date();\r\n minRangeDate: Date;\r\n @Input() maxdate: Date;\r\n maxRangeDate: Date;\r\n maxDate: Date;\r\n @Output()\r\n readonly _dateChange: EventEmitter<{}> = new EventEmitter<{}>();\r\n constructor() {}\r\n ngOnChanges() {\r\n this.minRangeDate = new Date(this.mindate);\r\n this.maxRangeDate = new Date(this.maxdate);\r\n }\r\n mindateChange(e: OuiDatepickerInputEvent) {\r\n this.minDate = new Date(e.value);\r\n console.log(this.minDate);\r\n if (this.maxDate) {\r\n this._dateChange.emit({\r\n min: this.minDate,\r\n max: this.maxDate,\r\n });\r\n }\r\n // this._dateChange.emit(e);\r\n }\r\n maxdateChange(e: OuiDatepickerInputEvent) {\r\n this.maxDate = new Date(e.value);\r\n console.log(this.maxDate);\r\n if (this.minDate) {\r\n this._dateChange.emit({\r\n min: this.minDate,\r\n max: this.maxDate,\r\n });\r\n }\r\n // this._dateChange.emit(e);\r\n }\r\n}\r\n", + "sourceCode": "import {\n Component,\n Output,\n EventEmitter,\n Input,\n OnChanges,\n} from '@angular/core';\nimport { OuiDatepickerInputEvent } from '../../components/datepicker';\nimport { OuiDateFormats, OUI_DATE_FORMATS } from '../../components';\n\nexport const OUI_CUSTOM_DATE_FORMATS: OuiDateFormats = {\n parse: {\n dateInput: null,\n },\n display: {\n dateInput: {\n year: 'numeric',\n day: '2-digit',\n month: '2-digit',\n },\n monthYearLabel: { year: 'numeric', month: 'short' },\n dateA11yLabel: { year: 'numeric', month: 'long', day: 'numeric' },\n monthYearA11yLabel: { year: 'numeric', month: 'long' },\n },\n};\n\n@Component({\n selector: 'oui-datepicker-storybook',\n template: `\n
\n \n \n \n \n \n
\n `,\n})\nexport class OuiDatepickerStorybook implements OnChanges {\n @Input() appearance = 'standard';\n @Input() color = 'primary';\n @Input() startView = ['month', 'year', 'multi-year'];\n @Input() opened = false;\n @Input() disabled = false;\n @Input() mindate: Date = new Date();\n minDate: Date = new Date();\n @Input() value: Date = new Date();\n _value: Date = new Date();\n @Input() maxdate: Date = new Date();\n maxDate: Date = new Date();\n @Output()\n readonly _closed: EventEmitter = new EventEmitter();\n @Output()\n readonly _monthSelected: EventEmitter = new EventEmitter();\n @Output()\n readonly _opened: EventEmitter = new EventEmitter();\n @Output()\n readonly _yearSelected: EventEmitter = new EventEmitter();\n @Output()\n readonly _dateChange: EventEmitter = new EventEmitter();\n constructor() {}\n ngOnChanges() {\n this.minDate = new Date(this.mindate);\n this.maxDate = new Date(this.maxdate);\n this._value = new Date(this.value);\n if (this.opened) {\n (document.querySelector('.oui-datepicker-toggle') as HTMLElement).focus();\n }\n }\n\n closed(e?: string) {\n this._closed.emit(e);\n }\n monthSelected(e?: string) {\n this._monthSelected.emit(e);\n }\n datepickeropened(e?: string) {\n this._opened.emit(e);\n }\n yearSelected(e?: string) {\n this._yearSelected.emit(e);\n }\n dateChange(e?: string) {\n this._dateChange.emit(e);\n }\n}\n\n@Component({\n selector: 'oui-datepicker-custom-storybook',\n template: `\n
\n \n \n \n \n \n
\n `,\n providers: [{ provide: OUI_DATE_FORMATS, useValue: OUI_CUSTOM_DATE_FORMATS }],\n})\nexport class OuiDatepickerCustomStorybook implements OnChanges {\n @Input() appearance = 'standard';\n @Input() color = 'primary';\n @Input() startView = ['month', 'year', 'multi-year'];\n @Input() opened = false;\n @Input() disabled = false;\n @Input() mindate: Date = new Date();\n minDate: Date = new Date();\n @Input() value: Date = new Date();\n _value: Date = new Date();\n @Input() maxdate: Date = new Date();\n maxDate: Date = new Date();\n @Output()\n readonly _closed: EventEmitter = new EventEmitter();\n @Output()\n readonly _monthSelected: EventEmitter = new EventEmitter();\n @Output()\n readonly _opened: EventEmitter = new EventEmitter();\n @Output()\n readonly _yearSelected: EventEmitter = new EventEmitter();\n @Output()\n readonly _dateChange: EventEmitter = new EventEmitter();\n constructor() {}\n ngOnChanges() {\n this.minDate = new Date(this.mindate);\n this.maxDate = new Date(this.maxdate);\n this._value = new Date(this.value);\n if (this.opened) {\n (document.querySelector('.oui-datepicker-toggle') as HTMLElement).focus();\n }\n }\n closed(e?: string) {\n this._closed.emit(e);\n }\n monthSelected(e?: string) {\n this._monthSelected.emit(e);\n }\n datepickeropened(e?: string) {\n this._opened.emit(e);\n }\n yearSelected(e?: string) {\n this._yearSelected.emit(e);\n }\n dateChange(e?: string) {\n this._dateChange.emit(e);\n }\n}\n\n@Component({\n selector: 'oui-daterangepicker-storybook',\n template: `\n
\n
\n \n \n \n \n \n
\n
\n \n \n \n \n \n
\n
\n `,\n})\nexport class OuiDaterangepickerStorybook implements OnChanges {\n @Input() appearance = 'standard';\n @Input() color = 'primary';\n @Input() startView = 'primary';\n @Input() opened = false;\n @Input() disabled = false;\n @Input() mindate: Date;\n minDate: Date = new Date();\n minRangeDate: Date;\n @Input() maxdate: Date;\n maxRangeDate: Date;\n maxDate: Date;\n @Output()\n readonly _dateChange: EventEmitter<{}> = new EventEmitter<{}>();\n constructor() {}\n ngOnChanges() {\n this.minRangeDate = new Date(this.mindate);\n this.maxRangeDate = new Date(this.maxdate);\n }\n mindateChange(e: OuiDatepickerInputEvent) {\n this.minDate = new Date(e.value);\n console.log(this.minDate);\n if (this.maxDate) {\n this._dateChange.emit({\n min: this.minDate,\n max: this.maxDate,\n });\n }\n // this._dateChange.emit(e);\n }\n maxdateChange(e: OuiDatepickerInputEvent) {\n this.maxDate = new Date(e.value);\n console.log(this.maxDate);\n if (this.minDate) {\n this._dateChange.emit({\n min: this.minDate,\n max: this.maxDate,\n });\n }\n // this._dateChange.emit(e);\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -24857,7 +34715,7 @@ }, { "name": "OuiDatepickerToggle", - "id": "component-OuiDatepickerToggle-42a35da21f43e183ca7075b4db8b0fd307da1df2e9d4eb408ab1dad0d46020ce064d8defc71a3e85f8b7138d8d86554122feb77ab4e3c155410109d50b575e92", + "id": "component-OuiDatepickerToggle-9b1cdbd3291a207773962cd306bb54d33715b54205261f3ad63574b91a7ae2cc4f614a1447c6d4fa9b7e168e9e0a22cfbd064121461f7b1086081f79d851a553", "file": "ui/src/components/datepicker/datepicker-toggle.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -25080,11 +34938,11 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import { coerceBooleanProperty } from '@angular/cdk/coercion';\r\nimport {\r\n AfterContentInit,\r\n Attribute,\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n ContentChild,\r\n Directive,\r\n Input,\r\n OnChanges,\r\n OnDestroy,\r\n SimpleChanges,\r\n ViewEncapsulation,\r\n ViewChild,\r\n} from '@angular/core';\r\nimport { OuiButton } from '../button/button';\r\nimport { merge, Observable, of as observableOf, Subscription } from 'rxjs';\r\nimport { OuiDatepicker } from './datepicker';\r\nimport { OuiDatepickerIntl } from './datepicker-intl';\r\n\r\n/** Can be used to override the icon of a `ouiDatepickerToggle`. */\r\n@Directive({\r\n selector: '[ouiDatepickerToggleIcon]',\r\n})\r\nexport class OuiDatepickerToggleIcon {}\r\n\r\n@Component({\r\n selector: 'oui-datepicker-toggle',\r\n templateUrl: 'datepicker-toggle.html',\r\n styleUrls: ['datepicker-toggle.scss'],\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-datepicker-toggle',\r\n // Always set the tabindex to -1 so that it doesn't overlap with any custom tabindex the\r\n // consumer may have provided, while still being able to receive focus.\r\n '[attr.tabindex]': '-1',\r\n '[class.oui-datepicker-toggle-active]': 'datepicker && datepicker.opened',\r\n '[class.oui-accent]': 'datepicker && datepicker.color === \"accent\"',\r\n '[class.oui-warn]': 'datepicker && datepicker.color === \"warn\"',\r\n '(focus)': '_button.focus()',\r\n '[class.oui-datepicker-disabled]': 'disabled',\r\n },\r\n exportAs: 'ouiDatepickerToggle',\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class OuiDatepickerToggle\r\n implements AfterContentInit, OnChanges, OnDestroy\r\n{\r\n private _stateChanges = Subscription.EMPTY;\r\n\r\n /** Datepicker instance that the button will toggle. */\r\n // eslint-disable-next-line @angular-eslint/no-input-rename\r\n @Input('for') datepicker: OuiDatepicker;\r\n\r\n /** Tabindex for the toggle. */\r\n @Input() tabIndex: number | null;\r\n\r\n /** Whether the toggle button is disabled. */\r\n @Input()\r\n get disabled(): boolean {\r\n return this._disabled === undefined\r\n ? this.datepicker.disabled\r\n : !!this._disabled;\r\n }\r\n set disabled(value: boolean) {\r\n this._disabled = coerceBooleanProperty(value);\r\n }\r\n private _disabled: boolean;\r\n\r\n /** Custom icon set by the consumer. */\r\n @ContentChild(OuiDatepickerToggleIcon)\r\n _customIcon: OuiDatepickerToggleIcon;\r\n\r\n /** Underlying button element. */\r\n @ViewChild('button') _button: OuiButton;\r\n\r\n constructor(\r\n public _intl: OuiDatepickerIntl,\r\n private _changeDetectorRef: ChangeDetectorRef,\r\n @Attribute('tabindex') defaultTabIndex: string\r\n ) {\r\n const parsedTabIndex = Number(defaultTabIndex);\r\n this.tabIndex =\r\n parsedTabIndex || parsedTabIndex === 0 ? parsedTabIndex : null;\r\n }\r\n\r\n ngOnChanges(changes: SimpleChanges) {\r\n if (changes.datepicker) {\r\n this._watchStateChanges();\r\n }\r\n }\r\n\r\n ngOnDestroy() {\r\n this._stateChanges.unsubscribe();\r\n }\r\n\r\n ngAfterContentInit() {\r\n this._watchStateChanges();\r\n }\r\n\r\n _open(event: Event): void {\r\n if (this.datepicker && !this.disabled) {\r\n this.datepicker.open();\r\n event.stopPropagation();\r\n }\r\n }\r\n\r\n private _watchStateChanges() {\r\n const datepickerDisabled = this.datepicker\r\n ? this.datepicker._disabledChange\r\n : observableOf();\r\n const inputDisabled =\r\n this.datepicker && this.datepicker._datepickerInput\r\n ? this.datepicker._datepickerInput._disabledChange\r\n : observableOf();\r\n const datepickerToggled = this.datepicker\r\n ? merge(this.datepicker.openedStream, this.datepicker.closedStream)\r\n : observableOf();\r\n\r\n this._stateChanges.unsubscribe();\r\n this._stateChanges = merge(\r\n this._intl.changes,\r\n datepickerDisabled as Observable,\r\n inputDisabled as Observable,\r\n datepickerToggled\r\n ).subscribe(() => this._changeDetectorRef.markForCheck());\r\n }\r\n}\r\n", + "sourceCode": "import { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport {\n AfterContentInit,\n Attribute,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n ContentChild,\n Directive,\n Input,\n OnChanges,\n OnDestroy,\n SimpleChanges,\n ViewEncapsulation,\n ViewChild,\n} from '@angular/core';\nimport { OuiButton } from '../button/button';\nimport { merge, Observable, of as observableOf, Subscription } from 'rxjs';\nimport { OuiDatepicker } from './datepicker';\nimport { OuiDatepickerIntl } from './datepicker-intl';\n\n/** Can be used to override the icon of a `ouiDatepickerToggle`. */\n@Directive({\n selector: '[ouiDatepickerToggleIcon]',\n})\nexport class OuiDatepickerToggleIcon {}\n\n@Component({\n selector: 'oui-datepicker-toggle',\n templateUrl: 'datepicker-toggle.html',\n styleUrls: ['datepicker-toggle.scss'],\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-datepicker-toggle',\n // Always set the tabindex to -1 so that it doesn't overlap with any custom tabindex the\n // consumer may have provided, while still being able to receive focus.\n '[attr.tabindex]': '-1',\n '[class.oui-datepicker-toggle-active]': 'datepicker && datepicker.opened',\n '[class.oui-accent]': 'datepicker && datepicker.color === \"accent\"',\n '[class.oui-warn]': 'datepicker && datepicker.color === \"warn\"',\n '(focus)': '_button.focus()',\n '[class.oui-datepicker-disabled]': 'disabled',\n },\n exportAs: 'ouiDatepickerToggle',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class OuiDatepickerToggle\n implements AfterContentInit, OnChanges, OnDestroy\n{\n private _stateChanges = Subscription.EMPTY;\n\n /** Datepicker instance that the button will toggle. */\n // eslint-disable-next-line @angular-eslint/no-input-rename\n @Input('for') datepicker: OuiDatepicker;\n\n /** Tabindex for the toggle. */\n @Input() tabIndex: number | null;\n\n /** Whether the toggle button is disabled. */\n @Input()\n get disabled(): boolean {\n return this._disabled === undefined\n ? this.datepicker.disabled\n : !!this._disabled;\n }\n set disabled(value: boolean) {\n this._disabled = coerceBooleanProperty(value);\n }\n private _disabled: boolean;\n\n /** Custom icon set by the consumer. */\n @ContentChild(OuiDatepickerToggleIcon)\n _customIcon: OuiDatepickerToggleIcon;\n\n /** Underlying button element. */\n @ViewChild('button') _button: OuiButton;\n\n constructor(\n public _intl: OuiDatepickerIntl,\n private _changeDetectorRef: ChangeDetectorRef,\n @Attribute('tabindex') defaultTabIndex: string\n ) {\n const parsedTabIndex = Number(defaultTabIndex);\n this.tabIndex =\n parsedTabIndex || parsedTabIndex === 0 ? parsedTabIndex : null;\n }\n\n ngOnChanges(changes: SimpleChanges) {\n if (changes.datepicker) {\n this._watchStateChanges();\n }\n }\n\n ngOnDestroy() {\n this._stateChanges.unsubscribe();\n }\n\n ngAfterContentInit() {\n this._watchStateChanges();\n }\n\n _open(event: Event): void {\n if (this.datepicker && !this.disabled) {\n this.datepicker.open();\n event.stopPropagation();\n }\n }\n\n private _watchStateChanges() {\n const datepickerDisabled = this.datepicker\n ? this.datepicker._disabledChange\n : observableOf();\n const inputDisabled =\n this.datepicker && this.datepicker._datepickerInput\n ? this.datepicker._datepickerInput._disabledChange\n : observableOf();\n const datepickerToggled = this.datepicker\n ? merge(this.datepicker.openedStream, this.datepicker.closedStream)\n : observableOf();\n\n this._stateChanges.unsubscribe();\n this._stateChanges = merge(\n this._intl.changes,\n datepickerDisabled as Observable,\n inputDisabled as Observable,\n datepickerToggled\n ).subscribe(() => this._changeDetectorRef.markForCheck());\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": ".oui-form-field-appearance-legacy {\r\n .oui-form-field-prefix,\r\n .oui-form-field-suffix {\r\n .oui-datepicker-toggle-default-icon {\r\n width: 1em;\r\n }\r\n }\r\n}\r\n\r\n.oui-form-field:not(.oui-form-field-appearance-legacy) {\r\n .oui-form-field-prefix,\r\n .oui-form-field-suffix {\r\n .oui-datepicker-toggle-default-icon {\r\n display: block;\r\n width: 1.5em;\r\n height: 1.5em;\r\n }\r\n\r\n .oui-icon-button .oui-datepicker-toggle-default-icon {\r\n margin: auto;\r\n }\r\n }\r\n}\r\n.oui-datepicker-toggle .oui-icon-button {\r\n position: absolute;\r\n padding: 0px;\r\n width: 100%;\r\n height: 100%;\r\n left: 0px;\r\n top: 0px;\r\n &:hover {\r\n background: none;\r\n }\r\n .oui-datepicker-toggle-default-icon {\r\n margin-left: auto;\r\n margin-top: 4px;\r\n margin-right: 7px;\r\n color: #4a4a4a;\r\n }\r\n}\r\n\r\n.oui-datepicker-toggle .oui-icon-button {\r\n &:hover {\r\n background: none !important;\r\n }\r\n}\r\n\r\n.oui-calendar-next-button {\r\n margin-right: 5px;\r\n}\r\n", + "data": ".oui-form-field-appearance-legacy {\n .oui-form-field-prefix,\n .oui-form-field-suffix {\n .oui-datepicker-toggle-default-icon {\n width: 1em;\n }\n }\n}\n\n.oui-form-field:not(.oui-form-field-appearance-legacy) {\n .oui-form-field-prefix,\n .oui-form-field-suffix {\n .oui-datepicker-toggle-default-icon {\n display: block;\n width: 1.5em;\n height: 1.5em;\n }\n\n .oui-icon-button .oui-datepicker-toggle-default-icon {\n margin: auto;\n }\n }\n}\n.oui-datepicker-toggle .oui-icon-button {\n position: absolute;\n padding: 0px;\n width: 100%;\n height: 100%;\n left: 0px;\n top: 0px;\n &:hover {\n background: none;\n }\n .oui-datepicker-toggle-default-icon {\n margin-left: auto;\n margin-top: 4px;\n margin-right: 7px;\n color: #4a4a4a;\n }\n}\n\n.oui-datepicker-toggle .oui-icon-button {\n &:hover {\n background: none !important;\n }\n}\n\n.oui-calendar-next-button {\n margin-right: 5px;\n}\n", "styleUrl": "datepicker-toggle.scss" } ], @@ -25190,11 +35048,11 @@ } } }, - "templateData": "\r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n" + "templateData": "\n \n \n \n \n \n \n\n" }, { "name": "OuiDaterangepickerStorybook", - "id": "component-OuiDaterangepickerStorybook-e4c53c638e6ede616808c9cc4a8740cb9557c0da4945cbc3e3427e5a572b2fde2c05a3ea15bede5a2b62c9b9a18fbb4b1c0b4f208802b6c79f24bb1332af13cf", + "id": "component-OuiDaterangepickerStorybook-c9a90e05e9b508008603b4e1a0926da24f4560c1236a614a980077e61ed15818b92e3cec2458de749a4bcf2476affeffacc8bf666d4ac4bf5e12b34f09554afc", "file": "ui/src/stories/datepicker/datepicker.component.ts", "encapsulation": [], "entryComponents": [], @@ -25394,7 +35252,7 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import {\r\n Component,\r\n Output,\r\n EventEmitter,\r\n Input,\r\n OnChanges,\r\n} from '@angular/core';\r\nimport { OuiDatepickerInputEvent } from '../../components/datepicker';\r\nimport { OuiDateFormats, OUI_DATE_FORMATS } from '../../components';\r\n\r\nexport const OUI_CUSTOM_DATE_FORMATS: OuiDateFormats = {\r\n parse: {\r\n dateInput: null,\r\n },\r\n display: {\r\n dateInput: {\r\n year: 'numeric',\r\n day: '2-digit',\r\n month: '2-digit',\r\n },\r\n monthYearLabel: { year: 'numeric', month: 'short' },\r\n dateA11yLabel: { year: 'numeric', month: 'long', day: 'numeric' },\r\n monthYearA11yLabel: { year: 'numeric', month: 'long' },\r\n },\r\n};\r\n\r\n@Component({\r\n selector: 'oui-datepicker-storybook',\r\n template: `\r\n
\r\n \r\n \r\n \r\n \r\n \r\n
\r\n `,\r\n})\r\nexport class OuiDatepickerStorybook implements OnChanges {\r\n @Input() appearance = 'standard';\r\n @Input() color = 'primary';\r\n @Input() startView = ['month', 'year', 'multi-year'];\r\n @Input() opened = false;\r\n @Input() disabled = false;\r\n @Input() mindate: Date = new Date();\r\n minDate: Date = new Date();\r\n @Input() value: Date = new Date();\r\n _value: Date = new Date();\r\n @Input() maxdate: Date = new Date();\r\n maxDate: Date = new Date();\r\n @Output()\r\n readonly _closed: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _monthSelected: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _opened: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _yearSelected: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _dateChange: EventEmitter = new EventEmitter();\r\n constructor() {}\r\n ngOnChanges() {\r\n this.minDate = new Date(this.mindate);\r\n this.maxDate = new Date(this.maxdate);\r\n this._value = new Date(this.value);\r\n if (this.opened) {\r\n (document.querySelector('.oui-datepicker-toggle') as HTMLElement).focus();\r\n }\r\n }\r\n\r\n closed(e?: string) {\r\n this._closed.emit(e);\r\n }\r\n monthSelected(e?: string) {\r\n this._monthSelected.emit(e);\r\n }\r\n datepickeropened(e?: string) {\r\n this._opened.emit(e);\r\n }\r\n yearSelected(e?: string) {\r\n this._yearSelected.emit(e);\r\n }\r\n dateChange(e?: string) {\r\n this._dateChange.emit(e);\r\n }\r\n}\r\n\r\n@Component({\r\n selector: 'oui-datepicker-custom-storybook',\r\n template: `\r\n
\r\n \r\n \r\n \r\n \r\n \r\n
\r\n `,\r\n providers: [{ provide: OUI_DATE_FORMATS, useValue: OUI_CUSTOM_DATE_FORMATS }],\r\n})\r\nexport class OuiDatepickerCustomStorybook implements OnChanges {\r\n @Input() appearance = 'standard';\r\n @Input() color = 'primary';\r\n @Input() startView = ['month', 'year', 'multi-year'];\r\n @Input() opened = false;\r\n @Input() disabled = false;\r\n @Input() mindate: Date = new Date();\r\n minDate: Date = new Date();\r\n @Input() value: Date = new Date();\r\n _value: Date = new Date();\r\n @Input() maxdate: Date = new Date();\r\n maxDate: Date = new Date();\r\n @Output()\r\n readonly _closed: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _monthSelected: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _opened: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _yearSelected: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _dateChange: EventEmitter = new EventEmitter();\r\n constructor() {}\r\n ngOnChanges() {\r\n this.minDate = new Date(this.mindate);\r\n this.maxDate = new Date(this.maxdate);\r\n this._value = new Date(this.value);\r\n if (this.opened) {\r\n (document.querySelector('.oui-datepicker-toggle') as HTMLElement).focus();\r\n }\r\n }\r\n closed(e?: string) {\r\n this._closed.emit(e);\r\n }\r\n monthSelected(e?: string) {\r\n this._monthSelected.emit(e);\r\n }\r\n datepickeropened(e?: string) {\r\n this._opened.emit(e);\r\n }\r\n yearSelected(e?: string) {\r\n this._yearSelected.emit(e);\r\n }\r\n dateChange(e?: string) {\r\n this._dateChange.emit(e);\r\n }\r\n}\r\n\r\n@Component({\r\n selector: 'oui-daterangepicker-storybook',\r\n template: `\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n \r\n
\r\n
\r\n \r\n \r\n \r\n \r\n \r\n
\r\n
\r\n `,\r\n})\r\nexport class OuiDaterangepickerStorybook implements OnChanges {\r\n @Input() appearance = 'standard';\r\n @Input() color = 'primary';\r\n @Input() startView = 'primary';\r\n @Input() opened = false;\r\n @Input() disabled = false;\r\n @Input() mindate: Date;\r\n minDate: Date = new Date();\r\n minRangeDate: Date;\r\n @Input() maxdate: Date;\r\n maxRangeDate: Date;\r\n maxDate: Date;\r\n @Output()\r\n readonly _dateChange: EventEmitter<{}> = new EventEmitter<{}>();\r\n constructor() {}\r\n ngOnChanges() {\r\n this.minRangeDate = new Date(this.mindate);\r\n this.maxRangeDate = new Date(this.maxdate);\r\n }\r\n mindateChange(e: OuiDatepickerInputEvent) {\r\n this.minDate = new Date(e.value);\r\n console.log(this.minDate);\r\n if (this.maxDate) {\r\n this._dateChange.emit({\r\n min: this.minDate,\r\n max: this.maxDate,\r\n });\r\n }\r\n // this._dateChange.emit(e);\r\n }\r\n maxdateChange(e: OuiDatepickerInputEvent) {\r\n this.maxDate = new Date(e.value);\r\n console.log(this.maxDate);\r\n if (this.minDate) {\r\n this._dateChange.emit({\r\n min: this.minDate,\r\n max: this.maxDate,\r\n });\r\n }\r\n // this._dateChange.emit(e);\r\n }\r\n}\r\n", + "sourceCode": "import {\n Component,\n Output,\n EventEmitter,\n Input,\n OnChanges,\n} from '@angular/core';\nimport { OuiDatepickerInputEvent } from '../../components/datepicker';\nimport { OuiDateFormats, OUI_DATE_FORMATS } from '../../components';\n\nexport const OUI_CUSTOM_DATE_FORMATS: OuiDateFormats = {\n parse: {\n dateInput: null,\n },\n display: {\n dateInput: {\n year: 'numeric',\n day: '2-digit',\n month: '2-digit',\n },\n monthYearLabel: { year: 'numeric', month: 'short' },\n dateA11yLabel: { year: 'numeric', month: 'long', day: 'numeric' },\n monthYearA11yLabel: { year: 'numeric', month: 'long' },\n },\n};\n\n@Component({\n selector: 'oui-datepicker-storybook',\n template: `\n
\n \n \n \n \n \n
\n `,\n})\nexport class OuiDatepickerStorybook implements OnChanges {\n @Input() appearance = 'standard';\n @Input() color = 'primary';\n @Input() startView = ['month', 'year', 'multi-year'];\n @Input() opened = false;\n @Input() disabled = false;\n @Input() mindate: Date = new Date();\n minDate: Date = new Date();\n @Input() value: Date = new Date();\n _value: Date = new Date();\n @Input() maxdate: Date = new Date();\n maxDate: Date = new Date();\n @Output()\n readonly _closed: EventEmitter = new EventEmitter();\n @Output()\n readonly _monthSelected: EventEmitter = new EventEmitter();\n @Output()\n readonly _opened: EventEmitter = new EventEmitter();\n @Output()\n readonly _yearSelected: EventEmitter = new EventEmitter();\n @Output()\n readonly _dateChange: EventEmitter = new EventEmitter();\n constructor() {}\n ngOnChanges() {\n this.minDate = new Date(this.mindate);\n this.maxDate = new Date(this.maxdate);\n this._value = new Date(this.value);\n if (this.opened) {\n (document.querySelector('.oui-datepicker-toggle') as HTMLElement).focus();\n }\n }\n\n closed(e?: string) {\n this._closed.emit(e);\n }\n monthSelected(e?: string) {\n this._monthSelected.emit(e);\n }\n datepickeropened(e?: string) {\n this._opened.emit(e);\n }\n yearSelected(e?: string) {\n this._yearSelected.emit(e);\n }\n dateChange(e?: string) {\n this._dateChange.emit(e);\n }\n}\n\n@Component({\n selector: 'oui-datepicker-custom-storybook',\n template: `\n
\n \n \n \n \n \n
\n `,\n providers: [{ provide: OUI_DATE_FORMATS, useValue: OUI_CUSTOM_DATE_FORMATS }],\n})\nexport class OuiDatepickerCustomStorybook implements OnChanges {\n @Input() appearance = 'standard';\n @Input() color = 'primary';\n @Input() startView = ['month', 'year', 'multi-year'];\n @Input() opened = false;\n @Input() disabled = false;\n @Input() mindate: Date = new Date();\n minDate: Date = new Date();\n @Input() value: Date = new Date();\n _value: Date = new Date();\n @Input() maxdate: Date = new Date();\n maxDate: Date = new Date();\n @Output()\n readonly _closed: EventEmitter = new EventEmitter();\n @Output()\n readonly _monthSelected: EventEmitter = new EventEmitter();\n @Output()\n readonly _opened: EventEmitter = new EventEmitter();\n @Output()\n readonly _yearSelected: EventEmitter = new EventEmitter();\n @Output()\n readonly _dateChange: EventEmitter = new EventEmitter();\n constructor() {}\n ngOnChanges() {\n this.minDate = new Date(this.mindate);\n this.maxDate = new Date(this.maxdate);\n this._value = new Date(this.value);\n if (this.opened) {\n (document.querySelector('.oui-datepicker-toggle') as HTMLElement).focus();\n }\n }\n closed(e?: string) {\n this._closed.emit(e);\n }\n monthSelected(e?: string) {\n this._monthSelected.emit(e);\n }\n datepickeropened(e?: string) {\n this._opened.emit(e);\n }\n yearSelected(e?: string) {\n this._yearSelected.emit(e);\n }\n dateChange(e?: string) {\n this._dateChange.emit(e);\n }\n}\n\n@Component({\n selector: 'oui-daterangepicker-storybook',\n template: `\n
\n
\n \n \n \n \n \n
\n
\n \n \n \n \n \n
\n
\n `,\n})\nexport class OuiDaterangepickerStorybook implements OnChanges {\n @Input() appearance = 'standard';\n @Input() color = 'primary';\n @Input() startView = 'primary';\n @Input() opened = false;\n @Input() disabled = false;\n @Input() mindate: Date;\n minDate: Date = new Date();\n minRangeDate: Date;\n @Input() maxdate: Date;\n maxRangeDate: Date;\n maxDate: Date;\n @Output()\n readonly _dateChange: EventEmitter<{}> = new EventEmitter<{}>();\n constructor() {}\n ngOnChanges() {\n this.minRangeDate = new Date(this.mindate);\n this.maxRangeDate = new Date(this.maxdate);\n }\n mindateChange(e: OuiDatepickerInputEvent) {\n this.minDate = new Date(e.value);\n console.log(this.minDate);\n if (this.maxDate) {\n this._dateChange.emit({\n min: this.minDate,\n max: this.maxDate,\n });\n }\n // this._dateChange.emit(e);\n }\n maxdateChange(e: OuiDatepickerInputEvent) {\n this.maxDate = new Date(e.value);\n console.log(this.maxDate);\n if (this.minDate) {\n this._dateChange.emit({\n min: this.minDate,\n max: this.maxDate,\n });\n }\n // this._dateChange.emit(e);\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -25412,7 +35270,7 @@ }, { "name": "OuiDialogContainer", - "id": "component-OuiDialogContainer-33e81beb4158b7b4189b1c0302c4a7352b5b84a5484e807716c87638d28323c644ecc6561221df6894d151bf54321be9f697334efb7fb09a0b182b21794f365b", + "id": "component-OuiDialogContainer-812b6d5b3f5aebad2798a2e317e30788275e165d75f464b80ee5aff6d7b89d0c8037734c23489f5b72c88bd1fcbd75749f731547beb8bc8c4e94536107df6212", "file": "ui/src/components/dialog/dialog-container.ts", "changeDetection": "ChangeDetectionStrategy.Default", "encapsulation": [ @@ -25621,8 +35479,8 @@ "jsdoctags": [ { "name": { - "pos": 3039, - "end": 3045, + "pos": 2942, + "end": 2948, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -25633,8 +35491,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 3033, - "end": 3038, + "pos": 2936, + "end": 2941, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -25668,8 +35526,8 @@ "jsdoctags": [ { "name": { - "pos": 3466, - "end": 3472, + "pos": 3356, + "end": 3362, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -25680,8 +35538,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 3460, - "end": 3465, + "pos": 3350, + "end": 3355, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -25710,11 +35568,11 @@ "description": "

Internal component that wraps user-provided dialog content.

\n", "rawdescription": "\n\nInternal component that wraps user-provided dialog content.\n\n", "type": "component", - "sourceCode": "import {\r\n Component,\r\n ComponentRef,\r\n EmbeddedViewRef,\r\n ViewChild,\r\n ViewEncapsulation,\r\n ChangeDetectionStrategy,\r\n ElementRef,\r\n OnInit,\r\n Inject,\r\n Optional,\r\n} from '@angular/core';\r\nimport {\r\n BasePortalOutlet,\r\n ComponentPortal,\r\n CdkPortalOutlet,\r\n TemplatePortal,\r\n} from '@angular/cdk/portal';\r\nimport { DOCUMENT } from '@angular/common';\r\nimport { OuiDialogConfig } from './dialog-config';\r\nimport { FocusTrap, ConfigurableFocusTrapFactory } from '@angular/cdk/a11y';\r\n\r\n/**\r\n * Throws an exception for the case when a ComponentPortal is\r\n * attached to a DomPortalOutlet without an origin.\r\n *\r\n * @docs-private\r\n */\r\nexport function throwOuiDialogContentAlreadyAttachedError() {\r\n throw Error(\r\n 'Attempting to attach dialog content after content is already attached'\r\n );\r\n}\r\n\r\n/**\r\n * Internal component that wraps user-provided dialog content.\r\n *\r\n * @docs-private\r\n */\r\n@Component({\r\n selector: 'oui-dialog-container',\r\n templateUrl: 'dialog-container.html',\r\n styleUrls: ['dialog.scss'],\r\n encapsulation: ViewEncapsulation.None,\r\n // Using OnPush for dialogs caused some G3 sync issues. Disabled until we can track them down.\r\n // eslint-disable-next-line\r\n changeDetection: ChangeDetectionStrategy.Default,\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-container',\r\n tabindex: '-1',\r\n 'aria-modal': 'true',\r\n '[attr.id]': '_id',\r\n '[attr.role]': '_config.role',\r\n '[attr.aria-labelledby]': '_config.ariaLabel ? null : _ariaLabelledBy',\r\n '[attr.aria-label]': '_config.ariaLabel',\r\n '[attr.aria-describedby]': '_config.ariaDescribedBy || null',\r\n },\r\n})\r\nexport class OuiDialogContainer extends BasePortalOutlet implements OnInit {\r\n /** The portal outlet inside of this container into which the dialog content will be loaded. */\r\n @ViewChild(CdkPortalOutlet, { static: true })\r\n _portalOutlet: CdkPortalOutlet;\r\n\r\n /** The class that traps and manages focus within the dialog. */\r\n private _focusTrap: FocusTrap;\r\n\r\n /** Element that was focused before the dialog was opened. Save this to restore upon close. */\r\n private _elementFocusedBeforeDialogWasOpened: HTMLElement = null;\r\n\r\n /** ID of the element that should be considered as the dialog's label. */\r\n _ariaLabelledBy: string | null = null;\r\n\r\n /** ID for the container DOM element. */\r\n _id: string;\r\n\r\n constructor(\r\n private _focusTrapFactory: ConfigurableFocusTrapFactory,\r\n public _config: OuiDialogConfig,\r\n public elementRef: ElementRef,\r\n @Optional() @Inject(DOCUMENT) private _document: Document\r\n ) {\r\n super();\r\n }\r\n\r\n ngOnInit() {\r\n this._addMarginForDefaultScroll();\r\n }\r\n private _addMarginForDefaultScroll() {\r\n if (!this._config.scrollStrategy) {\r\n this.elementRef.nativeElement.style.marginTop = '40px';\r\n this.elementRef.nativeElement.style.marginBottom = '40px';\r\n }\r\n }\r\n /**\r\n * Attach a ComponentPortal as content to this dialog container.\r\n *\r\n * @param portal Portal to be attached as the dialog content.\r\n */\r\n attachComponentPortal(portal: ComponentPortal): ComponentRef {\r\n if (this._portalOutlet.hasAttached()) {\r\n throwOuiDialogContentAlreadyAttachedError();\r\n }\r\n this._addFocusTrap();\r\n return this._portalOutlet.attachComponentPortal(portal);\r\n }\r\n\r\n /**\r\n * Attach a TemplatePortal as content to this dialog container.\r\n *\r\n * @param portal Portal to be attached as the dialog content.\r\n */\r\n attachTemplatePortal(portal: TemplatePortal): EmbeddedViewRef {\r\n if (this._portalOutlet.hasAttached()) {\r\n throwOuiDialogContentAlreadyAttachedError();\r\n }\r\n this._addFocusTrap();\r\n return this._portalOutlet.attachTemplatePortal(portal);\r\n }\r\n\r\n /** Moves the focus inside the focus trap. */\r\n public _trapFocus() {\r\n // If we were to attempt to focus immediately, then the content of the dialog would not yet be\r\n // ready in instances where change detection has to run first. To deal with this, we simply\r\n // wait for the microtask queue to be empty.\r\n if (this._config.autoFocus) {\r\n this._focusTrap.focusInitialElementWhenReady();\r\n } else if (!this._containsFocus()) {\r\n // Otherwise ensure that focus is on the dialog container. It's possible that a different\r\n // component tried to move focus while the open animation was running.\r\n // if the focus isn't inside the dialog already, because it's possible that the consumer\r\n // turned off `autoFocus` in order to move focus themselves.\r\n this.elementRef.nativeElement.focus();\r\n }\r\n }\r\n\r\n /** Restores focus to the element that was focused before the dialog opened. */\r\n public _restoreFocus() {\r\n const toFocus = this._elementFocusedBeforeDialogWasOpened;\r\n\r\n // We need the extra check, because IE can set the `activeElement` to null in some cases.\r\n if (\r\n this._config.restoreFocus &&\r\n toFocus &&\r\n typeof toFocus.focus === 'function'\r\n ) {\r\n const activeElement = this._document.activeElement as HTMLElement;\r\n const element = this.elementRef.nativeElement;\r\n if (\r\n !activeElement ||\r\n activeElement === this._document.body ||\r\n activeElement === element ||\r\n element.contains(activeElement)\r\n ) {\r\n toFocus.focus();\r\n }\r\n }\r\n\r\n if (this._focusTrap) {\r\n this._focusTrap.destroy();\r\n }\r\n }\r\n\r\n /**\r\n * Setting up the focus trap and saves a reference to the element that was focused before the dialog was open.\r\n */\r\n private _addFocusTrap() {\r\n if (!this._focusTrap) {\r\n this._focusTrap = this._focusTrapFactory.create(\r\n this.elementRef.nativeElement\r\n );\r\n }\r\n if (this._document) {\r\n this._elementFocusedBeforeDialogWasOpened = this._document\r\n .activeElement as HTMLElement;\r\n // Note that there is no focus method when rendering on the server.\r\n if (this.elementRef.nativeElement.focus) {\r\n // Move focus onto the dialog immediately in order to prevent the user from accidentally\r\n // opening multiple dialogs at the same time. Needs to be async, because the element\r\n // may not be focusable immediately.\r\n Promise.resolve().then(() => this.elementRef.nativeElement.focus());\r\n }\r\n }\r\n }\r\n\r\n /** Only return when there is focus inside the dialog */\r\n private _containsFocus() {\r\n const element = this.elementRef.nativeElement as HTMLElement;\r\n const activeElement = this._document.activeElement;\r\n return element === activeElement || element.contains(activeElement);\r\n }\r\n}\r\n", + "sourceCode": "import {\n Component,\n ComponentRef,\n EmbeddedViewRef,\n ViewChild,\n ViewEncapsulation,\n ChangeDetectionStrategy,\n ElementRef,\n OnInit,\n Inject,\n Optional,\n} from '@angular/core';\nimport {\n BasePortalOutlet,\n ComponentPortal,\n CdkPortalOutlet,\n TemplatePortal,\n} from '@angular/cdk/portal';\nimport { DOCUMENT } from '@angular/common';\nimport { OuiDialogConfig } from './dialog-config';\nimport { FocusTrap, ConfigurableFocusTrapFactory } from '@angular/cdk/a11y';\n\n/**\n * Throws an exception for the case when a ComponentPortal is\n * attached to a DomPortalOutlet without an origin.\n *\n * @docs-private\n */\nexport function throwOuiDialogContentAlreadyAttachedError() {\n throw Error(\n 'Attempting to attach dialog content after content is already attached'\n );\n}\n\n/**\n * Internal component that wraps user-provided dialog content.\n *\n * @docs-private\n */\n@Component({\n selector: 'oui-dialog-container',\n templateUrl: 'dialog-container.html',\n styleUrls: ['dialog.scss'],\n encapsulation: ViewEncapsulation.None,\n // Using OnPush for dialogs caused some G3 sync issues. Disabled until we can track them down.\n // eslint-disable-next-line\n changeDetection: ChangeDetectionStrategy.Default,\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-container',\n tabindex: '-1',\n 'aria-modal': 'true',\n '[attr.id]': '_id',\n '[attr.role]': '_config.role',\n '[attr.aria-labelledby]': '_config.ariaLabel ? null : _ariaLabelledBy',\n '[attr.aria-label]': '_config.ariaLabel',\n '[attr.aria-describedby]': '_config.ariaDescribedBy || null',\n },\n})\nexport class OuiDialogContainer extends BasePortalOutlet implements OnInit {\n /** The portal outlet inside of this container into which the dialog content will be loaded. */\n @ViewChild(CdkPortalOutlet, { static: true })\n _portalOutlet: CdkPortalOutlet;\n\n /** The class that traps and manages focus within the dialog. */\n private _focusTrap: FocusTrap;\n\n /** Element that was focused before the dialog was opened. Save this to restore upon close. */\n private _elementFocusedBeforeDialogWasOpened: HTMLElement = null;\n\n /** ID of the element that should be considered as the dialog's label. */\n _ariaLabelledBy: string | null = null;\n\n /** ID for the container DOM element. */\n _id: string;\n\n constructor(\n private _focusTrapFactory: ConfigurableFocusTrapFactory,\n public _config: OuiDialogConfig,\n public elementRef: ElementRef,\n @Optional() @Inject(DOCUMENT) private _document: Document\n ) {\n super();\n }\n\n ngOnInit() {\n this._addMarginForDefaultScroll();\n }\n private _addMarginForDefaultScroll() {\n if (!this._config.scrollStrategy) {\n this.elementRef.nativeElement.style.marginTop = '40px';\n this.elementRef.nativeElement.style.marginBottom = '40px';\n }\n }\n /**\n * Attach a ComponentPortal as content to this dialog container.\n *\n * @param portal Portal to be attached as the dialog content.\n */\n attachComponentPortal(portal: ComponentPortal): ComponentRef {\n if (this._portalOutlet.hasAttached()) {\n throwOuiDialogContentAlreadyAttachedError();\n }\n this._addFocusTrap();\n return this._portalOutlet.attachComponentPortal(portal);\n }\n\n /**\n * Attach a TemplatePortal as content to this dialog container.\n *\n * @param portal Portal to be attached as the dialog content.\n */\n attachTemplatePortal(portal: TemplatePortal): EmbeddedViewRef {\n if (this._portalOutlet.hasAttached()) {\n throwOuiDialogContentAlreadyAttachedError();\n }\n this._addFocusTrap();\n return this._portalOutlet.attachTemplatePortal(portal);\n }\n\n /** Moves the focus inside the focus trap. */\n public _trapFocus() {\n // If we were to attempt to focus immediately, then the content of the dialog would not yet be\n // ready in instances where change detection has to run first. To deal with this, we simply\n // wait for the microtask queue to be empty.\n if (this._config.autoFocus) {\n this._focusTrap.focusInitialElementWhenReady();\n } else if (!this._containsFocus()) {\n // Otherwise ensure that focus is on the dialog container. It's possible that a different\n // component tried to move focus while the open animation was running.\n // if the focus isn't inside the dialog already, because it's possible that the consumer\n // turned off `autoFocus` in order to move focus themselves.\n this.elementRef.nativeElement.focus();\n }\n }\n\n /** Restores focus to the element that was focused before the dialog opened. */\n public _restoreFocus() {\n const toFocus = this._elementFocusedBeforeDialogWasOpened;\n\n // We need the extra check, because IE can set the `activeElement` to null in some cases.\n if (\n this._config.restoreFocus &&\n toFocus &&\n typeof toFocus.focus === 'function'\n ) {\n const activeElement = this._document.activeElement as HTMLElement;\n const element = this.elementRef.nativeElement;\n if (\n !activeElement ||\n activeElement === this._document.body ||\n activeElement === element ||\n element.contains(activeElement)\n ) {\n toFocus.focus();\n }\n }\n\n if (this._focusTrap) {\n this._focusTrap.destroy();\n }\n }\n\n /**\n * Setting up the focus trap and saves a reference to the element that was focused before the dialog was open.\n */\n private _addFocusTrap() {\n if (!this._focusTrap) {\n this._focusTrap = this._focusTrapFactory.create(\n this.elementRef.nativeElement\n );\n }\n if (this._document) {\n this._elementFocusedBeforeDialogWasOpened = this._document\n .activeElement as HTMLElement;\n // Note that there is no focus method when rendering on the server.\n if (this.elementRef.nativeElement.focus) {\n // Move focus onto the dialog immediately in order to prevent the user from accidentally\n // opening multiple dialogs at the same time. Needs to be async, because the element\n // may not be focusable immediately.\n Promise.resolve().then(() => this.elementRef.nativeElement.focus());\n }\n }\n }\n\n /** Only return when there is focus inside the dialog */\n private _containsFocus() {\n const element = this.elementRef.nativeElement as HTMLElement;\n const activeElement = this._document.activeElement;\n return element === activeElement || element.contains(activeElement);\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": "@import 'dialog-theme';\r\n$icon-focus-background: rgba(200, 200, 200, 0.4);\r\n$icon-focus-transition: opacity 0.2s cubic-bezier(0.35, 0, 0.25, 1),\r\n background-color 0.2s cubic-bezier(0.35, 0, 0.25, 1);\r\n$header-icon-width: 24px;\r\n$header-icon-height: 24px;\r\n\r\n.cdk-overlay-backdrop {\r\n background: rgba(255, 255, 255, 0.8) !important;\r\n}\r\n.cdk-overlay-pane {\r\n display: block;\r\n}\r\n* {\r\n outline: 0 none !important;\r\n}\r\n\r\n.oui-dialog-container {\r\n width: 100%;\r\n height: 100%;\r\n min-height: inherit;\r\n max-height: inherit;\r\n z-index: 29991;\r\n border-radius: 0;\r\n margin: 0 auto;\r\n display: block;\r\n -webkit-box-shadow: 0 1px 3px 2px rgba(167, 167, 167, 0.5);\r\n -moz-box-shadow: 0 1px 3px 2px rgba(167, 167, 167, 0.5);\r\n box-shadow: 0 1px 3px 2px rgba(167, 167, 167, 0.5);\r\n box-sizing: border-box;\r\n -webkit-box-sizing: border-box;\r\n font-size: 14px;\r\n color: #333;\r\n padding: 0px;\r\n background: #fff;\r\n .oui-dialog-header {\r\n font-size: 22px;\r\n line-height: 30px;\r\n padding: 13px 22px 20px;\r\n position: relative;\r\n color: #333;\r\n min-height: 29px;\r\n .oui-dialog-header-title {\r\n width: calc(100% - 118px);\r\n text-overflow: ellipsis;\r\n overflow: hidden;\r\n white-space: nowrap;\r\n display: block;\r\n }\r\n .oui-dialog-header-image {\r\n position: absolute;\r\n width: 20px;\r\n margin-right: 10px;\r\n text-align: center;\r\n height: 20px;\r\n top: 18px;\r\n img {\r\n vertical-align: super;\r\n margin-top: 3.5px;\r\n }\r\n }\r\n }\r\n}\r\n\r\n.oui-dialog-header-image + .oui-dialog-header-title {\r\n margin-left: 30px;\r\n width: calc(100% - 148px) !important;\r\n}\r\n\r\n.oui-dialog-header-action {\r\n position: absolute;\r\n top: 16px;\r\n left: auto;\r\n right: 16px;\r\n bottom: auto;\r\n svg {\r\n fill: #4a4a4a;\r\n }\r\n}\r\n.oui-dialog-header-action::after {\r\n clear: both;\r\n content: '';\r\n display: block;\r\n}\r\n.oui-dialog-header-action div {\r\n margin: 0px 0px 0px 4px;\r\n float: right;\r\n box-sizing: border-box;\r\n -webkit-box-sizing: border-box;\r\n -moz-box-sizing: border-box;\r\n}\r\n.oui-dialog-header-action a {\r\n box-sizing: border-box;\r\n -webkit-box-sizing: border-box;\r\n -moz-box-sizing: border-box;\r\n}\r\n\r\n.oui-dialog-header-action div:nth-last-child(1) {\r\n margin: 0px;\r\n}\r\n\r\n.oui-dialog-header-close {\r\n cursor: pointer;\r\n width: $header-icon-width;\r\n height: $header-icon-height;\r\n line-height: 1;\r\n padding: 2px;\r\n text-align: center;\r\n .oui-icon {\r\n width: 20px;\r\n height: 20px;\r\n svg {\r\n vertical-align: top;\r\n }\r\n }\r\n &[class^='cdk'],\r\n &[class$='focused'],\r\n &:hover {\r\n background: $icon-focus-background;\r\n border-radius: 2px;\r\n transition: $icon-focus-transition;\r\n }\r\n}\r\n.oui-dialog-header-article {\r\n width: $header-icon-width;\r\n height: $header-icon-height;\r\n float: right;\r\n margin-left: 4px;\r\n line-height: 1;\r\n padding: 2px;\r\n text-align: center;\r\n .oui-icon {\r\n width: 16px;\r\n height: 20px;\r\n padding-top: 3px;\r\n svg {\r\n vertical-align: top;\r\n }\r\n }\r\n &[class^='cdk'],\r\n &[class$='focused'] {\r\n background: $icon-focus-background;\r\n border-radius: 2px;\r\n transition: $icon-focus-transition;\r\n }\r\n svg {\r\n fill: #006bb1;\r\n }\r\n}\r\n.oui-dialog-header-video {\r\n width: $header-icon-width;\r\n height: $header-icon-height;\r\n position: relative;\r\n float: right;\r\n margin-left: 4px;\r\n line-height: 1;\r\n padding: 2px;\r\n .oui-icon {\r\n width: 20px;\r\n height: 20px;\r\n }\r\n &[class^='cdk'],\r\n &[class$='focused'] {\r\n background: $icon-focus-background;\r\n border-radius: 2px;\r\n transition: $icon-focus-transition;\r\n }\r\n svg {\r\n fill: #006bb1;\r\n }\r\n}\r\n.oui-dialog-header-separator {\r\n margin-right: 5px !important;\r\n}\r\n.oui-dialog-header-separator::after {\r\n border-right: 1px solid #333;\r\n content: '';\r\n height: 20px;\r\n position: absolute;\r\n right: -5px;\r\n}\r\n\r\n.oui-dialog-content {\r\n color: #333;\r\n min-height: 30px;\r\n line-height: 22px;\r\n padding: 0 22px 30px;\r\n font-size: 14px;\r\n -webkit-user-select: none;\r\n -moz-user-select: none;\r\n -ms-user-select: none;\r\n -o-user-select: none;\r\n user-select: none;\r\n cursor: default;\r\n overflow: auto;\r\n max-height: 65vh;\r\n}\r\n.oui-dialog-footer {\r\n padding: 12px 22px;\r\n width: 100%;\r\n border-top: 1px solid #c8c8c8;\r\n box-sizing: border-box;\r\n .oui-dialog-footer-action-left {\r\n float: left;\r\n button {\r\n margin-right: 36px;\r\n &:nth-last-child(1) {\r\n margin-right: 0px;\r\n }\r\n }\r\n }\r\n .oui-dialog-footer-action-right {\r\n float: right;\r\n button {\r\n margin-left: 12px;\r\n &:nth-child(1) {\r\n margin-left: 0px;\r\n }\r\n }\r\n }\r\n}\r\n.oui-dialog-footer::after {\r\n clear: both;\r\n content: '';\r\n display: block;\r\n}\r\n.cross-disabled {\r\n pointer-events: none;\r\n cursor: default;\r\n opacity: 0.6;\r\n}\r\n", + "data": "@import 'dialog-theme';\n$icon-focus-background: rgba(200, 200, 200, 0.4);\n$icon-focus-transition: opacity 0.2s cubic-bezier(0.35, 0, 0.25, 1),\n background-color 0.2s cubic-bezier(0.35, 0, 0.25, 1);\n$header-icon-width: 24px;\n$header-icon-height: 24px;\n\n.cdk-overlay-backdrop {\n background: rgba(255, 255, 255, 0.8) !important;\n}\n.cdk-overlay-pane {\n display: block;\n}\n* {\n outline: 0 none !important;\n}\n\n.oui-dialog-container {\n width: 100%;\n height: 100%;\n min-height: inherit;\n max-height: inherit;\n z-index: 29991;\n border-radius: 0;\n margin: 0 auto;\n display: block;\n -webkit-box-shadow: 0 1px 3px 2px rgba(167, 167, 167, 0.5);\n -moz-box-shadow: 0 1px 3px 2px rgba(167, 167, 167, 0.5);\n box-shadow: 0 1px 3px 2px rgba(167, 167, 167, 0.5);\n box-sizing: border-box;\n -webkit-box-sizing: border-box;\n font-size: 14px;\n color: #333;\n padding: 0px;\n background: #fff;\n .oui-dialog-header {\n font-size: 22px;\n line-height: 30px;\n padding: 13px 22px 20px;\n position: relative;\n color: #333;\n min-height: 29px;\n .oui-dialog-header-title {\n width: calc(100% - 118px);\n text-overflow: ellipsis;\n overflow: hidden;\n white-space: nowrap;\n display: block;\n }\n .oui-dialog-header-image {\n position: absolute;\n width: 20px;\n margin-right: 10px;\n text-align: center;\n height: 20px;\n top: 18px;\n img {\n vertical-align: super;\n margin-top: 3.5px;\n }\n }\n }\n}\n\n.oui-dialog-header-image + .oui-dialog-header-title {\n margin-left: 30px;\n width: calc(100% - 148px) !important;\n}\n\n.oui-dialog-header-action {\n position: absolute;\n top: 16px;\n left: auto;\n right: 16px;\n bottom: auto;\n svg {\n fill: #4a4a4a;\n }\n}\n.oui-dialog-header-action::after {\n clear: both;\n content: '';\n display: block;\n}\n.oui-dialog-header-action div {\n margin: 0px 0px 0px 4px;\n float: right;\n box-sizing: border-box;\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n}\n.oui-dialog-header-action a {\n box-sizing: border-box;\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n}\n\n.oui-dialog-header-action div:nth-last-child(1) {\n margin: 0px;\n}\n\n.oui-dialog-header-close {\n cursor: pointer;\n width: $header-icon-width;\n height: $header-icon-height;\n line-height: 1;\n padding: 2px;\n text-align: center;\n .oui-icon {\n width: 20px;\n height: 20px;\n svg {\n vertical-align: top;\n }\n }\n &[class^='cdk'],\n &[class$='focused'],\n &:hover {\n background: $icon-focus-background;\n border-radius: 2px;\n transition: $icon-focus-transition;\n }\n}\n.oui-dialog-header-article {\n width: $header-icon-width;\n height: $header-icon-height;\n float: right;\n margin-left: 4px;\n line-height: 1;\n padding: 2px;\n text-align: center;\n .oui-icon {\n width: 16px;\n height: 20px;\n padding-top: 3px;\n svg {\n vertical-align: top;\n }\n }\n &[class^='cdk'],\n &[class$='focused'] {\n background: $icon-focus-background;\n border-radius: 2px;\n transition: $icon-focus-transition;\n }\n svg {\n fill: #006bb1;\n }\n}\n.oui-dialog-header-video {\n width: $header-icon-width;\n height: $header-icon-height;\n position: relative;\n float: right;\n margin-left: 4px;\n line-height: 1;\n padding: 2px;\n .oui-icon {\n width: 20px;\n height: 20px;\n }\n &[class^='cdk'],\n &[class$='focused'] {\n background: $icon-focus-background;\n border-radius: 2px;\n transition: $icon-focus-transition;\n }\n svg {\n fill: #006bb1;\n }\n}\n.oui-dialog-header-separator {\n margin-right: 5px !important;\n}\n.oui-dialog-header-separator::after {\n border-right: 1px solid #333;\n content: '';\n height: 20px;\n position: absolute;\n right: -5px;\n}\n\n.oui-dialog-content {\n color: #333;\n min-height: 30px;\n line-height: 22px;\n padding: 0 22px 30px;\n font-size: 14px;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n -o-user-select: none;\n user-select: none;\n cursor: default;\n overflow: auto;\n max-height: 65vh;\n}\n.oui-dialog-footer {\n padding: 12px 22px;\n width: 100%;\n border-top: 1px solid #c8c8c8;\n box-sizing: border-box;\n .oui-dialog-footer-action-left {\n float: left;\n button {\n margin-right: 36px;\n &:nth-last-child(1) {\n margin-right: 0px;\n }\n }\n }\n .oui-dialog-footer-action-right {\n float: right;\n button {\n margin-left: 12px;\n &:nth-child(1) {\n margin-left: 0px;\n }\n }\n }\n}\n.oui-dialog-footer::after {\n clear: both;\n content: '';\n display: block;\n}\n.cross-disabled {\n pointer-events: none;\n cursor: default;\n opacity: 0.6;\n}\n", "styleUrl": "dialog.scss" } ], @@ -25794,11 +35652,11 @@ "implements": [ "OnInit" ], - "templateData": "\r\n" + "templateData": "\n" }, { "name": "OuiDialogHeaderArticle", - "id": "component-OuiDialogHeaderArticle-21f12d9c1a536f543ca0b2382133ab9c7d8281d8279da860ce9e216e5628d8177bee88c4cb99e329b63128f545080dc9b92f03edac8ce76301d884b2180759e5", + "id": "directive-OuiDialogHeader-4c5d8cb794ce2091937eb4ba504351b486d5a89c92a54ad2f3dd97a0a2a183caa83e298a99eae85fcde8c0a630d680ec98c1ab764323a209b8ec838331d1f6fe", "file": "ui/src/components/dialog/dialog-content.ts", "encapsulation": [], "entryComponents": [], @@ -25849,7 +35707,7 @@ "description": "

header action article.

\n", "rawdescription": "\n\nheader action article.\n", "type": "component", - "sourceCode": "import {\r\n Directive,\r\n Input,\r\n OnChanges,\r\n OnInit,\r\n Optional,\r\n SimpleChanges,\r\n ElementRef,\r\n Component,\r\n NgZone,\r\n OnDestroy,\r\n} from '@angular/core';\r\nimport { OuiDialog } from './dialog';\r\nimport { OuiDialogRef } from './dialog-ref';\r\nimport { OuiIconRegistry } from '../icon/icon-registery';\r\nimport { DomSanitizer } from '@angular/platform-browser';\r\nimport { ICONS } from '../core/shared/icons';\r\nimport { Subscription } from 'rxjs';\r\nimport { FocusMonitor } from '@angular/cdk/a11y';\r\n\r\n/** Counter used to generate unique IDs for dialog elements. */\r\nlet dialogElementUid = 0;\r\n\r\n/**\r\n * Header section of ui.\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-header], [ouiDialogHeader]',\r\n exportAs: 'ouiDialogHeader',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-header',\r\n },\r\n})\r\nexport class OuiDialogHeader {\r\n constructor() {}\r\n}\r\n\r\n/**\r\n * Header Title of ui.\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-header-title], [ouiDialogHeaderTitle]',\r\n exportAs: 'ouiDialogHeaderTitle',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-header-title',\r\n },\r\n})\r\nexport class OuiDialogHeaderTitle {\r\n constructor() {}\r\n}\r\n\r\n/**\r\n * Header Image of ui.\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-header-image], [ouiDialogHeaderImage]',\r\n exportAs: 'ouiDialogHeaderImage',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-header-image',\r\n },\r\n})\r\nexport class OuiDialogHeaderImage {\r\n constructor() {}\r\n}\r\n\r\n/**\r\n * Header action area of dialog.\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-header-action], [ouiDialogHeaderAction]',\r\n exportAs: 'ouiDialogHeaderAction',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-header-action',\r\n },\r\n})\r\nexport class OuiDialogHeaderAction {\r\n constructor() {}\r\n}\r\n\r\n/**\r\n * header action article.\r\n */\r\n@Component({\r\n // eslint-disable-next-line @angular-eslint/component-selector\r\n selector: '[oui-dialog-header-article], [ouiDialogHeaderArticle]',\r\n template: '',\r\n exportAs: 'ouiDialogHeaderArticle',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-header-article',\r\n },\r\n})\r\nexport class OuiDialogHeaderArticle implements OnDestroy {\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n constructor(\r\n private ouiIconRegistry: OuiIconRegistry,\r\n private domSanitizer: DomSanitizer,\r\n protected elementRef: ElementRef,\r\n private _focusMonitor: FocusMonitor,\r\n private _ngZone: NgZone\r\n ) {\r\n this.ouiIconRegistry.addSvgIconLiteral(\r\n `article-icon`,\r\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.ARTICLE_ICON)\r\n );\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this.elementRef, true)\r\n .subscribe(() => this._ngZone.run(() => {}));\r\n }\r\n ngOnDestroy() {\r\n this._monitorSubscription.unsubscribe();\r\n }\r\n}\r\n\r\n/**\r\n * header action article.\r\n */\r\n@Component({\r\n // eslint-disable-next-line @angular-eslint/component-selector\r\n selector: '[oui-dialog-header-video], [ouiDialogHeaderVideo]',\r\n template: '',\r\n exportAs: 'ouiDialogHeaderVideo',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-header-video',\r\n },\r\n})\r\nexport class OuiDialogHeaderVideo implements OnDestroy {\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n\r\n constructor(\r\n private ouiIconRegistry: OuiIconRegistry,\r\n private domSanitizer: DomSanitizer,\r\n protected elementRef: ElementRef,\r\n private _focusMonitor: FocusMonitor,\r\n private _ngZone: NgZone\r\n ) {\r\n this.ouiIconRegistry.addSvgIconLiteral(\r\n `video-icon`,\r\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.VIDEO_ICON)\r\n );\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this.elementRef, true)\r\n .subscribe(() => this._ngZone.run(() => {}));\r\n }\r\n ngOnDestroy() {\r\n this._monitorSubscription.unsubscribe();\r\n }\r\n}\r\n\r\n/**\r\n * header action close\r\n */\r\n@Component({\r\n // eslint-disable-next-line @angular-eslint/component-selector\r\n selector: '[oui-dialog-header-close], [ouiDialogHeaderClose]',\r\n template: '',\r\n exportAs: 'ouiDialogHeaderClose',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-header-close',\r\n '[attr.tabindex]': '0',\r\n },\r\n})\r\nexport class OuiDialogHeaderClose implements OnDestroy {\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n constructor(\r\n private ouiIconRegistry: OuiIconRegistry,\r\n private domSanitizer: DomSanitizer,\r\n protected elementRef: ElementRef,\r\n private _focusMonitor: FocusMonitor,\r\n private _ngZone: NgZone\r\n ) {\r\n this.ouiIconRegistry.addSvgIconLiteral(\r\n `close-icon`,\r\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.CLOSE_ICON)\r\n );\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this.elementRef, true)\r\n .subscribe(() => this._ngZone.run(() => {}));\r\n }\r\n ngOnDestroy() {\r\n this._monitorSubscription.unsubscribe();\r\n }\r\n}\r\n\r\n/**\r\n * header action separator close\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-header-separator], [ouiDialogHeaderSeparator]',\r\n exportAs: 'ouiDialogHeaderSeparator',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-header-separator',\r\n },\r\n})\r\nexport class OuiDialogHeaderSeparator {\r\n constructor() {}\r\n}\r\n\r\n/**\r\n * directive for close the current dialog.\r\n */\r\n@Directive({\r\n selector: `[oui-dialog-close], [ouiDialogClose]`,\r\n exportAs: 'ouiDialogClose',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n '[class.cross-disabled]': 'dialogResult===false',\r\n '(click)': 'closeDialog()',\r\n '[attr.aria-label]': 'ariaLabel',\r\n '(keyup.space)': 'handleKeydown($event)',\r\n '(keydown.enter)': 'handleKeydown($event)',\r\n },\r\n})\r\nexport class OuiDialogClose implements OnInit, OnChanges {\r\n /** Screenreader label for the button. */\r\n @Input('aria-label')\r\n ariaLabel = 'Close dialog';\r\n\r\n /** Dialog close input. */\r\n @Input('oui-dialog-close')\r\n dialogResult: any;\r\n\r\n @Input('ouiDialogClose')\r\n _ouiDialogClose: any;\r\n\r\n constructor(\r\n @Optional() public dialogRef: OuiDialogRef,\r\n private _elementRef: ElementRef,\r\n private _dialog: OuiDialog\r\n ) {}\r\n\r\n /** Ensures the option is selected when activated from the keyboard. */\r\n handleKeydown(event: KeyboardEvent): void {\r\n this.closeDialog();\r\n event.preventDefault();\r\n }\r\n\r\n closeDialog() {\r\n if (this.dialogResult !== false) {\r\n this.dialogRef.close(this.dialogResult);\r\n }\r\n }\r\n\r\n ngOnInit() {\r\n if (!this.dialogRef) {\r\n // When this directive is included in a dialog via TemplateRef (rather than being\r\n // in a Component), the DialogRef isn't available via injection because embedded\r\n // views cannot be given a custom injector. Instead, we look up the DialogRef by\r\n // ID. This must occur in `onInit`, as the ID binding for the dialog container won't\r\n // be resolved at constructor time.\r\n this.dialogRef = getClosestDialog(\r\n this._elementRef,\r\n this._dialog.openDialogs\r\n )!;\r\n }\r\n }\r\n\r\n ngOnChanges(changes: SimpleChanges) {\r\n const proxiedChange =\r\n changes._ouiDialogClose || changes._ouiDialogCloseResult;\r\n if (proxiedChange) {\r\n this.dialogResult = proxiedChange.currentValue;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Content section of dialog.\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-content], [ouiDialogContent]',\r\n exportAs: 'ouiDialogContent',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-content',\r\n },\r\n})\r\nexport class OuiDialogContent implements OnInit {\r\n constructor(\r\n @Optional() public dialogRef: OuiDialogRef,\r\n private _elementRef: ElementRef,\r\n private _dialog: OuiDialog\r\n ) {}\r\n\r\n ngOnInit() {\r\n if (!this.dialogRef) {\r\n // When this directive is included in a dialog via TemplateRef (rather than being\r\n // in a Component), the DialogRef isn't available via injection because embedded\r\n // views cannot be given a custom injector. Instead, we look up the DialogRef by\r\n // ID. This must occur in `onInit`, as the ID binding for the dialog container won't\r\n // be resolved at constructor time.\r\n this.dialogRef = getClosestDialog(\r\n this._elementRef,\r\n this._dialog.openDialogs\r\n )!;\r\n }\r\n this._setContentHeight();\r\n }\r\n /* prevent content scroll in default scroll strategy **/\r\n private _setContentHeight() {\r\n if (!this.dialogRef.dialogConfig.scrollStrategy) {\r\n this._elementRef.nativeElement.style.maxHeight = 'none';\r\n this._elementRef.nativeElement.style.overflow = 'visible';\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Content section of dialog.\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-footer], [ouiDialogFooter]',\r\n exportAs: 'ouiDialogFooter',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-footer',\r\n },\r\n})\r\nexport class OuiDialogFooter {\r\n @Input()\r\n id = `oui-dialog-footer-${dialogElementUid++}`;\r\n constructor() {}\r\n}\r\n\r\n/**\r\n * footer action left\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-footer-action-left], [ouiDialogFooterActionLeft]',\r\n exportAs: 'ouiDialogFooterActionLeft',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-footer-action-left',\r\n },\r\n})\r\nexport class OuiDialogFooterActionLeft {\r\n constructor() {}\r\n}\r\n\r\n/**\r\n * footer action right\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-footer-action-right], [ouiDialogFooterActionRight]',\r\n exportAs: 'ouiDialogFooterActionRight',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-footer-action-right',\r\n },\r\n})\r\nexport class OuiDialogFooterActionRight {\r\n constructor() {}\r\n}\r\n\r\n/**\r\n * Finds the closest MatDialogRef to an element by looking at the DOM.\r\n *\r\n * @param element Element relative to which to look for a dialog.\r\n * @param openDialogs References to the currently-open dialogs.\r\n */\r\nfunction getClosestDialog(\r\n element: ElementRef,\r\n openDialogs: OuiDialogRef[]\r\n) {\r\n let parent: HTMLElement | null = element.nativeElement.parentElement;\r\n\r\n while (parent && !parent.classList.contains('oui-dialog-container')) {\r\n parent = parent.parentElement;\r\n }\r\n\r\n return parent ? openDialogs.find((dialog) => dialog.id === parent!.id) : null;\r\n}\r\n", + "sourceCode": "import {\n Directive,\n Input,\n OnChanges,\n OnInit,\n Optional,\n SimpleChanges,\n ElementRef,\n Component,\n NgZone,\n OnDestroy,\n} from '@angular/core';\nimport { OuiDialog } from './dialog';\nimport { OuiDialogRef } from './dialog-ref';\nimport { OuiIconRegistry } from '../icon/icon-registery';\nimport { DomSanitizer } from '@angular/platform-browser';\nimport { ICONS } from '../core/shared/icons';\nimport { Subscription } from 'rxjs';\nimport { FocusMonitor } from '@angular/cdk/a11y';\n\n/** Counter used to generate unique IDs for dialog elements. */\nlet dialogElementUid = 0;\n\n/**\n * Header section of ui.\n */\n@Directive({\n selector: '[oui-dialog-header], [ouiDialogHeader]',\n exportAs: 'ouiDialogHeader',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-header',\n },\n})\nexport class OuiDialogHeader {\n constructor() {}\n}\n\n/**\n * Header Title of ui.\n */\n@Directive({\n selector: '[oui-dialog-header-title], [ouiDialogHeaderTitle]',\n exportAs: 'ouiDialogHeaderTitle',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-header-title',\n },\n})\nexport class OuiDialogHeaderTitle {\n constructor() {}\n}\n\n/**\n * Header Image of ui.\n */\n@Directive({\n selector: '[oui-dialog-header-image], [ouiDialogHeaderImage]',\n exportAs: 'ouiDialogHeaderImage',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-header-image',\n },\n})\nexport class OuiDialogHeaderImage {\n constructor() {}\n}\n\n/**\n * Header action area of dialog.\n */\n@Directive({\n selector: '[oui-dialog-header-action], [ouiDialogHeaderAction]',\n exportAs: 'ouiDialogHeaderAction',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-header-action',\n },\n})\nexport class OuiDialogHeaderAction {\n constructor() {}\n}\n\n/**\n * header action article.\n */\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: '[oui-dialog-header-article], [ouiDialogHeaderArticle]',\n template: '',\n exportAs: 'ouiDialogHeaderArticle',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-header-article',\n },\n})\nexport class OuiDialogHeaderArticle implements OnDestroy {\n private _monitorSubscription: Subscription = Subscription.EMPTY;\n constructor(\n private ouiIconRegistry: OuiIconRegistry,\n private domSanitizer: DomSanitizer,\n protected elementRef: ElementRef,\n private _focusMonitor: FocusMonitor,\n private _ngZone: NgZone\n ) {\n this.ouiIconRegistry.addSvgIconLiteral(\n `article-icon`,\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.ARTICLE_ICON)\n );\n this._monitorSubscription = this._focusMonitor\n .monitor(this.elementRef, true)\n .subscribe(() => this._ngZone.run(() => {}));\n }\n ngOnDestroy() {\n this._monitorSubscription.unsubscribe();\n }\n}\n\n/**\n * header action article.\n */\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: '[oui-dialog-header-video], [ouiDialogHeaderVideo]',\n template: '',\n exportAs: 'ouiDialogHeaderVideo',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-header-video',\n },\n})\nexport class OuiDialogHeaderVideo implements OnDestroy {\n private _monitorSubscription: Subscription = Subscription.EMPTY;\n\n constructor(\n private ouiIconRegistry: OuiIconRegistry,\n private domSanitizer: DomSanitizer,\n protected elementRef: ElementRef,\n private _focusMonitor: FocusMonitor,\n private _ngZone: NgZone\n ) {\n this.ouiIconRegistry.addSvgIconLiteral(\n `video-icon`,\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.VIDEO_ICON)\n );\n this._monitorSubscription = this._focusMonitor\n .monitor(this.elementRef, true)\n .subscribe(() => this._ngZone.run(() => {}));\n }\n ngOnDestroy() {\n this._monitorSubscription.unsubscribe();\n }\n}\n\n/**\n * header action close\n */\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: '[oui-dialog-header-close], [ouiDialogHeaderClose]',\n template: '',\n exportAs: 'ouiDialogHeaderClose',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-header-close',\n '[attr.tabindex]': '0',\n },\n})\nexport class OuiDialogHeaderClose implements OnDestroy {\n private _monitorSubscription: Subscription = Subscription.EMPTY;\n constructor(\n private ouiIconRegistry: OuiIconRegistry,\n private domSanitizer: DomSanitizer,\n protected elementRef: ElementRef,\n private _focusMonitor: FocusMonitor,\n private _ngZone: NgZone\n ) {\n this.ouiIconRegistry.addSvgIconLiteral(\n `close-icon`,\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.CLOSE_ICON)\n );\n this._monitorSubscription = this._focusMonitor\n .monitor(this.elementRef, true)\n .subscribe(() => this._ngZone.run(() => {}));\n }\n ngOnDestroy() {\n this._monitorSubscription.unsubscribe();\n }\n}\n\n/**\n * header action separator close\n */\n@Directive({\n selector: '[oui-dialog-header-separator], [ouiDialogHeaderSeparator]',\n exportAs: 'ouiDialogHeaderSeparator',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-header-separator',\n },\n})\nexport class OuiDialogHeaderSeparator {\n constructor() {}\n}\n\n/**\n * directive for close the current dialog.\n */\n@Directive({\n selector: `[oui-dialog-close], [ouiDialogClose]`,\n exportAs: 'ouiDialogClose',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n '[class.cross-disabled]': 'dialogResult===false',\n '(click)': 'closeDialog()',\n '[attr.aria-label]': 'ariaLabel',\n '(keyup.space)': 'handleKeydown($event)',\n '(keydown.enter)': 'handleKeydown($event)',\n },\n})\nexport class OuiDialogClose implements OnInit, OnChanges {\n /** Screenreader label for the button. */\n @Input('aria-label')\n ariaLabel = 'Close dialog';\n\n /** Dialog close input. */\n @Input('oui-dialog-close')\n dialogResult: any;\n\n @Input('ouiDialogClose')\n _ouiDialogClose: any;\n\n constructor(\n @Optional() public dialogRef: OuiDialogRef,\n private _elementRef: ElementRef,\n private _dialog: OuiDialog\n ) {}\n\n /** Ensures the option is selected when activated from the keyboard. */\n handleKeydown(event: KeyboardEvent): void {\n this.closeDialog();\n event.preventDefault();\n }\n\n closeDialog() {\n if (this.dialogResult !== false) {\n this.dialogRef.close(this.dialogResult);\n }\n }\n\n ngOnInit() {\n if (!this.dialogRef) {\n // When this directive is included in a dialog via TemplateRef (rather than being\n // in a Component), the DialogRef isn't available via injection because embedded\n // views cannot be given a custom injector. Instead, we look up the DialogRef by\n // ID. This must occur in `onInit`, as the ID binding for the dialog container won't\n // be resolved at constructor time.\n this.dialogRef = getClosestDialog(\n this._elementRef,\n this._dialog.openDialogs\n )!;\n }\n }\n\n ngOnChanges(changes: SimpleChanges) {\n const proxiedChange =\n changes._ouiDialogClose || changes._ouiDialogCloseResult;\n if (proxiedChange) {\n this.dialogResult = proxiedChange.currentValue;\n }\n }\n}\n\n/**\n * Content section of dialog.\n */\n@Directive({\n selector: '[oui-dialog-content], [ouiDialogContent]',\n exportAs: 'ouiDialogContent',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-content',\n },\n})\nexport class OuiDialogContent implements OnInit {\n constructor(\n @Optional() public dialogRef: OuiDialogRef,\n private _elementRef: ElementRef,\n private _dialog: OuiDialog\n ) {}\n\n ngOnInit() {\n if (!this.dialogRef) {\n // When this directive is included in a dialog via TemplateRef (rather than being\n // in a Component), the DialogRef isn't available via injection because embedded\n // views cannot be given a custom injector. Instead, we look up the DialogRef by\n // ID. This must occur in `onInit`, as the ID binding for the dialog container won't\n // be resolved at constructor time.\n this.dialogRef = getClosestDialog(\n this._elementRef,\n this._dialog.openDialogs\n )!;\n }\n this._setContentHeight();\n }\n /* prevent content scroll in default scroll strategy **/\n private _setContentHeight() {\n if (!this.dialogRef.dialogConfig.scrollStrategy) {\n this._elementRef.nativeElement.style.maxHeight = 'none';\n this._elementRef.nativeElement.style.overflow = 'visible';\n }\n }\n}\n\n/**\n * Content section of dialog.\n */\n@Directive({\n selector: '[oui-dialog-footer], [ouiDialogFooter]',\n exportAs: 'ouiDialogFooter',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-footer',\n },\n})\nexport class OuiDialogFooter {\n @Input()\n id = `oui-dialog-footer-${dialogElementUid++}`;\n constructor() {}\n}\n\n/**\n * footer action left\n */\n@Directive({\n selector: '[oui-dialog-footer-action-left], [ouiDialogFooterActionLeft]',\n exportAs: 'ouiDialogFooterActionLeft',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-footer-action-left',\n },\n})\nexport class OuiDialogFooterActionLeft {\n constructor() {}\n}\n\n/**\n * footer action right\n */\n@Directive({\n selector: '[oui-dialog-footer-action-right], [ouiDialogFooterActionRight]',\n exportAs: 'ouiDialogFooterActionRight',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-footer-action-right',\n },\n})\nexport class OuiDialogFooterActionRight {\n constructor() {}\n}\n\n/**\n * Finds the closest MatDialogRef to an element by looking at the DOM.\n *\n * @param element Element relative to which to look for a dialog.\n * @param openDialogs References to the currently-open dialogs.\n */\nfunction getClosestDialog(\n element: ElementRef,\n openDialogs: OuiDialogRef[]\n) {\n let parent: HTMLElement | null = element.nativeElement.parentElement;\n\n while (parent && !parent.classList.contains('oui-dialog-container')) {\n parent = parent.parentElement;\n }\n\n return parent ? openDialogs.find((dialog) => dialog.id === parent!.id) : null;\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -25945,7 +35803,7 @@ }, { "name": "OuiDialogHeaderClose", - "id": "component-OuiDialogHeaderClose-21f12d9c1a536f543ca0b2382133ab9c7d8281d8279da860ce9e216e5628d8177bee88c4cb99e329b63128f545080dc9b92f03edac8ce76301d884b2180759e5", + "id": "directive-OuiDialogHeader-4c5d8cb794ce2091937eb4ba504351b486d5a89c92a54ad2f3dd97a0a2a183caa83e298a99eae85fcde8c0a630d680ec98c1ab764323a209b8ec838331d1f6fe", "file": "ui/src/components/dialog/dialog-content.ts", "encapsulation": [], "entryComponents": [], @@ -25996,7 +35854,7 @@ "description": "

header action close

\n", "rawdescription": "\n\nheader action close\n", "type": "component", - "sourceCode": "import {\r\n Directive,\r\n Input,\r\n OnChanges,\r\n OnInit,\r\n Optional,\r\n SimpleChanges,\r\n ElementRef,\r\n Component,\r\n NgZone,\r\n OnDestroy,\r\n} from '@angular/core';\r\nimport { OuiDialog } from './dialog';\r\nimport { OuiDialogRef } from './dialog-ref';\r\nimport { OuiIconRegistry } from '../icon/icon-registery';\r\nimport { DomSanitizer } from '@angular/platform-browser';\r\nimport { ICONS } from '../core/shared/icons';\r\nimport { Subscription } from 'rxjs';\r\nimport { FocusMonitor } from '@angular/cdk/a11y';\r\n\r\n/** Counter used to generate unique IDs for dialog elements. */\r\nlet dialogElementUid = 0;\r\n\r\n/**\r\n * Header section of ui.\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-header], [ouiDialogHeader]',\r\n exportAs: 'ouiDialogHeader',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-header',\r\n },\r\n})\r\nexport class OuiDialogHeader {\r\n constructor() {}\r\n}\r\n\r\n/**\r\n * Header Title of ui.\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-header-title], [ouiDialogHeaderTitle]',\r\n exportAs: 'ouiDialogHeaderTitle',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-header-title',\r\n },\r\n})\r\nexport class OuiDialogHeaderTitle {\r\n constructor() {}\r\n}\r\n\r\n/**\r\n * Header Image of ui.\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-header-image], [ouiDialogHeaderImage]',\r\n exportAs: 'ouiDialogHeaderImage',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-header-image',\r\n },\r\n})\r\nexport class OuiDialogHeaderImage {\r\n constructor() {}\r\n}\r\n\r\n/**\r\n * Header action area of dialog.\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-header-action], [ouiDialogHeaderAction]',\r\n exportAs: 'ouiDialogHeaderAction',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-header-action',\r\n },\r\n})\r\nexport class OuiDialogHeaderAction {\r\n constructor() {}\r\n}\r\n\r\n/**\r\n * header action article.\r\n */\r\n@Component({\r\n // eslint-disable-next-line @angular-eslint/component-selector\r\n selector: '[oui-dialog-header-article], [ouiDialogHeaderArticle]',\r\n template: '',\r\n exportAs: 'ouiDialogHeaderArticle',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-header-article',\r\n },\r\n})\r\nexport class OuiDialogHeaderArticle implements OnDestroy {\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n constructor(\r\n private ouiIconRegistry: OuiIconRegistry,\r\n private domSanitizer: DomSanitizer,\r\n protected elementRef: ElementRef,\r\n private _focusMonitor: FocusMonitor,\r\n private _ngZone: NgZone\r\n ) {\r\n this.ouiIconRegistry.addSvgIconLiteral(\r\n `article-icon`,\r\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.ARTICLE_ICON)\r\n );\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this.elementRef, true)\r\n .subscribe(() => this._ngZone.run(() => {}));\r\n }\r\n ngOnDestroy() {\r\n this._monitorSubscription.unsubscribe();\r\n }\r\n}\r\n\r\n/**\r\n * header action article.\r\n */\r\n@Component({\r\n // eslint-disable-next-line @angular-eslint/component-selector\r\n selector: '[oui-dialog-header-video], [ouiDialogHeaderVideo]',\r\n template: '',\r\n exportAs: 'ouiDialogHeaderVideo',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-header-video',\r\n },\r\n})\r\nexport class OuiDialogHeaderVideo implements OnDestroy {\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n\r\n constructor(\r\n private ouiIconRegistry: OuiIconRegistry,\r\n private domSanitizer: DomSanitizer,\r\n protected elementRef: ElementRef,\r\n private _focusMonitor: FocusMonitor,\r\n private _ngZone: NgZone\r\n ) {\r\n this.ouiIconRegistry.addSvgIconLiteral(\r\n `video-icon`,\r\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.VIDEO_ICON)\r\n );\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this.elementRef, true)\r\n .subscribe(() => this._ngZone.run(() => {}));\r\n }\r\n ngOnDestroy() {\r\n this._monitorSubscription.unsubscribe();\r\n }\r\n}\r\n\r\n/**\r\n * header action close\r\n */\r\n@Component({\r\n // eslint-disable-next-line @angular-eslint/component-selector\r\n selector: '[oui-dialog-header-close], [ouiDialogHeaderClose]',\r\n template: '',\r\n exportAs: 'ouiDialogHeaderClose',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-header-close',\r\n '[attr.tabindex]': '0',\r\n },\r\n})\r\nexport class OuiDialogHeaderClose implements OnDestroy {\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n constructor(\r\n private ouiIconRegistry: OuiIconRegistry,\r\n private domSanitizer: DomSanitizer,\r\n protected elementRef: ElementRef,\r\n private _focusMonitor: FocusMonitor,\r\n private _ngZone: NgZone\r\n ) {\r\n this.ouiIconRegistry.addSvgIconLiteral(\r\n `close-icon`,\r\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.CLOSE_ICON)\r\n );\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this.elementRef, true)\r\n .subscribe(() => this._ngZone.run(() => {}));\r\n }\r\n ngOnDestroy() {\r\n this._monitorSubscription.unsubscribe();\r\n }\r\n}\r\n\r\n/**\r\n * header action separator close\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-header-separator], [ouiDialogHeaderSeparator]',\r\n exportAs: 'ouiDialogHeaderSeparator',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-header-separator',\r\n },\r\n})\r\nexport class OuiDialogHeaderSeparator {\r\n constructor() {}\r\n}\r\n\r\n/**\r\n * directive for close the current dialog.\r\n */\r\n@Directive({\r\n selector: `[oui-dialog-close], [ouiDialogClose]`,\r\n exportAs: 'ouiDialogClose',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n '[class.cross-disabled]': 'dialogResult===false',\r\n '(click)': 'closeDialog()',\r\n '[attr.aria-label]': 'ariaLabel',\r\n '(keyup.space)': 'handleKeydown($event)',\r\n '(keydown.enter)': 'handleKeydown($event)',\r\n },\r\n})\r\nexport class OuiDialogClose implements OnInit, OnChanges {\r\n /** Screenreader label for the button. */\r\n @Input('aria-label')\r\n ariaLabel = 'Close dialog';\r\n\r\n /** Dialog close input. */\r\n @Input('oui-dialog-close')\r\n dialogResult: any;\r\n\r\n @Input('ouiDialogClose')\r\n _ouiDialogClose: any;\r\n\r\n constructor(\r\n @Optional() public dialogRef: OuiDialogRef,\r\n private _elementRef: ElementRef,\r\n private _dialog: OuiDialog\r\n ) {}\r\n\r\n /** Ensures the option is selected when activated from the keyboard. */\r\n handleKeydown(event: KeyboardEvent): void {\r\n this.closeDialog();\r\n event.preventDefault();\r\n }\r\n\r\n closeDialog() {\r\n if (this.dialogResult !== false) {\r\n this.dialogRef.close(this.dialogResult);\r\n }\r\n }\r\n\r\n ngOnInit() {\r\n if (!this.dialogRef) {\r\n // When this directive is included in a dialog via TemplateRef (rather than being\r\n // in a Component), the DialogRef isn't available via injection because embedded\r\n // views cannot be given a custom injector. Instead, we look up the DialogRef by\r\n // ID. This must occur in `onInit`, as the ID binding for the dialog container won't\r\n // be resolved at constructor time.\r\n this.dialogRef = getClosestDialog(\r\n this._elementRef,\r\n this._dialog.openDialogs\r\n )!;\r\n }\r\n }\r\n\r\n ngOnChanges(changes: SimpleChanges) {\r\n const proxiedChange =\r\n changes._ouiDialogClose || changes._ouiDialogCloseResult;\r\n if (proxiedChange) {\r\n this.dialogResult = proxiedChange.currentValue;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Content section of dialog.\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-content], [ouiDialogContent]',\r\n exportAs: 'ouiDialogContent',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-content',\r\n },\r\n})\r\nexport class OuiDialogContent implements OnInit {\r\n constructor(\r\n @Optional() public dialogRef: OuiDialogRef,\r\n private _elementRef: ElementRef,\r\n private _dialog: OuiDialog\r\n ) {}\r\n\r\n ngOnInit() {\r\n if (!this.dialogRef) {\r\n // When this directive is included in a dialog via TemplateRef (rather than being\r\n // in a Component), the DialogRef isn't available via injection because embedded\r\n // views cannot be given a custom injector. Instead, we look up the DialogRef by\r\n // ID. This must occur in `onInit`, as the ID binding for the dialog container won't\r\n // be resolved at constructor time.\r\n this.dialogRef = getClosestDialog(\r\n this._elementRef,\r\n this._dialog.openDialogs\r\n )!;\r\n }\r\n this._setContentHeight();\r\n }\r\n /* prevent content scroll in default scroll strategy **/\r\n private _setContentHeight() {\r\n if (!this.dialogRef.dialogConfig.scrollStrategy) {\r\n this._elementRef.nativeElement.style.maxHeight = 'none';\r\n this._elementRef.nativeElement.style.overflow = 'visible';\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Content section of dialog.\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-footer], [ouiDialogFooter]',\r\n exportAs: 'ouiDialogFooter',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-footer',\r\n },\r\n})\r\nexport class OuiDialogFooter {\r\n @Input()\r\n id = `oui-dialog-footer-${dialogElementUid++}`;\r\n constructor() {}\r\n}\r\n\r\n/**\r\n * footer action left\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-footer-action-left], [ouiDialogFooterActionLeft]',\r\n exportAs: 'ouiDialogFooterActionLeft',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-footer-action-left',\r\n },\r\n})\r\nexport class OuiDialogFooterActionLeft {\r\n constructor() {}\r\n}\r\n\r\n/**\r\n * footer action right\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-footer-action-right], [ouiDialogFooterActionRight]',\r\n exportAs: 'ouiDialogFooterActionRight',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-footer-action-right',\r\n },\r\n})\r\nexport class OuiDialogFooterActionRight {\r\n constructor() {}\r\n}\r\n\r\n/**\r\n * Finds the closest MatDialogRef to an element by looking at the DOM.\r\n *\r\n * @param element Element relative to which to look for a dialog.\r\n * @param openDialogs References to the currently-open dialogs.\r\n */\r\nfunction getClosestDialog(\r\n element: ElementRef,\r\n openDialogs: OuiDialogRef[]\r\n) {\r\n let parent: HTMLElement | null = element.nativeElement.parentElement;\r\n\r\n while (parent && !parent.classList.contains('oui-dialog-container')) {\r\n parent = parent.parentElement;\r\n }\r\n\r\n return parent ? openDialogs.find((dialog) => dialog.id === parent!.id) : null;\r\n}\r\n", + "sourceCode": "import {\n Directive,\n Input,\n OnChanges,\n OnInit,\n Optional,\n SimpleChanges,\n ElementRef,\n Component,\n NgZone,\n OnDestroy,\n} from '@angular/core';\nimport { OuiDialog } from './dialog';\nimport { OuiDialogRef } from './dialog-ref';\nimport { OuiIconRegistry } from '../icon/icon-registery';\nimport { DomSanitizer } from '@angular/platform-browser';\nimport { ICONS } from '../core/shared/icons';\nimport { Subscription } from 'rxjs';\nimport { FocusMonitor } from '@angular/cdk/a11y';\n\n/** Counter used to generate unique IDs for dialog elements. */\nlet dialogElementUid = 0;\n\n/**\n * Header section of ui.\n */\n@Directive({\n selector: '[oui-dialog-header], [ouiDialogHeader]',\n exportAs: 'ouiDialogHeader',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-header',\n },\n})\nexport class OuiDialogHeader {\n constructor() {}\n}\n\n/**\n * Header Title of ui.\n */\n@Directive({\n selector: '[oui-dialog-header-title], [ouiDialogHeaderTitle]',\n exportAs: 'ouiDialogHeaderTitle',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-header-title',\n },\n})\nexport class OuiDialogHeaderTitle {\n constructor() {}\n}\n\n/**\n * Header Image of ui.\n */\n@Directive({\n selector: '[oui-dialog-header-image], [ouiDialogHeaderImage]',\n exportAs: 'ouiDialogHeaderImage',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-header-image',\n },\n})\nexport class OuiDialogHeaderImage {\n constructor() {}\n}\n\n/**\n * Header action area of dialog.\n */\n@Directive({\n selector: '[oui-dialog-header-action], [ouiDialogHeaderAction]',\n exportAs: 'ouiDialogHeaderAction',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-header-action',\n },\n})\nexport class OuiDialogHeaderAction {\n constructor() {}\n}\n\n/**\n * header action article.\n */\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: '[oui-dialog-header-article], [ouiDialogHeaderArticle]',\n template: '',\n exportAs: 'ouiDialogHeaderArticle',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-header-article',\n },\n})\nexport class OuiDialogHeaderArticle implements OnDestroy {\n private _monitorSubscription: Subscription = Subscription.EMPTY;\n constructor(\n private ouiIconRegistry: OuiIconRegistry,\n private domSanitizer: DomSanitizer,\n protected elementRef: ElementRef,\n private _focusMonitor: FocusMonitor,\n private _ngZone: NgZone\n ) {\n this.ouiIconRegistry.addSvgIconLiteral(\n `article-icon`,\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.ARTICLE_ICON)\n );\n this._monitorSubscription = this._focusMonitor\n .monitor(this.elementRef, true)\n .subscribe(() => this._ngZone.run(() => {}));\n }\n ngOnDestroy() {\n this._monitorSubscription.unsubscribe();\n }\n}\n\n/**\n * header action article.\n */\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: '[oui-dialog-header-video], [ouiDialogHeaderVideo]',\n template: '',\n exportAs: 'ouiDialogHeaderVideo',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-header-video',\n },\n})\nexport class OuiDialogHeaderVideo implements OnDestroy {\n private _monitorSubscription: Subscription = Subscription.EMPTY;\n\n constructor(\n private ouiIconRegistry: OuiIconRegistry,\n private domSanitizer: DomSanitizer,\n protected elementRef: ElementRef,\n private _focusMonitor: FocusMonitor,\n private _ngZone: NgZone\n ) {\n this.ouiIconRegistry.addSvgIconLiteral(\n `video-icon`,\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.VIDEO_ICON)\n );\n this._monitorSubscription = this._focusMonitor\n .monitor(this.elementRef, true)\n .subscribe(() => this._ngZone.run(() => {}));\n }\n ngOnDestroy() {\n this._monitorSubscription.unsubscribe();\n }\n}\n\n/**\n * header action close\n */\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: '[oui-dialog-header-close], [ouiDialogHeaderClose]',\n template: '',\n exportAs: 'ouiDialogHeaderClose',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-header-close',\n '[attr.tabindex]': '0',\n },\n})\nexport class OuiDialogHeaderClose implements OnDestroy {\n private _monitorSubscription: Subscription = Subscription.EMPTY;\n constructor(\n private ouiIconRegistry: OuiIconRegistry,\n private domSanitizer: DomSanitizer,\n protected elementRef: ElementRef,\n private _focusMonitor: FocusMonitor,\n private _ngZone: NgZone\n ) {\n this.ouiIconRegistry.addSvgIconLiteral(\n `close-icon`,\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.CLOSE_ICON)\n );\n this._monitorSubscription = this._focusMonitor\n .monitor(this.elementRef, true)\n .subscribe(() => this._ngZone.run(() => {}));\n }\n ngOnDestroy() {\n this._monitorSubscription.unsubscribe();\n }\n}\n\n/**\n * header action separator close\n */\n@Directive({\n selector: '[oui-dialog-header-separator], [ouiDialogHeaderSeparator]',\n exportAs: 'ouiDialogHeaderSeparator',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-header-separator',\n },\n})\nexport class OuiDialogHeaderSeparator {\n constructor() {}\n}\n\n/**\n * directive for close the current dialog.\n */\n@Directive({\n selector: `[oui-dialog-close], [ouiDialogClose]`,\n exportAs: 'ouiDialogClose',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n '[class.cross-disabled]': 'dialogResult===false',\n '(click)': 'closeDialog()',\n '[attr.aria-label]': 'ariaLabel',\n '(keyup.space)': 'handleKeydown($event)',\n '(keydown.enter)': 'handleKeydown($event)',\n },\n})\nexport class OuiDialogClose implements OnInit, OnChanges {\n /** Screenreader label for the button. */\n @Input('aria-label')\n ariaLabel = 'Close dialog';\n\n /** Dialog close input. */\n @Input('oui-dialog-close')\n dialogResult: any;\n\n @Input('ouiDialogClose')\n _ouiDialogClose: any;\n\n constructor(\n @Optional() public dialogRef: OuiDialogRef,\n private _elementRef: ElementRef,\n private _dialog: OuiDialog\n ) {}\n\n /** Ensures the option is selected when activated from the keyboard. */\n handleKeydown(event: KeyboardEvent): void {\n this.closeDialog();\n event.preventDefault();\n }\n\n closeDialog() {\n if (this.dialogResult !== false) {\n this.dialogRef.close(this.dialogResult);\n }\n }\n\n ngOnInit() {\n if (!this.dialogRef) {\n // When this directive is included in a dialog via TemplateRef (rather than being\n // in a Component), the DialogRef isn't available via injection because embedded\n // views cannot be given a custom injector. Instead, we look up the DialogRef by\n // ID. This must occur in `onInit`, as the ID binding for the dialog container won't\n // be resolved at constructor time.\n this.dialogRef = getClosestDialog(\n this._elementRef,\n this._dialog.openDialogs\n )!;\n }\n }\n\n ngOnChanges(changes: SimpleChanges) {\n const proxiedChange =\n changes._ouiDialogClose || changes._ouiDialogCloseResult;\n if (proxiedChange) {\n this.dialogResult = proxiedChange.currentValue;\n }\n }\n}\n\n/**\n * Content section of dialog.\n */\n@Directive({\n selector: '[oui-dialog-content], [ouiDialogContent]',\n exportAs: 'ouiDialogContent',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-content',\n },\n})\nexport class OuiDialogContent implements OnInit {\n constructor(\n @Optional() public dialogRef: OuiDialogRef,\n private _elementRef: ElementRef,\n private _dialog: OuiDialog\n ) {}\n\n ngOnInit() {\n if (!this.dialogRef) {\n // When this directive is included in a dialog via TemplateRef (rather than being\n // in a Component), the DialogRef isn't available via injection because embedded\n // views cannot be given a custom injector. Instead, we look up the DialogRef by\n // ID. This must occur in `onInit`, as the ID binding for the dialog container won't\n // be resolved at constructor time.\n this.dialogRef = getClosestDialog(\n this._elementRef,\n this._dialog.openDialogs\n )!;\n }\n this._setContentHeight();\n }\n /* prevent content scroll in default scroll strategy **/\n private _setContentHeight() {\n if (!this.dialogRef.dialogConfig.scrollStrategy) {\n this._elementRef.nativeElement.style.maxHeight = 'none';\n this._elementRef.nativeElement.style.overflow = 'visible';\n }\n }\n}\n\n/**\n * Content section of dialog.\n */\n@Directive({\n selector: '[oui-dialog-footer], [ouiDialogFooter]',\n exportAs: 'ouiDialogFooter',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-footer',\n },\n})\nexport class OuiDialogFooter {\n @Input()\n id = `oui-dialog-footer-${dialogElementUid++}`;\n constructor() {}\n}\n\n/**\n * footer action left\n */\n@Directive({\n selector: '[oui-dialog-footer-action-left], [ouiDialogFooterActionLeft]',\n exportAs: 'ouiDialogFooterActionLeft',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-footer-action-left',\n },\n})\nexport class OuiDialogFooterActionLeft {\n constructor() {}\n}\n\n/**\n * footer action right\n */\n@Directive({\n selector: '[oui-dialog-footer-action-right], [ouiDialogFooterActionRight]',\n exportAs: 'ouiDialogFooterActionRight',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-footer-action-right',\n },\n})\nexport class OuiDialogFooterActionRight {\n constructor() {}\n}\n\n/**\n * Finds the closest MatDialogRef to an element by looking at the DOM.\n *\n * @param element Element relative to which to look for a dialog.\n * @param openDialogs References to the currently-open dialogs.\n */\nfunction getClosestDialog(\n element: ElementRef,\n openDialogs: OuiDialogRef[]\n) {\n let parent: HTMLElement | null = element.nativeElement.parentElement;\n\n while (parent && !parent.classList.contains('oui-dialog-container')) {\n parent = parent.parentElement;\n }\n\n return parent ? openDialogs.find((dialog) => dialog.id === parent!.id) : null;\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -26092,7 +35950,7 @@ }, { "name": "OuiDialogHeaderVideo", - "id": "component-OuiDialogHeaderVideo-21f12d9c1a536f543ca0b2382133ab9c7d8281d8279da860ce9e216e5628d8177bee88c4cb99e329b63128f545080dc9b92f03edac8ce76301d884b2180759e5", + "id": "directive-OuiDialogHeader-4c5d8cb794ce2091937eb4ba504351b486d5a89c92a54ad2f3dd97a0a2a183caa83e298a99eae85fcde8c0a630d680ec98c1ab764323a209b8ec838331d1f6fe", "file": "ui/src/components/dialog/dialog-content.ts", "encapsulation": [], "entryComponents": [], @@ -26143,7 +36001,7 @@ "description": "

header action article.

\n", "rawdescription": "\n\nheader action article.\n", "type": "component", - "sourceCode": "import {\r\n Directive,\r\n Input,\r\n OnChanges,\r\n OnInit,\r\n Optional,\r\n SimpleChanges,\r\n ElementRef,\r\n Component,\r\n NgZone,\r\n OnDestroy,\r\n} from '@angular/core';\r\nimport { OuiDialog } from './dialog';\r\nimport { OuiDialogRef } from './dialog-ref';\r\nimport { OuiIconRegistry } from '../icon/icon-registery';\r\nimport { DomSanitizer } from '@angular/platform-browser';\r\nimport { ICONS } from '../core/shared/icons';\r\nimport { Subscription } from 'rxjs';\r\nimport { FocusMonitor } from '@angular/cdk/a11y';\r\n\r\n/** Counter used to generate unique IDs for dialog elements. */\r\nlet dialogElementUid = 0;\r\n\r\n/**\r\n * Header section of ui.\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-header], [ouiDialogHeader]',\r\n exportAs: 'ouiDialogHeader',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-header',\r\n },\r\n})\r\nexport class OuiDialogHeader {\r\n constructor() {}\r\n}\r\n\r\n/**\r\n * Header Title of ui.\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-header-title], [ouiDialogHeaderTitle]',\r\n exportAs: 'ouiDialogHeaderTitle',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-header-title',\r\n },\r\n})\r\nexport class OuiDialogHeaderTitle {\r\n constructor() {}\r\n}\r\n\r\n/**\r\n * Header Image of ui.\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-header-image], [ouiDialogHeaderImage]',\r\n exportAs: 'ouiDialogHeaderImage',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-header-image',\r\n },\r\n})\r\nexport class OuiDialogHeaderImage {\r\n constructor() {}\r\n}\r\n\r\n/**\r\n * Header action area of dialog.\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-header-action], [ouiDialogHeaderAction]',\r\n exportAs: 'ouiDialogHeaderAction',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-header-action',\r\n },\r\n})\r\nexport class OuiDialogHeaderAction {\r\n constructor() {}\r\n}\r\n\r\n/**\r\n * header action article.\r\n */\r\n@Component({\r\n // eslint-disable-next-line @angular-eslint/component-selector\r\n selector: '[oui-dialog-header-article], [ouiDialogHeaderArticle]',\r\n template: '',\r\n exportAs: 'ouiDialogHeaderArticle',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-header-article',\r\n },\r\n})\r\nexport class OuiDialogHeaderArticle implements OnDestroy {\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n constructor(\r\n private ouiIconRegistry: OuiIconRegistry,\r\n private domSanitizer: DomSanitizer,\r\n protected elementRef: ElementRef,\r\n private _focusMonitor: FocusMonitor,\r\n private _ngZone: NgZone\r\n ) {\r\n this.ouiIconRegistry.addSvgIconLiteral(\r\n `article-icon`,\r\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.ARTICLE_ICON)\r\n );\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this.elementRef, true)\r\n .subscribe(() => this._ngZone.run(() => {}));\r\n }\r\n ngOnDestroy() {\r\n this._monitorSubscription.unsubscribe();\r\n }\r\n}\r\n\r\n/**\r\n * header action article.\r\n */\r\n@Component({\r\n // eslint-disable-next-line @angular-eslint/component-selector\r\n selector: '[oui-dialog-header-video], [ouiDialogHeaderVideo]',\r\n template: '',\r\n exportAs: 'ouiDialogHeaderVideo',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-header-video',\r\n },\r\n})\r\nexport class OuiDialogHeaderVideo implements OnDestroy {\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n\r\n constructor(\r\n private ouiIconRegistry: OuiIconRegistry,\r\n private domSanitizer: DomSanitizer,\r\n protected elementRef: ElementRef,\r\n private _focusMonitor: FocusMonitor,\r\n private _ngZone: NgZone\r\n ) {\r\n this.ouiIconRegistry.addSvgIconLiteral(\r\n `video-icon`,\r\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.VIDEO_ICON)\r\n );\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this.elementRef, true)\r\n .subscribe(() => this._ngZone.run(() => {}));\r\n }\r\n ngOnDestroy() {\r\n this._monitorSubscription.unsubscribe();\r\n }\r\n}\r\n\r\n/**\r\n * header action close\r\n */\r\n@Component({\r\n // eslint-disable-next-line @angular-eslint/component-selector\r\n selector: '[oui-dialog-header-close], [ouiDialogHeaderClose]',\r\n template: '',\r\n exportAs: 'ouiDialogHeaderClose',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-header-close',\r\n '[attr.tabindex]': '0',\r\n },\r\n})\r\nexport class OuiDialogHeaderClose implements OnDestroy {\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n constructor(\r\n private ouiIconRegistry: OuiIconRegistry,\r\n private domSanitizer: DomSanitizer,\r\n protected elementRef: ElementRef,\r\n private _focusMonitor: FocusMonitor,\r\n private _ngZone: NgZone\r\n ) {\r\n this.ouiIconRegistry.addSvgIconLiteral(\r\n `close-icon`,\r\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.CLOSE_ICON)\r\n );\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this.elementRef, true)\r\n .subscribe(() => this._ngZone.run(() => {}));\r\n }\r\n ngOnDestroy() {\r\n this._monitorSubscription.unsubscribe();\r\n }\r\n}\r\n\r\n/**\r\n * header action separator close\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-header-separator], [ouiDialogHeaderSeparator]',\r\n exportAs: 'ouiDialogHeaderSeparator',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-header-separator',\r\n },\r\n})\r\nexport class OuiDialogHeaderSeparator {\r\n constructor() {}\r\n}\r\n\r\n/**\r\n * directive for close the current dialog.\r\n */\r\n@Directive({\r\n selector: `[oui-dialog-close], [ouiDialogClose]`,\r\n exportAs: 'ouiDialogClose',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n '[class.cross-disabled]': 'dialogResult===false',\r\n '(click)': 'closeDialog()',\r\n '[attr.aria-label]': 'ariaLabel',\r\n '(keyup.space)': 'handleKeydown($event)',\r\n '(keydown.enter)': 'handleKeydown($event)',\r\n },\r\n})\r\nexport class OuiDialogClose implements OnInit, OnChanges {\r\n /** Screenreader label for the button. */\r\n @Input('aria-label')\r\n ariaLabel = 'Close dialog';\r\n\r\n /** Dialog close input. */\r\n @Input('oui-dialog-close')\r\n dialogResult: any;\r\n\r\n @Input('ouiDialogClose')\r\n _ouiDialogClose: any;\r\n\r\n constructor(\r\n @Optional() public dialogRef: OuiDialogRef,\r\n private _elementRef: ElementRef,\r\n private _dialog: OuiDialog\r\n ) {}\r\n\r\n /** Ensures the option is selected when activated from the keyboard. */\r\n handleKeydown(event: KeyboardEvent): void {\r\n this.closeDialog();\r\n event.preventDefault();\r\n }\r\n\r\n closeDialog() {\r\n if (this.dialogResult !== false) {\r\n this.dialogRef.close(this.dialogResult);\r\n }\r\n }\r\n\r\n ngOnInit() {\r\n if (!this.dialogRef) {\r\n // When this directive is included in a dialog via TemplateRef (rather than being\r\n // in a Component), the DialogRef isn't available via injection because embedded\r\n // views cannot be given a custom injector. Instead, we look up the DialogRef by\r\n // ID. This must occur in `onInit`, as the ID binding for the dialog container won't\r\n // be resolved at constructor time.\r\n this.dialogRef = getClosestDialog(\r\n this._elementRef,\r\n this._dialog.openDialogs\r\n )!;\r\n }\r\n }\r\n\r\n ngOnChanges(changes: SimpleChanges) {\r\n const proxiedChange =\r\n changes._ouiDialogClose || changes._ouiDialogCloseResult;\r\n if (proxiedChange) {\r\n this.dialogResult = proxiedChange.currentValue;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Content section of dialog.\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-content], [ouiDialogContent]',\r\n exportAs: 'ouiDialogContent',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-content',\r\n },\r\n})\r\nexport class OuiDialogContent implements OnInit {\r\n constructor(\r\n @Optional() public dialogRef: OuiDialogRef,\r\n private _elementRef: ElementRef,\r\n private _dialog: OuiDialog\r\n ) {}\r\n\r\n ngOnInit() {\r\n if (!this.dialogRef) {\r\n // When this directive is included in a dialog via TemplateRef (rather than being\r\n // in a Component), the DialogRef isn't available via injection because embedded\r\n // views cannot be given a custom injector. Instead, we look up the DialogRef by\r\n // ID. This must occur in `onInit`, as the ID binding for the dialog container won't\r\n // be resolved at constructor time.\r\n this.dialogRef = getClosestDialog(\r\n this._elementRef,\r\n this._dialog.openDialogs\r\n )!;\r\n }\r\n this._setContentHeight();\r\n }\r\n /* prevent content scroll in default scroll strategy **/\r\n private _setContentHeight() {\r\n if (!this.dialogRef.dialogConfig.scrollStrategy) {\r\n this._elementRef.nativeElement.style.maxHeight = 'none';\r\n this._elementRef.nativeElement.style.overflow = 'visible';\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Content section of dialog.\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-footer], [ouiDialogFooter]',\r\n exportAs: 'ouiDialogFooter',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-footer',\r\n },\r\n})\r\nexport class OuiDialogFooter {\r\n @Input()\r\n id = `oui-dialog-footer-${dialogElementUid++}`;\r\n constructor() {}\r\n}\r\n\r\n/**\r\n * footer action left\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-footer-action-left], [ouiDialogFooterActionLeft]',\r\n exportAs: 'ouiDialogFooterActionLeft',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-footer-action-left',\r\n },\r\n})\r\nexport class OuiDialogFooterActionLeft {\r\n constructor() {}\r\n}\r\n\r\n/**\r\n * footer action right\r\n */\r\n@Directive({\r\n selector: '[oui-dialog-footer-action-right], [ouiDialogFooterActionRight]',\r\n exportAs: 'ouiDialogFooterActionRight',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-dialog-footer-action-right',\r\n },\r\n})\r\nexport class OuiDialogFooterActionRight {\r\n constructor() {}\r\n}\r\n\r\n/**\r\n * Finds the closest MatDialogRef to an element by looking at the DOM.\r\n *\r\n * @param element Element relative to which to look for a dialog.\r\n * @param openDialogs References to the currently-open dialogs.\r\n */\r\nfunction getClosestDialog(\r\n element: ElementRef,\r\n openDialogs: OuiDialogRef[]\r\n) {\r\n let parent: HTMLElement | null = element.nativeElement.parentElement;\r\n\r\n while (parent && !parent.classList.contains('oui-dialog-container')) {\r\n parent = parent.parentElement;\r\n }\r\n\r\n return parent ? openDialogs.find((dialog) => dialog.id === parent!.id) : null;\r\n}\r\n", + "sourceCode": "import {\n Directive,\n Input,\n OnChanges,\n OnInit,\n Optional,\n SimpleChanges,\n ElementRef,\n Component,\n NgZone,\n OnDestroy,\n} from '@angular/core';\nimport { OuiDialog } from './dialog';\nimport { OuiDialogRef } from './dialog-ref';\nimport { OuiIconRegistry } from '../icon/icon-registery';\nimport { DomSanitizer } from '@angular/platform-browser';\nimport { ICONS } from '../core/shared/icons';\nimport { Subscription } from 'rxjs';\nimport { FocusMonitor } from '@angular/cdk/a11y';\n\n/** Counter used to generate unique IDs for dialog elements. */\nlet dialogElementUid = 0;\n\n/**\n * Header section of ui.\n */\n@Directive({\n selector: '[oui-dialog-header], [ouiDialogHeader]',\n exportAs: 'ouiDialogHeader',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-header',\n },\n})\nexport class OuiDialogHeader {\n constructor() {}\n}\n\n/**\n * Header Title of ui.\n */\n@Directive({\n selector: '[oui-dialog-header-title], [ouiDialogHeaderTitle]',\n exportAs: 'ouiDialogHeaderTitle',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-header-title',\n },\n})\nexport class OuiDialogHeaderTitle {\n constructor() {}\n}\n\n/**\n * Header Image of ui.\n */\n@Directive({\n selector: '[oui-dialog-header-image], [ouiDialogHeaderImage]',\n exportAs: 'ouiDialogHeaderImage',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-header-image',\n },\n})\nexport class OuiDialogHeaderImage {\n constructor() {}\n}\n\n/**\n * Header action area of dialog.\n */\n@Directive({\n selector: '[oui-dialog-header-action], [ouiDialogHeaderAction]',\n exportAs: 'ouiDialogHeaderAction',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-header-action',\n },\n})\nexport class OuiDialogHeaderAction {\n constructor() {}\n}\n\n/**\n * header action article.\n */\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: '[oui-dialog-header-article], [ouiDialogHeaderArticle]',\n template: '',\n exportAs: 'ouiDialogHeaderArticle',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-header-article',\n },\n})\nexport class OuiDialogHeaderArticle implements OnDestroy {\n private _monitorSubscription: Subscription = Subscription.EMPTY;\n constructor(\n private ouiIconRegistry: OuiIconRegistry,\n private domSanitizer: DomSanitizer,\n protected elementRef: ElementRef,\n private _focusMonitor: FocusMonitor,\n private _ngZone: NgZone\n ) {\n this.ouiIconRegistry.addSvgIconLiteral(\n `article-icon`,\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.ARTICLE_ICON)\n );\n this._monitorSubscription = this._focusMonitor\n .monitor(this.elementRef, true)\n .subscribe(() => this._ngZone.run(() => {}));\n }\n ngOnDestroy() {\n this._monitorSubscription.unsubscribe();\n }\n}\n\n/**\n * header action article.\n */\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: '[oui-dialog-header-video], [ouiDialogHeaderVideo]',\n template: '',\n exportAs: 'ouiDialogHeaderVideo',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-header-video',\n },\n})\nexport class OuiDialogHeaderVideo implements OnDestroy {\n private _monitorSubscription: Subscription = Subscription.EMPTY;\n\n constructor(\n private ouiIconRegistry: OuiIconRegistry,\n private domSanitizer: DomSanitizer,\n protected elementRef: ElementRef,\n private _focusMonitor: FocusMonitor,\n private _ngZone: NgZone\n ) {\n this.ouiIconRegistry.addSvgIconLiteral(\n `video-icon`,\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.VIDEO_ICON)\n );\n this._monitorSubscription = this._focusMonitor\n .monitor(this.elementRef, true)\n .subscribe(() => this._ngZone.run(() => {}));\n }\n ngOnDestroy() {\n this._monitorSubscription.unsubscribe();\n }\n}\n\n/**\n * header action close\n */\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: '[oui-dialog-header-close], [ouiDialogHeaderClose]',\n template: '',\n exportAs: 'ouiDialogHeaderClose',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-header-close',\n '[attr.tabindex]': '0',\n },\n})\nexport class OuiDialogHeaderClose implements OnDestroy {\n private _monitorSubscription: Subscription = Subscription.EMPTY;\n constructor(\n private ouiIconRegistry: OuiIconRegistry,\n private domSanitizer: DomSanitizer,\n protected elementRef: ElementRef,\n private _focusMonitor: FocusMonitor,\n private _ngZone: NgZone\n ) {\n this.ouiIconRegistry.addSvgIconLiteral(\n `close-icon`,\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.CLOSE_ICON)\n );\n this._monitorSubscription = this._focusMonitor\n .monitor(this.elementRef, true)\n .subscribe(() => this._ngZone.run(() => {}));\n }\n ngOnDestroy() {\n this._monitorSubscription.unsubscribe();\n }\n}\n\n/**\n * header action separator close\n */\n@Directive({\n selector: '[oui-dialog-header-separator], [ouiDialogHeaderSeparator]',\n exportAs: 'ouiDialogHeaderSeparator',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-header-separator',\n },\n})\nexport class OuiDialogHeaderSeparator {\n constructor() {}\n}\n\n/**\n * directive for close the current dialog.\n */\n@Directive({\n selector: `[oui-dialog-close], [ouiDialogClose]`,\n exportAs: 'ouiDialogClose',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n '[class.cross-disabled]': 'dialogResult===false',\n '(click)': 'closeDialog()',\n '[attr.aria-label]': 'ariaLabel',\n '(keyup.space)': 'handleKeydown($event)',\n '(keydown.enter)': 'handleKeydown($event)',\n },\n})\nexport class OuiDialogClose implements OnInit, OnChanges {\n /** Screenreader label for the button. */\n @Input('aria-label')\n ariaLabel = 'Close dialog';\n\n /** Dialog close input. */\n @Input('oui-dialog-close')\n dialogResult: any;\n\n @Input('ouiDialogClose')\n _ouiDialogClose: any;\n\n constructor(\n @Optional() public dialogRef: OuiDialogRef,\n private _elementRef: ElementRef,\n private _dialog: OuiDialog\n ) {}\n\n /** Ensures the option is selected when activated from the keyboard. */\n handleKeydown(event: KeyboardEvent): void {\n this.closeDialog();\n event.preventDefault();\n }\n\n closeDialog() {\n if (this.dialogResult !== false) {\n this.dialogRef.close(this.dialogResult);\n }\n }\n\n ngOnInit() {\n if (!this.dialogRef) {\n // When this directive is included in a dialog via TemplateRef (rather than being\n // in a Component), the DialogRef isn't available via injection because embedded\n // views cannot be given a custom injector. Instead, we look up the DialogRef by\n // ID. This must occur in `onInit`, as the ID binding for the dialog container won't\n // be resolved at constructor time.\n this.dialogRef = getClosestDialog(\n this._elementRef,\n this._dialog.openDialogs\n )!;\n }\n }\n\n ngOnChanges(changes: SimpleChanges) {\n const proxiedChange =\n changes._ouiDialogClose || changes._ouiDialogCloseResult;\n if (proxiedChange) {\n this.dialogResult = proxiedChange.currentValue;\n }\n }\n}\n\n/**\n * Content section of dialog.\n */\n@Directive({\n selector: '[oui-dialog-content], [ouiDialogContent]',\n exportAs: 'ouiDialogContent',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-content',\n },\n})\nexport class OuiDialogContent implements OnInit {\n constructor(\n @Optional() public dialogRef: OuiDialogRef,\n private _elementRef: ElementRef,\n private _dialog: OuiDialog\n ) {}\n\n ngOnInit() {\n if (!this.dialogRef) {\n // When this directive is included in a dialog via TemplateRef (rather than being\n // in a Component), the DialogRef isn't available via injection because embedded\n // views cannot be given a custom injector. Instead, we look up the DialogRef by\n // ID. This must occur in `onInit`, as the ID binding for the dialog container won't\n // be resolved at constructor time.\n this.dialogRef = getClosestDialog(\n this._elementRef,\n this._dialog.openDialogs\n )!;\n }\n this._setContentHeight();\n }\n /* prevent content scroll in default scroll strategy **/\n private _setContentHeight() {\n if (!this.dialogRef.dialogConfig.scrollStrategy) {\n this._elementRef.nativeElement.style.maxHeight = 'none';\n this._elementRef.nativeElement.style.overflow = 'visible';\n }\n }\n}\n\n/**\n * Content section of dialog.\n */\n@Directive({\n selector: '[oui-dialog-footer], [ouiDialogFooter]',\n exportAs: 'ouiDialogFooter',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-footer',\n },\n})\nexport class OuiDialogFooter {\n @Input()\n id = `oui-dialog-footer-${dialogElementUid++}`;\n constructor() {}\n}\n\n/**\n * footer action left\n */\n@Directive({\n selector: '[oui-dialog-footer-action-left], [ouiDialogFooterActionLeft]',\n exportAs: 'ouiDialogFooterActionLeft',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-footer-action-left',\n },\n})\nexport class OuiDialogFooterActionLeft {\n constructor() {}\n}\n\n/**\n * footer action right\n */\n@Directive({\n selector: '[oui-dialog-footer-action-right], [ouiDialogFooterActionRight]',\n exportAs: 'ouiDialogFooterActionRight',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-dialog-footer-action-right',\n },\n})\nexport class OuiDialogFooterActionRight {\n constructor() {}\n}\n\n/**\n * Finds the closest MatDialogRef to an element by looking at the DOM.\n *\n * @param element Element relative to which to look for a dialog.\n * @param openDialogs References to the currently-open dialogs.\n */\nfunction getClosestDialog(\n element: ElementRef,\n openDialogs: OuiDialogRef[]\n) {\n let parent: HTMLElement | null = element.nativeElement.parentElement;\n\n while (parent && !parent.classList.contains('oui-dialog-container')) {\n parent = parent.parentElement;\n }\n\n return parent ? openDialogs.find((dialog) => dialog.id === parent!.id) : null;\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -26239,7 +36097,7 @@ }, { "name": "OuiDialogStorybook", - "id": "component-OuiDialogStorybook-cf43f72bfcc142b341380963bab89ae99cd5ff724d85d488114795fa0ce54ca34c2386727f03e09e734e3179bd6c2e751d048d1f14f95a3a0e7aecd8eaf41b91", + "id": "component-OuiDialogStorybook-f6616329f06860157d1dfe8e02db995cae21ae3c80bd83e59b6c6aced403c3d97541a01a563829623094a2c750877b4beefb784493ea2c16fbacd01045a9c0eb", "file": "ui/src/stories/dialog/dialog.component.ts", "encapsulation": [], "entryComponents": [], @@ -26329,7 +36187,7 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import {\r\n Component,\r\n ViewChild,\r\n Output,\r\n EventEmitter,\r\n Input,\r\n TemplateRef,\r\n} from '@angular/core';\r\nimport { OuiDialog } from '../../components';\r\n\r\n@Component({\r\n selector: 'oui-dialog-storybook',\r\n template: `\r\n \r\n \r\n
\r\n \r\n
\r\n
\r\n \r\n \r\n \r\n
\r\n
\r\n
\r\n
\r\n
\r\n \r\n \r\n
\r\n
\r\n \r\n \r\n
\r\n
\r\n
\r\n `,\r\n})\r\nexport class OuiDialogStorybook {\r\n @Output()\r\n readonly close: EventEmitter = new EventEmitter();\r\n @Input() disabled = false;\r\n @ViewChild('dialogTemplate')\r\n dialogTemplate: TemplateRef;\r\n constructor(private dialog: OuiDialog) {}\r\n openDialog(e?: string) {\r\n const dialogRef = this.dialog.open(this.dialogTemplate);\r\n dialogRef.afterClosed().subscribe(() => {\r\n this.close.emit(e);\r\n });\r\n }\r\n}\r\n", + "sourceCode": "import {\n Component,\n ViewChild,\n Output,\n EventEmitter,\n Input,\n TemplateRef,\n} from '@angular/core';\nimport { OuiDialog } from '../../components';\n\n@Component({\n selector: 'oui-dialog-storybook',\n template: `\n \n \n
\n \n
\n
\n \n \n \n
\n
\n
\n
\n
\n \n \n
\n
\n \n \n
\n
\n
\n `,\n})\nexport class OuiDialogStorybook {\n @Output()\n readonly close: EventEmitter = new EventEmitter();\n @Input() disabled = false;\n @ViewChild('dialogTemplate')\n dialogTemplate: TemplateRef;\n constructor(private dialog: OuiDialog) {}\n openDialog(e?: string) {\n const dialogRef = this.dialog.open(this.dialogTemplate);\n dialogRef.afterClosed().subscribe(() => {\n this.close.emit(e);\n });\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -26362,7 +36220,7 @@ }, { "name": "OuiFooterRow", - "id": "component-OuiFooterRow-2c11beafcdd8c12a01329de8f4f053505e5dbbd179540cefba04203174c4dbaef3f70b9edd0846497b3f172b61f71885e2ae7d091d3b9bd6ed01366b24783bc2", + "id": "component-OuiFooterRow-231d97eaa7f9a073f68bb47891eb7839f7a6610df3284b8b1e98593fcac5b8a47362c5920e2b3f0a60958ecee910f5d4d98d6ac257b31323cdd32c67e5768b7b", "file": "ui/src/components/table/row.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -26395,7 +36253,7 @@ "description": "

Footer template container that contains the cell outlet. Adds the right class and role.

\n", "rawdescription": "\nFooter template container that contains the cell outlet. Adds the right class and role.", "type": "component", - "sourceCode": "import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n Directive,\r\n ViewEncapsulation,\r\n OnDestroy,\r\n ElementRef,\r\n NgZone,\r\n IterableDiffers,\r\n} from '@angular/core';\r\nimport {\r\n CDK_ROW_TEMPLATE,\r\n CdkFooterRow,\r\n CdkFooterRowDef,\r\n CdkHeaderRow,\r\n CdkHeaderRowDef,\r\n CdkRow,\r\n CdkRowDef,\r\n} from '@angular/cdk/table';\r\nimport { FocusMonitor } from '@angular/cdk/a11y';\r\nimport { Subscription } from 'rxjs';\r\n\r\n/**\r\n * Header row definition for the oui-table.\r\n * Captures the header row's template and other header properties such as the columns to display.\r\n */\r\n@Directive({\r\n selector: '[ouiHeaderRowDef]',\r\n providers: [{ provide: CdkHeaderRowDef, useExisting: OuiHeaderRowDef }],\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['columns: ouiHeaderRowDef'],\r\n})\r\nexport class OuiHeaderRowDef extends CdkHeaderRowDef {}\r\n\r\n/**\r\n * Footer row definition for the oui-table.\r\n * Captures the footer row's template and other footer properties such as the columns to display.\r\n */\r\n@Directive({\r\n selector: '[ouiFooterRowDef]',\r\n providers: [{ provide: CdkFooterRowDef, useExisting: OuiFooterRowDef }],\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['columns: ouiFooterRowDef'],\r\n})\r\nexport class OuiFooterRowDef extends CdkFooterRowDef {}\r\n\r\n/**\r\n * Data row definition for the oui-table.\r\n * Captures the data row's template and other properties such as the columns to display and\r\n * a when predicate that describes when this row should be used.\r\n */\r\n@Directive({\r\n selector: '[ouiRowDef]',\r\n providers: [{ provide: CdkRowDef, useExisting: OuiRowDef }],\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['columns: ouiRowDefColumns', 'when: ouiRowDefWhen'],\r\n})\r\nexport class OuiRowDef extends CdkRowDef {}\r\n\r\n/** Footer template container that contains the cell outlet. Adds the right class and role. */\r\n@Component({\r\n selector: 'oui-header-row, tr[oui-header-row]',\r\n template: CDK_ROW_TEMPLATE,\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-header-row',\r\n role: 'row',\r\n },\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n exportAs: 'ouiHeaderRow',\r\n providers: [{ provide: CdkHeaderRow, useExisting: OuiHeaderRow }],\r\n})\r\nexport class OuiHeaderRow extends CdkHeaderRow {}\r\n\r\n/** Footer template container that contains the cell outlet. Adds the right class and role. */\r\n@Component({\r\n selector: 'oui-footer-row, tr[oui-footer-row]',\r\n template: CDK_ROW_TEMPLATE,\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-footer-row',\r\n role: 'row',\r\n },\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n exportAs: 'ouiFooterRow',\r\n providers: [{ provide: CdkFooterRow, useExisting: OuiFooterRow }],\r\n})\r\nexport class OuiFooterRow extends CdkFooterRow {}\r\n\r\n/** Data row template container that contains the cell outlet. Adds the right class and role. */\r\n@Component({\r\n selector: 'oui-row, tr[oui-row]',\r\n template: CDK_ROW_TEMPLATE,\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-row',\r\n role: 'row',\r\n tabindex: '0',\r\n },\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n exportAs: 'ouiRow',\r\n providers: [{ provide: CdkRow, useExisting: OuiRow }],\r\n})\r\nexport class OuiRow extends CdkRow implements OnDestroy {\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n constructor(\r\n protected elementRef: ElementRef,\r\n protected _differs: IterableDiffers,\r\n private _focusMonitor: FocusMonitor,\r\n private _ngZone: NgZone\r\n ) {\r\n super();\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this.elementRef, true)\r\n .subscribe(() => this._ngZone.run(() => {}));\r\n }\r\n ngOnDestroy(): void {\r\n this._focusMonitor.stopMonitoring(this.elementRef);\r\n this._monitorSubscription.unsubscribe();\r\n }\r\n}\r\n", + "sourceCode": "import {\n ChangeDetectionStrategy,\n Component,\n Directive,\n ViewEncapsulation,\n OnDestroy,\n ElementRef,\n NgZone,\n IterableDiffers,\n} from '@angular/core';\nimport {\n CDK_ROW_TEMPLATE,\n CdkFooterRow,\n CdkFooterRowDef,\n CdkHeaderRow,\n CdkHeaderRowDef,\n CdkRow,\n CdkRowDef,\n} from '@angular/cdk/table';\nimport { FocusMonitor } from '@angular/cdk/a11y';\nimport { Subscription } from 'rxjs';\n\n/**\n * Header row definition for the oui-table.\n * Captures the header row's template and other header properties such as the columns to display.\n */\n@Directive({\n selector: '[ouiHeaderRowDef]',\n providers: [{ provide: CdkHeaderRowDef, useExisting: OuiHeaderRowDef }],\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\n inputs: ['columns: ouiHeaderRowDef'],\n})\nexport class OuiHeaderRowDef extends CdkHeaderRowDef {}\n\n/**\n * Footer row definition for the oui-table.\n * Captures the footer row's template and other footer properties such as the columns to display.\n */\n@Directive({\n selector: '[ouiFooterRowDef]',\n providers: [{ provide: CdkFooterRowDef, useExisting: OuiFooterRowDef }],\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\n inputs: ['columns: ouiFooterRowDef'],\n})\nexport class OuiFooterRowDef extends CdkFooterRowDef {}\n\n/**\n * Data row definition for the oui-table.\n * Captures the data row's template and other properties such as the columns to display and\n * a when predicate that describes when this row should be used.\n */\n@Directive({\n selector: '[ouiRowDef]',\n providers: [{ provide: CdkRowDef, useExisting: OuiRowDef }],\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\n inputs: ['columns: ouiRowDefColumns', 'when: ouiRowDefWhen'],\n})\nexport class OuiRowDef extends CdkRowDef {}\n\n/** Footer template container that contains the cell outlet. Adds the right class and role. */\n@Component({\n selector: 'oui-header-row, tr[oui-header-row]',\n template: CDK_ROW_TEMPLATE,\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-header-row',\n role: 'row',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n exportAs: 'ouiHeaderRow',\n providers: [{ provide: CdkHeaderRow, useExisting: OuiHeaderRow }],\n})\nexport class OuiHeaderRow extends CdkHeaderRow {}\n\n/** Footer template container that contains the cell outlet. Adds the right class and role. */\n@Component({\n selector: 'oui-footer-row, tr[oui-footer-row]',\n template: CDK_ROW_TEMPLATE,\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-footer-row',\n role: 'row',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n exportAs: 'ouiFooterRow',\n providers: [{ provide: CdkFooterRow, useExisting: OuiFooterRow }],\n})\nexport class OuiFooterRow extends CdkFooterRow {}\n\n/** Data row template container that contains the cell outlet. Adds the right class and role. */\n@Component({\n selector: 'oui-row, tr[oui-row]',\n template: CDK_ROW_TEMPLATE,\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-row',\n role: 'row',\n tabindex: '0',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n exportAs: 'ouiRow',\n providers: [{ provide: CdkRow, useExisting: OuiRow }],\n})\nexport class OuiRow extends CdkRow implements OnDestroy {\n private _monitorSubscription: Subscription = Subscription.EMPTY;\n constructor(\n protected elementRef: ElementRef,\n protected _differs: IterableDiffers,\n private _focusMonitor: FocusMonitor,\n private _ngZone: NgZone\n ) {\n super();\n this._monitorSubscription = this._focusMonitor\n .monitor(this.elementRef, true)\n .subscribe(() => this._ngZone.run(() => {}));\n }\n ngOnDestroy(): void {\n this._focusMonitor.stopMonitoring(this.elementRef);\n this._monitorSubscription.unsubscribe();\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -26403,7 +36261,7 @@ }, { "name": "OuiFormField", - "id": "component-OuiFormField-9aa78587b2eacda6fb1222e0ddbaa46d83eb32464d8f0c03f69a366df7912304f5bf0edb0e34cd911e0c6f18a5c6b78136baccdc5a369f2892679c11772d0fff", + "id": "component-OuiFormField-c90db05a2622d093fb708f05b1327487f220eb799cec2cd3c83291595f83648d68bc2ffca27d32fdeb8d2a25ba50e1db61511dab426ab1f4664b88684421baaf", "file": "ui/src/components/form-field/form-field.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -26590,11 +36448,11 @@ "description": "

Container for form controls that applies Oncehub Design styling and behavior.

\n", "rawdescription": "\nContainer for form controls that applies Oncehub Design styling and behavior.", "type": "component", - "sourceCode": "import {\r\n AfterContentChecked,\r\n AfterContentInit,\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n ContentChild,\r\n ElementRef,\r\n Inject,\r\n InjectionToken,\r\n Optional,\r\n ViewChild,\r\n ViewEncapsulation,\r\n OnDestroy,\r\n Input,\r\n} from '@angular/core';\r\nimport { mixinColor, ThemePalette } from '../core';\r\nimport { Subject } from 'rxjs';\r\nimport { takeUntil } from 'rxjs/operators';\r\nimport { OuiFormFieldControl } from './form-field-control';\r\nimport { getOuiFormFieldMissingControlError } from './form-field-errors';\r\n\r\n/**\r\n * Boilerplate for applying mixins to OuiFormField.\r\n *\r\n * @docs-private\r\n */\r\nexport class OuiFormFieldBase {\r\n constructor(public _elementRef: ElementRef) {}\r\n}\r\n\r\n/**\r\n * Base class to which we're applying the form field mixins.\r\n *\r\n * @docs-private\r\n */\r\nexport const _OuiFormFieldMixinBase: typeof OuiFormFieldBase =\r\n mixinColor(OuiFormFieldBase);\r\n\r\n/** Possible appearance styles for the form field. */\r\nexport type OuiFormFieldAppearance = 'standard' | 'underline';\r\n\r\n/**\r\n * Represents the default options form the form field that can be configured\r\n * using the `OUI_FORM_FIELD_DEFAULT_OPTIONS` injection token.\r\n */\r\nexport interface OuiFormFieldDefaultOptions {\r\n appearance?: OuiFormFieldAppearance;\r\n}\r\n\r\n/**\r\n * Injection token that can be used to configure the\r\n * default options for all form field within an app.\r\n */\r\nexport const OUI_FORM_FIELD_DEFAULT_OPTIONS =\r\n new InjectionToken(\r\n 'OUI_FORM_FIELD_DEFAULT_OPTIONS'\r\n );\r\n\r\n/** Container for form controls that applies Oncehub Design styling and behavior. */\r\n@Component({\r\n selector: 'oui-form-field',\r\n exportAs: 'ouiFormField',\r\n templateUrl: 'form-field.html',\r\n // OuiInput is a directive and can't have styles, so we need to include its styles here\r\n // in form-field-input.css. The OuiInput styles are fairly minimal so it shouldn't be a\r\n // big deal for people who aren't using OuiInput.\r\n styleUrls: ['form-field.scss'],\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-form-field',\r\n '[class.oui-focused]': '_control.focused',\r\n '[class.oui-disabled]': '_control.disabled',\r\n '[class.oui-form-field-appearance-standard]': 'appearance == \"standard\"',\r\n '[class.oui-form-field-appearance-underline]': 'appearance == \"underline\"',\r\n },\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class OuiFormField\r\n extends _OuiFormFieldMixinBase\r\n implements AfterContentInit, AfterContentChecked, OnDestroy\r\n{\r\n private _destroyed = new Subject();\r\n @Input() color: ThemePalette;\r\n /** The form-field appearance style. */\r\n @Input()\r\n get appearance(): OuiFormFieldAppearance {\r\n return this._appearance;\r\n }\r\n set appearance(value: OuiFormFieldAppearance) {\r\n this._appearance =\r\n value || (this._defaults && this._defaults.appearance) || 'standard';\r\n }\r\n _appearance: OuiFormFieldAppearance;\r\n\r\n @ViewChild('connectionContainer')\r\n _connectionContainerRef: ElementRef;\r\n @ViewChild('inputContainer')\r\n _inputContainerRef: ElementRef;\r\n @ContentChild(OuiFormFieldControl)\r\n _control: OuiFormFieldControl;\r\n constructor(\r\n public _elementRef: ElementRef,\r\n private _changeDetectorRef: ChangeDetectorRef,\r\n @Optional()\r\n @Inject(OUI_FORM_FIELD_DEFAULT_OPTIONS)\r\n private _defaults: OuiFormFieldDefaultOptions\r\n ) {\r\n super(_elementRef);\r\n\r\n // Set the default through here so we invoke the setter on the first run.\r\n this.appearance =\r\n _defaults && _defaults.appearance ? _defaults.appearance : 'standard';\r\n }\r\n\r\n ngAfterContentInit() {\r\n this._validateControlChild();\r\n const control = this._control;\r\n\r\n if (control.controlType) {\r\n this._elementRef.nativeElement.classList.add(\r\n `oui-form-field-type-${control.controlType}`\r\n );\r\n }\r\n\r\n // Run change detection if the value changes.\r\n if (control.ngControl && control.ngControl.valueChanges) {\r\n control.ngControl.valueChanges\r\n .pipe(takeUntil(this._destroyed))\r\n .subscribe(() => this._changeDetectorRef.markForCheck());\r\n }\r\n }\r\n\r\n getConnectedOverlayOrigin(): ElementRef {\r\n return this._connectionContainerRef || this._elementRef;\r\n }\r\n\r\n ngAfterContentChecked() {\r\n this._validateControlChild();\r\n }\r\n\r\n ngOnDestroy() {\r\n this._destroyed.next();\r\n this._destroyed.complete();\r\n }\r\n\r\n /** Throws an error if the form field's control is missing. */\r\n protected _validateControlChild() {\r\n if (!this._control) {\r\n throw getOuiFormFieldMissingControlError();\r\n }\r\n }\r\n}\r\n", + "sourceCode": "import {\n AfterContentChecked,\n AfterContentInit,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n ContentChild,\n ElementRef,\n Inject,\n InjectionToken,\n Optional,\n ViewChild,\n ViewEncapsulation,\n OnDestroy,\n Input,\n} from '@angular/core';\nimport { mixinColor, ThemePalette } from '../core';\nimport { Subject } from 'rxjs';\nimport { takeUntil } from 'rxjs/operators';\nimport { OuiFormFieldControl } from './form-field-control';\nimport { getOuiFormFieldMissingControlError } from './form-field-errors';\n\n/**\n * Boilerplate for applying mixins to OuiFormField.\n *\n * @docs-private\n */\nexport class OuiFormFieldBase {\n constructor(public _elementRef: ElementRef) {}\n}\n\n/**\n * Base class to which we're applying the form field mixins.\n *\n * @docs-private\n */\nexport const _OuiFormFieldMixinBase: typeof OuiFormFieldBase =\n mixinColor(OuiFormFieldBase);\n\n/** Possible appearance styles for the form field. */\nexport type OuiFormFieldAppearance = 'standard' | 'underline';\n\n/**\n * Represents the default options form the form field that can be configured\n * using the `OUI_FORM_FIELD_DEFAULT_OPTIONS` injection token.\n */\nexport interface OuiFormFieldDefaultOptions {\n appearance?: OuiFormFieldAppearance;\n}\n\n/**\n * Injection token that can be used to configure the\n * default options for all form field within an app.\n */\nexport const OUI_FORM_FIELD_DEFAULT_OPTIONS =\n new InjectionToken(\n 'OUI_FORM_FIELD_DEFAULT_OPTIONS'\n );\n\n/** Container for form controls that applies Oncehub Design styling and behavior. */\n@Component({\n selector: 'oui-form-field',\n exportAs: 'ouiFormField',\n templateUrl: 'form-field.html',\n // OuiInput is a directive and can't have styles, so we need to include its styles here\n // in form-field-input.css. The OuiInput styles are fairly minimal so it shouldn't be a\n // big deal for people who aren't using OuiInput.\n styleUrls: ['form-field.scss'],\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-form-field',\n '[class.oui-focused]': '_control.focused',\n '[class.oui-disabled]': '_control.disabled',\n '[class.oui-form-field-appearance-standard]': 'appearance == \"standard\"',\n '[class.oui-form-field-appearance-underline]': 'appearance == \"underline\"',\n },\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class OuiFormField\n extends _OuiFormFieldMixinBase\n implements AfterContentInit, AfterContentChecked, OnDestroy\n{\n private _destroyed = new Subject();\n @Input() color: ThemePalette;\n /** The form-field appearance style. */\n @Input()\n get appearance(): OuiFormFieldAppearance {\n return this._appearance;\n }\n set appearance(value: OuiFormFieldAppearance) {\n this._appearance =\n value || (this._defaults && this._defaults.appearance) || 'standard';\n }\n _appearance: OuiFormFieldAppearance;\n\n @ViewChild('connectionContainer')\n _connectionContainerRef: ElementRef;\n @ViewChild('inputContainer')\n _inputContainerRef: ElementRef;\n @ContentChild(OuiFormFieldControl)\n _control: OuiFormFieldControl;\n constructor(\n public _elementRef: ElementRef,\n private _changeDetectorRef: ChangeDetectorRef,\n @Optional()\n @Inject(OUI_FORM_FIELD_DEFAULT_OPTIONS)\n private _defaults: OuiFormFieldDefaultOptions\n ) {\n super(_elementRef);\n\n // Set the default through here so we invoke the setter on the first run.\n this.appearance =\n _defaults && _defaults.appearance ? _defaults.appearance : 'standard';\n }\n\n ngAfterContentInit() {\n this._validateControlChild();\n const control = this._control;\n\n if (control.controlType) {\n this._elementRef.nativeElement.classList.add(\n `oui-form-field-type-${control.controlType}`\n );\n }\n\n // Run change detection if the value changes.\n if (control.ngControl && control.ngControl.valueChanges) {\n control.ngControl.valueChanges\n .pipe(takeUntil(this._destroyed))\n .subscribe(() => this._changeDetectorRef.markForCheck());\n }\n }\n\n getConnectedOverlayOrigin(): ElementRef {\n return this._connectionContainerRef || this._elementRef;\n }\n\n ngAfterContentChecked() {\n this._validateControlChild();\n }\n\n ngOnDestroy() {\n this._destroyed.next();\n this._destroyed.complete();\n }\n\n /** Throws an error if the form field's control is missing. */\n protected _validateControlChild() {\n if (!this._control) {\n throw getOuiFormFieldMissingControlError();\n }\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": "@import 'form-field-theme';\r\n.oui-form-field-infix {\r\n position: relative;\r\n}\r\n", + "data": "@import 'form-field-theme';\n.oui-form-field-infix {\n position: relative;\n}\n", "styleUrl": "form-field.scss" } ], @@ -26701,11 +36559,11 @@ } } }, - "templateData": "
\r\n \r\n \r\n\r\n
\r\n \r\n
\r\n\r\n \r\n
\r\n\r\n" + "templateData": "
\n \n \n\n
\n \n
\n\n \n
\n\n" }, { "name": "OuiHeaderRow", - "id": "component-OuiHeaderRow-2c11beafcdd8c12a01329de8f4f053505e5dbbd179540cefba04203174c4dbaef3f70b9edd0846497b3f172b61f71885e2ae7d091d3b9bd6ed01366b24783bc2", + "id": "component-OuiHeaderRow-231d97eaa7f9a073f68bb47891eb7839f7a6610df3284b8b1e98593fcac5b8a47362c5920e2b3f0a60958ecee910f5d4d98d6ac257b31323cdd32c67e5768b7b", "file": "ui/src/components/table/row.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -26738,7 +36596,7 @@ "description": "

Footer template container that contains the cell outlet. Adds the right class and role.

\n", "rawdescription": "\nFooter template container that contains the cell outlet. Adds the right class and role.", "type": "component", - "sourceCode": "import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n Directive,\r\n ViewEncapsulation,\r\n OnDestroy,\r\n ElementRef,\r\n NgZone,\r\n IterableDiffers,\r\n} from '@angular/core';\r\nimport {\r\n CDK_ROW_TEMPLATE,\r\n CdkFooterRow,\r\n CdkFooterRowDef,\r\n CdkHeaderRow,\r\n CdkHeaderRowDef,\r\n CdkRow,\r\n CdkRowDef,\r\n} from '@angular/cdk/table';\r\nimport { FocusMonitor } from '@angular/cdk/a11y';\r\nimport { Subscription } from 'rxjs';\r\n\r\n/**\r\n * Header row definition for the oui-table.\r\n * Captures the header row's template and other header properties such as the columns to display.\r\n */\r\n@Directive({\r\n selector: '[ouiHeaderRowDef]',\r\n providers: [{ provide: CdkHeaderRowDef, useExisting: OuiHeaderRowDef }],\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['columns: ouiHeaderRowDef'],\r\n})\r\nexport class OuiHeaderRowDef extends CdkHeaderRowDef {}\r\n\r\n/**\r\n * Footer row definition for the oui-table.\r\n * Captures the footer row's template and other footer properties such as the columns to display.\r\n */\r\n@Directive({\r\n selector: '[ouiFooterRowDef]',\r\n providers: [{ provide: CdkFooterRowDef, useExisting: OuiFooterRowDef }],\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['columns: ouiFooterRowDef'],\r\n})\r\nexport class OuiFooterRowDef extends CdkFooterRowDef {}\r\n\r\n/**\r\n * Data row definition for the oui-table.\r\n * Captures the data row's template and other properties such as the columns to display and\r\n * a when predicate that describes when this row should be used.\r\n */\r\n@Directive({\r\n selector: '[ouiRowDef]',\r\n providers: [{ provide: CdkRowDef, useExisting: OuiRowDef }],\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['columns: ouiRowDefColumns', 'when: ouiRowDefWhen'],\r\n})\r\nexport class OuiRowDef extends CdkRowDef {}\r\n\r\n/** Footer template container that contains the cell outlet. Adds the right class and role. */\r\n@Component({\r\n selector: 'oui-header-row, tr[oui-header-row]',\r\n template: CDK_ROW_TEMPLATE,\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-header-row',\r\n role: 'row',\r\n },\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n exportAs: 'ouiHeaderRow',\r\n providers: [{ provide: CdkHeaderRow, useExisting: OuiHeaderRow }],\r\n})\r\nexport class OuiHeaderRow extends CdkHeaderRow {}\r\n\r\n/** Footer template container that contains the cell outlet. Adds the right class and role. */\r\n@Component({\r\n selector: 'oui-footer-row, tr[oui-footer-row]',\r\n template: CDK_ROW_TEMPLATE,\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-footer-row',\r\n role: 'row',\r\n },\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n exportAs: 'ouiFooterRow',\r\n providers: [{ provide: CdkFooterRow, useExisting: OuiFooterRow }],\r\n})\r\nexport class OuiFooterRow extends CdkFooterRow {}\r\n\r\n/** Data row template container that contains the cell outlet. Adds the right class and role. */\r\n@Component({\r\n selector: 'oui-row, tr[oui-row]',\r\n template: CDK_ROW_TEMPLATE,\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-row',\r\n role: 'row',\r\n tabindex: '0',\r\n },\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n exportAs: 'ouiRow',\r\n providers: [{ provide: CdkRow, useExisting: OuiRow }],\r\n})\r\nexport class OuiRow extends CdkRow implements OnDestroy {\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n constructor(\r\n protected elementRef: ElementRef,\r\n protected _differs: IterableDiffers,\r\n private _focusMonitor: FocusMonitor,\r\n private _ngZone: NgZone\r\n ) {\r\n super();\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this.elementRef, true)\r\n .subscribe(() => this._ngZone.run(() => {}));\r\n }\r\n ngOnDestroy(): void {\r\n this._focusMonitor.stopMonitoring(this.elementRef);\r\n this._monitorSubscription.unsubscribe();\r\n }\r\n}\r\n", + "sourceCode": "import {\n ChangeDetectionStrategy,\n Component,\n Directive,\n ViewEncapsulation,\n OnDestroy,\n ElementRef,\n NgZone,\n IterableDiffers,\n} from '@angular/core';\nimport {\n CDK_ROW_TEMPLATE,\n CdkFooterRow,\n CdkFooterRowDef,\n CdkHeaderRow,\n CdkHeaderRowDef,\n CdkRow,\n CdkRowDef,\n} from '@angular/cdk/table';\nimport { FocusMonitor } from '@angular/cdk/a11y';\nimport { Subscription } from 'rxjs';\n\n/**\n * Header row definition for the oui-table.\n * Captures the header row's template and other header properties such as the columns to display.\n */\n@Directive({\n selector: '[ouiHeaderRowDef]',\n providers: [{ provide: CdkHeaderRowDef, useExisting: OuiHeaderRowDef }],\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\n inputs: ['columns: ouiHeaderRowDef'],\n})\nexport class OuiHeaderRowDef extends CdkHeaderRowDef {}\n\n/**\n * Footer row definition for the oui-table.\n * Captures the footer row's template and other footer properties such as the columns to display.\n */\n@Directive({\n selector: '[ouiFooterRowDef]',\n providers: [{ provide: CdkFooterRowDef, useExisting: OuiFooterRowDef }],\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\n inputs: ['columns: ouiFooterRowDef'],\n})\nexport class OuiFooterRowDef extends CdkFooterRowDef {}\n\n/**\n * Data row definition for the oui-table.\n * Captures the data row's template and other properties such as the columns to display and\n * a when predicate that describes when this row should be used.\n */\n@Directive({\n selector: '[ouiRowDef]',\n providers: [{ provide: CdkRowDef, useExisting: OuiRowDef }],\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\n inputs: ['columns: ouiRowDefColumns', 'when: ouiRowDefWhen'],\n})\nexport class OuiRowDef extends CdkRowDef {}\n\n/** Footer template container that contains the cell outlet. Adds the right class and role. */\n@Component({\n selector: 'oui-header-row, tr[oui-header-row]',\n template: CDK_ROW_TEMPLATE,\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-header-row',\n role: 'row',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n exportAs: 'ouiHeaderRow',\n providers: [{ provide: CdkHeaderRow, useExisting: OuiHeaderRow }],\n})\nexport class OuiHeaderRow extends CdkHeaderRow {}\n\n/** Footer template container that contains the cell outlet. Adds the right class and role. */\n@Component({\n selector: 'oui-footer-row, tr[oui-footer-row]',\n template: CDK_ROW_TEMPLATE,\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-footer-row',\n role: 'row',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n exportAs: 'ouiFooterRow',\n providers: [{ provide: CdkFooterRow, useExisting: OuiFooterRow }],\n})\nexport class OuiFooterRow extends CdkFooterRow {}\n\n/** Data row template container that contains the cell outlet. Adds the right class and role. */\n@Component({\n selector: 'oui-row, tr[oui-row]',\n template: CDK_ROW_TEMPLATE,\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-row',\n role: 'row',\n tabindex: '0',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n exportAs: 'ouiRow',\n providers: [{ provide: CdkRow, useExisting: OuiRow }],\n})\nexport class OuiRow extends CdkRow implements OnDestroy {\n private _monitorSubscription: Subscription = Subscription.EMPTY;\n constructor(\n protected elementRef: ElementRef,\n protected _differs: IterableDiffers,\n private _focusMonitor: FocusMonitor,\n private _ngZone: NgZone\n ) {\n super();\n this._monitorSubscription = this._focusMonitor\n .monitor(this.elementRef, true)\n .subscribe(() => this._ngZone.run(() => {}));\n }\n ngOnDestroy(): void {\n this._focusMonitor.stopMonitoring(this.elementRef);\n this._monitorSubscription.unsubscribe();\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -26746,7 +36604,7 @@ }, { "name": "OuiIconButtonStorybook", - "id": "component-OuiIconButtonStorybook-b9455c19a0fecbddcfd6553b78471c60d18cf18c67f35692a8eac5f077b5bb31e26594abab3209bf8dafc8b0365d1b441dd82fb06005a49b4992ddd82d1a2976", + "id": "component-OuiIconButtonStorybook-c89f93f9a9d641b78944a9ed4da5a770c9ce2a88429cae30825026418607664089647169c08cc70e0009a1f1504ceb00a59c36e8c162ca2ebfc641662355c2c2", "file": "ui/src/stories/button/button.component.ts", "encapsulation": [], "entryComponents": [], @@ -26770,7 +36628,7 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import { Component } from '@angular/core';\r\nimport { DomSanitizer } from '@angular/platform-browser';\r\nimport { OuiIconRegistry } from '../../components';\r\n@Component({\r\n selector: 'oui-icon-button-storybook',\r\n template: `\r\n \r\n {{ text }}\r\n \r\n `,\r\n})\r\nexport class OuiIconButtonStorybook {\r\n constructor(\r\n private ouiIconRegistry: OuiIconRegistry,\r\n private domSanitizer: DomSanitizer\r\n ) {\r\n this.ouiIconRegistry.addSvgIcon(\r\n `local`,\r\n this.domSanitizer.bypassSecurityTrustResourceUrl(\r\n `/assets/images/v-green.svg`\r\n )\r\n );\r\n\r\n this.ouiIconRegistry.addSvgIconSet(\r\n this.domSanitizer.bypassSecurityTrustResourceUrl(\r\n 'https://d1azc1qln24ryf.cloudfront.net/135790/oncehub-20/symbol-defs.svg?10k0w6'\r\n )\r\n );\r\n }\r\n}\r\n", + "sourceCode": "import { Component } from '@angular/core';\nimport { DomSanitizer } from '@angular/platform-browser';\nimport { OuiIconRegistry } from '../../components';\n@Component({\n selector: 'oui-icon-button-storybook',\n template: `\n \n {{ text }}\n \n `,\n})\nexport class OuiIconButtonStorybook {\n constructor(\n private ouiIconRegistry: OuiIconRegistry,\n private domSanitizer: DomSanitizer\n ) {\n this.ouiIconRegistry.addSvgIcon(\n `local`,\n this.domSanitizer.bypassSecurityTrustResourceUrl(\n `/assets/images/v-green.svg`\n )\n );\n\n this.ouiIconRegistry.addSvgIconSet(\n this.domSanitizer.bypassSecurityTrustResourceUrl(\n 'https://d1azc1qln24ryf.cloudfront.net/135790/oncehub-20/symbol-defs.svg?10k0w6'\n )\n );\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -26818,7 +36676,7 @@ }, { "name": "OuiiconStorybook", - "id": "component-OuiiconStorybook-bdf66b0540269f8a2bae6659e79bef5d6b7f56be3e0c604660cf543dfefdf3bc4c3039afaf11f184a607643bbbcf24ae4d68fa49c151a6ffe040d5ecc11dda29", + "id": "component-OuiiconStorybook-7bbcdc4c83da1875951fbc83aa453cf0da495b2297cfaa10e078aa3d85243768287e9c85894c4ce75c74e2ef4a01df887f918b5fd76708226f1bfb278181ccc8", "file": "ui/src/stories/icon/icon.component.ts", "encapsulation": [], "entryComponents": [], @@ -26870,7 +36728,7 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import { OuiIconRegistry } from '../../components';\r\nimport { Component, Input } from '@angular/core';\r\nimport { DomSanitizer } from '@angular/platform-browser';\r\n@Component({\r\n selector: 'oui-icon-storybook',\r\n template: `\r\n
\r\n `,\r\n})\r\nexport class OuiiconStorybook {\r\n @Input() icon = 'notification-editor';\r\n @Input() color = 'primary';\r\n @Input() size = 20;\r\n constructor(\r\n private ouiIconRegistry: OuiIconRegistry,\r\n private domSanitizer: DomSanitizer\r\n ) {\r\n this.ouiIconRegistry.addSvgIcon(\r\n `local`,\r\n this.domSanitizer.bypassSecurityTrustResourceUrl(\r\n `/assets/images/v-green.svg`\r\n )\r\n );\r\n\r\n this.ouiIconRegistry.addSvgIconSet(\r\n this.domSanitizer.bypassSecurityTrustResourceUrl(\r\n 'https://d1azc1qln24ryf.cloudfront.net/135790/oncehub-20/symbol-defs.svg?hn1bl5'\r\n )\r\n );\r\n }\r\n}\r\n", + "sourceCode": "import { OuiIconRegistry } from '../../components';\nimport { Component, Input } from '@angular/core';\nimport { DomSanitizer } from '@angular/platform-browser';\n@Component({\n selector: 'oui-icon-storybook',\n template: `\n
\n `,\n})\nexport class OuiiconStorybook {\n @Input() icon = 'notification-editor';\n @Input() color = 'primary';\n @Input() size = 20;\n constructor(\n private ouiIconRegistry: OuiIconRegistry,\n private domSanitizer: DomSanitizer\n ) {\n this.ouiIconRegistry.addSvgIcon(\n `local`,\n this.domSanitizer.bypassSecurityTrustResourceUrl(\n `/assets/images/v-green.svg`\n )\n );\n\n this.ouiIconRegistry.addSvgIconSet(\n this.domSanitizer.bypassSecurityTrustResourceUrl(\n 'https://d1azc1qln24ryf.cloudfront.net/135790/oncehub-20/symbol-defs.svg?hn1bl5'\n )\n );\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -26918,7 +36776,7 @@ }, { "name": "OuiMenu", - "id": "component-OuiMenu-2bf4d7c00cfbc06807de57f4b641c7e400989b6e616aa17d776c14ff4178db40aeb244e9c53e896484a12380ef6612ea6428b6d4e4b7ec01312e667fbd49c006", + "id": "component-OuiMenu-d33a7aaca7e12e067b9d85f9ea9297bc7bd98a4d1520aa2572190a2816b9f0d081c442f160e1071b45f8f3b345d20976e0f13ec6891c9fe77768103d2d3b4fde", "file": "ui/src/components/menu/menu-directive.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -26960,15 +36818,15 @@ "deprecationMessage": "", "jsdoctags": [ { - "pos": 5260, - "end": 5299, + "pos": 5079, + "end": 5117, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, "kind": 338, "tagName": { - "pos": 5261, - "end": 5266, + "pos": 5080, + "end": 5085, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -26977,8 +36835,8 @@ }, "comment": "

list of class names

\n", "name": { - "pos": 5267, - "end": 5274, + "pos": 5086, + "end": 5093, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -27046,15 +36904,15 @@ "description": "

Event emitted when the menu is closed.

\n", "jsdoctags": [ { - "pos": 5906, - "end": 5951, + "pos": 5701, + "end": 5745, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, "kind": 329, "tagName": { - "pos": 5907, - "end": 5917, + "pos": 5702, + "end": 5712, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -27064,15 +36922,15 @@ "comment": "

Switch to closed instead

\n" }, { - "pos": 5951, - "end": 5978, + "pos": 5745, + "end": 5771, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, "kind": 325, "tagName": { - "pos": 5952, - "end": 5967, + "pos": 5746, + "end": 5761, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -27230,15 +37088,15 @@ ], "jsdoctags": [ { - "pos": 4408, - "end": 4426, + "pos": 4256, + "end": 4273, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, "kind": 329, "tagName": { - "pos": 4409, - "end": 4419, + "pos": 4257, + "end": 4267, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -27248,15 +37106,15 @@ "comment": "" }, { - "pos": 4426, - "end": 4453, + "pos": 4273, + "end": 4299, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, "kind": 325, "tagName": { - "pos": 4427, - "end": 4442, + "pos": 4274, + "end": 4289, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -27284,15 +37142,15 @@ ], "jsdoctags": [ { - "pos": 4593, - "end": 4611, + "pos": 4432, + "end": 4449, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, "kind": 325, "tagName": { - "pos": 4594, - "end": 4606, + "pos": 4433, + "end": 4445, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -27330,15 +37188,15 @@ ], "jsdoctags": [ { - "pos": 4267, - "end": 4281, + "pos": 4122, + "end": 4136, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, "kind": 325, "tagName": { - "pos": 4268, - "end": 4280, + "pos": 4123, + "end": 4135, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -27445,8 +37303,8 @@ "jsdoctags": [ { "name": { - "pos": 7548, - "end": 7554, + "pos": 7280, + "end": 7286, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -27458,8 +37316,8 @@ "deprecationMessage": "", "defaultValue": "'program'", "tagName": { - "pos": 7542, - "end": 7547, + "pos": 7274, + "end": 7279, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -27571,8 +37429,8 @@ "jsdoctags": [ { "name": { - "pos": 9487, - "end": 9491, + "pos": 9160, + "end": 9164, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -27584,8 +37442,8 @@ "deprecationMessage": "", "defaultValue": "this.xPosition", "tagName": { - "pos": 9481, - "end": 9486, + "pos": 9154, + "end": 9159, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -27596,8 +37454,8 @@ }, { "name": { - "pos": 9544, - "end": 9548, + "pos": 9216, + "end": 9220, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -27609,8 +37467,8 @@ "deprecationMessage": "", "defaultValue": "this.yPosition", "tagName": { - "pos": 9538, - "end": 9543, + "pos": 9210, + "end": 9215, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -27629,11 +37487,11 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import { FocusKeyManager, FocusOrigin } from '@angular/cdk/a11y';\r\nimport {\r\n ESCAPE,\r\n LEFT_ARROW,\r\n DOWN_ARROW,\r\n UP_ARROW,\r\n} from '@angular/cdk/keycodes';\r\nimport {\r\n AfterContentInit,\r\n ChangeDetectionStrategy,\r\n Component,\r\n ContentChild,\r\n ContentChildren,\r\n ElementRef,\r\n EventEmitter,\r\n Inject,\r\n InjectionToken,\r\n Input,\r\n NgZone,\r\n OnDestroy,\r\n Output,\r\n TemplateRef,\r\n QueryList,\r\n ViewChild,\r\n ViewEncapsulation,\r\n OnInit,\r\n} from '@angular/core';\r\nimport { merge, Observable, Subject, Subscription } from 'rxjs';\r\nimport { startWith, switchMap, take } from 'rxjs/operators';\r\nimport { OuiMenuContent } from './menu-content';\r\nimport {\r\n throwOuiMenuInvalidPositionX,\r\n throwOuiMenuInvalidPositionY,\r\n} from './menu-errors';\r\nimport { OuiMenuItem } from './menu-item';\r\nimport { OUI_MENU_PANEL, OuiMenuPanel } from './menu-panel';\r\nimport { MenuPositionX, MenuPositionY } from './menu-positions';\r\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\r\n\r\n/** Default `oui-menu` options that can be overridden. */\r\nexport interface OuiMenuDefaultOptions {\r\n /** The x-axis position of the menu. */\r\n xPosition: MenuPositionX;\r\n\r\n /** The y-axis position of the menu. */\r\n yPosition: MenuPositionY;\r\n\r\n /** Whether the menu should overlap the menu trigger. */\r\n overlapTrigger: boolean;\r\n\r\n /** Class to be applied to the menu's backdrop. */\r\n backdropClass: string;\r\n\r\n /** Whether the menu has a backdrop. */\r\n hasBackdrop?: boolean;\r\n}\r\n\r\n/** Injection token to be used to override the default options for `oui-menu`. */\r\nexport const OUI_MENU_DEFAULT_OPTIONS =\r\n new InjectionToken('oui-menu-default-options', {\r\n providedIn: 'root',\r\n factory: OUI_MENU_DEFAULT_OPTIONS_FACTORY,\r\n });\r\n\r\n/** @docs-private */\r\nexport function OUI_MENU_DEFAULT_OPTIONS_FACTORY(): OuiMenuDefaultOptions {\r\n return {\r\n overlapTrigger: false,\r\n xPosition: 'after',\r\n yPosition: 'below',\r\n backdropClass: 'cdk-overlay-transparent-backdrop',\r\n };\r\n}\r\n\r\n@Component({\r\n selector: 'oui-menu',\r\n templateUrl: 'menu.html',\r\n styleUrls: ['menu.scss'],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n exportAs: 'ouiMenu',\r\n providers: [{ provide: OUI_MENU_PANEL, useExisting: OuiMenu }],\r\n})\r\nexport class OuiMenu\r\n implements AfterContentInit, OuiMenuPanel, OnInit, OnDestroy\r\n{\r\n private _keyManager: FocusKeyManager;\r\n private _xPosition: MenuPositionX = this._defaultOptions.xPosition;\r\n private _yPosition: MenuPositionY = this._defaultOptions.yPosition;\r\n\r\n /** Menu items inside the current menu. */\r\n private _items: OuiMenuItem[] = [];\r\n\r\n /** Emits whenever the amount of menu items changes. */\r\n private _itemChanges = new Subject();\r\n\r\n /** Subscription to tab events on the menu panel */\r\n private _tabSubscription = Subscription.EMPTY;\r\n\r\n /** Config object to be passed into the menu's ngClass */\r\n _classList: { [key: string]: boolean } = {};\r\n\r\n /** Parent menu of the current menu panel. */\r\n parentMenu: OuiMenuPanel | undefined;\r\n\r\n /** Class to be added to the backdrop element. */\r\n @Input()\r\n backdropClass: string = this._defaultOptions.backdropClass;\r\n\r\n /** Whether the menu has a backdrop. */\r\n @Input()\r\n get hasBackdrop(): boolean | undefined {\r\n return this._hasBackdrop;\r\n }\r\n set hasBackdrop(value: boolean | undefined) {\r\n this._hasBackdrop = coerceBooleanProperty(value);\r\n }\r\n private _hasBackdrop: boolean | undefined = this._defaultOptions.hasBackdrop;\r\n\r\n /** Position of the menu in the X axis. */\r\n @Input()\r\n get xPosition(): MenuPositionX {\r\n return this._xPosition;\r\n }\r\n set xPosition(value: MenuPositionX) {\r\n if (value !== 'before' && value !== 'after') {\r\n throwOuiMenuInvalidPositionX();\r\n }\r\n this._xPosition = value;\r\n this.setPositionClasses();\r\n }\r\n\r\n /** Position of the menu in the Y axis. */\r\n @Input()\r\n get yPosition(): MenuPositionY {\r\n return this._yPosition;\r\n }\r\n set yPosition(value: MenuPositionY) {\r\n if (value !== 'above' && value !== 'below') {\r\n throwOuiMenuInvalidPositionY();\r\n }\r\n this._yPosition = value;\r\n this.setPositionClasses();\r\n }\r\n\r\n /** @docs-private */\r\n @ViewChild(TemplateRef)\r\n templateRef: TemplateRef;\r\n\r\n /**\r\n * List of the items inside of a menu.\r\n *\r\n * @deprecated\r\n * @breaking-change 8.0.0\r\n */\r\n @ContentChildren(OuiMenuItem)\r\n items: QueryList;\r\n\r\n /**\r\n * Menu content that will be rendered lazily.\r\n *\r\n * @docs-private\r\n */\r\n @ContentChild(OuiMenuContent)\r\n lazyContent: OuiMenuContent;\r\n\r\n /** Whether the menu should overlap its trigger. */\r\n @Input()\r\n get overlapTrigger(): boolean {\r\n return this._overlapTrigger;\r\n }\r\n set overlapTrigger(value: boolean) {\r\n this._overlapTrigger = coerceBooleanProperty(value);\r\n }\r\n private _overlapTrigger: boolean = this._defaultOptions.overlapTrigger;\r\n\r\n /**\r\n * This method takes classes set on the host oui-menu element and applies them on the\r\n * menu template that displays in the overlay container. Otherwise, it's difficult\r\n * to style the containing menu from outside the component.\r\n *\r\n * @param classes list of class names\r\n */\r\n @Input('class')\r\n set panelClass(classes: string) {\r\n if (classes && classes.length) {\r\n this._classList = classes\r\n .split(' ')\r\n .reduce((obj: any, className: string) => {\r\n obj[className] = true;\r\n return obj;\r\n }, {});\r\n\r\n this._elementRef.nativeElement.className = '';\r\n }\r\n }\r\n\r\n /** Event emitted when the menu is closed. */\r\n @Output()\r\n readonly closed: EventEmitter =\r\n new EventEmitter();\r\n\r\n /**\r\n * Event emitted when the menu is closed.\r\n *\r\n * @deprecated Switch to `closed` instead\r\n * @breaking-change 8.0.0\r\n */\r\n @Output()\r\n close = this.closed;\r\n\r\n constructor(\r\n private _elementRef: ElementRef,\r\n private _ngZone: NgZone,\r\n @Inject(OUI_MENU_DEFAULT_OPTIONS)\r\n private _defaultOptions: OuiMenuDefaultOptions\r\n ) {}\r\n\r\n ngOnInit() {\r\n this.setPositionClasses();\r\n }\r\n\r\n ngAfterContentInit() {\r\n this._keyManager = new FocusKeyManager(this._items)\r\n .withWrap()\r\n .withTypeAhead();\r\n this._tabSubscription = this._keyManager.tabOut.subscribe(() =>\r\n this.closed.emit('tab')\r\n );\r\n }\r\n\r\n ngOnDestroy() {\r\n this._tabSubscription.unsubscribe();\r\n this.closed.complete();\r\n }\r\n\r\n /** Stream that emits whenever the hovered menu item changes. */\r\n _hovered(): Observable {\r\n return this._itemChanges.pipe(\r\n startWith(this._items),\r\n switchMap((items) => merge(...items.map((item) => item._hovered)))\r\n );\r\n }\r\n\r\n /** Handle a keyboard event from the menu, delegating to the appropriate action. */\r\n _handleKeydown(event: KeyboardEvent) {\r\n const keyCode = event.keyCode;\r\n switch (keyCode) {\r\n case ESCAPE:\r\n this.closed.emit('keydown');\r\n break;\r\n case LEFT_ARROW:\r\n if (this.parentMenu) {\r\n this.closed.emit('keydown');\r\n }\r\n break;\r\n default:\r\n if (keyCode === UP_ARROW || keyCode === DOWN_ARROW) {\r\n this._keyManager.setFocusOrigin('keyboard');\r\n }\r\n\r\n this._keyManager.onKeydown(event);\r\n }\r\n }\r\n\r\n /**\r\n * Focus the first item in the menu.\r\n *\r\n * @param origin Action from which the focus originated. Used to set the correct styling.\r\n */\r\n focusFirstItem(origin: FocusOrigin = 'program'): void {\r\n // When the content is rendered lazily, it takes a bit before the items are inside the DOM.\r\n if (this.lazyContent) {\r\n this._ngZone.onStable\r\n .asObservable()\r\n .pipe(take(1))\r\n .subscribe(() =>\r\n this._keyManager.setFocusOrigin(origin).setFirstItemActive()\r\n );\r\n } else {\r\n this._keyManager.setFocusOrigin(origin).setFirstItemActive();\r\n }\r\n }\r\n\r\n /**\r\n * Resets the active item in the menu. This is used when the menu is opened, allowing\r\n * the user to start from the first option when pressing the down arrow.\r\n */\r\n resetActiveItem() {\r\n this._keyManager.setActiveItem(-1);\r\n }\r\n\r\n /**\r\n * Registers a menu item with the menu.\r\n *\r\n * @docs-private\r\n */\r\n addItem(item: OuiMenuItem) {\r\n // We register the items through this method, rather than picking them up through\r\n // `ContentChildren`, because we need the items to be picked up by their closest\r\n // `oui-menu` ancestor. If we used `@ContentChildren(OuiMenuItem, {descendants: true})`,\r\n // all descendant items will bleed into the top-level menu in the case where the consumer\r\n // has `oui-menu` instances nested inside each other.\r\n if (this._items.indexOf(item) === -1) {\r\n this._items.push(item);\r\n this._itemChanges.next(this._items);\r\n }\r\n }\r\n\r\n /**\r\n * Removes an item from the menu.\r\n *\r\n * @docs-private\r\n */\r\n removeItem(item: OuiMenuItem) {\r\n const index = this._items.indexOf(item);\r\n\r\n if (this._items.indexOf(item) > -1) {\r\n this._items.splice(index, 1);\r\n this._itemChanges.next(this._items);\r\n }\r\n }\r\n\r\n /**\r\n * Adds classes to the menu panel based on its position. Can be used by\r\n * consumers to add specific styling based on the position.\r\n *\r\n * @param posX Position of the menu along the x axis.\r\n * @param posY Position of the menu along the y axis.\r\n * @docs-private\r\n */\r\n setPositionClasses(\r\n posX: MenuPositionX = this.xPosition,\r\n posY: MenuPositionY = this.yPosition\r\n ) {\r\n const classes = this._classList;\r\n classes['oui-menu-before'] = posX === 'before';\r\n classes['oui-menu-after'] = posX === 'after';\r\n classes['oui-menu-above'] = posY === 'above';\r\n classes['oui-menu-below'] = posY === 'below';\r\n }\r\n}\r\n", + "sourceCode": "import { FocusKeyManager, FocusOrigin } from '@angular/cdk/a11y';\nimport {\n ESCAPE,\n LEFT_ARROW,\n DOWN_ARROW,\n UP_ARROW,\n} from '@angular/cdk/keycodes';\nimport {\n AfterContentInit,\n ChangeDetectionStrategy,\n Component,\n ContentChild,\n ContentChildren,\n ElementRef,\n EventEmitter,\n Inject,\n InjectionToken,\n Input,\n NgZone,\n OnDestroy,\n Output,\n TemplateRef,\n QueryList,\n ViewChild,\n ViewEncapsulation,\n OnInit,\n} from '@angular/core';\nimport { merge, Observable, Subject, Subscription } from 'rxjs';\nimport { startWith, switchMap, take } from 'rxjs/operators';\nimport { OuiMenuContent } from './menu-content';\nimport {\n throwOuiMenuInvalidPositionX,\n throwOuiMenuInvalidPositionY,\n} from './menu-errors';\nimport { OuiMenuItem } from './menu-item';\nimport { OUI_MENU_PANEL, OuiMenuPanel } from './menu-panel';\nimport { MenuPositionX, MenuPositionY } from './menu-positions';\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\n\n/** Default `oui-menu` options that can be overridden. */\nexport interface OuiMenuDefaultOptions {\n /** The x-axis position of the menu. */\n xPosition: MenuPositionX;\n\n /** The y-axis position of the menu. */\n yPosition: MenuPositionY;\n\n /** Whether the menu should overlap the menu trigger. */\n overlapTrigger: boolean;\n\n /** Class to be applied to the menu's backdrop. */\n backdropClass: string;\n\n /** Whether the menu has a backdrop. */\n hasBackdrop?: boolean;\n}\n\n/** Injection token to be used to override the default options for `oui-menu`. */\nexport const OUI_MENU_DEFAULT_OPTIONS =\n new InjectionToken('oui-menu-default-options', {\n providedIn: 'root',\n factory: OUI_MENU_DEFAULT_OPTIONS_FACTORY,\n });\n\n/** @docs-private */\nexport function OUI_MENU_DEFAULT_OPTIONS_FACTORY(): OuiMenuDefaultOptions {\n return {\n overlapTrigger: false,\n xPosition: 'after',\n yPosition: 'below',\n backdropClass: 'cdk-overlay-transparent-backdrop',\n };\n}\n\n@Component({\n selector: 'oui-menu',\n templateUrl: 'menu.html',\n styleUrls: ['menu.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n exportAs: 'ouiMenu',\n providers: [{ provide: OUI_MENU_PANEL, useExisting: OuiMenu }],\n})\nexport class OuiMenu\n implements AfterContentInit, OuiMenuPanel, OnInit, OnDestroy\n{\n private _keyManager: FocusKeyManager;\n private _xPosition: MenuPositionX = this._defaultOptions.xPosition;\n private _yPosition: MenuPositionY = this._defaultOptions.yPosition;\n\n /** Menu items inside the current menu. */\n private _items: OuiMenuItem[] = [];\n\n /** Emits whenever the amount of menu items changes. */\n private _itemChanges = new Subject();\n\n /** Subscription to tab events on the menu panel */\n private _tabSubscription = Subscription.EMPTY;\n\n /** Config object to be passed into the menu's ngClass */\n _classList: { [key: string]: boolean } = {};\n\n /** Parent menu of the current menu panel. */\n parentMenu: OuiMenuPanel | undefined;\n\n /** Class to be added to the backdrop element. */\n @Input()\n backdropClass: string = this._defaultOptions.backdropClass;\n\n /** Whether the menu has a backdrop. */\n @Input()\n get hasBackdrop(): boolean | undefined {\n return this._hasBackdrop;\n }\n set hasBackdrop(value: boolean | undefined) {\n this._hasBackdrop = coerceBooleanProperty(value);\n }\n private _hasBackdrop: boolean | undefined = this._defaultOptions.hasBackdrop;\n\n /** Position of the menu in the X axis. */\n @Input()\n get xPosition(): MenuPositionX {\n return this._xPosition;\n }\n set xPosition(value: MenuPositionX) {\n if (value !== 'before' && value !== 'after') {\n throwOuiMenuInvalidPositionX();\n }\n this._xPosition = value;\n this.setPositionClasses();\n }\n\n /** Position of the menu in the Y axis. */\n @Input()\n get yPosition(): MenuPositionY {\n return this._yPosition;\n }\n set yPosition(value: MenuPositionY) {\n if (value !== 'above' && value !== 'below') {\n throwOuiMenuInvalidPositionY();\n }\n this._yPosition = value;\n this.setPositionClasses();\n }\n\n /** @docs-private */\n @ViewChild(TemplateRef)\n templateRef: TemplateRef;\n\n /**\n * List of the items inside of a menu.\n *\n * @deprecated\n * @breaking-change 8.0.0\n */\n @ContentChildren(OuiMenuItem)\n items: QueryList;\n\n /**\n * Menu content that will be rendered lazily.\n *\n * @docs-private\n */\n @ContentChild(OuiMenuContent)\n lazyContent: OuiMenuContent;\n\n /** Whether the menu should overlap its trigger. */\n @Input()\n get overlapTrigger(): boolean {\n return this._overlapTrigger;\n }\n set overlapTrigger(value: boolean) {\n this._overlapTrigger = coerceBooleanProperty(value);\n }\n private _overlapTrigger: boolean = this._defaultOptions.overlapTrigger;\n\n /**\n * This method takes classes set on the host oui-menu element and applies them on the\n * menu template that displays in the overlay container. Otherwise, it's difficult\n * to style the containing menu from outside the component.\n *\n * @param classes list of class names\n */\n @Input('class')\n set panelClass(classes: string) {\n if (classes && classes.length) {\n this._classList = classes\n .split(' ')\n .reduce((obj: any, className: string) => {\n obj[className] = true;\n return obj;\n }, {});\n\n this._elementRef.nativeElement.className = '';\n }\n }\n\n /** Event emitted when the menu is closed. */\n @Output()\n readonly closed: EventEmitter =\n new EventEmitter();\n\n /**\n * Event emitted when the menu is closed.\n *\n * @deprecated Switch to `closed` instead\n * @breaking-change 8.0.0\n */\n @Output()\n close = this.closed;\n\n constructor(\n private _elementRef: ElementRef,\n private _ngZone: NgZone,\n @Inject(OUI_MENU_DEFAULT_OPTIONS)\n private _defaultOptions: OuiMenuDefaultOptions\n ) {}\n\n ngOnInit() {\n this.setPositionClasses();\n }\n\n ngAfterContentInit() {\n this._keyManager = new FocusKeyManager(this._items)\n .withWrap()\n .withTypeAhead();\n this._tabSubscription = this._keyManager.tabOut.subscribe(() =>\n this.closed.emit('tab')\n );\n }\n\n ngOnDestroy() {\n this._tabSubscription.unsubscribe();\n this.closed.complete();\n }\n\n /** Stream that emits whenever the hovered menu item changes. */\n _hovered(): Observable {\n return this._itemChanges.pipe(\n startWith(this._items),\n switchMap((items) => merge(...items.map((item) => item._hovered)))\n );\n }\n\n /** Handle a keyboard event from the menu, delegating to the appropriate action. */\n _handleKeydown(event: KeyboardEvent) {\n const keyCode = event.keyCode;\n switch (keyCode) {\n case ESCAPE:\n this.closed.emit('keydown');\n break;\n case LEFT_ARROW:\n if (this.parentMenu) {\n this.closed.emit('keydown');\n }\n break;\n default:\n if (keyCode === UP_ARROW || keyCode === DOWN_ARROW) {\n this._keyManager.setFocusOrigin('keyboard');\n }\n\n this._keyManager.onKeydown(event);\n }\n }\n\n /**\n * Focus the first item in the menu.\n *\n * @param origin Action from which the focus originated. Used to set the correct styling.\n */\n focusFirstItem(origin: FocusOrigin = 'program'): void {\n // When the content is rendered lazily, it takes a bit before the items are inside the DOM.\n if (this.lazyContent) {\n this._ngZone.onStable\n .asObservable()\n .pipe(take(1))\n .subscribe(() =>\n this._keyManager.setFocusOrigin(origin).setFirstItemActive()\n );\n } else {\n this._keyManager.setFocusOrigin(origin).setFirstItemActive();\n }\n }\n\n /**\n * Resets the active item in the menu. This is used when the menu is opened, allowing\n * the user to start from the first option when pressing the down arrow.\n */\n resetActiveItem() {\n this._keyManager.setActiveItem(-1);\n }\n\n /**\n * Registers a menu item with the menu.\n *\n * @docs-private\n */\n addItem(item: OuiMenuItem) {\n // We register the items through this method, rather than picking them up through\n // `ContentChildren`, because we need the items to be picked up by their closest\n // `oui-menu` ancestor. If we used `@ContentChildren(OuiMenuItem, {descendants: true})`,\n // all descendant items will bleed into the top-level menu in the case where the consumer\n // has `oui-menu` instances nested inside each other.\n if (this._items.indexOf(item) === -1) {\n this._items.push(item);\n this._itemChanges.next(this._items);\n }\n }\n\n /**\n * Removes an item from the menu.\n *\n * @docs-private\n */\n removeItem(item: OuiMenuItem) {\n const index = this._items.indexOf(item);\n\n if (this._items.indexOf(item) > -1) {\n this._items.splice(index, 1);\n this._itemChanges.next(this._items);\n }\n }\n\n /**\n * Adds classes to the menu panel based on its position. Can be used by\n * consumers to add specific styling based on the position.\n *\n * @param posX Position of the menu along the x axis.\n * @param posY Position of the menu along the y axis.\n * @docs-private\n */\n setPositionClasses(\n posX: MenuPositionX = this.xPosition,\n posY: MenuPositionY = this.yPosition\n ) {\n const classes = this._classList;\n classes['oui-menu-before'] = posX === 'before';\n classes['oui-menu-after'] = posX === 'after';\n classes['oui-menu-above'] = posY === 'above';\n classes['oui-menu-below'] = posY === 'below';\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": "@import './menu-theme';\r\n\r\n// menu width must be a multiple of 56px\r\n$oui-menu-overlay-min-width: 205px !default; // 56 * 2\r\n$oui-menu-overlay-max-width: 244px !default; // 56 * 5\r\n\r\n$oui-menu-item-height: 48px !default;\r\n$oui-menu-side-padding: 16px !default;\r\n$oui-menu-icon-margin: 16px !default;\r\n\r\n$oui-menu-border-radius: 4px !default;\r\n\r\n$oui-menu-z-index: 9999;\r\n\r\n$oui-menu-border-color: 1px solid #c8c8c8;\r\n\r\n/*box shadow and border as a boxshadow(good for zoom-in and zoom-out in browsers) */\r\n$oui-menu-box-shadow: 0px 1px 3px 0px #4a4a4a;\r\n$oui-menu-border-box-shadow: 0 0 0 1px #c8c8c8;\r\n\r\n/*font size and line-height*/\r\n$oui-menu-font-size: 13px;\r\n$oui-menu-line-height: 22px;\r\n\r\n/*reset the browser default properties*/\r\n@mixin oui-button-reset {\r\n border: 0 none;\r\n background: none;\r\n font-family: 'Open Sans', Helvetica, Arial, sans-serif;\r\n &:focus {\r\n outline: 0 none;\r\n }\r\n // The `outline: none` from above works on all browsers, however Firefox also\r\n // adds a special `focus-inner` which we have to disable explicitly. See:\r\n // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Firefox\r\n &::-moz-focus-inner {\r\n border: 0;\r\n }\r\n}\r\n\r\n@mixin oui-menu-base() {\r\n //min-width: $oui-menu-overlay-min-width;\r\n max-width: $oui-menu-overlay-max-width;\r\n overflow: auto;\r\n}\r\n\r\n.oui-menu-panel {\r\n @include oui-menu-base();\r\n max-height: calc(100vh - #{$oui-menu-item-height});\r\n background-color: #fff;\r\n box-shadow: $oui-menu-box-shadow;\r\n outline: 0;\r\n z-index: $oui-menu-z-index;\r\n font-size: $oui-menu-font-size;\r\n line-height: $oui-menu-line-height;\r\n}\r\n\r\n.oui-menu-content {\r\n //box-shadow: $oui-menu-border-box-shadow;\r\n padding: 10px 0px;\r\n //added border\r\n border: $oui-menu-border-color;\r\n overflow: hidden;\r\n}\r\n\r\n@mixin oui-menu-item-base() {\r\n //white-space: nowrap;\r\n font-size: 13px;\r\n font-family: 'Open Sans', Helvetica, Arial, sans-serif;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n // Needs to be a block for the ellipsis to work.\r\n display: block;\r\n line-height: $oui-menu-line-height;\r\n padding: 0 $oui-menu-side-padding;\r\n text-align: left;\r\n text-decoration: none; // necessary to reset anchor tags\r\n // Required for Edge not to show scrollbars when setting the width manually. See #12112.\r\n max-width: 100%;\r\n width: 100%;\r\n\r\n &[disabled] {\r\n cursor: default;\r\n }\r\n\r\n .oui-icon {\r\n display: inline-block;\r\n vertical-align: top;\r\n width: 20px;\r\n height: 20px;\r\n margin-right: 8px;\r\n text-align: center;\r\n margin-top: 1px;\r\n svg {\r\n vertical-align: top;\r\n }\r\n }\r\n}\r\n\r\n.oui-menu-item {\r\n @include oui-button-reset();\r\n @include oui-menu-item-base();\r\n position: relative;\r\n padding: 8px 8px;\r\n display: block;\r\n color: #333;\r\n cursor: pointer;\r\n position: relative;\r\n min-width: $oui-menu-overlay-min-width;\r\n max-width: $oui-menu-overlay-max-width;\r\n width: 100%;\r\n\r\n span {\r\n display: inline-block;\r\n max-width: 200px;\r\n vertical-align: top;\r\n line-height: 20px;\r\n margin-top: 1px;\r\n }\r\n}\r\n\r\n.oui-menu-item:hover,\r\n.oui-menu-item.cdk-program-focused,\r\n.oui-menu-item.cdk-keyboard-focused,\r\n.oui-menu-item-highlighted {\r\n &:not([disabled]) {\r\n background: #eeeeee;\r\n text-decoration: none;\r\n }\r\n}\r\n\r\n@mixin oui-menu-icon-hover {\r\n background-color: rgba(200, 200, 200, 0.4);\r\n border-radius: 2px;\r\n transition: opacity 0.2s cubic-bezier(0.35, 0, 0.25, 1),\r\n background-color 0.2s cubic-bezier(0.35, 0, 0.25, 1);\r\n cursor: pointer;\r\n}\r\n\r\n.oui-menu-icon-container {\r\n height: 20px;\r\n}\r\n.oui-menu-icon {\r\n svg {\r\n padding: 2px;\r\n pointer-events: auto !important;\r\n }\r\n}\r\n.oui-menu-icon,\r\n.oui-menu-icon-hover {\r\n display: inline-block;\r\n margin: 0;\r\n &:hover {\r\n @include oui-menu-icon-hover();\r\n }\r\n}\r\n.oui-menu-icon-hover {\r\n @include oui-menu-icon-hover();\r\n}\r\n.oui-menu-icon-container.oui-menu-icon-vertical {\r\n display: block;\r\n height: 24px;\r\n .oui-menu-icon {\r\n transform: rotate(90deg);\r\n }\r\n}\r\n\r\nbody {\r\n -ms-overflow-style: -ms-autohiding-scrollbar;\r\n}\r\n", + "data": "@import './menu-theme';\n\n// menu width must be a multiple of 56px\n$oui-menu-overlay-min-width: 205px !default; // 56 * 2\n$oui-menu-overlay-max-width: 244px !default; // 56 * 5\n\n$oui-menu-item-height: 48px !default;\n$oui-menu-side-padding: 16px !default;\n$oui-menu-icon-margin: 16px !default;\n\n$oui-menu-border-radius: 4px !default;\n\n$oui-menu-z-index: 9999;\n\n$oui-menu-border-color: 1px solid #c8c8c8;\n\n/*box shadow and border as a boxshadow(good for zoom-in and zoom-out in browsers) */\n$oui-menu-box-shadow: 0px 1px 3px 0px #4a4a4a;\n$oui-menu-border-box-shadow: 0 0 0 1px #c8c8c8;\n\n/*font size and line-height*/\n$oui-menu-font-size: 13px;\n$oui-menu-line-height: 22px;\n\n/*reset the browser default properties*/\n@mixin oui-button-reset {\n border: 0 none;\n background: none;\n font-family: 'Open Sans', Helvetica, Arial, sans-serif;\n &:focus {\n outline: 0 none;\n }\n // The `outline: none` from above works on all browsers, however Firefox also\n // adds a special `focus-inner` which we have to disable explicitly. See:\n // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Firefox\n &::-moz-focus-inner {\n border: 0;\n }\n}\n\n@mixin oui-menu-base() {\n //min-width: $oui-menu-overlay-min-width;\n max-width: $oui-menu-overlay-max-width;\n overflow: auto;\n}\n\n.oui-menu-panel {\n @include oui-menu-base();\n max-height: calc(100vh - #{$oui-menu-item-height});\n background-color: #fff;\n box-shadow: $oui-menu-box-shadow;\n outline: 0;\n z-index: $oui-menu-z-index;\n font-size: $oui-menu-font-size;\n line-height: $oui-menu-line-height;\n}\n\n.oui-menu-content {\n //box-shadow: $oui-menu-border-box-shadow;\n padding: 10px 0px;\n //added border\n border: $oui-menu-border-color;\n overflow: hidden;\n}\n\n@mixin oui-menu-item-base() {\n //white-space: nowrap;\n font-size: 13px;\n font-family: 'Open Sans', Helvetica, Arial, sans-serif;\n overflow: hidden;\n text-overflow: ellipsis;\n // Needs to be a block for the ellipsis to work.\n display: block;\n line-height: $oui-menu-line-height;\n padding: 0 $oui-menu-side-padding;\n text-align: left;\n text-decoration: none; // necessary to reset anchor tags\n // Required for Edge not to show scrollbars when setting the width manually. See #12112.\n max-width: 100%;\n width: 100%;\n\n &[disabled] {\n cursor: default;\n }\n\n .oui-icon {\n display: inline-block;\n vertical-align: top;\n width: 20px;\n height: 20px;\n margin-right: 8px;\n text-align: center;\n margin-top: 1px;\n svg {\n vertical-align: top;\n }\n }\n}\n\n.oui-menu-item {\n @include oui-button-reset();\n @include oui-menu-item-base();\n position: relative;\n padding: 8px 8px;\n display: block;\n color: #333;\n cursor: pointer;\n position: relative;\n min-width: $oui-menu-overlay-min-width;\n max-width: $oui-menu-overlay-max-width;\n width: 100%;\n\n span {\n display: inline-block;\n max-width: 200px;\n vertical-align: top;\n line-height: 20px;\n margin-top: 1px;\n }\n}\n\n.oui-menu-item:hover,\n.oui-menu-item.cdk-program-focused,\n.oui-menu-item.cdk-keyboard-focused,\n.oui-menu-item-highlighted {\n &:not([disabled]) {\n background: #eeeeee;\n text-decoration: none;\n }\n}\n\n@mixin oui-menu-icon-hover {\n background-color: rgba(200, 200, 200, 0.4);\n border-radius: 2px;\n transition: opacity 0.2s cubic-bezier(0.35, 0, 0.25, 1),\n background-color 0.2s cubic-bezier(0.35, 0, 0.25, 1);\n cursor: pointer;\n}\n\n.oui-menu-icon-container {\n height: 20px;\n}\n.oui-menu-icon {\n svg {\n padding: 2px;\n pointer-events: auto !important;\n }\n}\n.oui-menu-icon,\n.oui-menu-icon-hover {\n display: inline-block;\n margin: 0;\n &:hover {\n @include oui-menu-icon-hover();\n }\n}\n.oui-menu-icon-hover {\n @include oui-menu-icon-hover();\n}\n.oui-menu-icon-container.oui-menu-icon-vertical {\n display: block;\n height: 24px;\n .oui-menu-icon {\n transform: rotate(90deg);\n }\n}\n\nbody {\n -ms-overflow-style: -ms-autohiding-scrollbar;\n}\n", "styleUrl": "menu.scss" } ], @@ -27875,8 +37733,8 @@ "jsdoctags": [ { "name": { - "pos": 5267, - "end": 5274, + "pos": 5086, + "end": 5093, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -27887,8 +37745,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 5261, - "end": 5266, + "pos": 5080, + "end": 5085, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -27901,11 +37759,11 @@ } } }, - "templateData": "\r\n \r\n
\r\n \r\n
\r\n" + "templateData": "\n \n
\n \n
\n" }, { "name": "OuiMenuIcon", - "id": "component-OuiMenuIcon-0cd9a59f988860723b8ddcba8d15175b3309c73c3344e36a920f7d32685464a3dbe0d9dc2441e504048ae002f7c5f209247eeebd90d26e19b9c20021b9a1cb30", + "id": "component-OuiMenuIcon-f10c2f20189f975406c0eeda35ed3067128a127bd10d1976ebd73f2cb7ba0bcc9222bd188a57128bc3a6d573f80acf507147a4b9f83bc7e5c91068aad380222c", "file": "ui/src/components/menu/menu-icon.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -28013,11 +37871,11 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import {\r\n Component,\r\n ChangeDetectionStrategy,\r\n ViewEncapsulation,\r\n ElementRef,\r\n AfterContentInit,\r\n Input,\r\n OnChanges,\r\n} from '@angular/core';\r\nimport { OuiIconRegistry } from '../icon/icon-registery';\r\nimport { DomSanitizer } from '@angular/platform-browser';\r\nimport { ICONS } from '../core/shared/icons';\r\n\r\n@Component({\r\n selector: 'oui-menu-icon',\r\n template: `\r\n
\r\n \r\n
\r\n `,\r\n styleUrls: ['menu.scss'],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n exportAs: 'ouiMenuIcon',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n '(menuOpened)': 'menuOpened()',\r\n '(menuClosed)': 'menuClosed()',\r\n },\r\n})\r\nexport class OuiMenuIcon implements AfterContentInit, OnChanges {\r\n private _iconDiv: HTMLDivElement;\r\n @Input() vertical = false;\r\n constructor(\r\n private _elementRef: ElementRef,\r\n private ouiIconRegistry: OuiIconRegistry,\r\n private domSanitizer: DomSanitizer\r\n ) {\r\n this.ouiIconRegistry.addSvgIconLiteral(\r\n `menu-icon`,\r\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.THREE_DOT_MENU_ICON)\r\n );\r\n }\r\n\r\n ngAfterContentInit() {\r\n this._iconDiv =\r\n this._elementRef.nativeElement.querySelector('.oui-menu-icon');\r\n this._transformIcon();\r\n }\r\n\r\n ngOnChanges() {\r\n this._transformIcon();\r\n }\r\n\r\n private _transformIcon() {\r\n if (!this._iconDiv) {\r\n return;\r\n }\r\n // Added tabindex to make menu icon keyboard accessible.\r\n this._iconDiv.parentElement.parentElement.setAttribute('tabindex', '0');\r\n if (this.vertical) {\r\n this._iconDiv.parentElement.classList.add('oui-menu-icon-vertical');\r\n } else {\r\n this._iconDiv.parentElement.classList.remove('oui-menu-icon-vertical');\r\n }\r\n }\r\n\r\n menuOpened() {\r\n this._iconDiv.classList.add('oui-menu-icon-hover');\r\n }\r\n\r\n menuClosed() {\r\n this._iconDiv.classList.remove('oui-menu-icon-hover');\r\n }\r\n}\r\n", + "sourceCode": "import {\n Component,\n ChangeDetectionStrategy,\n ViewEncapsulation,\n ElementRef,\n AfterContentInit,\n Input,\n OnChanges,\n} from '@angular/core';\nimport { OuiIconRegistry } from '../icon/icon-registery';\nimport { DomSanitizer } from '@angular/platform-browser';\nimport { ICONS } from '../core/shared/icons';\n\n@Component({\n selector: 'oui-menu-icon',\n template: `\n
\n \n
\n `,\n styleUrls: ['menu.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n exportAs: 'ouiMenuIcon',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n '(menuOpened)': 'menuOpened()',\n '(menuClosed)': 'menuClosed()',\n },\n})\nexport class OuiMenuIcon implements AfterContentInit, OnChanges {\n private _iconDiv: HTMLDivElement;\n @Input() vertical = false;\n constructor(\n private _elementRef: ElementRef,\n private ouiIconRegistry: OuiIconRegistry,\n private domSanitizer: DomSanitizer\n ) {\n this.ouiIconRegistry.addSvgIconLiteral(\n `menu-icon`,\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.THREE_DOT_MENU_ICON)\n );\n }\n\n ngAfterContentInit() {\n this._iconDiv =\n this._elementRef.nativeElement.querySelector('.oui-menu-icon');\n this._transformIcon();\n }\n\n ngOnChanges() {\n this._transformIcon();\n }\n\n private _transformIcon() {\n if (!this._iconDiv) {\n return;\n }\n // Added tabindex to make menu icon keyboard accessible.\n this._iconDiv.parentElement.parentElement.setAttribute('tabindex', '0');\n if (this.vertical) {\n this._iconDiv.parentElement.classList.add('oui-menu-icon-vertical');\n } else {\n this._iconDiv.parentElement.classList.remove('oui-menu-icon-vertical');\n }\n }\n\n menuOpened() {\n this._iconDiv.classList.add('oui-menu-icon-hover');\n }\n\n menuClosed() {\n this._iconDiv.classList.remove('oui-menu-icon-hover');\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": "@import './menu-theme';\r\n\r\n// menu width must be a multiple of 56px\r\n$oui-menu-overlay-min-width: 205px !default; // 56 * 2\r\n$oui-menu-overlay-max-width: 244px !default; // 56 * 5\r\n\r\n$oui-menu-item-height: 48px !default;\r\n$oui-menu-side-padding: 16px !default;\r\n$oui-menu-icon-margin: 16px !default;\r\n\r\n$oui-menu-border-radius: 4px !default;\r\n\r\n$oui-menu-z-index: 9999;\r\n\r\n$oui-menu-border-color: 1px solid #c8c8c8;\r\n\r\n/*box shadow and border as a boxshadow(good for zoom-in and zoom-out in browsers) */\r\n$oui-menu-box-shadow: 0px 1px 3px 0px #4a4a4a;\r\n$oui-menu-border-box-shadow: 0 0 0 1px #c8c8c8;\r\n\r\n/*font size and line-height*/\r\n$oui-menu-font-size: 13px;\r\n$oui-menu-line-height: 22px;\r\n\r\n/*reset the browser default properties*/\r\n@mixin oui-button-reset {\r\n border: 0 none;\r\n background: none;\r\n font-family: 'Open Sans', Helvetica, Arial, sans-serif;\r\n &:focus {\r\n outline: 0 none;\r\n }\r\n // The `outline: none` from above works on all browsers, however Firefox also\r\n // adds a special `focus-inner` which we have to disable explicitly. See:\r\n // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Firefox\r\n &::-moz-focus-inner {\r\n border: 0;\r\n }\r\n}\r\n\r\n@mixin oui-menu-base() {\r\n //min-width: $oui-menu-overlay-min-width;\r\n max-width: $oui-menu-overlay-max-width;\r\n overflow: auto;\r\n}\r\n\r\n.oui-menu-panel {\r\n @include oui-menu-base();\r\n max-height: calc(100vh - #{$oui-menu-item-height});\r\n background-color: #fff;\r\n box-shadow: $oui-menu-box-shadow;\r\n outline: 0;\r\n z-index: $oui-menu-z-index;\r\n font-size: $oui-menu-font-size;\r\n line-height: $oui-menu-line-height;\r\n}\r\n\r\n.oui-menu-content {\r\n //box-shadow: $oui-menu-border-box-shadow;\r\n padding: 10px 0px;\r\n //added border\r\n border: $oui-menu-border-color;\r\n overflow: hidden;\r\n}\r\n\r\n@mixin oui-menu-item-base() {\r\n //white-space: nowrap;\r\n font-size: 13px;\r\n font-family: 'Open Sans', Helvetica, Arial, sans-serif;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n // Needs to be a block for the ellipsis to work.\r\n display: block;\r\n line-height: $oui-menu-line-height;\r\n padding: 0 $oui-menu-side-padding;\r\n text-align: left;\r\n text-decoration: none; // necessary to reset anchor tags\r\n // Required for Edge not to show scrollbars when setting the width manually. See #12112.\r\n max-width: 100%;\r\n width: 100%;\r\n\r\n &[disabled] {\r\n cursor: default;\r\n }\r\n\r\n .oui-icon {\r\n display: inline-block;\r\n vertical-align: top;\r\n width: 20px;\r\n height: 20px;\r\n margin-right: 8px;\r\n text-align: center;\r\n margin-top: 1px;\r\n svg {\r\n vertical-align: top;\r\n }\r\n }\r\n}\r\n\r\n.oui-menu-item {\r\n @include oui-button-reset();\r\n @include oui-menu-item-base();\r\n position: relative;\r\n padding: 8px 8px;\r\n display: block;\r\n color: #333;\r\n cursor: pointer;\r\n position: relative;\r\n min-width: $oui-menu-overlay-min-width;\r\n max-width: $oui-menu-overlay-max-width;\r\n width: 100%;\r\n\r\n span {\r\n display: inline-block;\r\n max-width: 200px;\r\n vertical-align: top;\r\n line-height: 20px;\r\n margin-top: 1px;\r\n }\r\n}\r\n\r\n.oui-menu-item:hover,\r\n.oui-menu-item.cdk-program-focused,\r\n.oui-menu-item.cdk-keyboard-focused,\r\n.oui-menu-item-highlighted {\r\n &:not([disabled]) {\r\n background: #eeeeee;\r\n text-decoration: none;\r\n }\r\n}\r\n\r\n@mixin oui-menu-icon-hover {\r\n background-color: rgba(200, 200, 200, 0.4);\r\n border-radius: 2px;\r\n transition: opacity 0.2s cubic-bezier(0.35, 0, 0.25, 1),\r\n background-color 0.2s cubic-bezier(0.35, 0, 0.25, 1);\r\n cursor: pointer;\r\n}\r\n\r\n.oui-menu-icon-container {\r\n height: 20px;\r\n}\r\n.oui-menu-icon {\r\n svg {\r\n padding: 2px;\r\n pointer-events: auto !important;\r\n }\r\n}\r\n.oui-menu-icon,\r\n.oui-menu-icon-hover {\r\n display: inline-block;\r\n margin: 0;\r\n &:hover {\r\n @include oui-menu-icon-hover();\r\n }\r\n}\r\n.oui-menu-icon-hover {\r\n @include oui-menu-icon-hover();\r\n}\r\n.oui-menu-icon-container.oui-menu-icon-vertical {\r\n display: block;\r\n height: 24px;\r\n .oui-menu-icon {\r\n transform: rotate(90deg);\r\n }\r\n}\r\n\r\nbody {\r\n -ms-overflow-style: -ms-autohiding-scrollbar;\r\n}\r\n", + "data": "@import './menu-theme';\n\n// menu width must be a multiple of 56px\n$oui-menu-overlay-min-width: 205px !default; // 56 * 2\n$oui-menu-overlay-max-width: 244px !default; // 56 * 5\n\n$oui-menu-item-height: 48px !default;\n$oui-menu-side-padding: 16px !default;\n$oui-menu-icon-margin: 16px !default;\n\n$oui-menu-border-radius: 4px !default;\n\n$oui-menu-z-index: 9999;\n\n$oui-menu-border-color: 1px solid #c8c8c8;\n\n/*box shadow and border as a boxshadow(good for zoom-in and zoom-out in browsers) */\n$oui-menu-box-shadow: 0px 1px 3px 0px #4a4a4a;\n$oui-menu-border-box-shadow: 0 0 0 1px #c8c8c8;\n\n/*font size and line-height*/\n$oui-menu-font-size: 13px;\n$oui-menu-line-height: 22px;\n\n/*reset the browser default properties*/\n@mixin oui-button-reset {\n border: 0 none;\n background: none;\n font-family: 'Open Sans', Helvetica, Arial, sans-serif;\n &:focus {\n outline: 0 none;\n }\n // The `outline: none` from above works on all browsers, however Firefox also\n // adds a special `focus-inner` which we have to disable explicitly. See:\n // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Firefox\n &::-moz-focus-inner {\n border: 0;\n }\n}\n\n@mixin oui-menu-base() {\n //min-width: $oui-menu-overlay-min-width;\n max-width: $oui-menu-overlay-max-width;\n overflow: auto;\n}\n\n.oui-menu-panel {\n @include oui-menu-base();\n max-height: calc(100vh - #{$oui-menu-item-height});\n background-color: #fff;\n box-shadow: $oui-menu-box-shadow;\n outline: 0;\n z-index: $oui-menu-z-index;\n font-size: $oui-menu-font-size;\n line-height: $oui-menu-line-height;\n}\n\n.oui-menu-content {\n //box-shadow: $oui-menu-border-box-shadow;\n padding: 10px 0px;\n //added border\n border: $oui-menu-border-color;\n overflow: hidden;\n}\n\n@mixin oui-menu-item-base() {\n //white-space: nowrap;\n font-size: 13px;\n font-family: 'Open Sans', Helvetica, Arial, sans-serif;\n overflow: hidden;\n text-overflow: ellipsis;\n // Needs to be a block for the ellipsis to work.\n display: block;\n line-height: $oui-menu-line-height;\n padding: 0 $oui-menu-side-padding;\n text-align: left;\n text-decoration: none; // necessary to reset anchor tags\n // Required for Edge not to show scrollbars when setting the width manually. See #12112.\n max-width: 100%;\n width: 100%;\n\n &[disabled] {\n cursor: default;\n }\n\n .oui-icon {\n display: inline-block;\n vertical-align: top;\n width: 20px;\n height: 20px;\n margin-right: 8px;\n text-align: center;\n margin-top: 1px;\n svg {\n vertical-align: top;\n }\n }\n}\n\n.oui-menu-item {\n @include oui-button-reset();\n @include oui-menu-item-base();\n position: relative;\n padding: 8px 8px;\n display: block;\n color: #333;\n cursor: pointer;\n position: relative;\n min-width: $oui-menu-overlay-min-width;\n max-width: $oui-menu-overlay-max-width;\n width: 100%;\n\n span {\n display: inline-block;\n max-width: 200px;\n vertical-align: top;\n line-height: 20px;\n margin-top: 1px;\n }\n}\n\n.oui-menu-item:hover,\n.oui-menu-item.cdk-program-focused,\n.oui-menu-item.cdk-keyboard-focused,\n.oui-menu-item-highlighted {\n &:not([disabled]) {\n background: #eeeeee;\n text-decoration: none;\n }\n}\n\n@mixin oui-menu-icon-hover {\n background-color: rgba(200, 200, 200, 0.4);\n border-radius: 2px;\n transition: opacity 0.2s cubic-bezier(0.35, 0, 0.25, 1),\n background-color 0.2s cubic-bezier(0.35, 0, 0.25, 1);\n cursor: pointer;\n}\n\n.oui-menu-icon-container {\n height: 20px;\n}\n.oui-menu-icon {\n svg {\n padding: 2px;\n pointer-events: auto !important;\n }\n}\n.oui-menu-icon,\n.oui-menu-icon-hover {\n display: inline-block;\n margin: 0;\n &:hover {\n @include oui-menu-icon-hover();\n }\n}\n.oui-menu-icon-hover {\n @include oui-menu-icon-hover();\n}\n.oui-menu-icon-container.oui-menu-icon-vertical {\n display: block;\n height: 24px;\n .oui-menu-icon {\n transform: rotate(90deg);\n }\n}\n\nbody {\n -ms-overflow-style: -ms-autohiding-scrollbar;\n}\n", "styleUrl": "menu.scss" } ], @@ -28085,7 +37943,7 @@ }, { "name": "OuiMenuItem", - "id": "component-OuiMenuItem-e9f79b5087349c3546b8b737e1f5c66562253190ac13c37879bd0438a38ab0c0f21f03e3f8c4431054ef73bbf27134ca0e289145e5c6dcc319acc918f852280b", + "id": "component-OuiMenuItem-478a21ca838e35cf24eecfc1c056e55ec78c1582d0170d3e66fda3b72f172aa5214ad5535eb96fcc9630e889d231bd412723540ca09642d93d01ebc19268901d", "file": "ui/src/components/menu/menu-item.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -28299,7 +38157,7 @@ "description": "

This directive is intended to be used inside an oui-menu tag.\nIt exists mostly to set the role attribute.

\n", "rawdescription": "\n\nThis directive is intended to be used inside an oui-menu tag.\nIt exists mostly to set the role attribute.\n", "type": "component", - "sourceCode": "import { FocusableOption, FocusMonitor, FocusOrigin } from '@angular/cdk/a11y';\r\nimport {\r\n ChangeDetectionStrategy,\r\n Component,\r\n ElementRef,\r\n OnDestroy,\r\n ViewEncapsulation,\r\n Inject,\r\n Optional,\r\n Input,\r\n} from '@angular/core';\r\nimport { CanDisable, CanDisableCtor, mixinDisabled } from '../core';\r\nimport { Subject } from 'rxjs';\r\nimport { DOCUMENT } from '@angular/common';\r\nimport { OUI_MENU_PANEL, OuiMenuPanel } from './menu-panel';\r\n\r\n// Boilerplate for applying mixins to OuiMenuItem.\r\n\r\nexport class OuiMenuItemBase {}\r\nexport const _OuiMenuItemMixinBase: CanDisableCtor & typeof OuiMenuItemBase =\r\n mixinDisabled(OuiMenuItemBase);\r\n\r\n/**\r\n * This directive is intended to be used inside an oui-menu tag.\r\n * It exists mostly to set the role attribute.\r\n */\r\n@Component({\r\n // eslint-disable-next-line @angular-eslint/component-selector\r\n selector: `[oui-menu-item]`,\r\n exportAs: 'ouiMenuItem',\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['disabled'],\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n '[attr.role]': 'role',\r\n class: 'oui-menu-item',\r\n '[class.oui-menu-item-highlighted]': '_highlighted',\r\n '[class.oui-menu-item-submenu-trigger]': '_triggersSubmenu',\r\n '[attr.tabindex]': '_getTabIndex()',\r\n '[attr.aria-disabled]': 'disabled.toString()',\r\n '[attr.disabled]': 'disabled || null',\r\n '(click)': '_checkDisabled($event)',\r\n '(mouseenter)': '_handleMouseEnter()',\r\n },\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n templateUrl: 'menu-item.html',\r\n})\r\nexport class OuiMenuItem\r\n extends _OuiMenuItemMixinBase\r\n implements FocusableOption, CanDisable, OnDestroy\r\n{\r\n /** ARIA role for the menu item. */\r\n @Input()\r\n role: 'menuitem' | 'menuitemradio' | 'menuitemcheckbox' = 'menuitem';\r\n\r\n private _document: Document;\r\n\r\n /** Stream that emits when the menu item is hovered. */\r\n readonly _hovered: Subject = new Subject();\r\n\r\n /** Whether the menu item is highlighted. */\r\n _highlighted = false;\r\n\r\n /** Whether the menu item acts as a trigger for a sub-menu. */\r\n _triggersSubmenu = false;\r\n\r\n constructor(\r\n private _elementRef: ElementRef,\r\n @Inject(DOCUMENT) document?: any,\r\n private _focusMonitor?: FocusMonitor,\r\n @Inject(OUI_MENU_PANEL)\r\n @Optional()\r\n private _parentMenu?: OuiMenuPanel\r\n ) {\r\n // @breaking-change 8.0.0 make `_focusMonitor` and `document` required params.\r\n super();\r\n\r\n if (_focusMonitor) {\r\n // Start monitoring the element so it gets the appropriate focused classes. We want\r\n // to show the focus style for menu items only when the focus was not caused by a\r\n // mouse or touch interaction.\r\n _focusMonitor.monitor(this._elementRef.nativeElement, false);\r\n }\r\n\r\n if (_parentMenu && _parentMenu.addItem) {\r\n _parentMenu.addItem(this);\r\n }\r\n\r\n this._document = document;\r\n }\r\n\r\n /** Focuses the menu item. */\r\n focus(origin: FocusOrigin = 'program'): void {\r\n if (this._focusMonitor) {\r\n this._focusMonitor.focusVia(this._getHostElement(), origin);\r\n } else {\r\n this._getHostElement().focus();\r\n }\r\n }\r\n\r\n ngOnDestroy() {\r\n if (this._focusMonitor) {\r\n this._focusMonitor.stopMonitoring(this._elementRef.nativeElement);\r\n }\r\n\r\n if (this._parentMenu && this._parentMenu.removeItem) {\r\n this._parentMenu.removeItem(this);\r\n }\r\n\r\n this._hovered.complete();\r\n }\r\n\r\n /** Used to set the `tabindex`. */\r\n _getTabIndex(): string {\r\n return this.disabled ? '-1' : '0';\r\n }\r\n\r\n /** Returns the host DOM element. */\r\n _getHostElement(): HTMLElement {\r\n return this._elementRef.nativeElement;\r\n }\r\n\r\n /** Prevents the default element actions if it is disabled. */\r\n _checkDisabled(event: Event): void {\r\n if (this.disabled) {\r\n event.preventDefault();\r\n event.stopPropagation();\r\n }\r\n }\r\n\r\n /** Emits to the hover stream. */\r\n _handleMouseEnter() {\r\n this._hovered.next(this);\r\n }\r\n\r\n /** Gets the label to be used when determining whether the option should be focused. */\r\n getLabel(): string {\r\n const element: HTMLElement = this._elementRef.nativeElement;\r\n const textNodeType = this._document ? this._document.TEXT_NODE : 3;\r\n let output = '';\r\n\r\n if (element.childNodes) {\r\n const length = element.childNodes.length;\r\n\r\n // Go through all the top-level text nodes and extract their text.\r\n // We skip anything that's not a text node to prevent the text from\r\n // being thrown off by something like an icon.\r\n for (let i = 0; i < length; i++) {\r\n if (element.childNodes[i].nodeType === textNodeType) {\r\n output += element.childNodes[i].textContent;\r\n }\r\n }\r\n }\r\n\r\n return output.trim();\r\n }\r\n}\r\n", + "sourceCode": "import { FocusableOption, FocusMonitor, FocusOrigin } from '@angular/cdk/a11y';\nimport {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n OnDestroy,\n ViewEncapsulation,\n Inject,\n Optional,\n Input,\n} from '@angular/core';\nimport { CanDisable, CanDisableCtor, mixinDisabled } from '../core';\nimport { Subject } from 'rxjs';\nimport { DOCUMENT } from '@angular/common';\nimport { OUI_MENU_PANEL, OuiMenuPanel } from './menu-panel';\n\n// Boilerplate for applying mixins to OuiMenuItem.\n\nexport class OuiMenuItemBase {}\nexport const _OuiMenuItemMixinBase: CanDisableCtor & typeof OuiMenuItemBase =\n mixinDisabled(OuiMenuItemBase);\n\n/**\n * This directive is intended to be used inside an oui-menu tag.\n * It exists mostly to set the role attribute.\n */\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: `[oui-menu-item]`,\n exportAs: 'ouiMenuItem',\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\n inputs: ['disabled'],\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n '[attr.role]': 'role',\n class: 'oui-menu-item',\n '[class.oui-menu-item-highlighted]': '_highlighted',\n '[class.oui-menu-item-submenu-trigger]': '_triggersSubmenu',\n '[attr.tabindex]': '_getTabIndex()',\n '[attr.aria-disabled]': 'disabled.toString()',\n '[attr.disabled]': 'disabled || null',\n '(click)': '_checkDisabled($event)',\n '(mouseenter)': '_handleMouseEnter()',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n templateUrl: 'menu-item.html',\n})\nexport class OuiMenuItem\n extends _OuiMenuItemMixinBase\n implements FocusableOption, CanDisable, OnDestroy\n{\n /** ARIA role for the menu item. */\n @Input()\n role: 'menuitem' | 'menuitemradio' | 'menuitemcheckbox' = 'menuitem';\n\n private _document: Document;\n\n /** Stream that emits when the menu item is hovered. */\n readonly _hovered: Subject = new Subject();\n\n /** Whether the menu item is highlighted. */\n _highlighted = false;\n\n /** Whether the menu item acts as a trigger for a sub-menu. */\n _triggersSubmenu = false;\n\n constructor(\n private _elementRef: ElementRef,\n @Inject(DOCUMENT) document?: any,\n private _focusMonitor?: FocusMonitor,\n @Inject(OUI_MENU_PANEL)\n @Optional()\n private _parentMenu?: OuiMenuPanel\n ) {\n // @breaking-change 8.0.0 make `_focusMonitor` and `document` required params.\n super();\n\n if (_focusMonitor) {\n // Start monitoring the element so it gets the appropriate focused classes. We want\n // to show the focus style for menu items only when the focus was not caused by a\n // mouse or touch interaction.\n _focusMonitor.monitor(this._elementRef.nativeElement, false);\n }\n\n if (_parentMenu && _parentMenu.addItem) {\n _parentMenu.addItem(this);\n }\n\n this._document = document;\n }\n\n /** Focuses the menu item. */\n focus(origin: FocusOrigin = 'program'): void {\n if (this._focusMonitor) {\n this._focusMonitor.focusVia(this._getHostElement(), origin);\n } else {\n this._getHostElement().focus();\n }\n }\n\n ngOnDestroy() {\n if (this._focusMonitor) {\n this._focusMonitor.stopMonitoring(this._elementRef.nativeElement);\n }\n\n if (this._parentMenu && this._parentMenu.removeItem) {\n this._parentMenu.removeItem(this);\n }\n\n this._hovered.complete();\n }\n\n /** Used to set the `tabindex`. */\n _getTabIndex(): string {\n return this.disabled ? '-1' : '0';\n }\n\n /** Returns the host DOM element. */\n _getHostElement(): HTMLElement {\n return this._elementRef.nativeElement;\n }\n\n /** Prevents the default element actions if it is disabled. */\n _checkDisabled(event: Event): void {\n if (this.disabled) {\n event.preventDefault();\n event.stopPropagation();\n }\n }\n\n /** Emits to the hover stream. */\n _handleMouseEnter() {\n this._hovered.next(this);\n }\n\n /** Gets the label to be used when determining whether the option should be focused. */\n getLabel(): string {\n const element: HTMLElement = this._elementRef.nativeElement;\n const textNodeType = this._document ? this._document.TEXT_NODE : 3;\n let output = '';\n\n if (element.childNodes) {\n const length = element.childNodes.length;\n\n // Go through all the top-level text nodes and extract their text.\n // We skip anything that's not a text node to prevent the text from\n // being thrown off by something like an icon.\n for (let i = 0; i < length; i++) {\n if (element.childNodes[i].nodeType === textNodeType) {\n output += element.childNodes[i].textContent;\n }\n }\n }\n\n return output.trim();\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -28386,11 +38244,11 @@ "CanDisable", "OnDestroy" ], - "templateData": "\r\n" + "templateData": "\n" }, { "name": "OuiMenuStorybook", - "id": "component-OuiMenuStorybook-f61fccaca4ea7412af3da4cf29b8b3d840217746285146fbb7fb7e1e2c9cb41f6d92a50c2a77747647b8ac7ed38d051641255d52314051bc7c1f6a837ec0164e", + "id": "component-OuiMenuStorybook-e30d8fe1bee5b4056ac8703f11c48017c65ba83c7a44ab8600e5f94e84eb24a087d322cc079379ecb38052105aca5ab8c36fd5eab1aa55ce7f5a1555df898836", "file": "ui/src/stories/menu/menu.component.ts", "encapsulation": [], "entryComponents": [], @@ -28507,7 +38365,7 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import { OuiIconRegistry } from '../../components';\r\nimport { Component, Input, Output, EventEmitter } from '@angular/core';\r\nimport { DomSanitizer } from '@angular/platform-browser';\r\n\r\n@Component({\r\n selector: 'oui-menu-storybook',\r\n template: `\r\n
\r\n \r\n \r\n
\r\n \r\n \r\n \r\n \r\n \r\n `,\r\n})\r\nexport class OuiMenuStorybook {\r\n @Input() xPosition = 'before';\r\n @Input() yPosition = 'above';\r\n @Input() vertical = false;\r\n @Input() hasBackdrop = true;\r\n\r\n @Output()\r\n readonly _closed: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _opened: EventEmitter = new EventEmitter();\r\n constructor(\r\n private ouiIconRegistry: OuiIconRegistry,\r\n private domSanitizer: DomSanitizer\r\n ) {\r\n this.ouiIconRegistry.addSvgIcon(\r\n `local`,\r\n this.domSanitizer.bypassSecurityTrustResourceUrl(\r\n `/assets/images/v-green.svg`\r\n )\r\n );\r\n\r\n this.ouiIconRegistry.addSvgIconSet(\r\n this.domSanitizer.bypassSecurityTrustResourceUrl(\r\n 'https://d1azc1qln24ryf.cloudfront.net/135790/oncehub-20/symbol-defs.svg?hn1bl5ss'\r\n )\r\n );\r\n }\r\n opened() {\r\n this._opened.emit();\r\n }\r\n closed(e: string) {\r\n this._closed.emit(e);\r\n }\r\n}\r\n\r\n@Component({\r\n selector: 'oui-nested-menu-storybook',\r\n template: `\r\n
\r\n \r\n \r\n
\r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n `,\r\n})\r\nexport class OuiNestedMenuStorybook {\r\n @Input() xPosition = 'before';\r\n @Input() yPosition = 'above';\r\n @Input() vertical = false;\r\n @Input() hasBackdrop = true;\r\n @Output()\r\n readonly _closed: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _opened: EventEmitter = new EventEmitter();\r\n constructor(\r\n private ouiIconRegistry: OuiIconRegistry,\r\n private domSanitizer: DomSanitizer\r\n ) {\r\n this.ouiIconRegistry.addSvgIcon(\r\n `local`,\r\n this.domSanitizer.bypassSecurityTrustResourceUrl(\r\n `/assets/images/v-green.svg`\r\n )\r\n );\r\n\r\n this.ouiIconRegistry.addSvgIconSet(\r\n this.domSanitizer.bypassSecurityTrustResourceUrl(\r\n 'https://d1azc1qln24ryf.cloudfront.net/135790/oncehub-20/symbol-defs.svg?hn1bl5'\r\n )\r\n );\r\n }\r\n opened() {\r\n this._opened.emit();\r\n }\r\n closed(e: string) {\r\n this._closed.emit(e);\r\n }\r\n}\r\n", + "sourceCode": "import { OuiIconRegistry } from '../../components';\nimport { Component, Input, Output, EventEmitter } from '@angular/core';\nimport { DomSanitizer } from '@angular/platform-browser';\n\n@Component({\n selector: 'oui-menu-storybook',\n template: `\n
\n \n \n
\n \n \n \n \n \n `,\n})\nexport class OuiMenuStorybook {\n @Input() xPosition = 'before';\n @Input() yPosition = 'above';\n @Input() vertical = false;\n @Input() hasBackdrop = true;\n\n @Output()\n readonly _closed: EventEmitter = new EventEmitter();\n @Output()\n readonly _opened: EventEmitter = new EventEmitter();\n constructor(\n private ouiIconRegistry: OuiIconRegistry,\n private domSanitizer: DomSanitizer\n ) {\n this.ouiIconRegistry.addSvgIcon(\n `local`,\n this.domSanitizer.bypassSecurityTrustResourceUrl(\n `/assets/images/v-green.svg`\n )\n );\n\n this.ouiIconRegistry.addSvgIconSet(\n this.domSanitizer.bypassSecurityTrustResourceUrl(\n 'https://d1azc1qln24ryf.cloudfront.net/135790/oncehub-20/symbol-defs.svg?hn1bl5ss'\n )\n );\n }\n opened() {\n this._opened.emit();\n }\n closed(e: string) {\n this._closed.emit(e);\n }\n}\n\n@Component({\n selector: 'oui-nested-menu-storybook',\n template: `\n
\n \n \n
\n \n \n \n \n\n \n \n \n \n \n `,\n})\nexport class OuiNestedMenuStorybook {\n @Input() xPosition = 'before';\n @Input() yPosition = 'above';\n @Input() vertical = false;\n @Input() hasBackdrop = true;\n @Output()\n readonly _closed: EventEmitter = new EventEmitter();\n @Output()\n readonly _opened: EventEmitter = new EventEmitter();\n constructor(\n private ouiIconRegistry: OuiIconRegistry,\n private domSanitizer: DomSanitizer\n ) {\n this.ouiIconRegistry.addSvgIcon(\n `local`,\n this.domSanitizer.bypassSecurityTrustResourceUrl(\n `/assets/images/v-green.svg`\n )\n );\n\n this.ouiIconRegistry.addSvgIconSet(\n this.domSanitizer.bypassSecurityTrustResourceUrl(\n 'https://d1azc1qln24ryf.cloudfront.net/135790/oncehub-20/symbol-defs.svg?hn1bl5'\n )\n );\n }\n opened() {\n this._opened.emit();\n }\n closed(e: string) {\n this._closed.emit(e);\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -28555,7 +38413,7 @@ }, { "name": "OuiMonthView", - "id": "component-OuiMonthView-f55a7232b17ebbf649657be263d73727675cff7d56d5630afd0794a453829f479e304798fdb5a12261056298b0cc2ea6dac4b32e9740d1adb3e26f50552e8dbc", + "id": "component-OuiMonthView-8198231f88c67cc6b98cc8f940404b5bd474af285fc5187739b0379087028df0a69945048f660e50a6e09eefdae6fdf6758d597efa614d1540091b06e0aa979b", "file": "ui/src/components/datepicker/month-view.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -28643,7 +38501,7 @@ "deprecationMessage": "", "rawdescription": "\nEmits when any date is selected.", "description": "

Emits when any date is selected.

\n", - "line": 121, + "line": 122, "type": "EventEmitter" }, { @@ -28653,7 +38511,7 @@ "deprecationMessage": "", "rawdescription": "\nEmits when any date is activated.", "description": "

Emits when any date is activated.

\n", - "line": 124, + "line": 125, "type": "EventEmitter" }, { @@ -28687,7 +38545,7 @@ "type": "DateAdapter", "optional": false, "description": "", - "line": 154, + "line": 155, "decorators": [ { "name": "Optional", @@ -28705,7 +38563,7 @@ "type": "number", "optional": false, "description": "

The number of blank cells in the first row before the 1st of the month.

\n", - "line": 137, + "line": 138, "rawdescription": "\nThe number of blank cells in the first row before the 1st of the month." }, { @@ -28739,7 +38597,7 @@ "type": "string", "optional": false, "description": "

The label for this month (e.g. "January 2017").

\n", - "line": 131, + "line": 132, "rawdescription": "\nThe label for this month (e.g. \"January 2017\")." }, { @@ -28749,7 +38607,7 @@ "type": "OuiCalendarBody", "optional": false, "description": "

The body of calendar table

\n", - "line": 128, + "line": 129, "rawdescription": "\nThe body of calendar table", "decorators": [ { @@ -28777,7 +38635,7 @@ "type": "number | null", "optional": false, "description": "

The date of the month that the currently selected Date falls on.\nNull if the currently selected Date is in another month.

\n", - "line": 143, + "line": 144, "rawdescription": "\n\nThe date of the month that the currently selected Date falls on.\nNull if the currently selected Date is in another month.\n" }, { @@ -28787,7 +38645,7 @@ "type": "number | null", "optional": false, "description": "

The date of the month that today falls on. Null if today is in another month.

\n", - "line": 146, + "line": 147, "rawdescription": "\nThe date of the month that today falls on. Null if today is in another month." }, { @@ -28797,7 +38655,7 @@ "type": "literal type[]", "optional": false, "description": "

The names of the weekdays.

\n", - "line": 149, + "line": 150, "rawdescription": "\nThe names of the weekdays." }, { @@ -28807,7 +38665,7 @@ "type": "OuiCalendarCell[][]", "optional": false, "description": "

Grid of calendar cells representing the dates of the month.

\n", - "line": 134, + "line": 135, "rawdescription": "\nGrid of calendar cells representing the dates of the month." } ], @@ -28818,7 +38676,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 307, + "line": 308, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nCreates OuiCalendarCells for the dates in this month.", @@ -28840,7 +38698,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 181, + "line": 182, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nHandles when a new date is selected.", @@ -28863,7 +38721,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 302, + "line": 303, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nFocuses the active cell after the microtask queue is empty.", @@ -28882,7 +38740,7 @@ "optional": false, "returnType": "number | null", "typeParameters": [], - "line": 359, + "line": 360, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nGets the date in this month that the given Date falls on.\nReturns null if the given Date is in another month.\n", @@ -28915,7 +38773,7 @@ "optional": false, "returnType": "D | null", "typeParameters": [], - "line": 379, + "line": 380, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\n", @@ -28926,8 +38784,8 @@ "jsdoctags": [ { "name": { - "pos": 11668, - "end": 11671, + "pos": 11297, + "end": 11300, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -28938,8 +38796,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 11662, - "end": 11667, + "pos": 11291, + "end": 11296, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -28950,8 +38808,8 @@ }, { "tagName": { - "pos": 11700, - "end": 11707, + "pos": 11328, + "end": 11335, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -28975,7 +38833,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 198, + "line": 199, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nHandles keydown events on the calendar body when calendar is in month view.", @@ -29011,7 +38869,7 @@ "optional": false, "returnType": "boolean", "typeParameters": [], - "line": 366, + "line": 367, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nChecks whether the 2 dates are non-null and fall within the same month of the same year.", @@ -29046,7 +38904,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 278, + "line": 279, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nInitializes this month view.", @@ -29058,7 +38916,7 @@ "optional": false, "returnType": "boolean", "typeParameters": [], - "line": 387, + "line": 388, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nDetermines whether the user has the RTL layout direction.", @@ -29080,7 +38938,7 @@ "optional": false, "returnType": "boolean", "typeParameters": [], - "line": 345, + "line": 346, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nDate filter for the month", @@ -29106,7 +38964,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 176, + "line": 177, "deprecated": false, "deprecationMessage": "" } @@ -29118,7 +38976,7 @@ "description": "

An internal component used to display a single month in the datepicker.

\n", "rawdescription": "\n\nAn internal component used to display a single month in the datepicker.\n", "type": "component", - "sourceCode": "import {\r\n DOWN_ARROW,\r\n END,\r\n ENTER,\r\n HOME,\r\n LEFT_ARROW,\r\n PAGE_DOWN,\r\n PAGE_UP,\r\n RIGHT_ARROW,\r\n UP_ARROW,\r\n SPACE,\r\n} from '@angular/cdk/keycodes';\r\nimport {\r\n AfterContentInit,\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n EventEmitter,\r\n Inject,\r\n Input,\r\n Optional,\r\n Output,\r\n ViewEncapsulation,\r\n ViewChild,\r\n} from '@angular/core';\r\nimport { Directionality } from '@angular/cdk/bidi';\r\nimport {\r\n OuiCalendarBody,\r\n OuiCalendarCell,\r\n OuiCalendarCellCssClasses,\r\n} from './calendar-body';\r\nimport { createMissingDateImplError } from './datepicker-errors';\r\nimport { OuiDateFormats, OUI_DATE_FORMATS } from './date-formats';\r\nimport { DateAdapter } from './date-adapter';\r\n\r\nconst DAYS_PER_WEEK = 7;\r\n\r\n/**\r\n * An internal component used to display a single month in the datepicker.\r\n */\r\n@Component({\r\n selector: 'oui-month-view',\r\n templateUrl: 'month-view.html',\r\n exportAs: 'ouiMonthView',\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class OuiMonthView implements AfterContentInit {\r\n /**\r\n * The date to display in this month view (everything other than the month and year is ignored).\r\n */\r\n @Input()\r\n get activeDate(): D {\r\n return this._activeDate;\r\n }\r\n set activeDate(value: D) {\r\n const oldActiveDate = this._activeDate;\r\n const validDate =\r\n this._getValidDateOrNull(this._dateAdapter.deserialize(value)) ||\r\n this._dateAdapter.today();\r\n this._activeDate = this._dateAdapter.clampDate(\r\n validDate,\r\n this.minDate,\r\n this.maxDate\r\n );\r\n if (!this._hasSameMonthAndYear(oldActiveDate, this._activeDate)) {\r\n this._init();\r\n }\r\n }\r\n private _activeDate: D;\r\n\r\n /** The currently selected date. */\r\n @Input()\r\n get selected(): D | null {\r\n return this._selected;\r\n }\r\n set selected(value: D | null) {\r\n this._selected = this._getValidDateOrNull(\r\n this._dateAdapter.deserialize(value)\r\n );\r\n this._selectedDate = this._getDateInCurrentMonth(this._selected);\r\n }\r\n private _selected: D | null;\r\n\r\n /** The minimum selectable date. */\r\n @Input()\r\n get minDate(): D | null {\r\n return this._minDate;\r\n }\r\n set minDate(value: D | null) {\r\n this._minDate = this._getValidDateOrNull(\r\n this._dateAdapter.deserialize(value)\r\n );\r\n }\r\n private _minDate: D | null;\r\n\r\n /** The maximum selectable date. */\r\n @Input()\r\n get maxDate(): D | null {\r\n return this._maxDate;\r\n }\r\n set maxDate(value: D | null) {\r\n this._maxDate = this._getValidDateOrNull(\r\n this._dateAdapter.deserialize(value)\r\n );\r\n }\r\n private _maxDate: D | null;\r\n\r\n /** Function used to filter which dates are selectable. */\r\n @Input() dateFilter: (date: D) => boolean;\r\n\r\n /** Function that can be used to add custom CSS classes to dates. */\r\n @Input() dateClass: (date: D) => OuiCalendarCellCssClasses;\r\n\r\n /** Emits when a new date is selected. */\r\n @Output()\r\n readonly selectedChange: EventEmitter = new EventEmitter();\r\n\r\n /** Emits when any date is selected. */\r\n @Output()\r\n readonly _userSelection: EventEmitter = new EventEmitter();\r\n\r\n /** Emits when any date is activated. */\r\n @Output() readonly activeDateChange: EventEmitter = new EventEmitter();\r\n\r\n /** The body of calendar table */\r\n @ViewChild(OuiCalendarBody)\r\n _ouiCalendarBody: OuiCalendarBody;\r\n\r\n /** The label for this month (e.g. \"January 2017\"). */\r\n _monthLabel: string;\r\n\r\n /** Grid of calendar cells representing the dates of the month. */\r\n _weeks: OuiCalendarCell[][];\r\n\r\n /** The number of blank cells in the first row before the 1st of the month. */\r\n _firstWeekOffset: number;\r\n\r\n /**\r\n * The date of the month that the currently selected Date falls on.\r\n * Null if the currently selected Date is in another month.\r\n */\r\n _selectedDate: number | null;\r\n\r\n /** The date of the month that today falls on. Null if today is in another month. */\r\n _todayDate: number | null;\r\n\r\n /** The names of the weekdays. */\r\n _weekdays: { long: string; narrow: string }[];\r\n\r\n constructor(\r\n private _changeDetectorRef: ChangeDetectorRef,\r\n @Optional() @Inject(OUI_DATE_FORMATS) private _dateFormats: OuiDateFormats,\r\n @Optional() public _dateAdapter: DateAdapter,\r\n @Optional() private _dir?: Directionality\r\n ) {\r\n if (!this._dateAdapter) {\r\n throw createMissingDateImplError('DateAdapter');\r\n }\r\n if (!this._dateFormats) {\r\n throw createMissingDateImplError('OUI_DATE_FORMATS');\r\n }\r\n\r\n const firstDayOfWeek = this._dateAdapter.getFirstDayOfWeek();\r\n const narrowWeekdays = this._dateAdapter.getDayOfWeekNames('narrow');\r\n const longWeekdays = this._dateAdapter.getDayOfWeekNames('long');\r\n // Rotate the labels for days of the week based on the configured first day of the week.\r\n const weekdays = longWeekdays.map((long, i) => ({\r\n long,\r\n narrow: narrowWeekdays[i],\r\n }));\r\n this._weekdays = weekdays.slice(firstDayOfWeek).concat();\r\n this._activeDate = this._dateAdapter.today();\r\n }\r\n\r\n ngAfterContentInit() {\r\n this._init();\r\n }\r\n\r\n /** Handles when a new date is selected. */\r\n _dateSelected(date: number) {\r\n if (this._selectedDate !== date) {\r\n const selectedYear = this._dateAdapter.getYear(this.activeDate);\r\n const selectedMonth = this._dateAdapter.getMonth(this.activeDate);\r\n const selectedDate = this._dateAdapter.createDate(\r\n selectedYear,\r\n selectedMonth,\r\n date\r\n );\r\n\r\n this.selectedChange.emit(selectedDate);\r\n }\r\n\r\n this._userSelection.emit();\r\n }\r\n\r\n /** Handles keydown events on the calendar body when calendar is in month view. */\r\n _handleCalendarBodyKeydown(event: KeyboardEvent): void {\r\n // TODO(mmalerba): We currently allow keyboard navigation to disabled dates, but just prevent\r\n // disabled ones from being selected. This may not be ideal, we should look into whether\r\n // navigation should skip over disabled dates, and if so, how to implement that efficiently.\r\n\r\n const oldActiveDate = this._activeDate;\r\n const isRtl = this._isRtl();\r\n\r\n switch (event.keyCode) {\r\n case LEFT_ARROW:\r\n this.activeDate = this._dateAdapter.addCalendarDays(\r\n this._activeDate,\r\n isRtl ? 1 : -1\r\n );\r\n break;\r\n case RIGHT_ARROW:\r\n this.activeDate = this._dateAdapter.addCalendarDays(\r\n this._activeDate,\r\n isRtl ? -1 : 1\r\n );\r\n break;\r\n case UP_ARROW:\r\n this.activeDate = this._dateAdapter.addCalendarDays(\r\n this._activeDate,\r\n -7\r\n );\r\n break;\r\n case DOWN_ARROW:\r\n this.activeDate = this._dateAdapter.addCalendarDays(\r\n this._activeDate,\r\n 7\r\n );\r\n break;\r\n case HOME:\r\n this.activeDate = this._dateAdapter.addCalendarDays(\r\n this._activeDate,\r\n 1 - this._dateAdapter.getDate(this._activeDate)\r\n );\r\n break;\r\n case END:\r\n this.activeDate = this._dateAdapter.addCalendarDays(\r\n this._activeDate,\r\n this._dateAdapter.getNumDaysInMonth(this._activeDate) -\r\n this._dateAdapter.getDate(this._activeDate)\r\n );\r\n break;\r\n case PAGE_UP:\r\n this.activeDate = event.altKey\r\n ? this._dateAdapter.addCalendarYears(this._activeDate, -1)\r\n : this._dateAdapter.addCalendarMonths(this._activeDate, -1);\r\n break;\r\n case PAGE_DOWN:\r\n this.activeDate = event.altKey\r\n ? this._dateAdapter.addCalendarYears(this._activeDate, 1)\r\n : this._dateAdapter.addCalendarMonths(this._activeDate, 1);\r\n break;\r\n case ENTER:\r\n case SPACE:\r\n if (!this.dateFilter || this.dateFilter(this._activeDate)) {\r\n this._dateSelected(this._dateAdapter.getDate(this._activeDate));\r\n this._userSelection.emit();\r\n // Prevent unexpected default actions such as form submission.\r\n event.preventDefault();\r\n }\r\n return;\r\n default:\r\n // Don't prevent default or focus active cell on keys that we don't explicitly handle.\r\n return;\r\n }\r\n\r\n if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) {\r\n this.activeDateChange.emit(this.activeDate);\r\n }\r\n\r\n this._focusActiveCell();\r\n // Prevent unexpected default actions such as form submission.\r\n event.preventDefault();\r\n }\r\n\r\n /** Initializes this month view. */\r\n _init() {\r\n this._selectedDate = this._getDateInCurrentMonth(this.selected);\r\n this._todayDate = this._getDateInCurrentMonth(this._dateAdapter.today());\r\n this._monthLabel =\r\n this._dateAdapter.getMonthNames('short')[\r\n this._dateAdapter.getMonth(this.activeDate)\r\n ];\r\n\r\n const firstOfMonth = this._dateAdapter.createDate(\r\n this._dateAdapter.getYear(this.activeDate),\r\n this._dateAdapter.getMonth(this.activeDate),\r\n 1\r\n );\r\n this._firstWeekOffset =\r\n (DAYS_PER_WEEK +\r\n this._dateAdapter.getDayOfWeek(firstOfMonth) -\r\n this._dateAdapter.getFirstDayOfWeek()) %\r\n DAYS_PER_WEEK;\r\n\r\n this._createWeekCells();\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n\r\n /** Focuses the active cell after the microtask queue is empty. */\r\n _focusActiveCell() {\r\n this._ouiCalendarBody._focusActiveCell();\r\n }\r\n\r\n /** Creates OuiCalendarCells for the dates in this month. */\r\n private _createWeekCells() {\r\n const daysInMonth = this._dateAdapter.getNumDaysInMonth(this.activeDate);\r\n const dateNames = this._dateAdapter.getDateNames();\r\n this._weeks = [[]];\r\n for (\r\n let i = 0, cell = this._firstWeekOffset;\r\n i < daysInMonth;\r\n i++, cell++\r\n ) {\r\n if (cell === DAYS_PER_WEEK) {\r\n this._weeks.push([]);\r\n cell = 0;\r\n }\r\n const date = this._dateAdapter.createDate(\r\n this._dateAdapter.getYear(this.activeDate),\r\n this._dateAdapter.getMonth(this.activeDate),\r\n i + 1\r\n );\r\n const enabled = this._shouldEnableDate(date);\r\n const ariaLabel = this._dateAdapter.format(\r\n date,\r\n this._dateFormats.display.dateA11yLabel\r\n );\r\n const cellClasses = this.dateClass ? this.dateClass(date) : undefined;\r\n\r\n this._weeks[this._weeks.length - 1].push(\r\n new OuiCalendarCell(\r\n i + 1,\r\n dateNames[i],\r\n ariaLabel,\r\n enabled,\r\n cellClasses\r\n )\r\n );\r\n }\r\n }\r\n\r\n /** Date filter for the month */\r\n private _shouldEnableDate(date: D): boolean {\r\n return (\r\n !!date &&\r\n (!this.dateFilter || this.dateFilter(date)) &&\r\n (!this.minDate ||\r\n this._dateAdapter.compareDate(date, this.minDate) >= 0) &&\r\n (!this.maxDate || this._dateAdapter.compareDate(date, this.maxDate) <= 0)\r\n );\r\n }\r\n\r\n /**\r\n * Gets the date in this month that the given Date falls on.\r\n * Returns null if the given Date is in another month.\r\n */\r\n private _getDateInCurrentMonth(date: D | null): number | null {\r\n return date && this._hasSameMonthAndYear(date, this.activeDate)\r\n ? this._dateAdapter.getDate(date)\r\n : null;\r\n }\r\n\r\n /** Checks whether the 2 dates are non-null and fall within the same month of the same year. */\r\n private _hasSameMonthAndYear(d1: D | null, d2: D | null): boolean {\r\n return !!(\r\n d1 &&\r\n d2 &&\r\n this._dateAdapter.getMonth(d1) === this._dateAdapter.getMonth(d2) &&\r\n this._dateAdapter.getYear(d1) === this._dateAdapter.getYear(d2)\r\n );\r\n }\r\n\r\n /**\r\n * @param obj The object to check.\r\n * @returns The given object if it is both a date instance and valid, otherwise null.\r\n */\r\n private _getValidDateOrNull(obj: any): D | null {\r\n return this._dateAdapter.isDateInstance(obj) &&\r\n this._dateAdapter.isValid(obj as any as D)\r\n ? obj\r\n : null;\r\n }\r\n\r\n /** Determines whether the user has the RTL layout direction. */\r\n private _isRtl() {\r\n return this._dir && this._dir.value === 'rtl';\r\n }\r\n}\r\n", + "sourceCode": "import {\n DOWN_ARROW,\n END,\n ENTER,\n HOME,\n LEFT_ARROW,\n PAGE_DOWN,\n PAGE_UP,\n RIGHT_ARROW,\n UP_ARROW,\n SPACE,\n} from '@angular/cdk/keycodes';\nimport {\n AfterContentInit,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n EventEmitter,\n Inject,\n Input,\n Optional,\n Output,\n ViewEncapsulation,\n ViewChild,\n} from '@angular/core';\nimport { Directionality } from '@angular/cdk/bidi';\nimport {\n OuiCalendarBody,\n OuiCalendarCell,\n OuiCalendarCellCssClasses,\n} from './calendar-body';\nimport { createMissingDateImplError } from './datepicker-errors';\nimport { OuiDateFormats, OUI_DATE_FORMATS } from './date-formats';\nimport { DateAdapter } from './date-adapter';\n\nconst DAYS_PER_WEEK = 7;\n\n/**\n * An internal component used to display a single month in the datepicker.\n */\n@Component({\n selector: 'oui-month-view',\n templateUrl: 'month-view.html',\n exportAs: 'ouiMonthView',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class OuiMonthView implements AfterContentInit {\n /**\n * The date to display in this month view (everything other than the month and year is ignored).\n */\n @Input()\n get activeDate(): D {\n return this._activeDate;\n }\n set activeDate(value: D) {\n const oldActiveDate = this._activeDate;\n const validDate =\n this._getValidDateOrNull(this._dateAdapter.deserialize(value)) ||\n this._dateAdapter.today();\n this._activeDate = this._dateAdapter.clampDate(\n validDate,\n this.minDate,\n this.maxDate\n );\n if (!this._hasSameMonthAndYear(oldActiveDate, this._activeDate)) {\n this._init();\n }\n }\n private _activeDate: D;\n\n /** The currently selected date. */\n @Input()\n get selected(): D | null {\n return this._selected;\n }\n set selected(value: D | null) {\n this._selected = this._getValidDateOrNull(\n this._dateAdapter.deserialize(value)\n );\n this._selectedDate = this._getDateInCurrentMonth(this._selected);\n }\n private _selected: D | null;\n\n /** The minimum selectable date. */\n @Input()\n get minDate(): D | null {\n return this._minDate;\n }\n set minDate(value: D | null) {\n this._minDate = this._getValidDateOrNull(\n this._dateAdapter.deserialize(value)\n );\n }\n private _minDate: D | null;\n\n /** The maximum selectable date. */\n @Input()\n get maxDate(): D | null {\n return this._maxDate;\n }\n set maxDate(value: D | null) {\n this._maxDate = this._getValidDateOrNull(\n this._dateAdapter.deserialize(value)\n );\n }\n private _maxDate: D | null;\n\n /** Function used to filter which dates are selectable. */\n @Input() dateFilter: (date: D) => boolean;\n\n /** Function that can be used to add custom CSS classes to dates. */\n @Input() dateClass: (date: D) => OuiCalendarCellCssClasses;\n\n /** Emits when a new date is selected. */\n @Output()\n readonly selectedChange: EventEmitter =\n new EventEmitter();\n\n /** Emits when any date is selected. */\n @Output()\n readonly _userSelection: EventEmitter = new EventEmitter();\n\n /** Emits when any date is activated. */\n @Output() readonly activeDateChange: EventEmitter = new EventEmitter();\n\n /** The body of calendar table */\n @ViewChild(OuiCalendarBody)\n _ouiCalendarBody: OuiCalendarBody;\n\n /** The label for this month (e.g. \"January 2017\"). */\n _monthLabel: string;\n\n /** Grid of calendar cells representing the dates of the month. */\n _weeks: OuiCalendarCell[][];\n\n /** The number of blank cells in the first row before the 1st of the month. */\n _firstWeekOffset: number;\n\n /**\n * The date of the month that the currently selected Date falls on.\n * Null if the currently selected Date is in another month.\n */\n _selectedDate: number | null;\n\n /** The date of the month that today falls on. Null if today is in another month. */\n _todayDate: number | null;\n\n /** The names of the weekdays. */\n _weekdays: { long: string; narrow: string }[];\n\n constructor(\n private _changeDetectorRef: ChangeDetectorRef,\n @Optional() @Inject(OUI_DATE_FORMATS) private _dateFormats: OuiDateFormats,\n @Optional() public _dateAdapter: DateAdapter,\n @Optional() private _dir?: Directionality\n ) {\n if (!this._dateAdapter) {\n throw createMissingDateImplError('DateAdapter');\n }\n if (!this._dateFormats) {\n throw createMissingDateImplError('OUI_DATE_FORMATS');\n }\n\n const firstDayOfWeek = this._dateAdapter.getFirstDayOfWeek();\n const narrowWeekdays = this._dateAdapter.getDayOfWeekNames('narrow');\n const longWeekdays = this._dateAdapter.getDayOfWeekNames('long');\n // Rotate the labels for days of the week based on the configured first day of the week.\n const weekdays = longWeekdays.map((long, i) => ({\n long,\n narrow: narrowWeekdays[i],\n }));\n this._weekdays = weekdays.slice(firstDayOfWeek).concat();\n this._activeDate = this._dateAdapter.today();\n }\n\n ngAfterContentInit() {\n this._init();\n }\n\n /** Handles when a new date is selected. */\n _dateSelected(date: number) {\n if (this._selectedDate !== date) {\n const selectedYear = this._dateAdapter.getYear(this.activeDate);\n const selectedMonth = this._dateAdapter.getMonth(this.activeDate);\n const selectedDate = this._dateAdapter.createDate(\n selectedYear,\n selectedMonth,\n date\n );\n\n this.selectedChange.emit(selectedDate);\n }\n\n this._userSelection.emit();\n }\n\n /** Handles keydown events on the calendar body when calendar is in month view. */\n _handleCalendarBodyKeydown(event: KeyboardEvent): void {\n // TODO(mmalerba): We currently allow keyboard navigation to disabled dates, but just prevent\n // disabled ones from being selected. This may not be ideal, we should look into whether\n // navigation should skip over disabled dates, and if so, how to implement that efficiently.\n\n const oldActiveDate = this._activeDate;\n const isRtl = this._isRtl();\n\n switch (event.keyCode) {\n case LEFT_ARROW:\n this.activeDate = this._dateAdapter.addCalendarDays(\n this._activeDate,\n isRtl ? 1 : -1\n );\n break;\n case RIGHT_ARROW:\n this.activeDate = this._dateAdapter.addCalendarDays(\n this._activeDate,\n isRtl ? -1 : 1\n );\n break;\n case UP_ARROW:\n this.activeDate = this._dateAdapter.addCalendarDays(\n this._activeDate,\n -7\n );\n break;\n case DOWN_ARROW:\n this.activeDate = this._dateAdapter.addCalendarDays(\n this._activeDate,\n 7\n );\n break;\n case HOME:\n this.activeDate = this._dateAdapter.addCalendarDays(\n this._activeDate,\n 1 - this._dateAdapter.getDate(this._activeDate)\n );\n break;\n case END:\n this.activeDate = this._dateAdapter.addCalendarDays(\n this._activeDate,\n this._dateAdapter.getNumDaysInMonth(this._activeDate) -\n this._dateAdapter.getDate(this._activeDate)\n );\n break;\n case PAGE_UP:\n this.activeDate = event.altKey\n ? this._dateAdapter.addCalendarYears(this._activeDate, -1)\n : this._dateAdapter.addCalendarMonths(this._activeDate, -1);\n break;\n case PAGE_DOWN:\n this.activeDate = event.altKey\n ? this._dateAdapter.addCalendarYears(this._activeDate, 1)\n : this._dateAdapter.addCalendarMonths(this._activeDate, 1);\n break;\n case ENTER:\n case SPACE:\n if (!this.dateFilter || this.dateFilter(this._activeDate)) {\n this._dateSelected(this._dateAdapter.getDate(this._activeDate));\n this._userSelection.emit();\n // Prevent unexpected default actions such as form submission.\n event.preventDefault();\n }\n return;\n default:\n // Don't prevent default or focus active cell on keys that we don't explicitly handle.\n return;\n }\n\n if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) {\n this.activeDateChange.emit(this.activeDate);\n }\n\n this._focusActiveCell();\n // Prevent unexpected default actions such as form submission.\n event.preventDefault();\n }\n\n /** Initializes this month view. */\n _init() {\n this._selectedDate = this._getDateInCurrentMonth(this.selected);\n this._todayDate = this._getDateInCurrentMonth(this._dateAdapter.today());\n this._monthLabel =\n this._dateAdapter.getMonthNames('short')[\n this._dateAdapter.getMonth(this.activeDate)\n ];\n\n const firstOfMonth = this._dateAdapter.createDate(\n this._dateAdapter.getYear(this.activeDate),\n this._dateAdapter.getMonth(this.activeDate),\n 1\n );\n this._firstWeekOffset =\n (DAYS_PER_WEEK +\n this._dateAdapter.getDayOfWeek(firstOfMonth) -\n this._dateAdapter.getFirstDayOfWeek()) %\n DAYS_PER_WEEK;\n\n this._createWeekCells();\n this._changeDetectorRef.markForCheck();\n }\n\n /** Focuses the active cell after the microtask queue is empty. */\n _focusActiveCell() {\n this._ouiCalendarBody._focusActiveCell();\n }\n\n /** Creates OuiCalendarCells for the dates in this month. */\n private _createWeekCells() {\n const daysInMonth = this._dateAdapter.getNumDaysInMonth(this.activeDate);\n const dateNames = this._dateAdapter.getDateNames();\n this._weeks = [[]];\n for (\n let i = 0, cell = this._firstWeekOffset;\n i < daysInMonth;\n i++, cell++\n ) {\n if (cell === DAYS_PER_WEEK) {\n this._weeks.push([]);\n cell = 0;\n }\n const date = this._dateAdapter.createDate(\n this._dateAdapter.getYear(this.activeDate),\n this._dateAdapter.getMonth(this.activeDate),\n i + 1\n );\n const enabled = this._shouldEnableDate(date);\n const ariaLabel = this._dateAdapter.format(\n date,\n this._dateFormats.display.dateA11yLabel\n );\n const cellClasses = this.dateClass ? this.dateClass(date) : undefined;\n\n this._weeks[this._weeks.length - 1].push(\n new OuiCalendarCell(\n i + 1,\n dateNames[i],\n ariaLabel,\n enabled,\n cellClasses\n )\n );\n }\n }\n\n /** Date filter for the month */\n private _shouldEnableDate(date: D): boolean {\n return (\n !!date &&\n (!this.dateFilter || this.dateFilter(date)) &&\n (!this.minDate ||\n this._dateAdapter.compareDate(date, this.minDate) >= 0) &&\n (!this.maxDate || this._dateAdapter.compareDate(date, this.maxDate) <= 0)\n );\n }\n\n /**\n * Gets the date in this month that the given Date falls on.\n * Returns null if the given Date is in another month.\n */\n private _getDateInCurrentMonth(date: D | null): number | null {\n return date && this._hasSameMonthAndYear(date, this.activeDate)\n ? this._dateAdapter.getDate(date)\n : null;\n }\n\n /** Checks whether the 2 dates are non-null and fall within the same month of the same year. */\n private _hasSameMonthAndYear(d1: D | null, d2: D | null): boolean {\n return !!(\n d1 &&\n d2 &&\n this._dateAdapter.getMonth(d1) === this._dateAdapter.getMonth(d2) &&\n this._dateAdapter.getYear(d1) === this._dateAdapter.getYear(d2)\n );\n }\n\n /**\n * @param obj The object to check.\n * @returns The given object if it is both a date instance and valid, otherwise null.\n */\n private _getValidDateOrNull(obj: any): D | null {\n return this._dateAdapter.isDateInstance(obj) &&\n this._dateAdapter.isValid(obj as any as D)\n ? obj\n : null;\n }\n\n /** Determines whether the user has the RTL layout direction. */\n private _isRtl() {\n return this._dir && this._dir.value === 'rtl';\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -29154,7 +39012,7 @@ "optional": true } ], - "line": 149, + "line": 150, "jsdoctags": [ { "name": "_changeDetectorRef", @@ -29352,11 +39210,11 @@ } } }, - "templateData": "\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n {{day.narrow}}\r\n
\r\n" + "templateData": "\n \n \n \n \n \n \n \n \n \n
\n {{day.narrow}}\n
\n" }, { "name": "OuiMultiYearView", - "id": "component-OuiMultiYearView-02685852f66d6401fd339c2b2fda529911f1929b816b3f30fae3b7a771a2b2ad6026708be031eee9d987255d6a44e049697b64e9dc69d224977c11f994605ae4", + "id": "component-OuiMultiYearView-4c21f14e26e53a5c4faf2baeb58669497fa17f71be28acdc3e1fca725249d679886f801be8e2c7f01f8838c1375e338927cf535cef972a3cb0c42e00d7c2ebbc", "file": "ui/src/components/datepicker/multi-year-view.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -29652,8 +39510,8 @@ "jsdoctags": [ { "name": { - "pos": 9349, - "end": 9352, + "pos": 9036, + "end": 9039, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -29664,8 +39522,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 9343, - "end": 9348, + "pos": 9030, + "end": 9035, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -29676,8 +39534,8 @@ }, { "tagName": { - "pos": 9381, - "end": 9388, + "pos": 9067, + "end": 9074, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -29826,7 +39684,7 @@ "description": "

An internal component used to display a year selector in the datepicker.

\n", "rawdescription": "\n\nAn internal component used to display a year selector in the datepicker.\n", "type": "component", - "sourceCode": "import {\r\n DOWN_ARROW,\r\n END,\r\n ENTER,\r\n HOME,\r\n LEFT_ARROW,\r\n PAGE_DOWN,\r\n PAGE_UP,\r\n RIGHT_ARROW,\r\n UP_ARROW,\r\n SPACE,\r\n} from '@angular/cdk/keycodes';\r\nimport {\r\n AfterContentInit,\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n EventEmitter,\r\n Input,\r\n Optional,\r\n Output,\r\n ViewChild,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\nimport { Directionality } from '@angular/cdk/bidi';\r\nimport { OuiCalendarBody, OuiCalendarCell } from './calendar-body';\r\nimport { createMissingDateImplError } from './datepicker-errors';\r\nimport { DateAdapter } from './date-adapter';\r\n\r\nexport const yearsPerPage = 24;\r\n\r\nexport const yearsPerRow = 4;\r\n\r\n/**\r\n * An internal component used to display a year selector in the datepicker.\r\n */\r\n@Component({\r\n selector: 'oui-multi-year-view',\r\n templateUrl: 'multi-year-view.html',\r\n exportAs: 'ouiMultiYearView',\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class OuiMultiYearView implements AfterContentInit {\r\n /** The date to display in this multi-year view (everything other than the year is ignored). */\r\n @Input()\r\n get activeDate(): D {\r\n return this._activeDate;\r\n }\r\n set activeDate(value: D) {\r\n const oldActiveDate = this._activeDate;\r\n const validDate =\r\n this._getValidDateOrNull(this._dateAdapter.deserialize(value)) ||\r\n this._dateAdapter.today();\r\n this._activeDate = this._dateAdapter.clampDate(\r\n validDate,\r\n this.minDate,\r\n this.maxDate\r\n );\r\n if (\r\n Math.floor(this._dateAdapter.getYear(oldActiveDate) / yearsPerPage) !==\r\n Math.floor(this._dateAdapter.getYear(this._activeDate) / yearsPerPage)\r\n ) {\r\n this._init();\r\n }\r\n }\r\n private _activeDate: D;\r\n\r\n /** The currently selected date. */\r\n @Input()\r\n get selected(): D | null {\r\n return this._selected;\r\n }\r\n set selected(value: D | null) {\r\n this._selected = this._getValidDateOrNull(\r\n this._dateAdapter.deserialize(value)\r\n );\r\n this._selectedYear =\r\n this._selected && this._dateAdapter.getYear(this._selected);\r\n }\r\n private _selected: D | null;\r\n\r\n /** The minimum selectable date. */\r\n @Input()\r\n get minDate(): D | null {\r\n return this._minDate;\r\n }\r\n set minDate(value: D | null) {\r\n this._minDate = this._getValidDateOrNull(\r\n this._dateAdapter.deserialize(value)\r\n );\r\n }\r\n private _minDate: D | null;\r\n\r\n /** The maximum selectable date. */\r\n @Input()\r\n get maxDate(): D | null {\r\n return this._maxDate;\r\n }\r\n set maxDate(value: D | null) {\r\n this._maxDate = this._getValidDateOrNull(\r\n this._dateAdapter.deserialize(value)\r\n );\r\n }\r\n private _maxDate: D | null;\r\n\r\n /** A function used to filter which dates are selectable. */\r\n @Input() dateFilter: (date: D) => boolean;\r\n\r\n /** Emits when a new year is selected. */\r\n @Output() readonly selectedChange: EventEmitter = new EventEmitter();\r\n\r\n /** Emits the selected year. This doesn't imply a change on the selected date */\r\n @Output() readonly yearSelected: EventEmitter = new EventEmitter();\r\n\r\n /** Emits when any date is activated. */\r\n @Output() readonly activeDateChange: EventEmitter = new EventEmitter();\r\n\r\n /** The body of calendar table */\r\n @ViewChild(OuiCalendarBody)\r\n _ouiCalendarBody: OuiCalendarBody;\r\n\r\n /** Grid of calendar cells representing the currently displayed years. */\r\n _years: OuiCalendarCell[][];\r\n\r\n /** The year that today falls on. */\r\n _todayYear: number;\r\n\r\n /** The year of the selected date. Null if the selected date is null. */\r\n _selectedYear: number | null;\r\n\r\n constructor(\r\n private _changeDetectorRef: ChangeDetectorRef,\r\n @Optional() public _dateAdapter: DateAdapter,\r\n @Optional() private _dir?: Directionality\r\n ) {\r\n if (!this._dateAdapter) {\r\n throw createMissingDateImplError('DateAdapter');\r\n }\r\n\r\n this._activeDate = this._dateAdapter.today();\r\n }\r\n\r\n ngAfterContentInit() {\r\n this._init();\r\n }\r\n\r\n /** Initializes this multi-year view. */\r\n _init() {\r\n this._todayYear = this._dateAdapter.getYear(this._dateAdapter.today());\r\n const activeYear = this._dateAdapter.getYear(this._activeDate);\r\n const activeOffset = activeYear % yearsPerPage;\r\n this._years = [];\r\n for (let i = 0, row: number[] = []; i < yearsPerPage; i++) {\r\n row.push(activeYear - activeOffset + i);\r\n if (row.length === yearsPerRow) {\r\n this._years.push(row.map((year) => this._createCellForYear(year)));\r\n row = [];\r\n }\r\n }\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n\r\n /** Handles when a new year is selected. */\r\n _yearSelected(year: number) {\r\n this.yearSelected.emit(this._dateAdapter.createDate(year, 0, 1));\r\n const month = this._dateAdapter.getMonth(this.activeDate);\r\n const daysInMonth = this._dateAdapter.getNumDaysInMonth(\r\n this._dateAdapter.createDate(year, month, 1)\r\n );\r\n this.selectedChange.emit(\r\n this._dateAdapter.createDate(\r\n year,\r\n month,\r\n Math.min(this._dateAdapter.getDate(this.activeDate), daysInMonth)\r\n )\r\n );\r\n }\r\n\r\n /** Handles keydown events on the calendar body when calendar is in multi-year view. */\r\n _handleCalendarBodyKeydown(event: KeyboardEvent): void {\r\n // TODO(mmalerba): We currently allow keyboard navigation to disabled dates, but just prevent\r\n // disabled ones from being selected. This may not be ideal, we should look into whether\r\n // navigation should skip over disabled dates, and if so, how to implement that efficiently.\r\n\r\n const oldActiveDate = this._activeDate;\r\n const isRtl = this._isRtl();\r\n\r\n switch (event.keyCode) {\r\n case LEFT_ARROW:\r\n this.activeDate = this._dateAdapter.addCalendarYears(\r\n this._activeDate,\r\n isRtl ? 1 : -1\r\n );\r\n break;\r\n case RIGHT_ARROW:\r\n this.activeDate = this._dateAdapter.addCalendarYears(\r\n this._activeDate,\r\n isRtl ? -1 : 1\r\n );\r\n break;\r\n case UP_ARROW:\r\n this.activeDate = this._dateAdapter.addCalendarYears(\r\n this._activeDate,\r\n -yearsPerRow\r\n );\r\n break;\r\n case DOWN_ARROW:\r\n this.activeDate = this._dateAdapter.addCalendarYears(\r\n this._activeDate,\r\n yearsPerRow\r\n );\r\n break;\r\n case HOME:\r\n this.activeDate = this._dateAdapter.addCalendarYears(\r\n this._activeDate,\r\n -this._dateAdapter.getYear(this._activeDate) % yearsPerPage\r\n );\r\n break;\r\n case END:\r\n this.activeDate = this._dateAdapter.addCalendarYears(\r\n this._activeDate,\r\n yearsPerPage -\r\n (this._dateAdapter.getYear(this._activeDate) % yearsPerPage) -\r\n 1\r\n );\r\n break;\r\n case PAGE_UP:\r\n this.activeDate = this._dateAdapter.addCalendarYears(\r\n this._activeDate,\r\n event.altKey ? -yearsPerPage * 10 : -yearsPerPage\r\n );\r\n break;\r\n case PAGE_DOWN:\r\n this.activeDate = this._dateAdapter.addCalendarYears(\r\n this._activeDate,\r\n event.altKey ? yearsPerPage * 10 : yearsPerPage\r\n );\r\n break;\r\n case ENTER:\r\n case SPACE:\r\n this._yearSelected(this._dateAdapter.getYear(this._activeDate));\r\n break;\r\n default:\r\n // Don't prevent default or focus active cell on keys that we don't explicitly handle.\r\n return;\r\n }\r\n\r\n if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) {\r\n this.activeDateChange.emit(this.activeDate);\r\n }\r\n\r\n this._focusActiveCell();\r\n // Prevent unexpected default actions such as form submission.\r\n event.preventDefault();\r\n }\r\n\r\n _getActiveCell(): number {\r\n return this._dateAdapter.getYear(this.activeDate) % yearsPerPage;\r\n }\r\n\r\n /** Focuses the active cell after the microtask queue is empty. */\r\n _focusActiveCell() {\r\n this._ouiCalendarBody._focusActiveCell();\r\n }\r\n\r\n /** Creates an OuiCalendarCell for the given year. */\r\n private _createCellForYear(year: number) {\r\n const yearName = this._dateAdapter.getYearName(\r\n this._dateAdapter.createDate(year, 0, 1)\r\n );\r\n return new OuiCalendarCell(\r\n year,\r\n yearName,\r\n yearName,\r\n this._shouldEnableYear(year)\r\n );\r\n }\r\n\r\n /** Whether the given year is enabled. */\r\n private _shouldEnableYear(year: number) {\r\n // disable if the year is greater than maxDate lower than minDate\r\n if (\r\n year === undefined ||\r\n year === null ||\r\n (this.maxDate && year > this._dateAdapter.getYear(this.maxDate)) ||\r\n (this.minDate && year < this._dateAdapter.getYear(this.minDate))\r\n ) {\r\n return false;\r\n }\r\n\r\n // enable if it reaches here and there's no filter defined\r\n if (!this.dateFilter) {\r\n return true;\r\n }\r\n\r\n const firstOfYear = this._dateAdapter.createDate(year, 0, 1);\r\n\r\n // If any date in the year is enabled count the year as enabled.\r\n for (\r\n let date = firstOfYear;\r\n this._dateAdapter.getYear(date) === year;\r\n date = this._dateAdapter.addCalendarDays(date, 1)\r\n ) {\r\n if (this.dateFilter(date)) {\r\n return true;\r\n }\r\n }\r\n\r\n return false;\r\n }\r\n\r\n /**\r\n * @param obj The object to check.\r\n * @returns The given object if it is both a date instance and valid, otherwise null.\r\n */\r\n private _getValidDateOrNull(obj: any): D | null {\r\n return this._dateAdapter.isDateInstance(obj) &&\r\n this._dateAdapter.isValid(obj as any as D)\r\n ? (obj as any as D)\r\n : null;\r\n }\r\n\r\n /** Determines whether the user has the RTL layout direction. */\r\n private _isRtl() {\r\n return this._dir && this._dir.value === 'rtl';\r\n }\r\n}\r\n", + "sourceCode": "import {\n DOWN_ARROW,\n END,\n ENTER,\n HOME,\n LEFT_ARROW,\n PAGE_DOWN,\n PAGE_UP,\n RIGHT_ARROW,\n UP_ARROW,\n SPACE,\n} from '@angular/cdk/keycodes';\nimport {\n AfterContentInit,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n EventEmitter,\n Input,\n Optional,\n Output,\n ViewChild,\n ViewEncapsulation,\n} from '@angular/core';\nimport { Directionality } from '@angular/cdk/bidi';\nimport { OuiCalendarBody, OuiCalendarCell } from './calendar-body';\nimport { createMissingDateImplError } from './datepicker-errors';\nimport { DateAdapter } from './date-adapter';\n\nexport const yearsPerPage = 24;\n\nexport const yearsPerRow = 4;\n\n/**\n * An internal component used to display a year selector in the datepicker.\n */\n@Component({\n selector: 'oui-multi-year-view',\n templateUrl: 'multi-year-view.html',\n exportAs: 'ouiMultiYearView',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class OuiMultiYearView implements AfterContentInit {\n /** The date to display in this multi-year view (everything other than the year is ignored). */\n @Input()\n get activeDate(): D {\n return this._activeDate;\n }\n set activeDate(value: D) {\n const oldActiveDate = this._activeDate;\n const validDate =\n this._getValidDateOrNull(this._dateAdapter.deserialize(value)) ||\n this._dateAdapter.today();\n this._activeDate = this._dateAdapter.clampDate(\n validDate,\n this.minDate,\n this.maxDate\n );\n if (\n Math.floor(this._dateAdapter.getYear(oldActiveDate) / yearsPerPage) !==\n Math.floor(this._dateAdapter.getYear(this._activeDate) / yearsPerPage)\n ) {\n this._init();\n }\n }\n private _activeDate: D;\n\n /** The currently selected date. */\n @Input()\n get selected(): D | null {\n return this._selected;\n }\n set selected(value: D | null) {\n this._selected = this._getValidDateOrNull(\n this._dateAdapter.deserialize(value)\n );\n this._selectedYear =\n this._selected && this._dateAdapter.getYear(this._selected);\n }\n private _selected: D | null;\n\n /** The minimum selectable date. */\n @Input()\n get minDate(): D | null {\n return this._minDate;\n }\n set minDate(value: D | null) {\n this._minDate = this._getValidDateOrNull(\n this._dateAdapter.deserialize(value)\n );\n }\n private _minDate: D | null;\n\n /** The maximum selectable date. */\n @Input()\n get maxDate(): D | null {\n return this._maxDate;\n }\n set maxDate(value: D | null) {\n this._maxDate = this._getValidDateOrNull(\n this._dateAdapter.deserialize(value)\n );\n }\n private _maxDate: D | null;\n\n /** A function used to filter which dates are selectable. */\n @Input() dateFilter: (date: D) => boolean;\n\n /** Emits when a new year is selected. */\n @Output() readonly selectedChange: EventEmitter = new EventEmitter();\n\n /** Emits the selected year. This doesn't imply a change on the selected date */\n @Output() readonly yearSelected: EventEmitter = new EventEmitter();\n\n /** Emits when any date is activated. */\n @Output() readonly activeDateChange: EventEmitter = new EventEmitter();\n\n /** The body of calendar table */\n @ViewChild(OuiCalendarBody)\n _ouiCalendarBody: OuiCalendarBody;\n\n /** Grid of calendar cells representing the currently displayed years. */\n _years: OuiCalendarCell[][];\n\n /** The year that today falls on. */\n _todayYear: number;\n\n /** The year of the selected date. Null if the selected date is null. */\n _selectedYear: number | null;\n\n constructor(\n private _changeDetectorRef: ChangeDetectorRef,\n @Optional() public _dateAdapter: DateAdapter,\n @Optional() private _dir?: Directionality\n ) {\n if (!this._dateAdapter) {\n throw createMissingDateImplError('DateAdapter');\n }\n\n this._activeDate = this._dateAdapter.today();\n }\n\n ngAfterContentInit() {\n this._init();\n }\n\n /** Initializes this multi-year view. */\n _init() {\n this._todayYear = this._dateAdapter.getYear(this._dateAdapter.today());\n const activeYear = this._dateAdapter.getYear(this._activeDate);\n const activeOffset = activeYear % yearsPerPage;\n this._years = [];\n for (let i = 0, row: number[] = []; i < yearsPerPage; i++) {\n row.push(activeYear - activeOffset + i);\n if (row.length === yearsPerRow) {\n this._years.push(row.map((year) => this._createCellForYear(year)));\n row = [];\n }\n }\n this._changeDetectorRef.markForCheck();\n }\n\n /** Handles when a new year is selected. */\n _yearSelected(year: number) {\n this.yearSelected.emit(this._dateAdapter.createDate(year, 0, 1));\n const month = this._dateAdapter.getMonth(this.activeDate);\n const daysInMonth = this._dateAdapter.getNumDaysInMonth(\n this._dateAdapter.createDate(year, month, 1)\n );\n this.selectedChange.emit(\n this._dateAdapter.createDate(\n year,\n month,\n Math.min(this._dateAdapter.getDate(this.activeDate), daysInMonth)\n )\n );\n }\n\n /** Handles keydown events on the calendar body when calendar is in multi-year view. */\n _handleCalendarBodyKeydown(event: KeyboardEvent): void {\n // TODO(mmalerba): We currently allow keyboard navigation to disabled dates, but just prevent\n // disabled ones from being selected. This may not be ideal, we should look into whether\n // navigation should skip over disabled dates, and if so, how to implement that efficiently.\n\n const oldActiveDate = this._activeDate;\n const isRtl = this._isRtl();\n\n switch (event.keyCode) {\n case LEFT_ARROW:\n this.activeDate = this._dateAdapter.addCalendarYears(\n this._activeDate,\n isRtl ? 1 : -1\n );\n break;\n case RIGHT_ARROW:\n this.activeDate = this._dateAdapter.addCalendarYears(\n this._activeDate,\n isRtl ? -1 : 1\n );\n break;\n case UP_ARROW:\n this.activeDate = this._dateAdapter.addCalendarYears(\n this._activeDate,\n -yearsPerRow\n );\n break;\n case DOWN_ARROW:\n this.activeDate = this._dateAdapter.addCalendarYears(\n this._activeDate,\n yearsPerRow\n );\n break;\n case HOME:\n this.activeDate = this._dateAdapter.addCalendarYears(\n this._activeDate,\n -this._dateAdapter.getYear(this._activeDate) % yearsPerPage\n );\n break;\n case END:\n this.activeDate = this._dateAdapter.addCalendarYears(\n this._activeDate,\n yearsPerPage -\n (this._dateAdapter.getYear(this._activeDate) % yearsPerPage) -\n 1\n );\n break;\n case PAGE_UP:\n this.activeDate = this._dateAdapter.addCalendarYears(\n this._activeDate,\n event.altKey ? -yearsPerPage * 10 : -yearsPerPage\n );\n break;\n case PAGE_DOWN:\n this.activeDate = this._dateAdapter.addCalendarYears(\n this._activeDate,\n event.altKey ? yearsPerPage * 10 : yearsPerPage\n );\n break;\n case ENTER:\n case SPACE:\n this._yearSelected(this._dateAdapter.getYear(this._activeDate));\n break;\n default:\n // Don't prevent default or focus active cell on keys that we don't explicitly handle.\n return;\n }\n\n if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) {\n this.activeDateChange.emit(this.activeDate);\n }\n\n this._focusActiveCell();\n // Prevent unexpected default actions such as form submission.\n event.preventDefault();\n }\n\n _getActiveCell(): number {\n return this._dateAdapter.getYear(this.activeDate) % yearsPerPage;\n }\n\n /** Focuses the active cell after the microtask queue is empty. */\n _focusActiveCell() {\n this._ouiCalendarBody._focusActiveCell();\n }\n\n /** Creates an OuiCalendarCell for the given year. */\n private _createCellForYear(year: number) {\n const yearName = this._dateAdapter.getYearName(\n this._dateAdapter.createDate(year, 0, 1)\n );\n return new OuiCalendarCell(\n year,\n yearName,\n yearName,\n this._shouldEnableYear(year)\n );\n }\n\n /** Whether the given year is enabled. */\n private _shouldEnableYear(year: number) {\n // disable if the year is greater than maxDate lower than minDate\n if (\n year === undefined ||\n year === null ||\n (this.maxDate && year > this._dateAdapter.getYear(this.maxDate)) ||\n (this.minDate && year < this._dateAdapter.getYear(this.minDate))\n ) {\n return false;\n }\n\n // enable if it reaches here and there's no filter defined\n if (!this.dateFilter) {\n return true;\n }\n\n const firstOfYear = this._dateAdapter.createDate(year, 0, 1);\n\n // If any date in the year is enabled count the year as enabled.\n for (\n let date = firstOfYear;\n this._dateAdapter.getYear(date) === year;\n date = this._dateAdapter.addCalendarDays(date, 1)\n ) {\n if (this.dateFilter(date)) {\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * @param obj The object to check.\n * @returns The given object if it is both a date instance and valid, otherwise null.\n */\n private _getValidDateOrNull(obj: any): D | null {\n return this._dateAdapter.isDateInstance(obj) &&\n this._dateAdapter.isValid(obj as any as D)\n ? (obj as any as D)\n : null;\n }\n\n /** Determines whether the user has the RTL layout direction. */\n private _isRtl() {\n return this._dir && this._dir.value === 'rtl';\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -30045,11 +39903,11 @@ } } }, - "templateData": "\r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n" + "templateData": "\n \n \n \n \n \n \n
\n" }, { "name": "OuiNestedMenuStorybook", - "id": "component-OuiNestedMenuStorybook-f61fccaca4ea7412af3da4cf29b8b3d840217746285146fbb7fb7e1e2c9cb41f6d92a50c2a77747647b8ac7ed38d051641255d52314051bc7c1f6a837ec0164e", + "id": "component-OuiNestedMenuStorybook-e30d8fe1bee5b4056ac8703f11c48017c65ba83c7a44ab8600e5f94e84eb24a087d322cc079379ecb38052105aca5ab8c36fd5eab1aa55ce7f5a1555df898836", "file": "ui/src/stories/menu/menu.component.ts", "encapsulation": [], "entryComponents": [], @@ -30166,7 +40024,7 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import { OuiIconRegistry } from '../../components';\r\nimport { Component, Input, Output, EventEmitter } from '@angular/core';\r\nimport { DomSanitizer } from '@angular/platform-browser';\r\n\r\n@Component({\r\n selector: 'oui-menu-storybook',\r\n template: `\r\n
\r\n \r\n \r\n
\r\n \r\n \r\n \r\n \r\n \r\n `,\r\n})\r\nexport class OuiMenuStorybook {\r\n @Input() xPosition = 'before';\r\n @Input() yPosition = 'above';\r\n @Input() vertical = false;\r\n @Input() hasBackdrop = true;\r\n\r\n @Output()\r\n readonly _closed: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _opened: EventEmitter = new EventEmitter();\r\n constructor(\r\n private ouiIconRegistry: OuiIconRegistry,\r\n private domSanitizer: DomSanitizer\r\n ) {\r\n this.ouiIconRegistry.addSvgIcon(\r\n `local`,\r\n this.domSanitizer.bypassSecurityTrustResourceUrl(\r\n `/assets/images/v-green.svg`\r\n )\r\n );\r\n\r\n this.ouiIconRegistry.addSvgIconSet(\r\n this.domSanitizer.bypassSecurityTrustResourceUrl(\r\n 'https://d1azc1qln24ryf.cloudfront.net/135790/oncehub-20/symbol-defs.svg?hn1bl5ss'\r\n )\r\n );\r\n }\r\n opened() {\r\n this._opened.emit();\r\n }\r\n closed(e: string) {\r\n this._closed.emit(e);\r\n }\r\n}\r\n\r\n@Component({\r\n selector: 'oui-nested-menu-storybook',\r\n template: `\r\n
\r\n \r\n \r\n
\r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n `,\r\n})\r\nexport class OuiNestedMenuStorybook {\r\n @Input() xPosition = 'before';\r\n @Input() yPosition = 'above';\r\n @Input() vertical = false;\r\n @Input() hasBackdrop = true;\r\n @Output()\r\n readonly _closed: EventEmitter = new EventEmitter();\r\n @Output()\r\n readonly _opened: EventEmitter = new EventEmitter();\r\n constructor(\r\n private ouiIconRegistry: OuiIconRegistry,\r\n private domSanitizer: DomSanitizer\r\n ) {\r\n this.ouiIconRegistry.addSvgIcon(\r\n `local`,\r\n this.domSanitizer.bypassSecurityTrustResourceUrl(\r\n `/assets/images/v-green.svg`\r\n )\r\n );\r\n\r\n this.ouiIconRegistry.addSvgIconSet(\r\n this.domSanitizer.bypassSecurityTrustResourceUrl(\r\n 'https://d1azc1qln24ryf.cloudfront.net/135790/oncehub-20/symbol-defs.svg?hn1bl5'\r\n )\r\n );\r\n }\r\n opened() {\r\n this._opened.emit();\r\n }\r\n closed(e: string) {\r\n this._closed.emit(e);\r\n }\r\n}\r\n", + "sourceCode": "import { OuiIconRegistry } from '../../components';\nimport { Component, Input, Output, EventEmitter } from '@angular/core';\nimport { DomSanitizer } from '@angular/platform-browser';\n\n@Component({\n selector: 'oui-menu-storybook',\n template: `\n
\n \n \n
\n \n \n \n \n \n `,\n})\nexport class OuiMenuStorybook {\n @Input() xPosition = 'before';\n @Input() yPosition = 'above';\n @Input() vertical = false;\n @Input() hasBackdrop = true;\n\n @Output()\n readonly _closed: EventEmitter = new EventEmitter();\n @Output()\n readonly _opened: EventEmitter = new EventEmitter();\n constructor(\n private ouiIconRegistry: OuiIconRegistry,\n private domSanitizer: DomSanitizer\n ) {\n this.ouiIconRegistry.addSvgIcon(\n `local`,\n this.domSanitizer.bypassSecurityTrustResourceUrl(\n `/assets/images/v-green.svg`\n )\n );\n\n this.ouiIconRegistry.addSvgIconSet(\n this.domSanitizer.bypassSecurityTrustResourceUrl(\n 'https://d1azc1qln24ryf.cloudfront.net/135790/oncehub-20/symbol-defs.svg?hn1bl5ss'\n )\n );\n }\n opened() {\n this._opened.emit();\n }\n closed(e: string) {\n this._closed.emit(e);\n }\n}\n\n@Component({\n selector: 'oui-nested-menu-storybook',\n template: `\n
\n \n \n
\n \n \n \n \n\n \n \n \n \n \n `,\n})\nexport class OuiNestedMenuStorybook {\n @Input() xPosition = 'before';\n @Input() yPosition = 'above';\n @Input() vertical = false;\n @Input() hasBackdrop = true;\n @Output()\n readonly _closed: EventEmitter = new EventEmitter();\n @Output()\n readonly _opened: EventEmitter = new EventEmitter();\n constructor(\n private ouiIconRegistry: OuiIconRegistry,\n private domSanitizer: DomSanitizer\n ) {\n this.ouiIconRegistry.addSvgIcon(\n `local`,\n this.domSanitizer.bypassSecurityTrustResourceUrl(\n `/assets/images/v-green.svg`\n )\n );\n\n this.ouiIconRegistry.addSvgIconSet(\n this.domSanitizer.bypassSecurityTrustResourceUrl(\n 'https://d1azc1qln24ryf.cloudfront.net/135790/oncehub-20/symbol-defs.svg?hn1bl5'\n )\n );\n }\n opened() {\n this._opened.emit();\n }\n closed(e: string) {\n this._closed.emit(e);\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -30214,7 +40072,7 @@ }, { "name": "OuiOptgroup", - "id": "component-OuiOptgroup-23e976f6ed33c55958bd5c662bad2117903484120aa015a52139b02b7e6ac42fabe8294c42177c66a50e1ae9107028b87169edc50b35f584f88bd68e6ba30f7b", + "id": "component-OuiOptgroup-54797be63f2be6c5feebb44b106c9321518d91d35eaaf3c85fad495ee4cebf75a24107557ff1d94b4c98d3aab98080e4d47f509155e1d1ed209c251d9c838955", "file": "ui/src/components/core/option/optgroup.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -30271,11 +40129,11 @@ "description": "

Component that is used to group instances of oui-option.

\n", "rawdescription": "\n\nComponent that is used to group instances of `oui-option`.\n", "type": "component", - "sourceCode": "import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n Input,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\nimport {\r\n CanDisable,\r\n CanDisableCtor,\r\n mixinDisabled,\r\n} from '../common-behaviors/disabled';\r\n\r\n// Boilerplate for applying mixins to OuiOptgroup.\r\n/** @docs-private */\r\nexport class OuiOptgroupBase {}\r\nexport const _OuiOptgroupMixinBase: CanDisableCtor & typeof OuiOptgroupBase =\r\n mixinDisabled(OuiOptgroupBase);\r\n\r\n// Counter for unique group ids.\r\nlet _uniqueOptgroupIdCounter = 0;\r\n\r\n/**\r\n * Component that is used to group instances of `oui-option`.\r\n */\r\n@Component({\r\n selector: 'oui-optgroup',\r\n exportAs: 'ouiOptgroup',\r\n templateUrl: 'optgroup.html',\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['disabled'],\r\n styleUrls: ['optgroup.scss'],\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-optgroup',\r\n role: 'group',\r\n '[class.oui-optgroup-disabled]': 'disabled',\r\n '[attr.aria-disabled]': 'disabled.toString()',\r\n '[attr.aria-labelledby]': '_labelId',\r\n },\r\n})\r\nexport class OuiOptgroup extends _OuiOptgroupMixinBase implements CanDisable {\r\n /** Label for the option group. */\r\n @Input()\r\n label: string;\r\n\r\n /** Unique id for the underlying label. */\r\n // eslint-disable-next-line @typescript-eslint/no-inferrable-types\r\n _labelId: string = `oui-optgroup-label-${_uniqueOptgroupIdCounter++}`;\r\n}\r\n", + "sourceCode": "import {\n ChangeDetectionStrategy,\n Component,\n Input,\n ViewEncapsulation,\n} from '@angular/core';\nimport {\n CanDisable,\n CanDisableCtor,\n mixinDisabled,\n} from '../common-behaviors/disabled';\n\n// Boilerplate for applying mixins to OuiOptgroup.\n/** @docs-private */\nexport class OuiOptgroupBase {}\nexport const _OuiOptgroupMixinBase: CanDisableCtor & typeof OuiOptgroupBase =\n mixinDisabled(OuiOptgroupBase);\n\n// Counter for unique group ids.\nlet _uniqueOptgroupIdCounter = 0;\n\n/**\n * Component that is used to group instances of `oui-option`.\n */\n@Component({\n selector: 'oui-optgroup',\n exportAs: 'ouiOptgroup',\n templateUrl: 'optgroup.html',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\n inputs: ['disabled'],\n styleUrls: ['optgroup.scss'],\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-optgroup',\n role: 'group',\n '[class.oui-optgroup-disabled]': 'disabled',\n '[attr.aria-disabled]': 'disabled.toString()',\n '[attr.aria-labelledby]': '_labelId',\n },\n})\nexport class OuiOptgroup extends _OuiOptgroupMixinBase implements CanDisable {\n /** Label for the option group. */\n @Input()\n label: string;\n\n /** Unique id for the underlying label. */\n // eslint-disable-next-line @typescript-eslint/no-inferrable-types\n _labelId: string = `oui-optgroup-label-${_uniqueOptgroupIdCounter++}`;\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": "/* Implement style for option group */\r\n", + "data": "/* Implement style for option group */\n", "styleUrl": "optgroup.scss" } ], @@ -30284,11 +40142,11 @@ "implements": [ "CanDisable" ], - "templateData": "\r\n\r\n" + "templateData": "\n\n" }, { "name": "OuiOption", - "id": "component-OuiOption-6fc64e610aee59f7080d997cfd2f5ef405370cdd8a2dbe3c9bb949eb30dd0f328535f07ea44816a68760f867d1c6382ee6fb95be7bff92c4ac15f28f621bd745", + "id": "component-OuiOption-436221d2917bb329efe9ec76f95eb377b81359f9380f577db0c0ce2da5a0904b6eee34d58bbfe6f9af17f79a4a5c6deb5879f211cbb416ef7a77f3b11b3bc534", "file": "ui/src/components/core/option/option.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -30637,11 +40495,11 @@ "description": "

Single option inside of a <oui-select> element.

\n", "rawdescription": "\n\nSingle option inside of a `` element.\n", "type": "component", - "sourceCode": "import { coerceBooleanProperty } from '@angular/cdk/coercion';\r\nimport { ENTER, SPACE } from '@angular/cdk/keycodes';\r\nimport {\r\n AfterViewChecked,\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n ElementRef,\r\n EventEmitter,\r\n Inject,\r\n InjectionToken,\r\n Input,\r\n OnDestroy,\r\n Optional,\r\n Output,\r\n QueryList,\r\n ViewEncapsulation,\r\n NgZone,\r\n} from '@angular/core';\r\nimport { Subject, Subscription } from 'rxjs';\r\nimport { OuiOptgroup } from './optgroup';\r\nimport { FocusMonitor } from '@angular/cdk/a11y';\r\n\r\n/**\r\n * Option IDs need to be unique across components, so this counter exists outside of\r\n * the component definition.\r\n */\r\nlet _uniqueIdCounter = 0;\r\n\r\n/** Event object emitted by OuiOption when selected or deselected. */\r\nexport class OuiOptionSelectionChange {\r\n constructor(\r\n /** Reference to the option that emitted the event. */\r\n public source: OuiOption,\r\n /** Whether the change in the option's value was a result of a user action. */\r\n public isUserInput = false\r\n ) {}\r\n}\r\n\r\n/**\r\n * Describes a parent component that manages a list of options.\r\n * Contains properties that the options can inherit.\r\n *\r\n * @docs-private\r\n */\r\nexport interface OuiOptionParentComponent {\r\n multiple?: boolean;\r\n}\r\n\r\n/**\r\n * Injection token used to provide the parent component to options.\r\n */\r\nexport const OUI_OPTION_PARENT_COMPONENT =\r\n new InjectionToken('OUI_OPTION_PARENT_COMPONENT');\r\n\r\n/**\r\n * Single option inside of a `` element.\r\n */\r\n@Component({\r\n selector: 'oui-option',\r\n exportAs: 'ouiOption',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n role: 'option',\r\n '[attr.tabindex]': '_getTabIndex()',\r\n '[class.oui-selected]': 'selected',\r\n '[class.oui-option-multiple]': 'multiple',\r\n '[class.oui-active]': 'active',\r\n '[id]': 'id',\r\n '[attr.aria-selected]': 'selected.toString()',\r\n '[attr.aria-disabled]': 'disabled.toString()',\r\n '[class.oui-option-disabled]': 'disabled',\r\n '(click)': '_selectViaInteraction()',\r\n '(keydown)': '_handleKeydown($event)',\r\n class: 'oui-option',\r\n },\r\n styleUrls: ['option.scss'],\r\n templateUrl: 'option.html',\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class OuiOption implements AfterViewChecked, OnDestroy {\r\n private _selected = false;\r\n private _active = false;\r\n private _disabled = false;\r\n private _mostRecentViewValue = '';\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n\r\n /** Whether the wrapping component is in multiple selection mode. */\r\n get multiple() {\r\n return this._parent && this._parent.multiple;\r\n }\r\n\r\n /** Whether or not the option is currently selected. */\r\n get selected(): boolean {\r\n return this._selected;\r\n }\r\n\r\n /** The form value of the option. */\r\n @Input()\r\n value: any;\r\n\r\n /** The unique ID of the option. */\r\n @Input()\r\n // eslint-disable-next-line @typescript-eslint/no-inferrable-types\r\n id = `oui-option-${_uniqueIdCounter++}`;\r\n\r\n /** Whether the option is disabled. */\r\n @Input()\r\n get disabled() {\r\n return (this.group && this.group.disabled) || this._disabled;\r\n }\r\n set disabled(value: any) {\r\n this._disabled = coerceBooleanProperty(value);\r\n }\r\n\r\n /** Event emitted when the option is selected or deselected. */\r\n // eslint-disable-next-line @angular-eslint/no-output-on-prefix\r\n @Output()\r\n // eslint-disable-next-line @angular-eslint/no-output-on-prefix\r\n readonly onSelectionChange = new EventEmitter();\r\n\r\n /** Emits when the state of the option changes and any parents have to be notified. */\r\n readonly _stateChanges = new Subject();\r\n\r\n constructor(\r\n private _element: ElementRef,\r\n private _changeDetectorRef: ChangeDetectorRef,\r\n protected elementRef: ElementRef,\r\n private _focusMonitor: FocusMonitor,\r\n private _ngZone: NgZone,\r\n @Optional()\r\n @Inject(OUI_OPTION_PARENT_COMPONENT)\r\n private _parent: OuiOptionParentComponent,\r\n @Optional() readonly group: OuiOptgroup\r\n ) {\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this.elementRef, true)\r\n .subscribe(() => this._ngZone.run(() => {}));\r\n }\r\n\r\n /**\r\n * Whether or not the option is currently active and ready to be selected.\r\n * An active option displays styles as if it is focused, but the\r\n * focus is actually retained somewhere else. This comes in handy\r\n * for components like autocomplete where focus must remain on the input.\r\n */\r\n get active(): boolean {\r\n return this._active;\r\n }\r\n\r\n /**\r\n * The displayed value of the option. It is necessary to show the selected option in the\r\n * select's trigger.\r\n */\r\n get viewValue(): string {\r\n return (this._getHostElement().textContent || '').trim();\r\n }\r\n\r\n /**\r\n * The displayed value of the option. It is necessary to show the selected option in the\r\n * select's trigger.\r\n */\r\n get viewValueForSelect(): string {\r\n return this._getHostElement().querySelector('.oui-option-text').innerHTML;\r\n }\r\n\r\n /** Selects the option. */\r\n select(): void {\r\n if (!this._selected) {\r\n this._selected = true;\r\n this._changeDetectorRef.markForCheck();\r\n this._emitSelectionChangeEvent();\r\n }\r\n }\r\n\r\n /** Deselects the option. */\r\n deselect(): void {\r\n if (this._selected) {\r\n this._selected = false;\r\n this._changeDetectorRef.markForCheck();\r\n this._emitSelectionChangeEvent();\r\n }\r\n }\r\n\r\n /** Sets focus onto this option. */\r\n focus(): void {\r\n const element = this._getHostElement();\r\n\r\n if (typeof element.focus === 'function') {\r\n element.focus();\r\n }\r\n }\r\n\r\n /**\r\n * This method sets display styles on the option to make it appear\r\n * active. This is used by the ActiveDescendantKeyManager so key\r\n * events will display the proper options as active on arrow key events.\r\n */\r\n setActiveStyles(): void {\r\n if (!this._active) {\r\n this._active = true;\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n }\r\n\r\n /**\r\n * This method removes display styles on the option that made it appear\r\n * active. This is used by the ActiveDescendantKeyManager so key\r\n * events will display the proper options as active on arrow key events.\r\n */\r\n setInactiveStyles(): void {\r\n if (this._active) {\r\n this._active = false;\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n }\r\n\r\n /** Gets the label to be used when determining whether the option should be focused. */\r\n getLabel(): string {\r\n return this.viewValue;\r\n }\r\n\r\n /** Ensures the option is selected when activated from the keyboard. */\r\n _handleKeydown(event: KeyboardEvent): void {\r\n if (event.keyCode === ENTER || event.keyCode === SPACE) {\r\n this._selectViaInteraction();\r\n\r\n // Prevent the page from scrolling down and form submits.\r\n event.preventDefault();\r\n }\r\n }\r\n\r\n /**\r\n * `Selects the option while indicating the selection came from the user. Used to\r\n * determine if the select's view -> model callback should be invoked.`\r\n */\r\n _selectViaInteraction(): void {\r\n if (!this.disabled) {\r\n this._selected = this.multiple ? !this._selected : true;\r\n this._changeDetectorRef.markForCheck();\r\n this._emitSelectionChangeEvent(true);\r\n }\r\n }\r\n\r\n /** Returns the correct tabindex for the option depending on disabled state. */\r\n _getTabIndex(): string {\r\n return this.disabled ? '-1' : '0';\r\n }\r\n\r\n /** Gets the host DOM element. */\r\n _getHostElement(): HTMLElement {\r\n return this._element.nativeElement;\r\n }\r\n\r\n ngAfterViewChecked() {\r\n // Since parent components could be using the option's label to display the selected values\r\n // (e.g. `oui-select`) and they don't have a way of knowing if the option's label has changed\r\n // we have to check for changes in the DOM ourselves and dispatch an event. These checks are\r\n // relatively cheap, however we still limit them only to selected options in order to avoid\r\n // hitting the DOM too often.\r\n if (this._selected) {\r\n const viewValue = this.viewValue;\r\n\r\n if (viewValue !== this._mostRecentViewValue) {\r\n this._mostRecentViewValue = viewValue;\r\n this._stateChanges.next();\r\n }\r\n }\r\n }\r\n\r\n ngOnDestroy() {\r\n this._stateChanges.complete();\r\n this._monitorSubscription.unsubscribe();\r\n this._focusMonitor.stopMonitoring(this.elementRef);\r\n }\r\n\r\n /** Emits the selection change event. */\r\n private _emitSelectionChangeEvent(isUserInput = false): void {\r\n this.onSelectionChange.emit(\r\n new OuiOptionSelectionChange(this, isUserInput)\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Counts the amount of option group labels that precede the specified option.\r\n *\r\n * @param optionIndex Index of the option at which to start counting.\r\n * @param options Flat list of all of the options.\r\n * @param optionGroups Flat list of all of the option groups.\r\n * @docs-private\r\n */\r\nexport function _countGroupLabelsBeforeOption(\r\n optionIndex: number,\r\n options: QueryList,\r\n optionGroups: QueryList\r\n): number {\r\n if (optionGroups.length) {\r\n const optionsArray = options.toArray();\r\n const groups = optionGroups.toArray();\r\n let groupCounter = 0;\r\n\r\n for (let i = 0; i <= optionIndex; i++) {\r\n if (\r\n optionsArray[i].group &&\r\n optionsArray[i].group === groups[groupCounter]\r\n ) {\r\n groupCounter++;\r\n }\r\n }\r\n\r\n return groupCounter;\r\n }\r\n\r\n return 0;\r\n}\r\n\r\n/**\r\n * Determines the position to which to scroll a panel in order for an option to be into view.\r\n *\r\n * @param optionIndex Index of the option to be scrolled into the view.\r\n * @param optionHeight Height of the options.\r\n * @param currentScrollPosition Current scroll position of the panel.\r\n * @param panelHeight Height of the panel.\r\n * @docs-private\r\n */\r\nexport function _getOptionScrollPosition(\r\n optionIndex: number,\r\n optionHeight: number,\r\n currentScrollPosition: number,\r\n panelHeight: number\r\n): number {\r\n const optionOffset = optionIndex * optionHeight;\r\n\r\n if (optionOffset < currentScrollPosition) {\r\n return optionOffset;\r\n }\r\n\r\n if (optionOffset + optionHeight > currentScrollPosition + panelHeight) {\r\n return Math.max(0, optionOffset - panelHeight + optionHeight);\r\n }\r\n\r\n return currentScrollPosition;\r\n}\r\n", + "sourceCode": "import { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { ENTER, SPACE } from '@angular/cdk/keycodes';\nimport {\n AfterViewChecked,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n ElementRef,\n EventEmitter,\n Inject,\n InjectionToken,\n Input,\n OnDestroy,\n Optional,\n Output,\n QueryList,\n ViewEncapsulation,\n NgZone,\n} from '@angular/core';\nimport { Subject, Subscription } from 'rxjs';\nimport { OuiOptgroup } from './optgroup';\nimport { FocusMonitor } from '@angular/cdk/a11y';\n\n/**\n * Option IDs need to be unique across components, so this counter exists outside of\n * the component definition.\n */\nlet _uniqueIdCounter = 0;\n\n/** Event object emitted by OuiOption when selected or deselected. */\nexport class OuiOptionSelectionChange {\n constructor(\n /** Reference to the option that emitted the event. */\n public source: OuiOption,\n /** Whether the change in the option's value was a result of a user action. */\n public isUserInput = false\n ) {}\n}\n\n/**\n * Describes a parent component that manages a list of options.\n * Contains properties that the options can inherit.\n *\n * @docs-private\n */\nexport interface OuiOptionParentComponent {\n multiple?: boolean;\n}\n\n/**\n * Injection token used to provide the parent component to options.\n */\nexport const OUI_OPTION_PARENT_COMPONENT =\n new InjectionToken('OUI_OPTION_PARENT_COMPONENT');\n\n/**\n * Single option inside of a `` element.\n */\n@Component({\n selector: 'oui-option',\n exportAs: 'ouiOption',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n role: 'option',\n '[attr.tabindex]': '_getTabIndex()',\n '[class.oui-selected]': 'selected',\n '[class.oui-option-multiple]': 'multiple',\n '[class.oui-active]': 'active',\n '[id]': 'id',\n '[attr.aria-selected]': 'selected.toString()',\n '[attr.aria-disabled]': 'disabled.toString()',\n '[class.oui-option-disabled]': 'disabled',\n '(click)': '_selectViaInteraction()',\n '(keydown)': '_handleKeydown($event)',\n class: 'oui-option',\n },\n styleUrls: ['option.scss'],\n templateUrl: 'option.html',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class OuiOption implements AfterViewChecked, OnDestroy {\n private _selected = false;\n private _active = false;\n private _disabled = false;\n private _mostRecentViewValue = '';\n private _monitorSubscription: Subscription = Subscription.EMPTY;\n\n /** Whether the wrapping component is in multiple selection mode. */\n get multiple() {\n return this._parent && this._parent.multiple;\n }\n\n /** Whether or not the option is currently selected. */\n get selected(): boolean {\n return this._selected;\n }\n\n /** The form value of the option. */\n @Input()\n value: any;\n\n /** The unique ID of the option. */\n @Input()\n // eslint-disable-next-line @typescript-eslint/no-inferrable-types\n id = `oui-option-${_uniqueIdCounter++}`;\n\n /** Whether the option is disabled. */\n @Input()\n get disabled() {\n return (this.group && this.group.disabled) || this._disabled;\n }\n set disabled(value: any) {\n this._disabled = coerceBooleanProperty(value);\n }\n\n /** Event emitted when the option is selected or deselected. */\n // eslint-disable-next-line @angular-eslint/no-output-on-prefix\n @Output()\n // eslint-disable-next-line @angular-eslint/no-output-on-prefix\n readonly onSelectionChange = new EventEmitter();\n\n /** Emits when the state of the option changes and any parents have to be notified. */\n readonly _stateChanges = new Subject();\n\n constructor(\n private _element: ElementRef,\n private _changeDetectorRef: ChangeDetectorRef,\n protected elementRef: ElementRef,\n private _focusMonitor: FocusMonitor,\n private _ngZone: NgZone,\n @Optional()\n @Inject(OUI_OPTION_PARENT_COMPONENT)\n private _parent: OuiOptionParentComponent,\n @Optional() readonly group: OuiOptgroup\n ) {\n this._monitorSubscription = this._focusMonitor\n .monitor(this.elementRef, true)\n .subscribe(() => this._ngZone.run(() => {}));\n }\n\n /**\n * Whether or not the option is currently active and ready to be selected.\n * An active option displays styles as if it is focused, but the\n * focus is actually retained somewhere else. This comes in handy\n * for components like autocomplete where focus must remain on the input.\n */\n get active(): boolean {\n return this._active;\n }\n\n /**\n * The displayed value of the option. It is necessary to show the selected option in the\n * select's trigger.\n */\n get viewValue(): string {\n return (this._getHostElement().textContent || '').trim();\n }\n\n /**\n * The displayed value of the option. It is necessary to show the selected option in the\n * select's trigger.\n */\n get viewValueForSelect(): string {\n return this._getHostElement().querySelector('.oui-option-text').innerHTML;\n }\n\n /** Selects the option. */\n select(): void {\n if (!this._selected) {\n this._selected = true;\n this._changeDetectorRef.markForCheck();\n this._emitSelectionChangeEvent();\n }\n }\n\n /** Deselects the option. */\n deselect(): void {\n if (this._selected) {\n this._selected = false;\n this._changeDetectorRef.markForCheck();\n this._emitSelectionChangeEvent();\n }\n }\n\n /** Sets focus onto this option. */\n focus(): void {\n const element = this._getHostElement();\n\n if (typeof element.focus === 'function') {\n element.focus();\n }\n }\n\n /**\n * This method sets display styles on the option to make it appear\n * active. This is used by the ActiveDescendantKeyManager so key\n * events will display the proper options as active on arrow key events.\n */\n setActiveStyles(): void {\n if (!this._active) {\n this._active = true;\n this._changeDetectorRef.markForCheck();\n }\n }\n\n /**\n * This method removes display styles on the option that made it appear\n * active. This is used by the ActiveDescendantKeyManager so key\n * events will display the proper options as active on arrow key events.\n */\n setInactiveStyles(): void {\n if (this._active) {\n this._active = false;\n this._changeDetectorRef.markForCheck();\n }\n }\n\n /** Gets the label to be used when determining whether the option should be focused. */\n getLabel(): string {\n return this.viewValue;\n }\n\n /** Ensures the option is selected when activated from the keyboard. */\n _handleKeydown(event: KeyboardEvent): void {\n if (event.keyCode === ENTER || event.keyCode === SPACE) {\n this._selectViaInteraction();\n\n // Prevent the page from scrolling down and form submits.\n event.preventDefault();\n }\n }\n\n /**\n * `Selects the option while indicating the selection came from the user. Used to\n * determine if the select's view -> model callback should be invoked.`\n */\n _selectViaInteraction(): void {\n if (!this.disabled) {\n this._selected = this.multiple ? !this._selected : true;\n this._changeDetectorRef.markForCheck();\n this._emitSelectionChangeEvent(true);\n }\n }\n\n /** Returns the correct tabindex for the option depending on disabled state. */\n _getTabIndex(): string {\n return this.disabled ? '-1' : '0';\n }\n\n /** Gets the host DOM element. */\n _getHostElement(): HTMLElement {\n return this._element.nativeElement;\n }\n\n ngAfterViewChecked() {\n // Since parent components could be using the option's label to display the selected values\n // (e.g. `oui-select`) and they don't have a way of knowing if the option's label has changed\n // we have to check for changes in the DOM ourselves and dispatch an event. These checks are\n // relatively cheap, however we still limit them only to selected options in order to avoid\n // hitting the DOM too often.\n if (this._selected) {\n const viewValue = this.viewValue;\n\n if (viewValue !== this._mostRecentViewValue) {\n this._mostRecentViewValue = viewValue;\n this._stateChanges.next();\n }\n }\n }\n\n ngOnDestroy() {\n this._stateChanges.complete();\n this._monitorSubscription.unsubscribe();\n this._focusMonitor.stopMonitoring(this.elementRef);\n }\n\n /** Emits the selection change event. */\n private _emitSelectionChangeEvent(isUserInput = false): void {\n this.onSelectionChange.emit(\n new OuiOptionSelectionChange(this, isUserInput)\n );\n }\n}\n\n/**\n * Counts the amount of option group labels that precede the specified option.\n *\n * @param optionIndex Index of the option at which to start counting.\n * @param options Flat list of all of the options.\n * @param optionGroups Flat list of all of the option groups.\n * @docs-private\n */\nexport function _countGroupLabelsBeforeOption(\n optionIndex: number,\n options: QueryList,\n optionGroups: QueryList\n): number {\n if (optionGroups.length) {\n const optionsArray = options.toArray();\n const groups = optionGroups.toArray();\n let groupCounter = 0;\n\n for (let i = 0; i <= optionIndex; i++) {\n if (\n optionsArray[i].group &&\n optionsArray[i].group === groups[groupCounter]\n ) {\n groupCounter++;\n }\n }\n\n return groupCounter;\n }\n\n return 0;\n}\n\n/**\n * Determines the position to which to scroll a panel in order for an option to be into view.\n *\n * @param optionIndex Index of the option to be scrolled into the view.\n * @param optionHeight Height of the options.\n * @param currentScrollPosition Current scroll position of the panel.\n * @param panelHeight Height of the panel.\n * @docs-private\n */\nexport function _getOptionScrollPosition(\n optionIndex: number,\n optionHeight: number,\n currentScrollPosition: number,\n panelHeight: number\n): number {\n const optionOffset = optionIndex * optionHeight;\n\n if (optionOffset < currentScrollPosition) {\n return optionOffset;\n }\n\n if (optionOffset + optionHeight > currentScrollPosition + panelHeight) {\n return Math.max(0, optionOffset - panelHeight + optionHeight);\n }\n\n return currentScrollPosition;\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": "@import '../style/menu-common';\r\n@import '../style/vendor-prefixes';\r\n\r\n// This mixin ensures an element spans to fill the nearest ancestor with defined positioning.\r\n@mixin oui-fill {\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n position: absolute;\r\n}\r\n\r\n.oui-option {\r\n @include oui-menu-item-base();\r\n position: relative;\r\n cursor: pointer;\r\n outline: none;\r\n display: flex;\r\n flex-direction: row;\r\n max-width: 100%;\r\n box-sizing: border-box;\r\n align-items: center;\r\n -webkit-tap-highlight-color: transparent;\r\n\r\n &[aria-disabled='true'] {\r\n @include user-select(none);\r\n cursor: default;\r\n }\r\n\r\n .oui-optgroup &:not(.oui-option-multiple) {\r\n [dir='rtl'] & {\r\n padding-left: $oui-menu-side-padding;\r\n padding-right: $oui-menu-side-padding * 2;\r\n }\r\n }\r\n &.oui-active {\r\n background: #eee !important;\r\n }\r\n}\r\n\r\n.single-type-checkbox {\r\n .oui-checkbox-inner-container {\r\n display: none;\r\n }\r\n}\r\n\r\n// Collapses unwanted whitespace created by newlines in code like the following:\r\n.oui-option-text {\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n display: inline-block;\r\n vertical-align: middle;\r\n width: 100%;\r\n}\r\n\r\n// Collapses unwanted whitespace created by newlines in code like the following:\r\n.oui-option-text {\r\n display: inline-block;\r\n flex-grow: 1;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n}\r\n\r\n.oui-option-multiple {\r\n .oui-checkbox-inner-container {\r\n display: block;\r\n }\r\n}\r\n\r\n.oui-option-pseudo-checkbox {\r\n $margin: $oui-menu-side-padding * 0.5;\r\n margin-right: $margin;\r\n\r\n [dir='rtl'] & {\r\n margin-left: $margin;\r\n margin-right: 0;\r\n }\r\n}\r\n", + "data": "@import '../style/menu-common';\n@import '../style/vendor-prefixes';\n\n// This mixin ensures an element spans to fill the nearest ancestor with defined positioning.\n@mixin oui-fill {\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n position: absolute;\n}\n\n.oui-option {\n @include oui-menu-item-base();\n position: relative;\n cursor: pointer;\n outline: none;\n display: flex;\n flex-direction: row;\n max-width: 100%;\n box-sizing: border-box;\n align-items: center;\n -webkit-tap-highlight-color: transparent;\n\n &[aria-disabled='true'] {\n @include user-select(none);\n cursor: default;\n }\n\n .oui-optgroup &:not(.oui-option-multiple) {\n [dir='rtl'] & {\n padding-left: $oui-menu-side-padding;\n padding-right: $oui-menu-side-padding * 2;\n }\n }\n &.oui-active {\n background: #eee !important;\n }\n}\n\n.single-type-checkbox {\n .oui-checkbox-inner-container {\n display: none;\n }\n}\n\n// Collapses unwanted whitespace created by newlines in code like the following:\n.oui-option-text {\n overflow: hidden;\n text-overflow: ellipsis;\n display: inline-block;\n vertical-align: middle;\n width: 100%;\n}\n\n// Collapses unwanted whitespace created by newlines in code like the following:\n.oui-option-text {\n display: inline-block;\n flex-grow: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.oui-option-multiple {\n .oui-checkbox-inner-container {\n display: block;\n }\n}\n\n.oui-option-pseudo-checkbox {\n $margin: $oui-menu-side-padding * 0.5;\n margin-right: $margin;\n\n [dir='rtl'] & {\n margin-left: $margin;\n margin-right: 0;\n }\n}\n", "styleUrl": "option.scss" } ], @@ -30861,11 +40719,11 @@ } } }, - "templateData": "\r\n\r\n \r\n" + "templateData": "\n\n \n" }, { "name": "OuiPaginator", - "id": "component-OuiPaginator-b25eb8ba70dbce36d1fe43caf6aed37ebb75e28851f9941b2325a9a146d07519fd3a2f2e779c1f030187e9f3c3c5b6004342fc1ebc9a5193750a812799033612", + "id": "component-OuiPaginator-16a6a96a973b191c2f2d233d276eeb07c72aacb9bb0352533e27c419456bbb2738cdb633d6715b007da23f7cc233d72ab2704c49080c6ecee6c3d87868e8a04a", "file": "ui/src/components/paginator/paginator.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -31248,11 +41106,11 @@ "description": "

Component to provide navigation between paged information. Displays the size of the current\npage, user-selectable options to change that size, what items are being shown, and\nnavigational button to go to the previous or next page.

\n", "rawdescription": "\n\nComponent to provide navigation between paged information. Displays the size of the current\npage, user-selectable options to change that size, what items are being shown, and\nnavigational button to go to the previous or next page.\n", "type": "component", - "sourceCode": "import {\r\n coerceNumberProperty,\r\n coerceBooleanProperty,\r\n} from '@angular/cdk/coercion';\r\nimport {\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n EventEmitter,\r\n Input,\r\n OnDestroy,\r\n OnInit,\r\n Output,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\nimport { Subscription } from 'rxjs';\r\nimport { OuiPaginatorIntl } from './paginator-intl';\r\nimport {\r\n HasInitialized,\r\n HasInitializedCtor,\r\n mixinInitialized,\r\n mixinDisabled,\r\n CanDisableCtor,\r\n CanDisable,\r\n} from '../core';\r\n\r\n/** The default page size if there is no page size and there are no provided page size options. */\r\nconst DEFAULT_PAGE_SIZE = 30;\r\n\r\n/**\r\n * Change event object that is emitted when the user selects a\r\n * different page size or navigates to another page.\r\n */\r\nexport class PageEvent {\r\n /** The current page index. */\r\n pageIndex: number;\r\n\r\n /**\r\n * Index of the page that was selected previously.\r\n *\r\n * @breaking-change 8.0.0 To be made into a required property.\r\n */\r\n previousPageIndex?: number;\r\n\r\n /** The current page size */\r\n pageSize: number;\r\n\r\n /** The current total number of items being paged */\r\n length: number;\r\n}\r\n\r\n// Boilerplate for applying mixins to OuiPaginator.\r\n/** @docs-private */\r\nexport class OuiPaginatorBase {}\r\nexport const _OuiPaginatorBase: CanDisableCtor &\r\n HasInitializedCtor &\r\n typeof OuiPaginatorBase = mixinDisabled(mixinInitialized(OuiPaginatorBase));\r\n\r\n/**\r\n * Component to provide navigation between paged information. Displays the size of the current\r\n * page, user-selectable options to change that size, what items are being shown, and\r\n * navigational button to go to the previous or next page.\r\n */\r\n@Component({\r\n selector: 'oui-paginator',\r\n exportAs: 'ouiPaginator',\r\n templateUrl: 'paginator.html',\r\n styleUrls: ['paginator.scss'],\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['disabled'],\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-paginator',\r\n },\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n})\r\nexport class OuiPaginator\r\n extends _OuiPaginatorBase\r\n implements OnInit, OnDestroy, CanDisable, HasInitialized\r\n{\r\n private _initialized: boolean;\r\n private _intlChanges: Subscription;\r\n\r\n /** The zero-based page index of the displayed list of items. Defaulted to 0. */\r\n @Input()\r\n get pageIndex(): number {\r\n return this._pageIndex;\r\n }\r\n set pageIndex(value: number) {\r\n this._pageIndex = Math.max(coerceNumberProperty(value), 0);\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n _pageIndex = 0;\r\n\r\n /** The length of the total number of items that are being paginated. Defaulted to 0. */\r\n @Input()\r\n get length(): number {\r\n return this._length;\r\n }\r\n set length(value: number) {\r\n this._length = coerceNumberProperty(value);\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n _length = 0;\r\n\r\n /** Number of items to display on a page. By default set to 50. */\r\n @Input()\r\n get pageSize(): number {\r\n return this._pageSize;\r\n }\r\n set pageSize(value: number) {\r\n this._pageSize = Math.max(coerceNumberProperty(value), 0);\r\n }\r\n private _pageSize: number = DEFAULT_PAGE_SIZE;\r\n\r\n /** Whether to hide the page size selection UI from the user. */\r\n @Input()\r\n get hidePageSize(): boolean {\r\n return this._hidePageSize;\r\n }\r\n set hidePageSize(value: boolean) {\r\n this._hidePageSize = coerceBooleanProperty(value);\r\n }\r\n private _hidePageSize = false;\r\n\r\n /** Event emitted when the paginator changes the page size or page index. */\r\n @Output()\r\n readonly page: EventEmitter = new EventEmitter();\r\n\r\n /** Displayed set of page size options. Will be sorted and include current page size. */\r\n _displayedPageSizeOptions: number;\r\n\r\n constructor(\r\n public _intl: OuiPaginatorIntl,\r\n private _changeDetectorRef: ChangeDetectorRef\r\n ) {\r\n super();\r\n this._intlChanges = _intl.changes.subscribe(() =>\r\n this._changeDetectorRef.markForCheck()\r\n );\r\n }\r\n\r\n ngOnInit() {\r\n this._initialized = true;\r\n this._markInitialized();\r\n }\r\n\r\n ngOnDestroy() {\r\n this._intlChanges.unsubscribe();\r\n }\r\n\r\n /** Advances to the next page if it exists. */\r\n nextPage(): void {\r\n if (!this.hasNextPage() || !this._initialized) {\r\n return;\r\n }\r\n\r\n const previousPageIndex = this.pageIndex;\r\n this.pageIndex++;\r\n this._emitPageEvent(previousPageIndex);\r\n }\r\n\r\n /** Move back to the previous page if it exists. */\r\n previousPage(): void {\r\n if (!this.hasPreviousPage()) {\r\n return;\r\n }\r\n\r\n const previousPageIndex = this.pageIndex;\r\n this.pageIndex--;\r\n this._emitPageEvent(previousPageIndex);\r\n }\r\n\r\n /** Move to the first page if not already there. */\r\n firstPage(): void {\r\n // hasPreviousPage being false implies at the start\r\n if (!this.hasPreviousPage()) {\r\n return;\r\n }\r\n\r\n const previousPageIndex = this.pageIndex;\r\n this.pageIndex = 0;\r\n this._emitPageEvent(previousPageIndex);\r\n }\r\n\r\n /** Move to the last page if not already there. */\r\n lastPage(): void {\r\n // hasNextPage being false implies at the end\r\n if (!this.hasNextPage()) {\r\n return;\r\n }\r\n\r\n const previousPageIndex = this.pageIndex;\r\n this.pageIndex = this.getNumberOfPages() - 1;\r\n this._emitPageEvent(previousPageIndex);\r\n }\r\n\r\n /** Whether there is a previous page. */\r\n hasPreviousPage(): boolean {\r\n return this.pageIndex >= 1 && this.pageSize !== 0;\r\n }\r\n\r\n /** Whether there is a next page. */\r\n hasNextPage(): boolean {\r\n const maxPageIndex = this.getNumberOfPages() - 1;\r\n return this.pageIndex < maxPageIndex && this.pageSize !== 0;\r\n }\r\n\r\n /** Calculate the number of pages */\r\n getNumberOfPages(): number {\r\n if (!this.pageSize || this.length < 0) {\r\n return 0;\r\n }\r\n\r\n return Math.ceil(this.length / this.pageSize);\r\n }\r\n\r\n /** Get current page */\r\n getCurrentPage(): number {\r\n if (!this.pageSize || this.length <= 0) {\r\n return 0;\r\n }\r\n\r\n return this.pageIndex + 1;\r\n }\r\n\r\n /**\r\n * Changes the page size so that the first item displayed on the page will still be\r\n * displayed using the new page size.\r\n *\r\n * For example, if the page size is 10 and on the second page (items indexed 10-19) then\r\n * switching so that the page size is 5 will set the third page as the current page so\r\n * that the 10th item will still be displayed.\r\n */\r\n _changePageSize(pageSize: number) {\r\n // Current page needs to be updated to reflect the new page size. Navigate to the page\r\n // containing the previous page's first item.\r\n const startIndex = this.pageIndex * this.pageSize;\r\n const previousPageIndex = this.pageIndex;\r\n\r\n this.pageIndex = Math.floor(startIndex / pageSize) || 0;\r\n this.pageSize = pageSize;\r\n this._emitPageEvent(previousPageIndex);\r\n }\r\n\r\n /** Checks whether the buttons for going forwards should be disabled. */\r\n _nextButtonsDisabled() {\r\n return this.disabled || !this.hasNextPage();\r\n }\r\n\r\n /** Checks whether the buttons for going backwards should be disabled. */\r\n _previousButtonsDisabled() {\r\n return this.disabled || !this.hasPreviousPage();\r\n }\r\n\r\n /** Emits an event notifying that a change of the paginator's properties has been triggered. */\r\n private _emitPageEvent(previousPageIndex: number) {\r\n this.page.emit({\r\n previousPageIndex,\r\n pageIndex: this.pageIndex,\r\n pageSize: this.pageSize,\r\n length: this.length,\r\n });\r\n }\r\n}\r\n", + "sourceCode": "import {\n coerceNumberProperty,\n coerceBooleanProperty,\n} from '@angular/cdk/coercion';\nimport {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n EventEmitter,\n Input,\n OnDestroy,\n OnInit,\n Output,\n ViewEncapsulation,\n} from '@angular/core';\nimport { Subscription } from 'rxjs';\nimport { OuiPaginatorIntl } from './paginator-intl';\nimport {\n HasInitialized,\n HasInitializedCtor,\n mixinInitialized,\n mixinDisabled,\n CanDisableCtor,\n CanDisable,\n} from '../core';\n\n/** The default page size if there is no page size and there are no provided page size options. */\nconst DEFAULT_PAGE_SIZE = 30;\n\n/**\n * Change event object that is emitted when the user selects a\n * different page size or navigates to another page.\n */\nexport class PageEvent {\n /** The current page index. */\n pageIndex: number;\n\n /**\n * Index of the page that was selected previously.\n *\n * @breaking-change 8.0.0 To be made into a required property.\n */\n previousPageIndex?: number;\n\n /** The current page size */\n pageSize: number;\n\n /** The current total number of items being paged */\n length: number;\n}\n\n// Boilerplate for applying mixins to OuiPaginator.\n/** @docs-private */\nexport class OuiPaginatorBase {}\nexport const _OuiPaginatorBase: CanDisableCtor &\n HasInitializedCtor &\n typeof OuiPaginatorBase = mixinDisabled(mixinInitialized(OuiPaginatorBase));\n\n/**\n * Component to provide navigation between paged information. Displays the size of the current\n * page, user-selectable options to change that size, what items are being shown, and\n * navigational button to go to the previous or next page.\n */\n@Component({\n selector: 'oui-paginator',\n exportAs: 'ouiPaginator',\n templateUrl: 'paginator.html',\n styleUrls: ['paginator.scss'],\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\n inputs: ['disabled'],\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-paginator',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n})\nexport class OuiPaginator\n extends _OuiPaginatorBase\n implements OnInit, OnDestroy, CanDisable, HasInitialized\n{\n private _initialized: boolean;\n private _intlChanges: Subscription;\n\n /** The zero-based page index of the displayed list of items. Defaulted to 0. */\n @Input()\n get pageIndex(): number {\n return this._pageIndex;\n }\n set pageIndex(value: number) {\n this._pageIndex = Math.max(coerceNumberProperty(value), 0);\n this._changeDetectorRef.markForCheck();\n }\n _pageIndex = 0;\n\n /** The length of the total number of items that are being paginated. Defaulted to 0. */\n @Input()\n get length(): number {\n return this._length;\n }\n set length(value: number) {\n this._length = coerceNumberProperty(value);\n this._changeDetectorRef.markForCheck();\n }\n _length = 0;\n\n /** Number of items to display on a page. By default set to 50. */\n @Input()\n get pageSize(): number {\n return this._pageSize;\n }\n set pageSize(value: number) {\n this._pageSize = Math.max(coerceNumberProperty(value), 0);\n }\n private _pageSize: number = DEFAULT_PAGE_SIZE;\n\n /** Whether to hide the page size selection UI from the user. */\n @Input()\n get hidePageSize(): boolean {\n return this._hidePageSize;\n }\n set hidePageSize(value: boolean) {\n this._hidePageSize = coerceBooleanProperty(value);\n }\n private _hidePageSize = false;\n\n /** Event emitted when the paginator changes the page size or page index. */\n @Output()\n readonly page: EventEmitter = new EventEmitter();\n\n /** Displayed set of page size options. Will be sorted and include current page size. */\n _displayedPageSizeOptions: number;\n\n constructor(\n public _intl: OuiPaginatorIntl,\n private _changeDetectorRef: ChangeDetectorRef\n ) {\n super();\n this._intlChanges = _intl.changes.subscribe(() =>\n this._changeDetectorRef.markForCheck()\n );\n }\n\n ngOnInit() {\n this._initialized = true;\n this._markInitialized();\n }\n\n ngOnDestroy() {\n this._intlChanges.unsubscribe();\n }\n\n /** Advances to the next page if it exists. */\n nextPage(): void {\n if (!this.hasNextPage() || !this._initialized) {\n return;\n }\n\n const previousPageIndex = this.pageIndex;\n this.pageIndex++;\n this._emitPageEvent(previousPageIndex);\n }\n\n /** Move back to the previous page if it exists. */\n previousPage(): void {\n if (!this.hasPreviousPage()) {\n return;\n }\n\n const previousPageIndex = this.pageIndex;\n this.pageIndex--;\n this._emitPageEvent(previousPageIndex);\n }\n\n /** Move to the first page if not already there. */\n firstPage(): void {\n // hasPreviousPage being false implies at the start\n if (!this.hasPreviousPage()) {\n return;\n }\n\n const previousPageIndex = this.pageIndex;\n this.pageIndex = 0;\n this._emitPageEvent(previousPageIndex);\n }\n\n /** Move to the last page if not already there. */\n lastPage(): void {\n // hasNextPage being false implies at the end\n if (!this.hasNextPage()) {\n return;\n }\n\n const previousPageIndex = this.pageIndex;\n this.pageIndex = this.getNumberOfPages() - 1;\n this._emitPageEvent(previousPageIndex);\n }\n\n /** Whether there is a previous page. */\n hasPreviousPage(): boolean {\n return this.pageIndex >= 1 && this.pageSize !== 0;\n }\n\n /** Whether there is a next page. */\n hasNextPage(): boolean {\n const maxPageIndex = this.getNumberOfPages() - 1;\n return this.pageIndex < maxPageIndex && this.pageSize !== 0;\n }\n\n /** Calculate the number of pages */\n getNumberOfPages(): number {\n if (!this.pageSize || this.length < 0) {\n return 0;\n }\n\n return Math.ceil(this.length / this.pageSize);\n }\n\n /** Get current page */\n getCurrentPage(): number {\n if (!this.pageSize || this.length <= 0) {\n return 0;\n }\n\n return this.pageIndex + 1;\n }\n\n /**\n * Changes the page size so that the first item displayed on the page will still be\n * displayed using the new page size.\n *\n * For example, if the page size is 10 and on the second page (items indexed 10-19) then\n * switching so that the page size is 5 will set the third page as the current page so\n * that the 10th item will still be displayed.\n */\n _changePageSize(pageSize: number) {\n // Current page needs to be updated to reflect the new page size. Navigate to the page\n // containing the previous page's first item.\n const startIndex = this.pageIndex * this.pageSize;\n const previousPageIndex = this.pageIndex;\n\n this.pageIndex = Math.floor(startIndex / pageSize) || 0;\n this.pageSize = pageSize;\n this._emitPageEvent(previousPageIndex);\n }\n\n /** Checks whether the buttons for going forwards should be disabled. */\n _nextButtonsDisabled() {\n return this.disabled || !this.hasNextPage();\n }\n\n /** Checks whether the buttons for going backwards should be disabled. */\n _previousButtonsDisabled() {\n return this.disabled || !this.hasPreviousPage();\n }\n\n /** Emits an event notifying that a change of the paginator's properties has been triggered. */\n private _emitPageEvent(previousPageIndex: number) {\n this.page.emit({\n previousPageIndex,\n pageIndex: this.pageIndex,\n pageSize: this.pageSize,\n length: this.length,\n });\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": "$single-arrow: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' style=' '%3E%3Cpath d='M11.373 10l-5.754 5.754 1.534 1.534 5.754-5.754L14.442 10 7.153 2.712 5.62 4.246z' fill='%234a4a4a' fill-rule='evenodd'/%3E%3C/svg%3E\");\r\n$double-arrow: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20'%3E%3Cg fill='%234a4a4a' fill-rule='evenodd'%3E%3Cpath d='M15.508 10.288l-5.754 5.754 1.534 1.534 5.754-5.754 1.534-1.534L11.288 3 9.754 4.534l5.754 5.754z'/%3E%3Cpath d='M7.508 10.288l-5.754 5.754 1.534 1.534 5.754-5.754 1.534-1.534L3.288 3 1.754 4.534l5.754 5.754z'/%3E%3C/g%3E%3C/svg%3E\");\r\n@mixin oui-paginator-button-disable {\r\n &:disabled {\r\n opacity: 0.3;\r\n cursor: default;\r\n border-color: #484848;\r\n &:hover {\r\n background-color: transparent !important;\r\n }\r\n }\r\n}\r\n\r\n.oui-paginator-outer-container {\r\n margin-top: 30px;\r\n}\r\n\r\n.oui-paginator-container {\r\n text-align: center;\r\n * {\r\n box-sizing: border-box;\r\n -webkit-box-sizing: border-box;\r\n -moz-box-sizing: border-box;\r\n }\r\n .oui-paginator-range-actions {\r\n text-align: center;\r\n border: 1px solid #c8c8c8;\r\n height: 35px;\r\n border-radius: 20px;\r\n background: #fff;\r\n display: inline-block;\r\n overflow: hidden;\r\n button {\r\n border: none;\r\n background: 0 0;\r\n display: inline-block;\r\n align-items: center;\r\n padding: 0;\r\n cursor: inherit;\r\n outline: 0;\r\n font: inherit;\r\n color: currentColor;\r\n cursor: pointer;\r\n vertical-align: top;\r\n border-radius: 0px;\r\n min-height: 33px;\r\n min-width: 10px;\r\n &[class^='cdk'],\r\n &[class$='focused'] {\r\n background-color: rgba(200, 200, 200, 0.4) !important;\r\n }\r\n }\r\n .oui-paginator-navigation-first {\r\n background: $double-arrow center center no-repeat;\r\n height: 33px;\r\n width: 46px;\r\n transform: rotate(180deg);\r\n @include oui-paginator-button-disable;\r\n background-position-y: 6px;\r\n &:hover {\r\n background-color: rgba(200, 200, 200, 0.4) !important;\r\n }\r\n }\r\n .oui-paginator-navigation-last {\r\n background: $double-arrow center center no-repeat;\r\n height: 33px;\r\n width: 46px;\r\n @include oui-paginator-button-disable;\r\n &:hover {\r\n background-color: rgba(200, 200, 200, 0.4) !important;\r\n }\r\n }\r\n .oui-paginator-navigation-previous {\r\n background: $single-arrow center center no-repeat;\r\n height: 33px;\r\n width: 38px;\r\n transform: rotate(180deg);\r\n border-right: 1px solid #c8c8c8;\r\n @include oui-paginator-button-disable;\r\n background-position-y: 6px;\r\n &:hover {\r\n background-color: rgba(200, 200, 200, 0.4) !important;\r\n }\r\n }\r\n .oui-paginator-navigation-next {\r\n background: $single-arrow center center no-repeat;\r\n height: 33px;\r\n width: 38px;\r\n border-right: 1px solid #c8c8c8;\r\n @include oui-paginator-button-disable;\r\n &:hover {\r\n background-color: rgba(200, 200, 200, 0.4) !important;\r\n }\r\n }\r\n .oui-paginator-current-page {\r\n display: inline-block;\r\n width: 50px;\r\n height: 33px;\r\n text-align: center;\r\n font-size: 16px;\r\n line-height: 34px;\r\n vertical-align: top;\r\n font-weight: 600;\r\n color: #333;\r\n }\r\n }\r\n .oui-paginator-total-count {\r\n display: inline-block;\r\n color: #666;\r\n font-size: 14px;\r\n line-height: 19px;\r\n font-weight: normal;\r\n vertical-align: top;\r\n padding-top: 9px;\r\n margin-left: 12px;\r\n }\r\n}\r\n", + "data": "$single-arrow: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' style=' '%3E%3Cpath d='M11.373 10l-5.754 5.754 1.534 1.534 5.754-5.754L14.442 10 7.153 2.712 5.62 4.246z' fill='%234a4a4a' fill-rule='evenodd'/%3E%3C/svg%3E\");\n$double-arrow: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20'%3E%3Cg fill='%234a4a4a' fill-rule='evenodd'%3E%3Cpath d='M15.508 10.288l-5.754 5.754 1.534 1.534 5.754-5.754 1.534-1.534L11.288 3 9.754 4.534l5.754 5.754z'/%3E%3Cpath d='M7.508 10.288l-5.754 5.754 1.534 1.534 5.754-5.754 1.534-1.534L3.288 3 1.754 4.534l5.754 5.754z'/%3E%3C/g%3E%3C/svg%3E\");\n@mixin oui-paginator-button-disable {\n &:disabled {\n opacity: 0.3;\n cursor: default;\n border-color: #484848;\n &:hover {\n background-color: transparent !important;\n }\n }\n}\n\n.oui-paginator-outer-container {\n margin-top: 30px;\n}\n\n.oui-paginator-container {\n text-align: center;\n * {\n box-sizing: border-box;\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n }\n .oui-paginator-range-actions {\n text-align: center;\n border: 1px solid #c8c8c8;\n height: 35px;\n border-radius: 20px;\n background: #fff;\n display: inline-block;\n overflow: hidden;\n button {\n border: none;\n background: 0 0;\n display: inline-block;\n align-items: center;\n padding: 0;\n cursor: inherit;\n outline: 0;\n font: inherit;\n color: currentColor;\n cursor: pointer;\n vertical-align: top;\n border-radius: 0px;\n min-height: 33px;\n min-width: 10px;\n &[class^='cdk'],\n &[class$='focused'] {\n background-color: rgba(200, 200, 200, 0.4) !important;\n }\n }\n .oui-paginator-navigation-first {\n background: $double-arrow center center no-repeat;\n height: 33px;\n width: 46px;\n transform: rotate(180deg);\n @include oui-paginator-button-disable;\n background-position-y: 6px;\n &:hover {\n background-color: rgba(200, 200, 200, 0.4) !important;\n }\n }\n .oui-paginator-navigation-last {\n background: $double-arrow center center no-repeat;\n height: 33px;\n width: 46px;\n @include oui-paginator-button-disable;\n &:hover {\n background-color: rgba(200, 200, 200, 0.4) !important;\n }\n }\n .oui-paginator-navigation-previous {\n background: $single-arrow center center no-repeat;\n height: 33px;\n width: 38px;\n transform: rotate(180deg);\n border-right: 1px solid #c8c8c8;\n @include oui-paginator-button-disable;\n background-position-y: 6px;\n &:hover {\n background-color: rgba(200, 200, 200, 0.4) !important;\n }\n }\n .oui-paginator-navigation-next {\n background: $single-arrow center center no-repeat;\n height: 33px;\n width: 38px;\n border-right: 1px solid #c8c8c8;\n @include oui-paginator-button-disable;\n &:hover {\n background-color: rgba(200, 200, 200, 0.4) !important;\n }\n }\n .oui-paginator-current-page {\n display: inline-block;\n width: 50px;\n height: 33px;\n text-align: center;\n font-size: 16px;\n line-height: 34px;\n vertical-align: top;\n font-weight: 600;\n color: #333;\n }\n }\n .oui-paginator-total-count {\n display: inline-block;\n color: #666;\n font-size: 14px;\n line-height: 19px;\n font-weight: normal;\n vertical-align: top;\n padding-top: 9px;\n margin-left: 12px;\n }\n}\n", "styleUrl": "paginator.scss" } ], @@ -31459,11 +41317,11 @@ } } }, - "templateData": "
\r\n
\r\n
\r\n \r\n \r\n
{{ getCurrentPage() }}
\r\n \r\n \r\n
\r\n
of {{ getNumberOfPages() }}
\r\n
\r\n
\r\n" + "templateData": "
\n
\n
\n \n \n
{{ getCurrentPage() }}
\n \n \n
\n
of {{ getNumberOfPages() }}
\n
\n
\n" }, { "name": "OuiPanel", - "id": "component-OuiPanel-7e8a6304332be97331bd4b1db28a9b5154aad7f524d9a57813084b315aa7381751c216507b2f3c0373d015e2b5f668e778da9205c83eee1e28c3f7d3db08fe5a", + "id": "component-OuiPanel-75834551469a77f2f75f2c9c42d0de964ac0ed9710e9d05128f9c9784e8e494b743a4a79301ee2a9f23c2c02744b30e7f33d4244928766088870a565b9863ed3", "file": "ui/src/components/panel/panel.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -31621,15 +41479,15 @@ ], "jsdoctags": [ { - "pos": 2565, - "end": 2583, + "pos": 2482, + "end": 2499, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, "kind": 325, "tagName": { - "pos": 2566, - "end": 2578, + "pos": 2483, + "end": 2495, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -31795,8 +41653,8 @@ "jsdoctags": [ { "name": { - "pos": 3918, - "end": 3922, + "pos": 3785, + "end": 3789, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -31808,8 +41666,8 @@ "deprecationMessage": "", "defaultValue": "this.xPosition", "tagName": { - "pos": 3912, - "end": 3917, + "pos": 3779, + "end": 3784, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -31820,8 +41678,8 @@ }, { "name": { - "pos": 3976, - "end": 3980, + "pos": 3842, + "end": 3846, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -31833,8 +41691,8 @@ "deprecationMessage": "", "defaultValue": "this.yPosition", "tagName": { - "pos": 3970, - "end": 3975, + "pos": 3836, + "end": 3841, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -31853,11 +41711,11 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import {\r\n ChangeDetectionStrategy,\r\n ViewEncapsulation,\r\n Component,\r\n InjectionToken,\r\n Inject,\r\n Input,\r\n ViewChild,\r\n TemplateRef,\r\n OnInit,\r\n ContentChild,\r\n Output,\r\n EventEmitter,\r\n Attribute,\r\n NgZone,\r\n ElementRef,\r\n OnDestroy,\r\n} from '@angular/core';\r\nimport { PanelPositionX, PanelPositionY } from './panel-positions';\r\nimport {\r\n throwOuiPanelInvalidPositionX,\r\n throwOuiPanelInvalidPositionY,\r\n} from './panel-errors';\r\nimport { OuiPanelOverlay } from './panel-overlay';\r\nimport { OuiPanelContent } from './panel-content';\r\nimport { Subject, Observable, Subscription } from 'rxjs';\r\nimport { OuiIconRegistry } from '../icon/icon-registery';\r\nimport { DomSanitizer } from '@angular/platform-browser';\r\nimport { ICONS } from '../core/shared/icons';\r\nimport { FocusMonitor } from '@angular/cdk/a11y';\r\n\r\n/** Default `oui-panel` options that can be overridden. */\r\nexport interface OuiPanelDefaultOptions {\r\n /** The x-axis position of the menu. */\r\n xPosition: PanelPositionX;\r\n\r\n /** The y-axis position of the menu. */\r\n yPosition: PanelPositionY;\r\n}\r\n\r\n/** Injection token to be used to override the default options for `oui-menu`. */\r\nexport const OUI_PANEL_DEFAULT_OPTIONS =\r\n new InjectionToken('oui-panel-default-options', {\r\n providedIn: 'root',\r\n factory: OUI_PANEL_DEFAULT_OPTIONS_FACTORY,\r\n });\r\n\r\n/** @docs-private */\r\nexport function OUI_PANEL_DEFAULT_OPTIONS_FACTORY(): OuiPanelDefaultOptions {\r\n return {\r\n xPosition: 'after',\r\n yPosition: 'below',\r\n };\r\n}\r\n\r\n@Component({\r\n selector: 'oui-panel',\r\n templateUrl: 'panel.html',\r\n styleUrls: ['panel.scss'],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n exportAs: 'ouiPanel',\r\n})\r\nexport class OuiPanel implements OnInit, OuiPanelOverlay {\r\n private _xPosition: PanelPositionX = this._defaultOptions.xPosition;\r\n private _yPosition: PanelPositionY = this._defaultOptions.yPosition;\r\n private readonly _mouseLeave: Subject = new Subject();\r\n public mouseLeave: Observable;\r\n private readonly _mouseEnter: Subject = new Subject();\r\n public mouseEnter: Observable;\r\n public escapeEvent: Subject = new Subject();\r\n\r\n @Input() width?: number;\r\n\r\n /** Config object to be passed into the menu's ngClass */\r\n _classList: { [key: string]: boolean } = {};\r\n\r\n @ViewChild(TemplateRef)\r\n templateRef: TemplateRef;\r\n\r\n /**\r\n * Panel content that will be rendered lazily.\r\n *\r\n * @docs-private\r\n */\r\n @ContentChild(OuiPanelContent)\r\n lazyContent: OuiPanelContent;\r\n\r\n /** Event emitted when the menu is closed. */\r\n @Output()\r\n readonly closed: EventEmitter = new EventEmitter();\r\n\r\n /** Position of the menu in the X axis. */\r\n @Input()\r\n get xPosition(): PanelPositionX {\r\n return this._xPosition;\r\n }\r\n set xPosition(value: PanelPositionX) {\r\n if (value !== 'before' && value !== 'after') {\r\n throwOuiPanelInvalidPositionX();\r\n }\r\n this._xPosition = value;\r\n this.setPositionClasses();\r\n }\r\n\r\n /** Position of the menu in the Y axis. */\r\n @Input()\r\n get yPosition(): PanelPositionY {\r\n return this._yPosition;\r\n }\r\n set yPosition(value: PanelPositionY) {\r\n if (value !== 'above' && value !== 'below') {\r\n throwOuiPanelInvalidPositionY();\r\n }\r\n this._yPosition = value;\r\n this.setPositionClasses();\r\n }\r\n constructor(\r\n @Inject(OUI_PANEL_DEFAULT_OPTIONS)\r\n private _defaultOptions: OuiPanelDefaultOptions\r\n ) {\r\n this.mouseLeave = this._mouseLeave.asObservable();\r\n this.mouseEnter = this._mouseEnter.asObservable();\r\n }\r\n\r\n ngOnInit() {\r\n this.setPositionClasses();\r\n }\r\n\r\n /**\r\n * Adds classes to the panel-overlay based on its position. Can be used by\r\n * consumers to add specific styling based on the position.\r\n *\r\n * @param posX Position of the panel along the x axis.\r\n * @param posY Position of the panel along the y axis.\r\n * @docs-private\r\n */\r\n setPositionClasses(\r\n posX: PanelPositionX = this.xPosition,\r\n posY: PanelPositionY = this.yPosition\r\n ) {\r\n const classes = this._classList;\r\n classes['oui-panel-before'] = posX === 'before';\r\n classes['oui-panel-after'] = posX === 'after';\r\n classes['oui-panel-above'] = posY === 'above';\r\n classes['oui-panel-below'] = posY === 'below';\r\n }\r\n\r\n public _handleMouseLeave(event: MouseEvent) {\r\n this._mouseLeave.next(event);\r\n }\r\n\r\n public _handleMouseEnter(event: MouseEvent) {\r\n this._mouseEnter.next(event);\r\n }\r\n\r\n public _handleCloseIcon() {\r\n this.escapeEvent.next();\r\n }\r\n}\r\n\r\n@Component({\r\n selector: 'oui-panel-icon',\r\n template:\r\n '',\r\n styleUrls: ['panel.scss'],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n exportAs: 'ouiPanelIcon',\r\n})\r\nexport class OuiPanelIcon implements OnDestroy {\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n tabIndex: any;\r\n constructor(\r\n private _elementRef: ElementRef,\r\n private ouiIconRegistry: OuiIconRegistry,\r\n private domSanitizer: DomSanitizer,\r\n private _focusMonitor: FocusMonitor,\r\n private _ngZone: NgZone,\r\n @Attribute('tabindex') tabIndex: string\r\n ) {\r\n this.tabIndex = parseInt(tabIndex, 10) || 0;\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this._elementRef, true)\r\n .subscribe(() => this._ngZone.run(() => {}));\r\n\r\n this.ouiIconRegistry.addSvgIconLiteral(\r\n `panel-icon`,\r\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.PANEL_ICON)\r\n );\r\n this.ouiIconRegistry.addSvgIconLiteral(\r\n `close-icon_8X8`,\r\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.CLOSE_ICON_8X8)\r\n );\r\n }\r\n\r\n ngOnDestroy() {\r\n this._focusMonitor.stopMonitoring(this._elementRef.nativeElement);\r\n this._monitorSubscription.unsubscribe();\r\n }\r\n}\r\n", + "sourceCode": "import {\n ChangeDetectionStrategy,\n ViewEncapsulation,\n Component,\n InjectionToken,\n Inject,\n Input,\n ViewChild,\n TemplateRef,\n OnInit,\n ContentChild,\n Output,\n EventEmitter,\n Attribute,\n NgZone,\n ElementRef,\n OnDestroy,\n} from '@angular/core';\nimport { PanelPositionX, PanelPositionY } from './panel-positions';\nimport {\n throwOuiPanelInvalidPositionX,\n throwOuiPanelInvalidPositionY,\n} from './panel-errors';\nimport { OuiPanelOverlay } from './panel-overlay';\nimport { OuiPanelContent } from './panel-content';\nimport { Subject, Observable, Subscription } from 'rxjs';\nimport { OuiIconRegistry } from '../icon/icon-registery';\nimport { DomSanitizer } from '@angular/platform-browser';\nimport { ICONS } from '../core/shared/icons';\nimport { FocusMonitor } from '@angular/cdk/a11y';\n\n/** Default `oui-panel` options that can be overridden. */\nexport interface OuiPanelDefaultOptions {\n /** The x-axis position of the menu. */\n xPosition: PanelPositionX;\n\n /** The y-axis position of the menu. */\n yPosition: PanelPositionY;\n}\n\n/** Injection token to be used to override the default options for `oui-menu`. */\nexport const OUI_PANEL_DEFAULT_OPTIONS =\n new InjectionToken('oui-panel-default-options', {\n providedIn: 'root',\n factory: OUI_PANEL_DEFAULT_OPTIONS_FACTORY,\n });\n\n/** @docs-private */\nexport function OUI_PANEL_DEFAULT_OPTIONS_FACTORY(): OuiPanelDefaultOptions {\n return {\n xPosition: 'after',\n yPosition: 'below',\n };\n}\n\n@Component({\n selector: 'oui-panel',\n templateUrl: 'panel.html',\n styleUrls: ['panel.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n exportAs: 'ouiPanel',\n})\nexport class OuiPanel implements OnInit, OuiPanelOverlay {\n private _xPosition: PanelPositionX = this._defaultOptions.xPosition;\n private _yPosition: PanelPositionY = this._defaultOptions.yPosition;\n private readonly _mouseLeave: Subject = new Subject();\n public mouseLeave: Observable;\n private readonly _mouseEnter: Subject = new Subject();\n public mouseEnter: Observable;\n public escapeEvent: Subject = new Subject();\n\n @Input() width?: number;\n\n /** Config object to be passed into the menu's ngClass */\n _classList: { [key: string]: boolean } = {};\n\n @ViewChild(TemplateRef)\n templateRef: TemplateRef;\n\n /**\n * Panel content that will be rendered lazily.\n *\n * @docs-private\n */\n @ContentChild(OuiPanelContent)\n lazyContent: OuiPanelContent;\n\n /** Event emitted when the menu is closed. */\n @Output()\n readonly closed: EventEmitter = new EventEmitter();\n\n /** Position of the menu in the X axis. */\n @Input()\n get xPosition(): PanelPositionX {\n return this._xPosition;\n }\n set xPosition(value: PanelPositionX) {\n if (value !== 'before' && value !== 'after') {\n throwOuiPanelInvalidPositionX();\n }\n this._xPosition = value;\n this.setPositionClasses();\n }\n\n /** Position of the menu in the Y axis. */\n @Input()\n get yPosition(): PanelPositionY {\n return this._yPosition;\n }\n set yPosition(value: PanelPositionY) {\n if (value !== 'above' && value !== 'below') {\n throwOuiPanelInvalidPositionY();\n }\n this._yPosition = value;\n this.setPositionClasses();\n }\n constructor(\n @Inject(OUI_PANEL_DEFAULT_OPTIONS)\n private _defaultOptions: OuiPanelDefaultOptions\n ) {\n this.mouseLeave = this._mouseLeave.asObservable();\n this.mouseEnter = this._mouseEnter.asObservable();\n }\n\n ngOnInit() {\n this.setPositionClasses();\n }\n\n /**\n * Adds classes to the panel-overlay based on its position. Can be used by\n * consumers to add specific styling based on the position.\n *\n * @param posX Position of the panel along the x axis.\n * @param posY Position of the panel along the y axis.\n * @docs-private\n */\n setPositionClasses(\n posX: PanelPositionX = this.xPosition,\n posY: PanelPositionY = this.yPosition\n ) {\n const classes = this._classList;\n classes['oui-panel-before'] = posX === 'before';\n classes['oui-panel-after'] = posX === 'after';\n classes['oui-panel-above'] = posY === 'above';\n classes['oui-panel-below'] = posY === 'below';\n }\n\n public _handleMouseLeave(event: MouseEvent) {\n this._mouseLeave.next(event);\n }\n\n public _handleMouseEnter(event: MouseEvent) {\n this._mouseEnter.next(event);\n }\n\n public _handleCloseIcon() {\n this.escapeEvent.next();\n }\n}\n\n@Component({\n selector: 'oui-panel-icon',\n template:\n '',\n styleUrls: ['panel.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n exportAs: 'ouiPanelIcon',\n})\nexport class OuiPanelIcon implements OnDestroy {\n private _monitorSubscription: Subscription = Subscription.EMPTY;\n tabIndex: any;\n constructor(\n private _elementRef: ElementRef,\n private ouiIconRegistry: OuiIconRegistry,\n private domSanitizer: DomSanitizer,\n private _focusMonitor: FocusMonitor,\n private _ngZone: NgZone,\n @Attribute('tabindex') tabIndex: string\n ) {\n this.tabIndex = parseInt(tabIndex, 10) || 0;\n this._monitorSubscription = this._focusMonitor\n .monitor(this._elementRef, true)\n .subscribe(() => this._ngZone.run(() => {}));\n\n this.ouiIconRegistry.addSvgIconLiteral(\n `panel-icon`,\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.PANEL_ICON)\n );\n this.ouiIconRegistry.addSvgIconLiteral(\n `close-icon_8X8`,\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.CLOSE_ICON_8X8)\n );\n }\n\n ngOnDestroy() {\n this._focusMonitor.stopMonitoring(this._elementRef.nativeElement);\n this._monitorSubscription.unsubscribe();\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": ".oui-panel-content {\r\n color: #333;\r\n background: #fff;\r\n padding: 11px 17px 12px 17px;\r\n box-shadow: 0 1px 2px 0 rgba(74, 74, 74, 0.5);\r\n border: 1px solid #c8c8c8;\r\n font-size: 13px;\r\n line-height: 19px;\r\n box-sizing: border-box;\r\n -webkit-box-sizing: border-box;\r\n -moz-box-sizing: border-box;\r\n word-wrap: break-word;\r\n width: 270px;\r\n max-width: 512px;\r\n outline: none;\r\n}\r\n\r\n.oui-panel-content-large {\r\n width: 512px;\r\n}\r\n\r\n.oui-panel-content h6 {\r\n font-size: 13px;\r\n line-height: 19px;\r\n font-weight: bold;\r\n color: #333;\r\n margin: 0px 0px 6px;\r\n}\r\n.oui-panel-content img {\r\n width: 100%;\r\n margin: 0px 0px 11px;\r\n}\r\n\r\n.oui-panel-content p {\r\n margin: 0px 0px 6px;\r\n font-size: 13px;\r\n line-height: 19px;\r\n color: #333;\r\n}\r\n.oui-panel-content a {\r\n font-size: 13px;\r\n line-height: 19px;\r\n color: #006bb1;\r\n text-decoration: none;\r\n &:hover {\r\n text-decoration: underline;\r\n }\r\n}\r\n.oui-panel-content p:nth-last-child(1) {\r\n margin: 0px;\r\n}\r\n\r\n.oui-panel-icon {\r\n width: 22px !important;\r\n height: 22px !important;\r\n display: inline-block;\r\n margin: 0;\r\n padding: 2px;\r\n box-sizing: border-box;\r\n -webkit-box-sizing: border-box;\r\n -moz-box-sizing: border-box;\r\n &:focus {\r\n outline: none;\r\n }\r\n svg {\r\n color: #4a4a4a;\r\n }\r\n}\r\n.radius {\r\n border-radius: 2px;\r\n -webkit-border-radius: 2px;\r\n -moz-border-radius: 2px;\r\n -ms-border-radius: 2px;\r\n -o-border-radius: 2px;\r\n}\r\n\r\noui-panel-icon {\r\n &[class^='cdk'],\r\n &[class$='focused'] {\r\n .oui-panel-icon {\r\n @extend .radius;\r\n background-color: #e8e8e8;\r\n }\r\n }\r\n box-sizing: border-box;\r\n -webkit-box-sizing: border-box;\r\n -moz-box-sizing: border-box;\r\n}\r\n\r\n.close-panel {\r\n @extend .radius;\r\n cursor: pointer;\r\n outline: none;\r\n width: 16px;\r\n height: 16px;\r\n background: #e9e9e9;\r\n position: absolute;\r\n top: 2px;\r\n right: 2px;\r\n opacity: 0;\r\n padding: 0;\r\n border: 0;\r\n text-align: center;\r\n line-height: 1;\r\n padding-bottom: 2px;\r\n cursor: default;\r\n pointer-events: none;\r\n &:focus {\r\n outline: none;\r\n pointer-events: all;\r\n opacity: 1;\r\n }\r\n svg {\r\n fill: #666;\r\n }\r\n}\r\n", + "data": ".oui-panel-content {\n color: #333;\n background: #fff;\n padding: 11px 17px 12px 17px;\n box-shadow: 0 1px 2px 0 rgba(74, 74, 74, 0.5);\n border: 1px solid #c8c8c8;\n font-size: 13px;\n line-height: 19px;\n box-sizing: border-box;\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n word-wrap: break-word;\n width: 270px;\n max-width: 512px;\n outline: none;\n}\n\n.oui-panel-content-large {\n width: 512px;\n}\n\n.oui-panel-content h6 {\n font-size: 13px;\n line-height: 19px;\n font-weight: bold;\n color: #333;\n margin: 0px 0px 6px;\n}\n.oui-panel-content img {\n width: 100%;\n margin: 0px 0px 11px;\n}\n\n.oui-panel-content p {\n margin: 0px 0px 6px;\n font-size: 13px;\n line-height: 19px;\n color: #333;\n}\n.oui-panel-content a {\n font-size: 13px;\n line-height: 19px;\n color: #006bb1;\n text-decoration: none;\n &:hover {\n text-decoration: underline;\n }\n}\n.oui-panel-content p:nth-last-child(1) {\n margin: 0px;\n}\n\n.oui-panel-icon {\n width: 22px !important;\n height: 22px !important;\n display: inline-block;\n margin: 0;\n padding: 2px;\n box-sizing: border-box;\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n &:focus {\n outline: none;\n }\n svg {\n color: #4a4a4a;\n }\n}\n.radius {\n border-radius: 2px;\n -webkit-border-radius: 2px;\n -moz-border-radius: 2px;\n -ms-border-radius: 2px;\n -o-border-radius: 2px;\n}\n\noui-panel-icon {\n &[class^='cdk'],\n &[class$='focused'] {\n .oui-panel-icon {\n @extend .radius;\n background-color: #e8e8e8;\n }\n }\n box-sizing: border-box;\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n}\n\n.close-panel {\n @extend .radius;\n cursor: pointer;\n outline: none;\n width: 16px;\n height: 16px;\n background: #e9e9e9;\n position: absolute;\n top: 2px;\n right: 2px;\n opacity: 0;\n padding: 0;\n border: 0;\n text-align: center;\n line-height: 1;\n padding-bottom: 2px;\n cursor: default;\n pointer-events: none;\n &:focus {\n outline: none;\n pointer-events: all;\n opacity: 1;\n }\n svg {\n fill: #666;\n }\n}\n", "styleUrl": "panel.scss" } ], @@ -31970,11 +41828,11 @@ } } }, - "templateData": "\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n" + "templateData": "\n \n \n \n \n \n \n \n \n\n" }, { "name": "OuiPanelIcon", - "id": "component-OuiPanelIcon-7e8a6304332be97331bd4b1db28a9b5154aad7f524d9a57813084b315aa7381751c216507b2f3c0373d015e2b5f668e778da9205c83eee1e28c3f7d3db08fe5a", + "id": "component-OuiPanelIcon-75834551469a77f2f75f2c9c42d0de964ac0ed9710e9d05128f9c9784e8e494b743a4a79301ee2a9f23c2c02744b30e7f33d4244928766088870a565b9863ed3", "file": "ui/src/components/panel/panel.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -32038,11 +41896,11 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import {\r\n ChangeDetectionStrategy,\r\n ViewEncapsulation,\r\n Component,\r\n InjectionToken,\r\n Inject,\r\n Input,\r\n ViewChild,\r\n TemplateRef,\r\n OnInit,\r\n ContentChild,\r\n Output,\r\n EventEmitter,\r\n Attribute,\r\n NgZone,\r\n ElementRef,\r\n OnDestroy,\r\n} from '@angular/core';\r\nimport { PanelPositionX, PanelPositionY } from './panel-positions';\r\nimport {\r\n throwOuiPanelInvalidPositionX,\r\n throwOuiPanelInvalidPositionY,\r\n} from './panel-errors';\r\nimport { OuiPanelOverlay } from './panel-overlay';\r\nimport { OuiPanelContent } from './panel-content';\r\nimport { Subject, Observable, Subscription } from 'rxjs';\r\nimport { OuiIconRegistry } from '../icon/icon-registery';\r\nimport { DomSanitizer } from '@angular/platform-browser';\r\nimport { ICONS } from '../core/shared/icons';\r\nimport { FocusMonitor } from '@angular/cdk/a11y';\r\n\r\n/** Default `oui-panel` options that can be overridden. */\r\nexport interface OuiPanelDefaultOptions {\r\n /** The x-axis position of the menu. */\r\n xPosition: PanelPositionX;\r\n\r\n /** The y-axis position of the menu. */\r\n yPosition: PanelPositionY;\r\n}\r\n\r\n/** Injection token to be used to override the default options for `oui-menu`. */\r\nexport const OUI_PANEL_DEFAULT_OPTIONS =\r\n new InjectionToken('oui-panel-default-options', {\r\n providedIn: 'root',\r\n factory: OUI_PANEL_DEFAULT_OPTIONS_FACTORY,\r\n });\r\n\r\n/** @docs-private */\r\nexport function OUI_PANEL_DEFAULT_OPTIONS_FACTORY(): OuiPanelDefaultOptions {\r\n return {\r\n xPosition: 'after',\r\n yPosition: 'below',\r\n };\r\n}\r\n\r\n@Component({\r\n selector: 'oui-panel',\r\n templateUrl: 'panel.html',\r\n styleUrls: ['panel.scss'],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n exportAs: 'ouiPanel',\r\n})\r\nexport class OuiPanel implements OnInit, OuiPanelOverlay {\r\n private _xPosition: PanelPositionX = this._defaultOptions.xPosition;\r\n private _yPosition: PanelPositionY = this._defaultOptions.yPosition;\r\n private readonly _mouseLeave: Subject = new Subject();\r\n public mouseLeave: Observable;\r\n private readonly _mouseEnter: Subject = new Subject();\r\n public mouseEnter: Observable;\r\n public escapeEvent: Subject = new Subject();\r\n\r\n @Input() width?: number;\r\n\r\n /** Config object to be passed into the menu's ngClass */\r\n _classList: { [key: string]: boolean } = {};\r\n\r\n @ViewChild(TemplateRef)\r\n templateRef: TemplateRef;\r\n\r\n /**\r\n * Panel content that will be rendered lazily.\r\n *\r\n * @docs-private\r\n */\r\n @ContentChild(OuiPanelContent)\r\n lazyContent: OuiPanelContent;\r\n\r\n /** Event emitted when the menu is closed. */\r\n @Output()\r\n readonly closed: EventEmitter = new EventEmitter();\r\n\r\n /** Position of the menu in the X axis. */\r\n @Input()\r\n get xPosition(): PanelPositionX {\r\n return this._xPosition;\r\n }\r\n set xPosition(value: PanelPositionX) {\r\n if (value !== 'before' && value !== 'after') {\r\n throwOuiPanelInvalidPositionX();\r\n }\r\n this._xPosition = value;\r\n this.setPositionClasses();\r\n }\r\n\r\n /** Position of the menu in the Y axis. */\r\n @Input()\r\n get yPosition(): PanelPositionY {\r\n return this._yPosition;\r\n }\r\n set yPosition(value: PanelPositionY) {\r\n if (value !== 'above' && value !== 'below') {\r\n throwOuiPanelInvalidPositionY();\r\n }\r\n this._yPosition = value;\r\n this.setPositionClasses();\r\n }\r\n constructor(\r\n @Inject(OUI_PANEL_DEFAULT_OPTIONS)\r\n private _defaultOptions: OuiPanelDefaultOptions\r\n ) {\r\n this.mouseLeave = this._mouseLeave.asObservable();\r\n this.mouseEnter = this._mouseEnter.asObservable();\r\n }\r\n\r\n ngOnInit() {\r\n this.setPositionClasses();\r\n }\r\n\r\n /**\r\n * Adds classes to the panel-overlay based on its position. Can be used by\r\n * consumers to add specific styling based on the position.\r\n *\r\n * @param posX Position of the panel along the x axis.\r\n * @param posY Position of the panel along the y axis.\r\n * @docs-private\r\n */\r\n setPositionClasses(\r\n posX: PanelPositionX = this.xPosition,\r\n posY: PanelPositionY = this.yPosition\r\n ) {\r\n const classes = this._classList;\r\n classes['oui-panel-before'] = posX === 'before';\r\n classes['oui-panel-after'] = posX === 'after';\r\n classes['oui-panel-above'] = posY === 'above';\r\n classes['oui-panel-below'] = posY === 'below';\r\n }\r\n\r\n public _handleMouseLeave(event: MouseEvent) {\r\n this._mouseLeave.next(event);\r\n }\r\n\r\n public _handleMouseEnter(event: MouseEvent) {\r\n this._mouseEnter.next(event);\r\n }\r\n\r\n public _handleCloseIcon() {\r\n this.escapeEvent.next();\r\n }\r\n}\r\n\r\n@Component({\r\n selector: 'oui-panel-icon',\r\n template:\r\n '',\r\n styleUrls: ['panel.scss'],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n exportAs: 'ouiPanelIcon',\r\n})\r\nexport class OuiPanelIcon implements OnDestroy {\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n tabIndex: any;\r\n constructor(\r\n private _elementRef: ElementRef,\r\n private ouiIconRegistry: OuiIconRegistry,\r\n private domSanitizer: DomSanitizer,\r\n private _focusMonitor: FocusMonitor,\r\n private _ngZone: NgZone,\r\n @Attribute('tabindex') tabIndex: string\r\n ) {\r\n this.tabIndex = parseInt(tabIndex, 10) || 0;\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this._elementRef, true)\r\n .subscribe(() => this._ngZone.run(() => {}));\r\n\r\n this.ouiIconRegistry.addSvgIconLiteral(\r\n `panel-icon`,\r\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.PANEL_ICON)\r\n );\r\n this.ouiIconRegistry.addSvgIconLiteral(\r\n `close-icon_8X8`,\r\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.CLOSE_ICON_8X8)\r\n );\r\n }\r\n\r\n ngOnDestroy() {\r\n this._focusMonitor.stopMonitoring(this._elementRef.nativeElement);\r\n this._monitorSubscription.unsubscribe();\r\n }\r\n}\r\n", + "sourceCode": "import {\n ChangeDetectionStrategy,\n ViewEncapsulation,\n Component,\n InjectionToken,\n Inject,\n Input,\n ViewChild,\n TemplateRef,\n OnInit,\n ContentChild,\n Output,\n EventEmitter,\n Attribute,\n NgZone,\n ElementRef,\n OnDestroy,\n} from '@angular/core';\nimport { PanelPositionX, PanelPositionY } from './panel-positions';\nimport {\n throwOuiPanelInvalidPositionX,\n throwOuiPanelInvalidPositionY,\n} from './panel-errors';\nimport { OuiPanelOverlay } from './panel-overlay';\nimport { OuiPanelContent } from './panel-content';\nimport { Subject, Observable, Subscription } from 'rxjs';\nimport { OuiIconRegistry } from '../icon/icon-registery';\nimport { DomSanitizer } from '@angular/platform-browser';\nimport { ICONS } from '../core/shared/icons';\nimport { FocusMonitor } from '@angular/cdk/a11y';\n\n/** Default `oui-panel` options that can be overridden. */\nexport interface OuiPanelDefaultOptions {\n /** The x-axis position of the menu. */\n xPosition: PanelPositionX;\n\n /** The y-axis position of the menu. */\n yPosition: PanelPositionY;\n}\n\n/** Injection token to be used to override the default options for `oui-menu`. */\nexport const OUI_PANEL_DEFAULT_OPTIONS =\n new InjectionToken('oui-panel-default-options', {\n providedIn: 'root',\n factory: OUI_PANEL_DEFAULT_OPTIONS_FACTORY,\n });\n\n/** @docs-private */\nexport function OUI_PANEL_DEFAULT_OPTIONS_FACTORY(): OuiPanelDefaultOptions {\n return {\n xPosition: 'after',\n yPosition: 'below',\n };\n}\n\n@Component({\n selector: 'oui-panel',\n templateUrl: 'panel.html',\n styleUrls: ['panel.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n exportAs: 'ouiPanel',\n})\nexport class OuiPanel implements OnInit, OuiPanelOverlay {\n private _xPosition: PanelPositionX = this._defaultOptions.xPosition;\n private _yPosition: PanelPositionY = this._defaultOptions.yPosition;\n private readonly _mouseLeave: Subject = new Subject();\n public mouseLeave: Observable;\n private readonly _mouseEnter: Subject = new Subject();\n public mouseEnter: Observable;\n public escapeEvent: Subject = new Subject();\n\n @Input() width?: number;\n\n /** Config object to be passed into the menu's ngClass */\n _classList: { [key: string]: boolean } = {};\n\n @ViewChild(TemplateRef)\n templateRef: TemplateRef;\n\n /**\n * Panel content that will be rendered lazily.\n *\n * @docs-private\n */\n @ContentChild(OuiPanelContent)\n lazyContent: OuiPanelContent;\n\n /** Event emitted when the menu is closed. */\n @Output()\n readonly closed: EventEmitter = new EventEmitter();\n\n /** Position of the menu in the X axis. */\n @Input()\n get xPosition(): PanelPositionX {\n return this._xPosition;\n }\n set xPosition(value: PanelPositionX) {\n if (value !== 'before' && value !== 'after') {\n throwOuiPanelInvalidPositionX();\n }\n this._xPosition = value;\n this.setPositionClasses();\n }\n\n /** Position of the menu in the Y axis. */\n @Input()\n get yPosition(): PanelPositionY {\n return this._yPosition;\n }\n set yPosition(value: PanelPositionY) {\n if (value !== 'above' && value !== 'below') {\n throwOuiPanelInvalidPositionY();\n }\n this._yPosition = value;\n this.setPositionClasses();\n }\n constructor(\n @Inject(OUI_PANEL_DEFAULT_OPTIONS)\n private _defaultOptions: OuiPanelDefaultOptions\n ) {\n this.mouseLeave = this._mouseLeave.asObservable();\n this.mouseEnter = this._mouseEnter.asObservable();\n }\n\n ngOnInit() {\n this.setPositionClasses();\n }\n\n /**\n * Adds classes to the panel-overlay based on its position. Can be used by\n * consumers to add specific styling based on the position.\n *\n * @param posX Position of the panel along the x axis.\n * @param posY Position of the panel along the y axis.\n * @docs-private\n */\n setPositionClasses(\n posX: PanelPositionX = this.xPosition,\n posY: PanelPositionY = this.yPosition\n ) {\n const classes = this._classList;\n classes['oui-panel-before'] = posX === 'before';\n classes['oui-panel-after'] = posX === 'after';\n classes['oui-panel-above'] = posY === 'above';\n classes['oui-panel-below'] = posY === 'below';\n }\n\n public _handleMouseLeave(event: MouseEvent) {\n this._mouseLeave.next(event);\n }\n\n public _handleMouseEnter(event: MouseEvent) {\n this._mouseEnter.next(event);\n }\n\n public _handleCloseIcon() {\n this.escapeEvent.next();\n }\n}\n\n@Component({\n selector: 'oui-panel-icon',\n template:\n '',\n styleUrls: ['panel.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n exportAs: 'ouiPanelIcon',\n})\nexport class OuiPanelIcon implements OnDestroy {\n private _monitorSubscription: Subscription = Subscription.EMPTY;\n tabIndex: any;\n constructor(\n private _elementRef: ElementRef,\n private ouiIconRegistry: OuiIconRegistry,\n private domSanitizer: DomSanitizer,\n private _focusMonitor: FocusMonitor,\n private _ngZone: NgZone,\n @Attribute('tabindex') tabIndex: string\n ) {\n this.tabIndex = parseInt(tabIndex, 10) || 0;\n this._monitorSubscription = this._focusMonitor\n .monitor(this._elementRef, true)\n .subscribe(() => this._ngZone.run(() => {}));\n\n this.ouiIconRegistry.addSvgIconLiteral(\n `panel-icon`,\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.PANEL_ICON)\n );\n this.ouiIconRegistry.addSvgIconLiteral(\n `close-icon_8X8`,\n this.domSanitizer.bypassSecurityTrustHtml(ICONS.CLOSE_ICON_8X8)\n );\n }\n\n ngOnDestroy() {\n this._focusMonitor.stopMonitoring(this._elementRef.nativeElement);\n this._monitorSubscription.unsubscribe();\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": ".oui-panel-content {\r\n color: #333;\r\n background: #fff;\r\n padding: 11px 17px 12px 17px;\r\n box-shadow: 0 1px 2px 0 rgba(74, 74, 74, 0.5);\r\n border: 1px solid #c8c8c8;\r\n font-size: 13px;\r\n line-height: 19px;\r\n box-sizing: border-box;\r\n -webkit-box-sizing: border-box;\r\n -moz-box-sizing: border-box;\r\n word-wrap: break-word;\r\n width: 270px;\r\n max-width: 512px;\r\n outline: none;\r\n}\r\n\r\n.oui-panel-content-large {\r\n width: 512px;\r\n}\r\n\r\n.oui-panel-content h6 {\r\n font-size: 13px;\r\n line-height: 19px;\r\n font-weight: bold;\r\n color: #333;\r\n margin: 0px 0px 6px;\r\n}\r\n.oui-panel-content img {\r\n width: 100%;\r\n margin: 0px 0px 11px;\r\n}\r\n\r\n.oui-panel-content p {\r\n margin: 0px 0px 6px;\r\n font-size: 13px;\r\n line-height: 19px;\r\n color: #333;\r\n}\r\n.oui-panel-content a {\r\n font-size: 13px;\r\n line-height: 19px;\r\n color: #006bb1;\r\n text-decoration: none;\r\n &:hover {\r\n text-decoration: underline;\r\n }\r\n}\r\n.oui-panel-content p:nth-last-child(1) {\r\n margin: 0px;\r\n}\r\n\r\n.oui-panel-icon {\r\n width: 22px !important;\r\n height: 22px !important;\r\n display: inline-block;\r\n margin: 0;\r\n padding: 2px;\r\n box-sizing: border-box;\r\n -webkit-box-sizing: border-box;\r\n -moz-box-sizing: border-box;\r\n &:focus {\r\n outline: none;\r\n }\r\n svg {\r\n color: #4a4a4a;\r\n }\r\n}\r\n.radius {\r\n border-radius: 2px;\r\n -webkit-border-radius: 2px;\r\n -moz-border-radius: 2px;\r\n -ms-border-radius: 2px;\r\n -o-border-radius: 2px;\r\n}\r\n\r\noui-panel-icon {\r\n &[class^='cdk'],\r\n &[class$='focused'] {\r\n .oui-panel-icon {\r\n @extend .radius;\r\n background-color: #e8e8e8;\r\n }\r\n }\r\n box-sizing: border-box;\r\n -webkit-box-sizing: border-box;\r\n -moz-box-sizing: border-box;\r\n}\r\n\r\n.close-panel {\r\n @extend .radius;\r\n cursor: pointer;\r\n outline: none;\r\n width: 16px;\r\n height: 16px;\r\n background: #e9e9e9;\r\n position: absolute;\r\n top: 2px;\r\n right: 2px;\r\n opacity: 0;\r\n padding: 0;\r\n border: 0;\r\n text-align: center;\r\n line-height: 1;\r\n padding-bottom: 2px;\r\n cursor: default;\r\n pointer-events: none;\r\n &:focus {\r\n outline: none;\r\n pointer-events: all;\r\n opacity: 1;\r\n }\r\n svg {\r\n fill: #666;\r\n }\r\n}\r\n", + "data": ".oui-panel-content {\n color: #333;\n background: #fff;\n padding: 11px 17px 12px 17px;\n box-shadow: 0 1px 2px 0 rgba(74, 74, 74, 0.5);\n border: 1px solid #c8c8c8;\n font-size: 13px;\n line-height: 19px;\n box-sizing: border-box;\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n word-wrap: break-word;\n width: 270px;\n max-width: 512px;\n outline: none;\n}\n\n.oui-panel-content-large {\n width: 512px;\n}\n\n.oui-panel-content h6 {\n font-size: 13px;\n line-height: 19px;\n font-weight: bold;\n color: #333;\n margin: 0px 0px 6px;\n}\n.oui-panel-content img {\n width: 100%;\n margin: 0px 0px 11px;\n}\n\n.oui-panel-content p {\n margin: 0px 0px 6px;\n font-size: 13px;\n line-height: 19px;\n color: #333;\n}\n.oui-panel-content a {\n font-size: 13px;\n line-height: 19px;\n color: #006bb1;\n text-decoration: none;\n &:hover {\n text-decoration: underline;\n }\n}\n.oui-panel-content p:nth-last-child(1) {\n margin: 0px;\n}\n\n.oui-panel-icon {\n width: 22px !important;\n height: 22px !important;\n display: inline-block;\n margin: 0;\n padding: 2px;\n box-sizing: border-box;\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n &:focus {\n outline: none;\n }\n svg {\n color: #4a4a4a;\n }\n}\n.radius {\n border-radius: 2px;\n -webkit-border-radius: 2px;\n -moz-border-radius: 2px;\n -ms-border-radius: 2px;\n -o-border-radius: 2px;\n}\n\noui-panel-icon {\n &[class^='cdk'],\n &[class$='focused'] {\n .oui-panel-icon {\n @extend .radius;\n background-color: #e8e8e8;\n }\n }\n box-sizing: border-box;\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n}\n\n.close-panel {\n @extend .radius;\n cursor: pointer;\n outline: none;\n width: 16px;\n height: 16px;\n background: #e9e9e9;\n position: absolute;\n top: 2px;\n right: 2px;\n opacity: 0;\n padding: 0;\n border: 0;\n text-align: center;\n line-height: 1;\n padding-bottom: 2px;\n cursor: default;\n pointer-events: none;\n &:focus {\n outline: none;\n pointer-events: all;\n opacity: 1;\n }\n svg {\n fill: #666;\n }\n}\n", "styleUrl": "panel.scss" } ], @@ -32154,7 +42012,7 @@ }, { "name": "OuiPanelStorybook", - "id": "component-OuiPanelStorybook-eef2423d180831775e15990d29ba5b50add25396c5654def4dbaccfb149d1822d2172ec22714b4b49ecd7ec945e75e94a0476b57237d78ee99866ebb0edf8287", + "id": "component-OuiPanelStorybook-f1ec604eb4aedd7f5df42f564049991a1109eafbf07cd28743dfac9d95796c45490482b78ce9d726913f313183c47aff2662c19802236e7a68745e65ed95b59b", "file": "ui/src/stories/panel/panel.component.ts", "encapsulation": [], "entryComponents": [], @@ -32206,7 +42064,7 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import { Component, Input } from '@angular/core';\r\n\r\n@Component({\r\n selector: 'oui-panel-storybook',\r\n template: `\r\n \r\n \r\n \r\n
Lorem ipsum, dolor sit amet consectetur
\r\n

\r\n Loremipsumdolorsit,ametconsecteturadipisicingelit. Cupiditate harum quod\r\n a incidunt? Obcaecati dolores omnis odio repudiandae quo quidem?\r\n Learn more\r\n

\r\n

\r\n Lorem ipsum dolor sit amet consectetur adipisicing elit. Cupiditate,\r\n dolorum! Reprehenderit reiciendis hic magnam esse odio asperiores qui\r\n tempora beatae.\r\n Learn more\r\n

\r\n \r\n `,\r\n})\r\nexport class OuiPanelStorybook {\r\n @Input() xPosition = 'before';\r\n @Input() yPosition = 'above';\r\n @Input() width = 270;\r\n constructor() {}\r\n}\r\n@Component({\r\n selector: 'oui-panel-with-image-storybook',\r\n template: `\r\n \r\n \r\n \r\n
Lorem ipsum, dolor sit amet consectetur
\r\n \r\n

\r\n Lorem ipsum dolor sit, amet consectetur adipisicing elit. Cupiditate\r\n harum quod a incidunt? Obcaecati dolores omnis odio repudiandae quo\r\n quidem?\r\n

\r\n

\r\n Lorem ipsum dolor sit amet consectetur adipisicing elit. Cupiditate,\r\n dolorum! Reprehenderit reiciendis hic magnam esse odio asperiores qui\r\n tempora beatae.\r\n

\r\n
\r\n `,\r\n})\r\nexport class OuiPanelWithImageStorybook {\r\n @Input() xPosition = 'before';\r\n @Input() yPosition = 'above';\r\n constructor() {}\r\n}\r\n", + "sourceCode": "import { Component, Input } from '@angular/core';\n\n@Component({\n selector: 'oui-panel-storybook',\n template: `\n \n \n \n
Lorem ipsum, dolor sit amet consectetur
\n

\n Loremipsumdolorsit,ametconsecteturadipisicingelit. Cupiditate harum quod\n a incidunt? Obcaecati dolores omnis odio repudiandae quo quidem?\n Learn more\n

\n

\n Lorem ipsum dolor sit amet consectetur adipisicing elit. Cupiditate,\n dolorum! Reprehenderit reiciendis hic magnam esse odio asperiores qui\n tempora beatae.\n Learn more\n

\n \n `,\n})\nexport class OuiPanelStorybook {\n @Input() xPosition = 'before';\n @Input() yPosition = 'above';\n @Input() width = 270;\n constructor() {}\n}\n@Component({\n selector: 'oui-panel-with-image-storybook',\n template: `\n \n \n \n
Lorem ipsum, dolor sit amet consectetur
\n \n

\n Lorem ipsum dolor sit, amet consectetur adipisicing elit. Cupiditate\n harum quod a incidunt? Obcaecati dolores omnis odio repudiandae quo\n quidem?\n

\n

\n Lorem ipsum dolor sit amet consectetur adipisicing elit. Cupiditate,\n dolorum! Reprehenderit reiciendis hic magnam esse odio asperiores qui\n tempora beatae.\n

\n
\n `,\n})\nexport class OuiPanelWithImageStorybook {\n @Input() xPosition = 'before';\n @Input() yPosition = 'above';\n constructor() {}\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -32221,7 +42079,7 @@ }, { "name": "OuiPanelWithImageStorybook", - "id": "component-OuiPanelWithImageStorybook-eef2423d180831775e15990d29ba5b50add25396c5654def4dbaccfb149d1822d2172ec22714b4b49ecd7ec945e75e94a0476b57237d78ee99866ebb0edf8287", + "id": "component-OuiPanelWithImageStorybook-f1ec604eb4aedd7f5df42f564049991a1109eafbf07cd28743dfac9d95796c45490482b78ce9d726913f313183c47aff2662c19802236e7a68745e65ed95b59b", "file": "ui/src/stories/panel/panel.component.ts", "encapsulation": [], "entryComponents": [], @@ -32264,7 +42122,7 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import { Component, Input } from '@angular/core';\r\n\r\n@Component({\r\n selector: 'oui-panel-storybook',\r\n template: `\r\n \r\n \r\n \r\n
Lorem ipsum, dolor sit amet consectetur
\r\n

\r\n Loremipsumdolorsit,ametconsecteturadipisicingelit. Cupiditate harum quod\r\n a incidunt? Obcaecati dolores omnis odio repudiandae quo quidem?\r\n Learn more\r\n

\r\n

\r\n Lorem ipsum dolor sit amet consectetur adipisicing elit. Cupiditate,\r\n dolorum! Reprehenderit reiciendis hic magnam esse odio asperiores qui\r\n tempora beatae.\r\n Learn more\r\n

\r\n \r\n `,\r\n})\r\nexport class OuiPanelStorybook {\r\n @Input() xPosition = 'before';\r\n @Input() yPosition = 'above';\r\n @Input() width = 270;\r\n constructor() {}\r\n}\r\n@Component({\r\n selector: 'oui-panel-with-image-storybook',\r\n template: `\r\n \r\n \r\n \r\n
Lorem ipsum, dolor sit amet consectetur
\r\n \r\n

\r\n Lorem ipsum dolor sit, amet consectetur adipisicing elit. Cupiditate\r\n harum quod a incidunt? Obcaecati dolores omnis odio repudiandae quo\r\n quidem?\r\n

\r\n

\r\n Lorem ipsum dolor sit amet consectetur adipisicing elit. Cupiditate,\r\n dolorum! Reprehenderit reiciendis hic magnam esse odio asperiores qui\r\n tempora beatae.\r\n

\r\n
\r\n `,\r\n})\r\nexport class OuiPanelWithImageStorybook {\r\n @Input() xPosition = 'before';\r\n @Input() yPosition = 'above';\r\n constructor() {}\r\n}\r\n", + "sourceCode": "import { Component, Input } from '@angular/core';\n\n@Component({\n selector: 'oui-panel-storybook',\n template: `\n \n \n \n
Lorem ipsum, dolor sit amet consectetur
\n

\n Loremipsumdolorsit,ametconsecteturadipisicingelit. Cupiditate harum quod\n a incidunt? Obcaecati dolores omnis odio repudiandae quo quidem?\n Learn more\n

\n

\n Lorem ipsum dolor sit amet consectetur adipisicing elit. Cupiditate,\n dolorum! Reprehenderit reiciendis hic magnam esse odio asperiores qui\n tempora beatae.\n Learn more\n

\n \n `,\n})\nexport class OuiPanelStorybook {\n @Input() xPosition = 'before';\n @Input() yPosition = 'above';\n @Input() width = 270;\n constructor() {}\n}\n@Component({\n selector: 'oui-panel-with-image-storybook',\n template: `\n \n \n \n
Lorem ipsum, dolor sit amet consectetur
\n \n

\n Lorem ipsum dolor sit, amet consectetur adipisicing elit. Cupiditate\n harum quod a incidunt? Obcaecati dolores omnis odio repudiandae quo\n quidem?\n

\n

\n Lorem ipsum dolor sit amet consectetur adipisicing elit. Cupiditate,\n dolorum! Reprehenderit reiciendis hic magnam esse odio asperiores qui\n tempora beatae.\n

\n
\n `,\n})\nexport class OuiPanelWithImageStorybook {\n @Input() xPosition = 'before';\n @Input() yPosition = 'above';\n constructor() {}\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -32279,7 +42137,7 @@ }, { "name": "OuiProgressBar", - "id": "component-OuiProgressBar-75c1497deb754b83ab450eb6c7d2b2a00f8489ad29e3d896c10f96d69a49d498cb6644aba378db27ae41acdac11d6b2ef6a4e7ffbb261947c859c206a693036d", + "id": "component-OuiProgressBar-bfcecce760edfd8e66a5f55599a7a9ecc905042a0c97538134d5636d3363e53f2ab3a61b1e0b4f427f361dfab9ecdb90cf73772275740c733e15cd91b3e4cc8c", "file": "ui/src/components/progress-bar/progress-bar.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -32374,11 +42232,11 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import {\r\n Component,\r\n ElementRef,\r\n ChangeDetectionStrategy,\r\n Input,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\nimport { coerceNumberProperty } from '@angular/cdk/coercion';\r\nimport { mixinColor } from '../core';\r\n\r\nexport class OuiProgressBarBase {\r\n constructor(public _elementRef: ElementRef) {}\r\n}\r\nexport const _OuiProgressBarMixinBase: typeof OuiProgressBarBase =\r\n mixinColor(OuiProgressBarBase);\r\n\r\n/** Possible mode for a progress spinner. */\r\nexport type ProgressBarMode = 'determinate' | 'indeterminate';\r\n\r\n@Component({\r\n templateUrl: './progress-bar.html',\r\n selector: 'oui-progress-bar',\r\n exportAs: 'OuiProgressBar',\r\n styleUrls: ['progress-bar.scss'],\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-progress-bar',\r\n '[attr.aria-valuemin]': 'mode === \"determinate\" ? 0 : null',\r\n '[attr.aria-valuemax]': 'mode === \"determinate\" ? 100 : null',\r\n '[attr.aria-valuenow]': 'value',\r\n '[attr.mode]': 'mode',\r\n },\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n})\r\nexport class OuiProgressBar extends _OuiProgressBarMixinBase {\r\n private _value = 0;\r\n private _strokeWidth: number;\r\n\r\n @Input() color = 'primary';\r\n\r\n /** Mode of the progress circle */\r\n mode: ProgressBarMode = 'indeterminate';\r\n\r\n @Input()\r\n get value(): number {\r\n return this.mode === 'determinate' ? this._value : 0;\r\n }\r\n set value(newValue: number) {\r\n this._value = Math.max(0, Math.min(100, coerceNumberProperty(newValue)));\r\n this.mode = 'determinate';\r\n }\r\n\r\n @Input() get strokeWidth(): number {\r\n return this._strokeWidth;\r\n }\r\n set strokeWidth(value: number) {\r\n this._strokeWidth = coerceNumberProperty(value);\r\n }\r\n\r\n constructor(elementRef: ElementRef) {\r\n super(elementRef);\r\n }\r\n}\r\n", + "sourceCode": "import {\n Component,\n ElementRef,\n ChangeDetectionStrategy,\n Input,\n ViewEncapsulation,\n} from '@angular/core';\nimport { coerceNumberProperty } from '@angular/cdk/coercion';\nimport { mixinColor } from '../core';\n\nexport class OuiProgressBarBase {\n constructor(public _elementRef: ElementRef) {}\n}\nexport const _OuiProgressBarMixinBase: typeof OuiProgressBarBase =\n mixinColor(OuiProgressBarBase);\n\n/** Possible mode for a progress spinner. */\nexport type ProgressBarMode = 'determinate' | 'indeterminate';\n\n@Component({\n templateUrl: './progress-bar.html',\n selector: 'oui-progress-bar',\n exportAs: 'OuiProgressBar',\n styleUrls: ['progress-bar.scss'],\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-progress-bar',\n '[attr.aria-valuemin]': 'mode === \"determinate\" ? 0 : null',\n '[attr.aria-valuemax]': 'mode === \"determinate\" ? 100 : null',\n '[attr.aria-valuenow]': 'value',\n '[attr.mode]': 'mode',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n})\nexport class OuiProgressBar extends _OuiProgressBarMixinBase {\n private _value = 0;\n private _strokeWidth: number;\n\n @Input() color = 'primary';\n\n /** Mode of the progress circle */\n mode: ProgressBarMode = 'indeterminate';\n\n @Input()\n get value(): number {\n return this.mode === 'determinate' ? this._value : 0;\n }\n set value(newValue: number) {\n this._value = Math.max(0, Math.min(100, coerceNumberProperty(newValue)));\n this.mode = 'determinate';\n }\n\n @Input() get strokeWidth(): number {\n return this._strokeWidth;\n }\n set strokeWidth(value: number) {\n this._strokeWidth = coerceNumberProperty(value);\n }\n\n constructor(elementRef: ElementRef) {\n super(elementRef);\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": ".progress {\r\n height: 4px;\r\n max-width: 100%;\r\n min-width: 260px;\r\n position: relative;\r\n display: block;\r\n overflow: hidden;\r\n margin: 0 auto;\r\n .progress-bar {\r\n display: block;\r\n position: absolute;\r\n left: -200px;\r\n width: 200px;\r\n height: 4px;\r\n animation: loading 2s linear infinite;\r\n -webkit-animation: loading 2s linear infinite;\r\n -moz-animation: loading 2s linear infinite;\r\n -o-animation: loading 2s linear infinite;\r\n -ms-animation: loading 2s linear infinite;\r\n }\r\n}\r\n@keyframes loading {\r\n from {\r\n left: -200px;\r\n width: 30%;\r\n }\r\n 50% {\r\n width: 30%;\r\n }\r\n 70% {\r\n width: 70%;\r\n }\r\n 80% {\r\n left: 50%;\r\n }\r\n 95% {\r\n left: 120%;\r\n }\r\n to {\r\n left: 100%;\r\n }\r\n}\r\n@-webkit-keyframes loading {\r\n from {\r\n left: -200px;\r\n width: 30%;\r\n }\r\n 50% {\r\n width: 30%;\r\n }\r\n 70% {\r\n width: 70%;\r\n }\r\n 80% {\r\n left: 50%;\r\n }\r\n 95% {\r\n left: 120%;\r\n }\r\n to {\r\n left: 100%;\r\n }\r\n}\r\n", + "data": ".progress {\n height: 4px;\n max-width: 100%;\n min-width: 260px;\n position: relative;\n display: block;\n overflow: hidden;\n margin: 0 auto;\n .progress-bar {\n display: block;\n position: absolute;\n left: -200px;\n width: 200px;\n height: 4px;\n animation: loading 2s linear infinite;\n -webkit-animation: loading 2s linear infinite;\n -moz-animation: loading 2s linear infinite;\n -o-animation: loading 2s linear infinite;\n -ms-animation: loading 2s linear infinite;\n }\n}\n@keyframes loading {\n from {\n left: -200px;\n width: 30%;\n }\n 50% {\n width: 30%;\n }\n 70% {\n width: 70%;\n }\n 80% {\n left: 50%;\n }\n 95% {\n left: 120%;\n }\n to {\n left: 100%;\n }\n}\n@-webkit-keyframes loading {\n from {\n left: -200px;\n width: 30%;\n }\n 50% {\n width: 30%;\n }\n 70% {\n width: 70%;\n }\n 80% {\n left: 50%;\n }\n 95% {\n left: 120%;\n }\n to {\n left: 100%;\n }\n}\n", "styleUrl": "progress-bar.scss" } ], @@ -32484,11 +42342,11 @@ } } }, - "templateData": "
\r\n
\r\n\r\n" + "templateData": "
\n
\n\n" }, { "name": "OuiProgressSpinner", - "id": "component-OuiProgressSpinner-bec9f4997202fc2a27c369a14263d4e0168abfaf0e869141b63a0166c39a066f7b91f75e7e59bd4e69f9c41ea9ff2ac6514d849c315a43a3949d5e5412a13db0", + "id": "component-OuiProgressSpinner-dfcd2edb197c146ef6a2c18cd1a3168a6c76fd5981f32e018f6c3dd451ed98da93b0a9b1fcb0387c95e2444d618f637ac5013435a0dc459d2c1645f286736a44", "file": "ui/src/components/progress-spinner/progress-spinner.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -32663,11 +42521,11 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import {\r\n Component,\r\n ElementRef,\r\n ChangeDetectionStrategy,\r\n Input,\r\n ViewEncapsulation,\r\n Inject,\r\n Optional,\r\n} from '@angular/core';\r\nimport { DOCUMENT } from '@angular/common';\r\nimport { coerceNumberProperty } from '@angular/cdk/coercion';\r\nimport { mixinColor } from '../core';\r\n\r\nexport class OuiProgressSpinnerBase {\r\n constructor(public _elementRef: ElementRef) {}\r\n}\r\nexport const _OuiProgressSpinnerMixinBase: typeof OuiProgressSpinnerBase =\r\n mixinColor(OuiProgressSpinnerBase);\r\n\r\n/** Possible mode for a progress spinner. */\r\nexport type ProgressSpinnerMode = 'determinate' | 'indeterminate';\r\n\r\n/**\r\n * Base reference size of the spinner.\r\n *\r\n * @docs-private\r\n */\r\nconst BASE_SIZE = 15;\r\n\r\n/**\r\n * Base reference stroke width of the spinner.\r\n *\r\n * @docs-private\r\n */\r\nconst BASE_STROKE_WIDTH = 2;\r\n\r\nconst INDETERMINATE_ANIMATION_TEMPLATE = `\r\n @keyframes oui-progress-spinner-stroke-rotate-DIAMETER {\r\n 0% { stroke-dashoffset: START_VALUE; transform: rotate(0); }\r\n 12.5% { stroke-dashoffset: END_VALUE; transform: rotate(0); }\r\n 12.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(72.5deg); }\r\n 25% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(72.5deg); }\r\n\r\n 25.0001% { stroke-dashoffset: START_VALUE; transform: rotate(270deg); }\r\n 37.5% { stroke-dashoffset: END_VALUE; transform: rotate(270deg); }\r\n 37.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(161.5deg); }\r\n 50% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(161.5deg); }\r\n\r\n 50.0001% { stroke-dashoffset: START_VALUE; transform: rotate(180deg); }\r\n 62.5% { stroke-dashoffset: END_VALUE; transform: rotate(180deg); }\r\n 62.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(251.5deg); }\r\n 75% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(251.5deg); }\r\n\r\n 75.0001% { stroke-dashoffset: START_VALUE; transform: rotate(90deg); }\r\n 87.5% { stroke-dashoffset: END_VALUE; transform: rotate(90deg); }\r\n 87.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(341.5deg); }\r\n 100% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(341.5deg); }\r\n }\r\n`;\r\n\r\n@Component({\r\n templateUrl: './progress-spinner.html',\r\n selector: 'oui-progress-spinner',\r\n exportAs: 'OuiProgressSpinner',\r\n styleUrls: ['progress-spinner.scss'],\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-progress-spinner',\r\n '[style.width.px]': 'diameter',\r\n '[style.height.px]': 'diameter',\r\n '[attr.aria-valuemin]': 'mode === \"determinate\" ? 0 : null',\r\n '[attr.aria-valuemax]': 'mode === \"determinate\" ? 100 : null',\r\n '[attr.aria-valuenow]': 'value',\r\n '[attr.mode]': 'mode',\r\n },\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n})\r\nexport class OuiProgressSpinner extends _OuiProgressSpinnerMixinBase {\r\n private static diameters = new Set([BASE_SIZE]);\r\n private static styleTag: HTMLStyleElement | null = null;\r\n private _value = 0;\r\n private _strokeWidth: number;\r\n\r\n @Input() color = 'primary';\r\n @Input()\r\n get diameter(): number {\r\n return this._diameter;\r\n }\r\n set diameter(size: number) {\r\n this._diameter = coerceNumberProperty(size);\r\n if (!OuiProgressSpinner.diameters.has(this._diameter)) {\r\n this._attachStyleNode();\r\n }\r\n }\r\n private _diameter = BASE_SIZE;\r\n\r\n /** Mode of the progress circle */\r\n mode: ProgressSpinnerMode = 'indeterminate';\r\n\r\n @Input()\r\n get value(): number {\r\n return this.mode === 'determinate' ? this._value : 0;\r\n }\r\n set value(newValue: number) {\r\n this._value = Math.max(0, Math.min(100, coerceNumberProperty(newValue)));\r\n this.mode = 'determinate';\r\n }\r\n\r\n @Input() get strokeWidth(): number {\r\n return this._strokeWidth || BASE_STROKE_WIDTH;\r\n }\r\n set strokeWidth(value: number) {\r\n this._strokeWidth = coerceNumberProperty(value);\r\n }\r\n constructor(\r\n _elementRef: ElementRef,\r\n @Optional() @Inject(DOCUMENT) private _document: any\r\n ) {\r\n super(_elementRef);\r\n }\r\n\r\n /** The radius of the spinner, adjusted for stroke width. */\r\n get _circleRadius() {\r\n return (this.diameter - this.strokeWidth) / 2;\r\n }\r\n\r\n /** The view box of the spinner's svg element. */\r\n get _viewBox() {\r\n const viewBox = this._circleRadius * 2 + this.strokeWidth;\r\n return `0 0 ${viewBox} ${viewBox}`;\r\n }\r\n get _circleStrokeWidth() {\r\n return (this.strokeWidth / this.diameter) * 100;\r\n }\r\n\r\n /** The stroke circumference of the svg circle. */\r\n get _strokeCircumference(): number {\r\n return 2 * Math.PI * this._circleRadius;\r\n }\r\n\r\n /** The dash offset of the svg circle. */\r\n get _strokeDashOffset() {\r\n if (this.mode === 'determinate') {\r\n return (this._strokeCircumference * (100 - this._value)) / 100;\r\n }\r\n\r\n // In fallback mode set the circle to 80% and rotate it with CSS.\r\n if (this.mode === 'indeterminate') {\r\n return this._strokeCircumference * 0.2;\r\n }\r\n\r\n return null;\r\n }\r\n\r\n /** Dynamically generates a style tag containing the correct animation for this diameter. */\r\n private _attachStyleNode(): void {\r\n let styleTag = OuiProgressSpinner.styleTag;\r\n\r\n if (!styleTag) {\r\n styleTag = this._document.createElement('style');\r\n this._document.head.appendChild(styleTag);\r\n OuiProgressSpinner.styleTag = styleTag;\r\n }\r\n\r\n if (styleTag && styleTag.sheet) {\r\n (styleTag.sheet as CSSStyleSheet).insertRule(this._getAnimationText(), 0);\r\n }\r\n\r\n OuiProgressSpinner.diameters.add(this.diameter);\r\n }\r\n\r\n /** Generates animation styles adjusted for the spinner's diameter. */\r\n private _getAnimationText(): string {\r\n return (\r\n INDETERMINATE_ANIMATION_TEMPLATE\r\n // Animation should begin at 5% and end at 80%\r\n .replace(/START_VALUE/g, `${0.95 * this._strokeCircumference}`)\r\n .replace(/END_VALUE/g, `${0.2 * this._strokeCircumference}`)\r\n .replace(/DIAMETER/g, `${this.diameter}`)\r\n );\r\n }\r\n}\r\n", + "sourceCode": "import {\n Component,\n ElementRef,\n ChangeDetectionStrategy,\n Input,\n ViewEncapsulation,\n Inject,\n Optional,\n} from '@angular/core';\nimport { DOCUMENT } from '@angular/common';\nimport { coerceNumberProperty } from '@angular/cdk/coercion';\nimport { mixinColor } from '../core';\n\nexport class OuiProgressSpinnerBase {\n constructor(public _elementRef: ElementRef) {}\n}\nexport const _OuiProgressSpinnerMixinBase: typeof OuiProgressSpinnerBase =\n mixinColor(OuiProgressSpinnerBase);\n\n/** Possible mode for a progress spinner. */\nexport type ProgressSpinnerMode = 'determinate' | 'indeterminate';\n\n/**\n * Base reference size of the spinner.\n *\n * @docs-private\n */\nconst BASE_SIZE = 15;\n\n/**\n * Base reference stroke width of the spinner.\n *\n * @docs-private\n */\nconst BASE_STROKE_WIDTH = 2;\n\nconst INDETERMINATE_ANIMATION_TEMPLATE = `\n @keyframes oui-progress-spinner-stroke-rotate-DIAMETER {\n 0% { stroke-dashoffset: START_VALUE; transform: rotate(0); }\n 12.5% { stroke-dashoffset: END_VALUE; transform: rotate(0); }\n 12.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(72.5deg); }\n 25% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(72.5deg); }\n\n 25.0001% { stroke-dashoffset: START_VALUE; transform: rotate(270deg); }\n 37.5% { stroke-dashoffset: END_VALUE; transform: rotate(270deg); }\n 37.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(161.5deg); }\n 50% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(161.5deg); }\n\n 50.0001% { stroke-dashoffset: START_VALUE; transform: rotate(180deg); }\n 62.5% { stroke-dashoffset: END_VALUE; transform: rotate(180deg); }\n 62.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(251.5deg); }\n 75% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(251.5deg); }\n\n 75.0001% { stroke-dashoffset: START_VALUE; transform: rotate(90deg); }\n 87.5% { stroke-dashoffset: END_VALUE; transform: rotate(90deg); }\n 87.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(341.5deg); }\n 100% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(341.5deg); }\n }\n`;\n\n@Component({\n templateUrl: './progress-spinner.html',\n selector: 'oui-progress-spinner',\n exportAs: 'OuiProgressSpinner',\n styleUrls: ['progress-spinner.scss'],\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-progress-spinner',\n '[style.width.px]': 'diameter',\n '[style.height.px]': 'diameter',\n '[attr.aria-valuemin]': 'mode === \"determinate\" ? 0 : null',\n '[attr.aria-valuemax]': 'mode === \"determinate\" ? 100 : null',\n '[attr.aria-valuenow]': 'value',\n '[attr.mode]': 'mode',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n})\nexport class OuiProgressSpinner extends _OuiProgressSpinnerMixinBase {\n private static diameters = new Set([BASE_SIZE]);\n private static styleTag: HTMLStyleElement | null = null;\n private _value = 0;\n private _strokeWidth: number;\n\n @Input() color = 'primary';\n @Input()\n get diameter(): number {\n return this._diameter;\n }\n set diameter(size: number) {\n this._diameter = coerceNumberProperty(size);\n if (!OuiProgressSpinner.diameters.has(this._diameter)) {\n this._attachStyleNode();\n }\n }\n private _diameter = BASE_SIZE;\n\n /** Mode of the progress circle */\n mode: ProgressSpinnerMode = 'indeterminate';\n\n @Input()\n get value(): number {\n return this.mode === 'determinate' ? this._value : 0;\n }\n set value(newValue: number) {\n this._value = Math.max(0, Math.min(100, coerceNumberProperty(newValue)));\n this.mode = 'determinate';\n }\n\n @Input() get strokeWidth(): number {\n return this._strokeWidth || BASE_STROKE_WIDTH;\n }\n set strokeWidth(value: number) {\n this._strokeWidth = coerceNumberProperty(value);\n }\n constructor(\n _elementRef: ElementRef,\n @Optional() @Inject(DOCUMENT) private _document: any\n ) {\n super(_elementRef);\n }\n\n /** The radius of the spinner, adjusted for stroke width. */\n get _circleRadius() {\n return (this.diameter - this.strokeWidth) / 2;\n }\n\n /** The view box of the spinner's svg element. */\n get _viewBox() {\n const viewBox = this._circleRadius * 2 + this.strokeWidth;\n return `0 0 ${viewBox} ${viewBox}`;\n }\n get _circleStrokeWidth() {\n return (this.strokeWidth / this.diameter) * 100;\n }\n\n /** The stroke circumference of the svg circle. */\n get _strokeCircumference(): number {\n return 2 * Math.PI * this._circleRadius;\n }\n\n /** The dash offset of the svg circle. */\n get _strokeDashOffset() {\n if (this.mode === 'determinate') {\n return (this._strokeCircumference * (100 - this._value)) / 100;\n }\n\n // In fallback mode set the circle to 80% and rotate it with CSS.\n if (this.mode === 'indeterminate') {\n return this._strokeCircumference * 0.2;\n }\n\n return null;\n }\n\n /** Dynamically generates a style tag containing the correct animation for this diameter. */\n private _attachStyleNode(): void {\n let styleTag = OuiProgressSpinner.styleTag;\n\n if (!styleTag) {\n styleTag = this._document.createElement('style');\n this._document.head.appendChild(styleTag);\n OuiProgressSpinner.styleTag = styleTag;\n }\n\n if (styleTag && styleTag.sheet) {\n (styleTag.sheet as CSSStyleSheet).insertRule(this._getAnimationText(), 0);\n }\n\n OuiProgressSpinner.diameters.add(this.diameter);\n }\n\n /** Generates animation styles adjusted for the spinner's diameter. */\n private _getAnimationText(): string {\n return (\n INDETERMINATE_ANIMATION_TEMPLATE\n // Animation should begin at 5% and end at 80%\n .replace(/START_VALUE/g, `${0.95 * this._strokeCircumference}`)\n .replace(/END_VALUE/g, `${0.2 * this._strokeCircumference}`)\n .replace(/DIAMETER/g, `${this.diameter}`)\n );\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": "$oui-progress-spinner-default-radius: 6.5px;\r\n$oui-progress-spinner-default-circumference: 3.14 *\r\n $oui-progress-spinner-default-radius * 2;\r\n$start: (1 - 0.05) * $oui-progress-spinner-default-circumference; // start the animation at 5%\r\n$end: (1 - 0.8) * $oui-progress-spinner-default-circumference; // end the animation at 80%\r\n\r\n// Progress Spinner\r\n.oui-progress-spinner {\r\n display: block;\r\n position: relative;\r\n svg {\r\n position: absolute;\r\n transform: rotate(-90deg);\r\n top: 0;\r\n left: 0;\r\n transform-origin: center;\r\n overflow: visible;\r\n circle {\r\n fill: transparent;\r\n transform-origin: center;\r\n stroke-linecap: square;\r\n }\r\n }\r\n &[mode='indeterminate'] {\r\n animation: oui-progress-spinner-linear-rotate 2s linear infinite;\r\n circle {\r\n transition-property: stroke;\r\n animation-duration: 4s;\r\n animation-timing-function: cubic-bezier(0.35, 0, 0.25, 1);\r\n animation-iteration-count: infinite;\r\n }\r\n }\r\n}\r\n\r\n// Indeterminate mode animation\r\n@keyframes oui-progress-spinner-linear-rotate {\r\n 0% {\r\n transform: rotate(0deg);\r\n }\r\n 100% {\r\n transform: rotate(360deg);\r\n }\r\n}\r\n\r\n@keyframes oui-progress-spinner-stroke-rotate-15 {\r\n 0% {\r\n stroke-dashoffset: $start;\r\n transform: rotate(0);\r\n }\r\n 12.5% {\r\n stroke-dashoffset: $end;\r\n transform: rotate(0);\r\n }\r\n 12.5001% {\r\n stroke-dashoffset: $end;\r\n transform: rotateX(180deg) rotate(72.5deg);\r\n }\r\n 25% {\r\n stroke-dashoffset: $start;\r\n transform: rotateX(180deg) rotate(72.5deg);\r\n }\r\n\r\n 25.0001% {\r\n stroke-dashoffset: $start;\r\n transform: rotate(270deg);\r\n }\r\n 37.5% {\r\n stroke-dashoffset: $end;\r\n transform: rotate(270deg);\r\n }\r\n 37.5001% {\r\n stroke-dashoffset: $end;\r\n transform: rotateX(180deg) rotate(161.5deg);\r\n }\r\n 50% {\r\n stroke-dashoffset: $start;\r\n transform: rotateX(180deg) rotate(161.5deg);\r\n }\r\n\r\n 50.0001% {\r\n stroke-dashoffset: $start;\r\n transform: rotate(180deg);\r\n }\r\n 62.5% {\r\n stroke-dashoffset: $end;\r\n transform: rotate(180deg);\r\n }\r\n 62.5001% {\r\n stroke-dashoffset: $end;\r\n transform: rotateX(180deg) rotate(251.5deg);\r\n }\r\n 75% {\r\n stroke-dashoffset: $start;\r\n transform: rotateX(180deg) rotate(251.5deg);\r\n }\r\n\r\n 75.0001% {\r\n stroke-dashoffset: $start;\r\n transform: rotate(90deg);\r\n }\r\n 87.5% {\r\n stroke-dashoffset: $end;\r\n transform: rotate(90deg);\r\n }\r\n 87.5001% {\r\n stroke-dashoffset: $end;\r\n transform: rotateX(180deg) rotate(341.5deg);\r\n }\r\n 100% {\r\n stroke-dashoffset: $start;\r\n transform: rotateX(180deg) rotate(341.5deg);\r\n }\r\n}\r\n", + "data": "$oui-progress-spinner-default-radius: 6.5px;\n$oui-progress-spinner-default-circumference: 3.14 *\n $oui-progress-spinner-default-radius * 2;\n$start: (1 - 0.05) * $oui-progress-spinner-default-circumference; // start the animation at 5%\n$end: (1 - 0.8) * $oui-progress-spinner-default-circumference; // end the animation at 80%\n\n// Progress Spinner\n.oui-progress-spinner {\n display: block;\n position: relative;\n svg {\n position: absolute;\n transform: rotate(-90deg);\n top: 0;\n left: 0;\n transform-origin: center;\n overflow: visible;\n circle {\n fill: transparent;\n transform-origin: center;\n stroke-linecap: square;\n }\n }\n &[mode='indeterminate'] {\n animation: oui-progress-spinner-linear-rotate 2s linear infinite;\n circle {\n transition-property: stroke;\n animation-duration: 4s;\n animation-timing-function: cubic-bezier(0.35, 0, 0.25, 1);\n animation-iteration-count: infinite;\n }\n }\n}\n\n// Indeterminate mode animation\n@keyframes oui-progress-spinner-linear-rotate {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n}\n\n@keyframes oui-progress-spinner-stroke-rotate-15 {\n 0% {\n stroke-dashoffset: $start;\n transform: rotate(0);\n }\n 12.5% {\n stroke-dashoffset: $end;\n transform: rotate(0);\n }\n 12.5001% {\n stroke-dashoffset: $end;\n transform: rotateX(180deg) rotate(72.5deg);\n }\n 25% {\n stroke-dashoffset: $start;\n transform: rotateX(180deg) rotate(72.5deg);\n }\n\n 25.0001% {\n stroke-dashoffset: $start;\n transform: rotate(270deg);\n }\n 37.5% {\n stroke-dashoffset: $end;\n transform: rotate(270deg);\n }\n 37.5001% {\n stroke-dashoffset: $end;\n transform: rotateX(180deg) rotate(161.5deg);\n }\n 50% {\n stroke-dashoffset: $start;\n transform: rotateX(180deg) rotate(161.5deg);\n }\n\n 50.0001% {\n stroke-dashoffset: $start;\n transform: rotate(180deg);\n }\n 62.5% {\n stroke-dashoffset: $end;\n transform: rotate(180deg);\n }\n 62.5001% {\n stroke-dashoffset: $end;\n transform: rotateX(180deg) rotate(251.5deg);\n }\n 75% {\n stroke-dashoffset: $start;\n transform: rotateX(180deg) rotate(251.5deg);\n }\n\n 75.0001% {\n stroke-dashoffset: $start;\n transform: rotate(90deg);\n }\n 87.5% {\n stroke-dashoffset: $end;\n transform: rotate(90deg);\n }\n 87.5001% {\n stroke-dashoffset: $end;\n transform: rotateX(180deg) rotate(341.5deg);\n }\n 100% {\n stroke-dashoffset: $start;\n transform: rotateX(180deg) rotate(341.5deg);\n }\n}\n", "styleUrl": "progress-spinner.scss" } ], @@ -32877,11 +42735,11 @@ } } }, - "templateData": "\r\n \r\n\r\n" + "templateData": "\n \n\n" }, { "name": "OuiPseudoCheckbox", - "id": "component-OuiPseudoCheckbox-29ed0452519a0a843e3eedd11d0cdbe18a439a7cb0488f741e76a4b486f3b4287158d349a7b4dbbd16f211407e8a9765b2131f39ebac2c33aaa89b209fa40870", + "id": "component-OuiPseudoCheckbox-b056ad9e6ad32a5e233df3fb36dd1b036dbface3b6af62af411150c716f8f34d58e0199ab7f6252dc9f74c0f615f502206a42ffb98ddc6eeed4e05542aef5a97", "file": "ui/src/components/core/selection/pseudo-checkbox/pseudo-checkbox.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -32981,11 +42839,11 @@ "description": "

Component that shows a simplified checkbox without including any kind of "real" checkbox.\nMeant to be used when the checkbox is purely decorative and a large number of them will be\nincluded, such as for the options in a multi-select. Uses no SVGs or complex animations.\nNote that theming is meant to be handled by the parent element, e.g.\noui-primary .oui-pseudo-checkbox.

\n

Note that this component will be completely invisible to screen-reader users. This is not\ninterchangeable with <oui-checkbox> and should not be used if the user would directly\ninteract with the checkbox. The pseudo-checkbox should only be used as an implementation detail\nof more complex components that appropriately handle selected / checked state.

\n", "rawdescription": "\n\nComponent that shows a simplified checkbox without including any kind of \"real\" checkbox.\nMeant to be used when the checkbox is purely decorative and a large number of them will be\nincluded, such as for the options in a multi-select. Uses no SVGs or complex animations.\nNote that theming is meant to be handled by the parent element, e.g.\n`oui-primary .oui-pseudo-checkbox`.\n\nNote that this component will be completely invisible to screen-reader users. This is *not*\ninterchangeable with `` and should *not* be used if the user would directly\ninteract with the checkbox. The pseudo-checkbox should only be used as an implementation detail\nof more complex components that appropriately handle selected / checked state.\n\n", "type": "component", - "sourceCode": "import {\r\n Component,\r\n ViewEncapsulation,\r\n Input,\r\n ChangeDetectionStrategy,\r\n Inject,\r\n Optional,\r\n OnDestroy,\r\n ElementRef,\r\n NgZone,\r\n} from '@angular/core';\r\nimport { ANIMATION_MODULE_TYPE } from '@angular/platform-browser/animations';\r\nimport { FocusMonitor } from '@angular/cdk/a11y';\r\nimport { Subscription } from 'rxjs';\r\n\r\n/**\r\n * Possible states for a pseudo checkbox.\r\n *\r\n * @docs-private\r\n */\r\nexport type OuiPseudoCheckboxState = 'unchecked' | 'checked';\r\n\r\n/**\r\n * Component that shows a simplified checkbox without including any kind of \"real\" checkbox.\r\n * Meant to be used when the checkbox is purely decorative and a large number of them will be\r\n * included, such as for the options in a multi-select. Uses no SVGs or complex animations.\r\n * Note that theming is meant to be handled by the parent element, e.g.\r\n * `oui-primary .oui-pseudo-checkbox`.\r\n *\r\n * Note that this component will be completely invisible to screen-reader users. This is *not*\r\n * interchangeable with `` and should *not* be used if the user would directly\r\n * interact with the checkbox. The pseudo-checkbox should only be used as an implementation detail\r\n * of more complex components that appropriately handle selected / checked state.\r\n *\r\n * @docs-private\r\n */\r\n@Component({\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n selector: 'oui-pseudo-checkbox',\r\n styleUrls: ['pseudo-checkbox.scss'],\r\n template: '',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-pseudo-checkbox',\r\n '[class.oui-pseudo-checkbox-checked]': 'state === \"checked\"',\r\n '[class.oui-pseudo-checkbox-disabled]': 'disabled',\r\n },\r\n})\r\nexport class OuiPseudoCheckbox implements OnDestroy {\r\n /** Display state of the checkbox. */\r\n @Input() state: OuiPseudoCheckboxState = 'unchecked';\r\n\r\n /** Whether the checkbox is disabled. */\r\n @Input() disabled = false;\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n\r\n constructor(\r\n protected elementRef: ElementRef,\r\n private _focusMonitor: FocusMonitor,\r\n private _ngZone: NgZone,\r\n @Optional() @Inject(ANIMATION_MODULE_TYPE) public _animationMode?: string\r\n ) {\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this.elementRef, true)\r\n .subscribe(() => this._ngZone.run(() => {}));\r\n }\r\n ngOnDestroy() {\r\n this._monitorSubscription.unsubscribe();\r\n this._focusMonitor.stopMonitoring(this.elementRef);\r\n }\r\n}\r\n", + "sourceCode": "import {\n Component,\n ViewEncapsulation,\n Input,\n ChangeDetectionStrategy,\n Inject,\n Optional,\n OnDestroy,\n ElementRef,\n NgZone,\n} from '@angular/core';\nimport { ANIMATION_MODULE_TYPE } from '@angular/platform-browser/animations';\nimport { FocusMonitor } from '@angular/cdk/a11y';\nimport { Subscription } from 'rxjs';\n\n/**\n * Possible states for a pseudo checkbox.\n *\n * @docs-private\n */\nexport type OuiPseudoCheckboxState = 'unchecked' | 'checked';\n\n/**\n * Component that shows a simplified checkbox without including any kind of \"real\" checkbox.\n * Meant to be used when the checkbox is purely decorative and a large number of them will be\n * included, such as for the options in a multi-select. Uses no SVGs or complex animations.\n * Note that theming is meant to be handled by the parent element, e.g.\n * `oui-primary .oui-pseudo-checkbox`.\n *\n * Note that this component will be completely invisible to screen-reader users. This is *not*\n * interchangeable with `` and should *not* be used if the user would directly\n * interact with the checkbox. The pseudo-checkbox should only be used as an implementation detail\n * of more complex components that appropriately handle selected / checked state.\n *\n * @docs-private\n */\n@Component({\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n selector: 'oui-pseudo-checkbox',\n styleUrls: ['pseudo-checkbox.scss'],\n template: '',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-pseudo-checkbox',\n '[class.oui-pseudo-checkbox-checked]': 'state === \"checked\"',\n '[class.oui-pseudo-checkbox-disabled]': 'disabled',\n },\n})\nexport class OuiPseudoCheckbox implements OnDestroy {\n /** Display state of the checkbox. */\n @Input() state: OuiPseudoCheckboxState = 'unchecked';\n\n /** Whether the checkbox is disabled. */\n @Input() disabled = false;\n private _monitorSubscription: Subscription = Subscription.EMPTY;\n\n constructor(\n protected elementRef: ElementRef,\n private _focusMonitor: FocusMonitor,\n private _ngZone: NgZone,\n @Optional() @Inject(ANIMATION_MODULE_TYPE) public _animationMode?: string\n ) {\n this._monitorSubscription = this._focusMonitor\n .monitor(this.elementRef, true)\n .subscribe(() => this._ngZone.run(() => {}));\n }\n ngOnDestroy() {\n this._monitorSubscription.unsubscribe();\n this._focusMonitor.stopMonitoring(this.elementRef);\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": "@import '../../../checkbox/checkbox.scss';\r\n\r\n// The width/height of the checkbox element.\r\n$oui-checkbox-size: 16px !default;\r\n\r\n// The width of the checkbox border shown when the checkbox is unchecked.\r\n$oui-checkbox-border-width: 1px;\r\n\r\n// The base duration used for the majority of transitions for the checkbox.\r\n$oui-checkbox-transition-duration: 90ms;\r\n\r\n// Padding inside of a pseudo checkbox.\r\n$_oui-pseudo-checkbox-padding: $oui-checkbox-border-width * 2;\r\n\r\n// Size of the checkmark in a pseudo checkbox.\r\n$_oui-pseudo-checkmark-size: $oui-checkbox-size -\r\n (3.5 * $_oui-pseudo-checkbox-padding);\r\n\r\n.oui-pseudo-checkbox {\r\n width: $oui-checkbox-size;\r\n height: $oui-checkbox-size;\r\n border: $oui-checkbox-border-width solid #9c9c9c;\r\n border-radius: 3px;\r\n cursor: pointer;\r\n display: inline-block;\r\n vertical-align: middle;\r\n box-sizing: border-box;\r\n position: relative;\r\n flex-shrink: 0;\r\n transition: border-color $oui-checkbox-transition-duration\r\n $oui-linear-out-slow-in-timing-function,\r\n background-color $oui-checkbox-transition-duration\r\n $oui-linear-out-slow-in-timing-function;\r\n\r\n // Used to render the checkmark/mixedmark inside of the box.\r\n &::after {\r\n position: absolute;\r\n opacity: 0;\r\n content: '';\r\n border-bottom: $oui-checkbox-border-width * 2 solid #333;\r\n transition: opacity $oui-checkbox-transition-duration\r\n $oui-linear-out-slow-in-timing-function;\r\n }\r\n}\r\n\r\n.oui-pseudo-checkbox-disabled {\r\n cursor: default;\r\n}\r\n\r\n.oui-pseudo-checkbox-checked::after {\r\n top: ($oui-checkbox-size * 0.5) - ($_oui-pseudo-checkmark-size * 0.25) -\r\n ($oui-checkbox-size * 0.1) - $oui-checkbox-border-width;\r\n left: $_oui-pseudo-checkbox-padding - $oui-checkbox-border-width * 0.5;\r\n width: $_oui-pseudo-checkmark-size;\r\n height: ($_oui-pseudo-checkmark-size - $oui-checkbox-border-width) * 0.5;\r\n border-left: $oui-checkbox-border-width * 2 solid #333;\r\n transform: rotate(-45deg);\r\n opacity: 1;\r\n}\r\n", + "data": "@import '../../../checkbox/checkbox.scss';\n\n// The width/height of the checkbox element.\n$oui-checkbox-size: 16px !default;\n\n// The width of the checkbox border shown when the checkbox is unchecked.\n$oui-checkbox-border-width: 1px;\n\n// The base duration used for the majority of transitions for the checkbox.\n$oui-checkbox-transition-duration: 90ms;\n\n// Padding inside of a pseudo checkbox.\n$_oui-pseudo-checkbox-padding: $oui-checkbox-border-width * 2;\n\n// Size of the checkmark in a pseudo checkbox.\n$_oui-pseudo-checkmark-size: $oui-checkbox-size -\n (3.5 * $_oui-pseudo-checkbox-padding);\n\n.oui-pseudo-checkbox {\n width: $oui-checkbox-size;\n height: $oui-checkbox-size;\n border: $oui-checkbox-border-width solid #9c9c9c;\n border-radius: 3px;\n cursor: pointer;\n display: inline-block;\n vertical-align: middle;\n box-sizing: border-box;\n position: relative;\n flex-shrink: 0;\n transition: border-color $oui-checkbox-transition-duration\n $oui-linear-out-slow-in-timing-function,\n background-color $oui-checkbox-transition-duration\n $oui-linear-out-slow-in-timing-function;\n\n // Used to render the checkmark/mixedmark inside of the box.\n &::after {\n position: absolute;\n opacity: 0;\n content: '';\n border-bottom: $oui-checkbox-border-width * 2 solid #333;\n transition: opacity $oui-checkbox-transition-duration\n $oui-linear-out-slow-in-timing-function;\n }\n}\n\n.oui-pseudo-checkbox-disabled {\n cursor: default;\n}\n\n.oui-pseudo-checkbox-checked::after {\n top: ($oui-checkbox-size * 0.5) - ($_oui-pseudo-checkmark-size * 0.25) -\n ($oui-checkbox-size * 0.1) - $oui-checkbox-border-width;\n left: $_oui-pseudo-checkbox-padding - $oui-checkbox-border-width * 0.5;\n width: 10px;\n height: 6px;\n border-left: $oui-checkbox-border-width * 2 solid #333;\n transform: rotate(-45deg);\n opacity: 1;\n}\n", "styleUrl": "pseudo-checkbox.scss" } ], @@ -33069,7 +42927,7 @@ }, { "name": "OuiRadioButton", - "id": "component-OuiRadioButton-7d11d4690b55d1942745ff1167a94fe08c4f452dd5c295a646dde09294239e4d0fc2fe722e202b13579ab34b7f46211cf0d265ab37f0b73ce63cb116c8f731e3", + "id": "component-OuiRadioButton-8c1800186b5ff980f973d7d600dcd7b5a64f6b86f979b39ddfc305e1e942a8a2d8512bbb5dd25fe4c0b0e1eaa769127c104157bd1cda506c02c0e12b36b7f8ce", "file": "ui/src/components/radio/radio.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -33097,7 +42955,7 @@ "deprecationMessage": "", "rawdescription": "\nThe `aria-describedby` attribute is read after the element label and field type.", "description": "

The aria-describedby attribute is read after the element label and field type.

\n", - "line": 358, + "line": 359, "type": "string", "decorators": [] }, @@ -33107,7 +42965,7 @@ "deprecationMessage": "", "rawdescription": "\nUsed to set the `aria-label` attribute on the underlying input element.", "description": "

Used to set the aria-label attribute on the underlying input element.

\n", - "line": 350, + "line": 351, "type": "string", "decorators": [] }, @@ -33117,7 +42975,7 @@ "deprecationMessage": "", "rawdescription": "\nThe `aria-labelledby` attribute takes precedence as the element text alternative.", "description": "

The aria-labelledby attribute takes precedence as the element text alternative.

\n", - "line": 353, + "line": 354, "type": "string", "decorators": [] }, @@ -33127,7 +42985,7 @@ "deprecationMessage": "", "rawdescription": "\nWhether this radio button is checked.", "description": "

Whether this radio button is checked.

\n", - "line": 362, + "line": 363, "type": "boolean", "decorators": [] }, @@ -33138,7 +42996,7 @@ "deprecationMessage": "", "rawdescription": "\n\nImplemented as part of CanColor.\n", "description": "

Implemented as part of CanColor.

\n", - "line": 342, + "line": 343, "type": "string", "decorators": [] }, @@ -33148,7 +43006,7 @@ "deprecationMessage": "", "rawdescription": "\nWhether the radio button is disabled.", "description": "

Whether the radio button is disabled.

\n", - "line": 429, + "line": 430, "type": "boolean", "decorators": [] }, @@ -33159,7 +43017,7 @@ "deprecationMessage": "", "rawdescription": "\nThe unique ID for the radio button.", "description": "

The unique ID for the radio button.

\n", - "line": 344, + "line": 345, "type": "string", "decorators": [] }, @@ -33169,7 +43027,7 @@ "deprecationMessage": "", "rawdescription": "\nWhether the label should appear after or before the radio button. Defaults to `after`", "description": "

Whether the label should appear after or before the radio button. Defaults to after

\n", - "line": 415, + "line": 416, "type": "\"before\" | \"after\"", "decorators": [] }, @@ -33179,7 +43037,7 @@ "deprecationMessage": "", "rawdescription": "\nAnalog to HTML `name` attribute used to group radios for unique selection.", "description": "

Analog to HTML name attribute used to group radios for unique selection.

\n", - "line": 347, + "line": 348, "type": "string", "decorators": [] }, @@ -33189,7 +43047,7 @@ "deprecationMessage": "", "rawdescription": "\nWhether the radio button is required.", "description": "

Whether the radio button is required.

\n", - "line": 444, + "line": 445, "type": "boolean", "decorators": [] }, @@ -33199,7 +43057,7 @@ "deprecationMessage": "", "rawdescription": "\nThe `aria-labelledby` attribute takes precedence as the element text alternative.", "description": "

The aria-labelledby attribute takes precedence as the element text alternative.

\n", - "line": 355, + "line": 356, "type": "number", "decorators": [] }, @@ -33209,7 +43067,7 @@ "deprecationMessage": "", "rawdescription": "\nThe value of this radio button.", "description": "

The value of this radio button.

\n", - "line": 395, + "line": 396, "type": "any", "decorators": [] } @@ -33222,7 +43080,7 @@ "deprecationMessage": "", "rawdescription": "\n\nEvent emitted when the checked state of this radio button changes.\nChange events are only emitted when the value changes due to user interaction with\nthe radio button (the same behavior as ``).\n", "description": "

Event emitted when the checked state of this radio button changes.\nChange events are only emitted when the value changes due to user interaction with\nthe radio button (the same behavior as <input type-"radio">).

\n", - "line": 457, + "line": 458, "type": "EventEmitter" } ], @@ -33234,7 +43092,7 @@ "type": "string", "optional": true, "description": "", - "line": 491, + "line": 493, "decorators": [ { "name": "Optional", @@ -33257,7 +43115,7 @@ "type": "", "optional": false, "description": "

Whether this radio is checked.

\n", - "line": 471, + "line": 473, "rawdescription": "\nWhether this radio is checked.", "modifierKind": [ 121 @@ -33270,7 +43128,7 @@ "type": "boolean", "optional": false, "description": "

Whether this radio is disabled.

\n", - "line": 474, + "line": 476, "rawdescription": "\nWhether this radio is disabled.", "modifierKind": [ 121 @@ -33283,7 +43141,7 @@ "type": "ElementRef", "optional": false, "description": "

The native <input type=radio> element

\n", - "line": 460, + "line": 462, "rawdescription": "\nThe native `` element", "decorators": [ { @@ -33299,7 +43157,7 @@ "type": "\"before\" | \"after\"", "optional": false, "description": "", - "line": 425, + "line": 426, "modifierKind": [ 121 ] @@ -33312,7 +43170,7 @@ "type": "Subscription", "optional": false, "description": "", - "line": 338, + "line": 339, "modifierKind": [ 121 ] @@ -33325,7 +43183,7 @@ "type": "function", "optional": false, "description": "

Unregister function for _radioDispatcher

\n", - "line": 483, + "line": 485, "rawdescription": "\nUnregister function for _radioDispatcher", "modifierKind": [ 121 @@ -33338,7 +43196,7 @@ "type": "boolean", "optional": false, "description": "

Whether this radio is required.

\n", - "line": 477, + "line": 479, "rawdescription": "\nWhether this radio is required.", "modifierKind": [ 121 @@ -33352,7 +43210,7 @@ "type": "", "optional": false, "description": "", - "line": 337, + "line": 338, "modifierKind": [ 121 ] @@ -33365,7 +43223,7 @@ "type": "any", "optional": false, "description": "

Value assigned to this radio.

\n", - "line": 480, + "line": 482, "rawdescription": "\nValue assigned to this radio.", "modifierKind": [ 121 @@ -33378,7 +43236,7 @@ "type": "OuiRadioGroup", "optional": false, "description": "

The parent radio group. May or may not be present.

\n", - "line": 463, + "line": 465, "rawdescription": "\nThe parent radio group. May or may not be present." } ], @@ -33389,7 +43247,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 557, + "line": 559, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nDispatch change event with current value.", @@ -33404,7 +43262,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 512, + "line": 514, "deprecated": false, "deprecationMessage": "" }, @@ -33414,7 +43272,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 525, + "line": 527, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nMarks the radio button as needing checking for change detection.\nThis method is exposed because the parent radio group will directly\nupdate bound properties of the radio button.\n", @@ -33433,7 +43291,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 576, + "line": 578, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nTriggered when the radio button received a click or the input recognized any change.\nClicking on a label element, will trigger a change event on the associated input.\n", @@ -33463,7 +43321,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 561, + "line": 563, "deprecated": false, "deprecationMessage": "", "jsdoctags": [ @@ -33484,7 +43342,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 508, + "line": 510, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nFocuses the radio button.", @@ -33496,7 +43354,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 540, + "line": 542, "deprecated": false, "deprecationMessage": "" }, @@ -33506,7 +43364,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 550, + "line": 552, "deprecated": false, "deprecationMessage": "" }, @@ -33516,7 +43374,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 531, + "line": 533, "deprecated": false, "deprecationMessage": "" } @@ -33528,11 +43386,11 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import { FocusMonitor } from '@angular/cdk/a11y';\r\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\r\nimport { UniqueSelectionDispatcher } from '@angular/cdk/collections';\r\nimport {\r\n AfterContentInit,\r\n AfterViewInit,\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n ContentChildren,\r\n ElementRef,\r\n EventEmitter,\r\n forwardRef,\r\n Inject,\r\n Input,\r\n OnDestroy,\r\n OnInit,\r\n Optional,\r\n Output,\r\n QueryList,\r\n ViewChild,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\n\r\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\r\nimport { ANIMATION_MODULE_TYPE } from '@angular/platform-browser/animations';\r\nimport { mixinColor } from '../core';\r\nimport { Subscription } from 'rxjs';\r\n// Increasing integer for generating unique ids for radio components.\r\nlet nextUniqueId = 0;\r\n\r\n/**\r\n * Provider Expression that allows oui-radio-group to register as a ControlValueAccessor. This\r\n * allows it to support [(ngModel)] and ngControl.\r\n *\r\n * @docs-private\r\n */\r\nexport const OUI_RADIO_GROUP_CONTROL_VALUE_ACCESSOR: any = {\r\n provide: NG_VALUE_ACCESSOR,\r\n useExisting: forwardRef(() => OuiRadioGroup),\r\n multi: true,\r\n};\r\n\r\n/** Change event object emitted by OuiRadio and OuiRadioGroup. */\r\nexport class OuiRadioChange {\r\n constructor(\r\n /** The OuiRadioButton that emits the change event. */\r\n public source: OuiRadioButton,\r\n /** The value of the OuiRadioButton. */\r\n public value: any\r\n ) {}\r\n}\r\n\r\n// Boilerplate for applying mixins to OuiRadioGroup.\r\n/** @docs-private */\r\nexport class OuiRadioGroupBase {}\r\n\r\n/**\r\n * A group of radio buttons. May contain one or more `` elements.\r\n */\r\n@Component({\r\n selector: 'oui-radio-group',\r\n exportAs: 'ouiRadioGroup',\r\n template: ` `,\r\n providers: [OUI_RADIO_GROUP_CONTROL_VALUE_ACCESSOR],\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n role: 'radiogroup',\r\n class: 'oui-radio-group',\r\n },\r\n})\r\nexport class OuiRadioGroup implements AfterContentInit, ControlValueAccessor {\r\n /**\r\n * Event emitted when the group value changes.\r\n * Change events are only emitted when the value changes due to user interaction with\r\n * a radio button (the same behavior as ``).\r\n */\r\n @Output()\r\n readonly change: EventEmitter = new EventEmitter();\r\n\r\n /** Child radio buttons. */\r\n @ContentChildren(forwardRef(() => OuiRadioButton), { descendants: true })\r\n _radios: QueryList;\r\n /** Selected value for the radio group. */\r\n private _value: any = null;\r\n\r\n /** The HTML name attribute applied to radio buttons in this group. */\r\n private _name = `oui-radio-group-${nextUniqueId++}`;\r\n\r\n /** The currently selected radio button. Should match value. */\r\n private _selected: OuiRadioButton | null = null;\r\n\r\n /** Whether the `value` has been set to its initial value. */\r\n private _isInitialized = false;\r\n\r\n /** Whether the labels should appear after or before the radio-buttons. Defaults to 'after' */\r\n private _labelPosition: 'before' | 'after' = 'after';\r\n\r\n /** Whether the radio group is disabled. */\r\n private _disabled = false;\r\n\r\n /** Whether the radio group is required. */\r\n private _required = false;\r\n\r\n /** The method to be called in order to update ngModel */\r\n _controlValueAccessorChangeFn: (value: any) => void = () => {};\r\n\r\n /**\r\n * onTouch function registered via registerOnTouch (ControlValueAccessor).\r\n *\r\n * @docs-private\r\n */\r\n onTouched: () => any = () => {};\r\n\r\n /** Name of the radio button group. All radio buttons inside this group will use this name. */\r\n @Input()\r\n get name(): string {\r\n return this._name;\r\n }\r\n set name(value: string) {\r\n this._name = value;\r\n this._updateRadioButtonNames();\r\n }\r\n\r\n /** Whether the labels should appear after or before the radio-buttons. Defaults to `after` */\r\n @Input()\r\n get labelPosition(): 'before' | 'after' {\r\n return this._labelPosition;\r\n }\r\n set labelPosition(v) {\r\n this._labelPosition = v === 'before' ? 'before' : 'after';\r\n this._markRadiosForCheck();\r\n }\r\n\r\n /**\r\n * Value for the radio-group. Should equal the value of the selected radio button if there is\r\n * a corresponding radio button with a matching value. If there is not such a corresponding\r\n * radio button, this value persists to be applied in case a new radio button is added with a\r\n * matching value.\r\n */\r\n @Input()\r\n get value(): any {\r\n return this._value;\r\n }\r\n set value(newValue: any) {\r\n if (this._value !== newValue) {\r\n // Set this before proceeding to ensure no circular loop occurs with selection.\r\n this._value = newValue;\r\n\r\n this._updateSelectedRadioFromValue();\r\n this._checkSelectedRadioButton();\r\n }\r\n }\r\n\r\n _checkSelectedRadioButton() {\r\n if (this._selected && !this._selected.checked) {\r\n this._selected.checked = true;\r\n }\r\n }\r\n\r\n /**\r\n * The currently selected radio button. If set to a new radio button, the radio group value\r\n * will be updated to match the new selected button.\r\n */\r\n @Input()\r\n get selected() {\r\n return this._selected;\r\n }\r\n set selected(selected) {\r\n this._selected = selected;\r\n this.value = selected ? selected.value : null;\r\n this._checkSelectedRadioButton();\r\n }\r\n\r\n /** Whether the radio group is disabled */\r\n @Input()\r\n get disabled(): boolean {\r\n return this._disabled;\r\n }\r\n set disabled(value) {\r\n this._disabled = coerceBooleanProperty(value);\r\n this._markRadiosForCheck();\r\n }\r\n\r\n /** Whether the radio group is required */\r\n @Input()\r\n get required(): boolean {\r\n return this._required;\r\n }\r\n set required(value: boolean) {\r\n this._required = coerceBooleanProperty(value);\r\n this._markRadiosForCheck();\r\n }\r\n\r\n constructor(private _changeDetector: ChangeDetectorRef) {}\r\n\r\n /**\r\n * Initialize properties once content children are available.\r\n * This allows us to propagate relevant attributes to associated buttons.\r\n */\r\n ngAfterContentInit() {\r\n // Mark this component as initialized in AfterContentInit because the initial value can\r\n // possibly be set by NgModel on OuiRadioGroup, and it is possible that the OnInit of the\r\n // NgModel occurs *after* the OnInit of the OuiRadioGroup.\r\n this._isInitialized = true;\r\n }\r\n\r\n /**\r\n * Mark this group as being \"touched\" (for ngModel). Meant to be called by the contained\r\n * radio buttons upon their blur.\r\n */\r\n _touch() {\r\n if (this.onTouched) {\r\n this.onTouched();\r\n }\r\n }\r\n\r\n private _updateRadioButtonNames(): void {\r\n if (this._radios) {\r\n this._radios.forEach((radio) => {\r\n radio.name = this.name;\r\n });\r\n }\r\n }\r\n\r\n /** Updates the `selected` radio button from the internal _value state. */\r\n private _updateSelectedRadioFromValue(): void {\r\n // If the value already matches the selected radio, do nothing.\r\n const isAlreadySelected =\r\n this._selected !== null && this._selected.value === this._value;\r\n\r\n if (this._radios && !isAlreadySelected) {\r\n this._selected = null;\r\n this._radios.forEach((radio) => {\r\n radio.checked = this.value === radio.value;\r\n if (radio.checked) {\r\n this._selected = radio;\r\n }\r\n });\r\n }\r\n }\r\n\r\n /** Dispatch change event with current selection and group value. */\r\n _emitChangeEvent(): void {\r\n if (this._isInitialized) {\r\n this.change.emit(new OuiRadioChange(this._selected!, this._value));\r\n }\r\n }\r\n\r\n _markRadiosForCheck() {\r\n if (this._radios) {\r\n this._radios.forEach((radio) => radio._markForCheck());\r\n }\r\n }\r\n\r\n /**\r\n * Sets the model value. Implemented as part of ControlValueAccessor.\r\n *\r\n * @param value\r\n */\r\n writeValue(value: any) {\r\n this.value = value;\r\n this._changeDetector.markForCheck();\r\n }\r\n\r\n /**\r\n * Registers a callback to be triggered when the model value changes.\r\n * Implemented as part of ControlValueAccessor.\r\n *\r\n * @param fn Callback to be registered.\r\n */\r\n registerOnChange(fn: (value: any) => void) {\r\n this._controlValueAccessorChangeFn = fn;\r\n }\r\n\r\n /**\r\n * Registers a callback to be triggered when the control is touched.\r\n * Implemented as part of ControlValueAccessor.\r\n *\r\n * @param fn Callback to be registered.\r\n */\r\n registerOnTouched(fn: any) {\r\n this.onTouched = fn;\r\n }\r\n\r\n /**\r\n * Sets the disabled state of the control. Implemented as a part of ControlValueAccessor.\r\n *\r\n * @param isDisabled Whether the control should be disabled.\r\n */\r\n setDisabledState(isDisabled: boolean) {\r\n this.disabled = isDisabled;\r\n this._changeDetector.markForCheck();\r\n }\r\n}\r\n\r\n// Boilerplate for applying mixins to OuiRadioButton.\r\n/** @docs-private */\r\nexport class OuiRadioButtonBase {\r\n // Since the disabled property is manually defined for the OuiRadioButton and isn't set up in\r\n // the mixin base class. To be able to use the tabindex mixin, a disabled property must be\r\n // defined to properly work.\r\n // disabled: boolean;\r\n\r\n constructor(public _elementRef: ElementRef) {}\r\n}\r\n\r\nexport const OuiRadioButtonMixinBase: typeof OuiRadioButtonBase =\r\n mixinColor(OuiRadioButtonBase);\r\n\r\n@Component({\r\n selector: 'oui-radio-button',\r\n templateUrl: 'radio.html',\r\n styleUrls: ['radio.scss'],\r\n encapsulation: ViewEncapsulation.None,\r\n exportAs: 'ouiRadioButton',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-radio-button',\r\n '[class.oui-radio-checked]': 'checked',\r\n '[class.oui-radio-disabled]': 'disabled',\r\n '[class._oui-animation-noopable]': '_animationMode === \"NoopAnimations\"',\r\n '[attr.tabindex]': 'null',\r\n '[attr.id]': 'id',\r\n // Note: under normal conditions focus shouldn't land on this element, however it may be\r\n // programmatically set, for example inside of a focus trap, in this case we want to forward\r\n // the focus to the native element.\r\n '(focus)': '_inputElement.nativeElement.focus()',\r\n '(click)': '_fireInputChange()',\r\n },\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class OuiRadioButton\r\n extends OuiRadioButtonMixinBase\r\n implements OnInit, AfterViewInit, OnDestroy\r\n{\r\n private _uniqueId = `oui-radio-${++nextUniqueId}`;\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n /**\r\n * Implemented as part of CanColor.\r\n */\r\n @Input() color = 'primary';\r\n /** The unique ID for the radio button. */\r\n @Input() id: string = this._uniqueId;\r\n\r\n /** Analog to HTML `name` attribute used to group radios for unique selection. */\r\n @Input() name: string;\r\n\r\n /** Used to set the `aria-label` attribute on the underlying input element. */\r\n @Input('aria-label') ariaLabel: string;\r\n\r\n /** The `aria-labelledby` attribute takes precedence as the element text alternative. */\r\n @Input('aria-labelledby') ariaLabelledby: string;\r\n /** The `aria-labelledby` attribute takes precedence as the element text alternative. */\r\n @Input() tabIndex: number;\r\n\r\n /** The `aria-describedby` attribute is read after the element label and field type. */\r\n @Input('aria-describedby') ariaDescribedby: string;\r\n\r\n /** Whether this radio button is checked. */\r\n @Input()\r\n get checked(): boolean {\r\n return this._checked;\r\n }\r\n set checked(value: boolean) {\r\n const newCheckedState = coerceBooleanProperty(value);\r\n if (this._checked !== newCheckedState) {\r\n this._checked = newCheckedState;\r\n if (\r\n newCheckedState &&\r\n this.radioGroup &&\r\n this.radioGroup.value !== this.value\r\n ) {\r\n this.radioGroup.selected = this;\r\n } else if (\r\n !newCheckedState &&\r\n this.radioGroup &&\r\n this.radioGroup.value === this.value\r\n ) {\r\n // When unchecking the selected radio button, update the selected radio\r\n // property on the group.\r\n this.radioGroup.selected = null;\r\n }\r\n\r\n if (newCheckedState) {\r\n // Notify all radio buttons with the same name to un-check.\r\n this._radioDispatcher.notify(this.id, this.name);\r\n }\r\n this._changeDetector.markForCheck();\r\n }\r\n }\r\n\r\n /** The value of this radio button. */\r\n @Input()\r\n get value(): any {\r\n return this._value;\r\n }\r\n set value(value: any) {\r\n if (this._value !== value) {\r\n this._value = value;\r\n if (this.radioGroup !== null) {\r\n if (!this.checked) {\r\n // Update checked when the value changed to match the radio group's value\r\n this.checked = this.radioGroup.value === value;\r\n }\r\n if (this.checked) {\r\n this.radioGroup.selected = this;\r\n }\r\n }\r\n }\r\n }\r\n\r\n /** Whether the label should appear after or before the radio button. Defaults to `after` */\r\n @Input()\r\n get labelPosition(): 'before' | 'after' {\r\n return (\r\n this._labelPosition ||\r\n (this.radioGroup && this.radioGroup.labelPosition) ||\r\n 'after'\r\n );\r\n }\r\n set labelPosition(value) {\r\n this._labelPosition = value;\r\n }\r\n private _labelPosition: 'before' | 'after';\r\n\r\n /** Whether the radio button is disabled. */\r\n @Input()\r\n get disabled(): boolean {\r\n return (\r\n this._disabled || (this.radioGroup !== null && this.radioGroup.disabled)\r\n );\r\n }\r\n set disabled(value: boolean) {\r\n const newDisabledState = coerceBooleanProperty(value);\r\n if (this._disabled !== newDisabledState) {\r\n this._disabled = newDisabledState;\r\n this._changeDetector.markForCheck();\r\n }\r\n }\r\n\r\n /** Whether the radio button is required. */\r\n @Input()\r\n get required(): boolean {\r\n return this._required || (this.radioGroup && this.radioGroup.required);\r\n }\r\n set required(value: boolean) {\r\n this._required = coerceBooleanProperty(value);\r\n }\r\n\r\n /**\r\n * Event emitted when the checked state of this radio button changes.\r\n * Change events are only emitted when the value changes due to user interaction with\r\n * the radio button (the same behavior as ``).\r\n */\r\n @Output()\r\n readonly change: EventEmitter = new EventEmitter();\r\n\r\n /** The native `` element */\r\n @ViewChild('input') _inputElement: ElementRef;\r\n\r\n /** The parent radio group. May or may not be present. */\r\n radioGroup: OuiRadioGroup;\r\n\r\n /** ID of the native input element inside `` */\r\n get inputId(): string {\r\n return `${this.id || this._uniqueId}-input`;\r\n }\r\n\r\n /** Whether this radio is checked. */\r\n private _checked = false;\r\n\r\n /** Whether this radio is disabled. */\r\n private _disabled: boolean;\r\n\r\n /** Whether this radio is required. */\r\n private _required: boolean;\r\n\r\n /** Value assigned to this radio. */\r\n private _value: any = null;\r\n\r\n /** Unregister function for _radioDispatcher */\r\n private _removeUniqueSelectionListener: () => void = () => {};\r\n\r\n constructor(\r\n @Optional() radioGroup: OuiRadioGroup,\r\n elementRef: ElementRef,\r\n private _changeDetector: ChangeDetectorRef,\r\n private _focusMonitor: FocusMonitor,\r\n private _radioDispatcher: UniqueSelectionDispatcher,\r\n @Optional() @Inject(ANIMATION_MODULE_TYPE) public _animationMode?: string\r\n ) {\r\n super(elementRef);\r\n // Assertions. Ideally these should be stripped out by the compiler.\r\n // TODO(jelbourn): Assert that there's no name binding AND a parent radio group.\r\n this.radioGroup = radioGroup;\r\n\r\n this._removeUniqueSelectionListener = _radioDispatcher.listen(\r\n (id: string, name: string) => {\r\n if (id !== this.id && name === this.name) {\r\n this.checked = false;\r\n }\r\n }\r\n );\r\n }\r\n\r\n /** Focuses the radio button. */\r\n focus(): void {\r\n this._focusMonitor.focusVia(this._inputElement, 'keyboard');\r\n }\r\n\r\n _fireInputChange(): void {\r\n if (!this.disabled) {\r\n const event = document.createEvent('Event');\r\n event.initEvent('change', false, true);\r\n this._inputElement.nativeElement.dispatchEvent(event);\r\n }\r\n }\r\n\r\n /**\r\n * Marks the radio button as needing checking for change detection.\r\n * This method is exposed because the parent radio group will directly\r\n * update bound properties of the radio button.\r\n */\r\n _markForCheck() {\r\n // When group value changes, the button will not be notified. Use `markForCheck` to explicit\r\n // update radio button's status\r\n this._changeDetector.markForCheck();\r\n }\r\n\r\n ngOnInit() {\r\n if (this.radioGroup) {\r\n // If the radio is inside a radio group, determine if it should be checked\r\n this.checked = this.radioGroup.value === this._value;\r\n // Copy name from parent radio group\r\n this.name = this.radioGroup.name;\r\n }\r\n }\r\n\r\n ngAfterViewInit() {\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this._elementRef, true)\r\n .subscribe((focusOrigin) => {\r\n if (!focusOrigin && this.radioGroup) {\r\n this.radioGroup._touch();\r\n }\r\n });\r\n }\r\n\r\n ngOnDestroy() {\r\n this._focusMonitor.stopMonitoring(this._elementRef);\r\n this._removeUniqueSelectionListener();\r\n this._monitorSubscription.unsubscribe();\r\n }\r\n\r\n /** Dispatch change event with current value. */\r\n private _emitChangeEvent(): void {\r\n this.change.emit(new OuiRadioChange(this, this._value));\r\n }\r\n\r\n _onInputClick(event: Event) {\r\n // We have to stop propagation for click events on the visual hidden input element.\r\n // By default, when a user clicks on a label element, a generated click event will be\r\n // dispatched on the associated input element. Since we are using a label element as our\r\n // root container, the click event on the `radio-button` will be executed twice.\r\n // The real click event will bubble up, and the generated click event also tries to bubble up.\r\n // This will lead to multiple click events.\r\n // Preventing bubbling for the second event will solve that issue.\r\n event.stopPropagation();\r\n }\r\n\r\n /**\r\n * Triggered when the radio button received a click or the input recognized any change.\r\n * Clicking on a label element, will trigger a change event on the associated input.\r\n */\r\n _onInputChange(event: Event) {\r\n // We always have to stop propagation on the change event.\r\n // Otherwise the change event, from the input element, will bubble up and\r\n // emit its event object to the `change` output.\r\n event.stopPropagation();\r\n const groupValueChanged =\r\n this.radioGroup && this.value !== this.radioGroup.value;\r\n this.checked = true;\r\n this._changeDetector.detectChanges();\r\n this._emitChangeEvent();\r\n\r\n if (this.radioGroup) {\r\n this.radioGroup._controlValueAccessorChangeFn(this.value);\r\n this.radioGroup._touch();\r\n if (groupValueChanged) {\r\n this.radioGroup._emitChangeEvent();\r\n }\r\n }\r\n }\r\n}\r\n", + "sourceCode": "import { FocusMonitor } from '@angular/cdk/a11y';\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { UniqueSelectionDispatcher } from '@angular/cdk/collections';\nimport {\n AfterContentInit,\n AfterViewInit,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n ContentChildren,\n ElementRef,\n EventEmitter,\n forwardRef,\n Inject,\n Input,\n OnDestroy,\n OnInit,\n Optional,\n Output,\n QueryList,\n ViewChild,\n ViewEncapsulation,\n} from '@angular/core';\n\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { ANIMATION_MODULE_TYPE } from '@angular/platform-browser/animations';\nimport { mixinColor } from '../core';\nimport { Subscription } from 'rxjs';\n// Increasing integer for generating unique ids for radio components.\nlet nextUniqueId = 0;\n\n/**\n * Provider Expression that allows oui-radio-group to register as a ControlValueAccessor. This\n * allows it to support [(ngModel)] and ngControl.\n *\n * @docs-private\n */\nexport const OUI_RADIO_GROUP_CONTROL_VALUE_ACCESSOR: any = {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => OuiRadioGroup),\n multi: true,\n};\n\n/** Change event object emitted by OuiRadio and OuiRadioGroup. */\nexport class OuiRadioChange {\n constructor(\n /** The OuiRadioButton that emits the change event. */\n public source: OuiRadioButton,\n /** The value of the OuiRadioButton. */\n public value: any\n ) {}\n}\n\n// Boilerplate for applying mixins to OuiRadioGroup.\n/** @docs-private */\nexport class OuiRadioGroupBase {}\n\n/**\n * A group of radio buttons. May contain one or more `` elements.\n */\n@Component({\n selector: 'oui-radio-group',\n exportAs: 'ouiRadioGroup',\n template: ` `,\n providers: [OUI_RADIO_GROUP_CONTROL_VALUE_ACCESSOR],\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n role: 'radiogroup',\n class: 'oui-radio-group',\n },\n})\nexport class OuiRadioGroup implements AfterContentInit, ControlValueAccessor {\n /**\n * Event emitted when the group value changes.\n * Change events are only emitted when the value changes due to user interaction with\n * a radio button (the same behavior as ``).\n */\n @Output()\n readonly change: EventEmitter =\n new EventEmitter();\n\n /** Child radio buttons. */\n @ContentChildren(forwardRef(() => OuiRadioButton), { descendants: true })\n _radios: QueryList;\n /** Selected value for the radio group. */\n private _value: any = null;\n\n /** The HTML name attribute applied to radio buttons in this group. */\n private _name = `oui-radio-group-${nextUniqueId++}`;\n\n /** The currently selected radio button. Should match value. */\n private _selected: OuiRadioButton | null = null;\n\n /** Whether the `value` has been set to its initial value. */\n private _isInitialized = false;\n\n /** Whether the labels should appear after or before the radio-buttons. Defaults to 'after' */\n private _labelPosition: 'before' | 'after' = 'after';\n\n /** Whether the radio group is disabled. */\n private _disabled = false;\n\n /** Whether the radio group is required. */\n private _required = false;\n\n /** The method to be called in order to update ngModel */\n _controlValueAccessorChangeFn: (value: any) => void = () => {};\n\n /**\n * onTouch function registered via registerOnTouch (ControlValueAccessor).\n *\n * @docs-private\n */\n onTouched: () => any = () => {};\n\n /** Name of the radio button group. All radio buttons inside this group will use this name. */\n @Input()\n get name(): string {\n return this._name;\n }\n set name(value: string) {\n this._name = value;\n this._updateRadioButtonNames();\n }\n\n /** Whether the labels should appear after or before the radio-buttons. Defaults to `after` */\n @Input()\n get labelPosition(): 'before' | 'after' {\n return this._labelPosition;\n }\n set labelPosition(v) {\n this._labelPosition = v === 'before' ? 'before' : 'after';\n this._markRadiosForCheck();\n }\n\n /**\n * Value for the radio-group. Should equal the value of the selected radio button if there is\n * a corresponding radio button with a matching value. If there is not such a corresponding\n * radio button, this value persists to be applied in case a new radio button is added with a\n * matching value.\n */\n @Input()\n get value(): any {\n return this._value;\n }\n set value(newValue: any) {\n if (this._value !== newValue) {\n // Set this before proceeding to ensure no circular loop occurs with selection.\n this._value = newValue;\n\n this._updateSelectedRadioFromValue();\n this._checkSelectedRadioButton();\n }\n }\n\n _checkSelectedRadioButton() {\n if (this._selected && !this._selected.checked) {\n this._selected.checked = true;\n }\n }\n\n /**\n * The currently selected radio button. If set to a new radio button, the radio group value\n * will be updated to match the new selected button.\n */\n @Input()\n get selected() {\n return this._selected;\n }\n set selected(selected) {\n this._selected = selected;\n this.value = selected ? selected.value : null;\n this._checkSelectedRadioButton();\n }\n\n /** Whether the radio group is disabled */\n @Input()\n get disabled(): boolean {\n return this._disabled;\n }\n set disabled(value) {\n this._disabled = coerceBooleanProperty(value);\n this._markRadiosForCheck();\n }\n\n /** Whether the radio group is required */\n @Input()\n get required(): boolean {\n return this._required;\n }\n set required(value: boolean) {\n this._required = coerceBooleanProperty(value);\n this._markRadiosForCheck();\n }\n\n constructor(private _changeDetector: ChangeDetectorRef) {}\n\n /**\n * Initialize properties once content children are available.\n * This allows us to propagate relevant attributes to associated buttons.\n */\n ngAfterContentInit() {\n // Mark this component as initialized in AfterContentInit because the initial value can\n // possibly be set by NgModel on OuiRadioGroup, and it is possible that the OnInit of the\n // NgModel occurs *after* the OnInit of the OuiRadioGroup.\n this._isInitialized = true;\n }\n\n /**\n * Mark this group as being \"touched\" (for ngModel). Meant to be called by the contained\n * radio buttons upon their blur.\n */\n _touch() {\n if (this.onTouched) {\n this.onTouched();\n }\n }\n\n private _updateRadioButtonNames(): void {\n if (this._radios) {\n this._radios.forEach((radio) => {\n radio.name = this.name;\n });\n }\n }\n\n /** Updates the `selected` radio button from the internal _value state. */\n private _updateSelectedRadioFromValue(): void {\n // If the value already matches the selected radio, do nothing.\n const isAlreadySelected =\n this._selected !== null && this._selected.value === this._value;\n\n if (this._radios && !isAlreadySelected) {\n this._selected = null;\n this._radios.forEach((radio) => {\n radio.checked = this.value === radio.value;\n if (radio.checked) {\n this._selected = radio;\n }\n });\n }\n }\n\n /** Dispatch change event with current selection and group value. */\n _emitChangeEvent(): void {\n if (this._isInitialized) {\n this.change.emit(new OuiRadioChange(this._selected!, this._value));\n }\n }\n\n _markRadiosForCheck() {\n if (this._radios) {\n this._radios.forEach((radio) => radio._markForCheck());\n }\n }\n\n /**\n * Sets the model value. Implemented as part of ControlValueAccessor.\n *\n * @param value\n */\n writeValue(value: any) {\n this.value = value;\n this._changeDetector.markForCheck();\n }\n\n /**\n * Registers a callback to be triggered when the model value changes.\n * Implemented as part of ControlValueAccessor.\n *\n * @param fn Callback to be registered.\n */\n registerOnChange(fn: (value: any) => void) {\n this._controlValueAccessorChangeFn = fn;\n }\n\n /**\n * Registers a callback to be triggered when the control is touched.\n * Implemented as part of ControlValueAccessor.\n *\n * @param fn Callback to be registered.\n */\n registerOnTouched(fn: any) {\n this.onTouched = fn;\n }\n\n /**\n * Sets the disabled state of the control. Implemented as a part of ControlValueAccessor.\n *\n * @param isDisabled Whether the control should be disabled.\n */\n setDisabledState(isDisabled: boolean) {\n this.disabled = isDisabled;\n this._changeDetector.markForCheck();\n }\n}\n\n// Boilerplate for applying mixins to OuiRadioButton.\n/** @docs-private */\nexport class OuiRadioButtonBase {\n // Since the disabled property is manually defined for the OuiRadioButton and isn't set up in\n // the mixin base class. To be able to use the tabindex mixin, a disabled property must be\n // defined to properly work.\n // disabled: boolean;\n\n constructor(public _elementRef: ElementRef) {}\n}\n\nexport const OuiRadioButtonMixinBase: typeof OuiRadioButtonBase =\n mixinColor(OuiRadioButtonBase);\n\n@Component({\n selector: 'oui-radio-button',\n templateUrl: 'radio.html',\n styleUrls: ['radio.scss'],\n encapsulation: ViewEncapsulation.None,\n exportAs: 'ouiRadioButton',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-radio-button',\n '[class.oui-radio-checked]': 'checked',\n '[class.oui-radio-disabled]': 'disabled',\n '[class._oui-animation-noopable]': '_animationMode === \"NoopAnimations\"',\n '[attr.tabindex]': 'null',\n '[attr.id]': 'id',\n // Note: under normal conditions focus shouldn't land on this element, however it may be\n // programmatically set, for example inside of a focus trap, in this case we want to forward\n // the focus to the native element.\n '(focus)': '_inputElement.nativeElement.focus()',\n '(click)': '_fireInputChange()',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class OuiRadioButton\n extends OuiRadioButtonMixinBase\n implements OnInit, AfterViewInit, OnDestroy\n{\n private _uniqueId = `oui-radio-${++nextUniqueId}`;\n private _monitorSubscription: Subscription = Subscription.EMPTY;\n /**\n * Implemented as part of CanColor.\n */\n @Input() color = 'primary';\n /** The unique ID for the radio button. */\n @Input() id: string = this._uniqueId;\n\n /** Analog to HTML `name` attribute used to group radios for unique selection. */\n @Input() name: string;\n\n /** Used to set the `aria-label` attribute on the underlying input element. */\n @Input('aria-label') ariaLabel: string;\n\n /** The `aria-labelledby` attribute takes precedence as the element text alternative. */\n @Input('aria-labelledby') ariaLabelledby: string;\n /** The `aria-labelledby` attribute takes precedence as the element text alternative. */\n @Input() tabIndex: number;\n\n /** The `aria-describedby` attribute is read after the element label and field type. */\n @Input('aria-describedby') ariaDescribedby: string;\n\n /** Whether this radio button is checked. */\n @Input()\n get checked(): boolean {\n return this._checked;\n }\n set checked(value: boolean) {\n const newCheckedState = coerceBooleanProperty(value);\n if (this._checked !== newCheckedState) {\n this._checked = newCheckedState;\n if (\n newCheckedState &&\n this.radioGroup &&\n this.radioGroup.value !== this.value\n ) {\n this.radioGroup.selected = this;\n } else if (\n !newCheckedState &&\n this.radioGroup &&\n this.radioGroup.value === this.value\n ) {\n // When unchecking the selected radio button, update the selected radio\n // property on the group.\n this.radioGroup.selected = null;\n }\n\n if (newCheckedState) {\n // Notify all radio buttons with the same name to un-check.\n this._radioDispatcher.notify(this.id, this.name);\n }\n this._changeDetector.markForCheck();\n }\n }\n\n /** The value of this radio button. */\n @Input()\n get value(): any {\n return this._value;\n }\n set value(value: any) {\n if (this._value !== value) {\n this._value = value;\n if (this.radioGroup !== null) {\n if (!this.checked) {\n // Update checked when the value changed to match the radio group's value\n this.checked = this.radioGroup.value === value;\n }\n if (this.checked) {\n this.radioGroup.selected = this;\n }\n }\n }\n }\n\n /** Whether the label should appear after or before the radio button. Defaults to `after` */\n @Input()\n get labelPosition(): 'before' | 'after' {\n return (\n this._labelPosition ||\n (this.radioGroup && this.radioGroup.labelPosition) ||\n 'after'\n );\n }\n set labelPosition(value) {\n this._labelPosition = value;\n }\n private _labelPosition: 'before' | 'after';\n\n /** Whether the radio button is disabled. */\n @Input()\n get disabled(): boolean {\n return (\n this._disabled || (this.radioGroup !== null && this.radioGroup.disabled)\n );\n }\n set disabled(value: boolean) {\n const newDisabledState = coerceBooleanProperty(value);\n if (this._disabled !== newDisabledState) {\n this._disabled = newDisabledState;\n this._changeDetector.markForCheck();\n }\n }\n\n /** Whether the radio button is required. */\n @Input()\n get required(): boolean {\n return this._required || (this.radioGroup && this.radioGroup.required);\n }\n set required(value: boolean) {\n this._required = coerceBooleanProperty(value);\n }\n\n /**\n * Event emitted when the checked state of this radio button changes.\n * Change events are only emitted when the value changes due to user interaction with\n * the radio button (the same behavior as ``).\n */\n @Output()\n readonly change: EventEmitter =\n new EventEmitter();\n\n /** The native `` element */\n @ViewChild('input') _inputElement: ElementRef;\n\n /** The parent radio group. May or may not be present. */\n radioGroup: OuiRadioGroup;\n\n /** ID of the native input element inside `` */\n get inputId(): string {\n return `${this.id || this._uniqueId}-input`;\n }\n\n /** Whether this radio is checked. */\n private _checked = false;\n\n /** Whether this radio is disabled. */\n private _disabled: boolean;\n\n /** Whether this radio is required. */\n private _required: boolean;\n\n /** Value assigned to this radio. */\n private _value: any = null;\n\n /** Unregister function for _radioDispatcher */\n private _removeUniqueSelectionListener: () => void = () => {};\n\n constructor(\n @Optional() radioGroup: OuiRadioGroup,\n elementRef: ElementRef,\n private _changeDetector: ChangeDetectorRef,\n private _focusMonitor: FocusMonitor,\n private _radioDispatcher: UniqueSelectionDispatcher,\n @Optional() @Inject(ANIMATION_MODULE_TYPE) public _animationMode?: string\n ) {\n super(elementRef);\n // Assertions. Ideally these should be stripped out by the compiler.\n // TODO(jelbourn): Assert that there's no name binding AND a parent radio group.\n this.radioGroup = radioGroup;\n\n this._removeUniqueSelectionListener = _radioDispatcher.listen(\n (id: string, name: string) => {\n if (id !== this.id && name === this.name) {\n this.checked = false;\n }\n }\n );\n }\n\n /** Focuses the radio button. */\n focus(): void {\n this._focusMonitor.focusVia(this._inputElement, 'keyboard');\n }\n\n _fireInputChange(): void {\n if (!this.disabled) {\n const event = document.createEvent('Event');\n event.initEvent('change', false, true);\n this._inputElement.nativeElement.dispatchEvent(event);\n }\n }\n\n /**\n * Marks the radio button as needing checking for change detection.\n * This method is exposed because the parent radio group will directly\n * update bound properties of the radio button.\n */\n _markForCheck() {\n // When group value changes, the button will not be notified. Use `markForCheck` to explicit\n // update radio button's status\n this._changeDetector.markForCheck();\n }\n\n ngOnInit() {\n if (this.radioGroup) {\n // If the radio is inside a radio group, determine if it should be checked\n this.checked = this.radioGroup.value === this._value;\n // Copy name from parent radio group\n this.name = this.radioGroup.name;\n }\n }\n\n ngAfterViewInit() {\n this._monitorSubscription = this._focusMonitor\n .monitor(this._elementRef, true)\n .subscribe((focusOrigin) => {\n if (!focusOrigin && this.radioGroup) {\n this.radioGroup._touch();\n }\n });\n }\n\n ngOnDestroy() {\n this._focusMonitor.stopMonitoring(this._elementRef);\n this._removeUniqueSelectionListener();\n this._monitorSubscription.unsubscribe();\n }\n\n /** Dispatch change event with current value. */\n private _emitChangeEvent(): void {\n this.change.emit(new OuiRadioChange(this, this._value));\n }\n\n _onInputClick(event: Event) {\n // We have to stop propagation for click events on the visual hidden input element.\n // By default, when a user clicks on a label element, a generated click event will be\n // dispatched on the associated input element. Since we are using a label element as our\n // root container, the click event on the `radio-button` will be executed twice.\n // The real click event will bubble up, and the generated click event also tries to bubble up.\n // This will lead to multiple click events.\n // Preventing bubbling for the second event will solve that issue.\n event.stopPropagation();\n }\n\n /**\n * Triggered when the radio button received a click or the input recognized any change.\n * Clicking on a label element, will trigger a change event on the associated input.\n */\n _onInputChange(event: Event) {\n // We always have to stop propagation on the change event.\n // Otherwise the change event, from the input element, will bubble up and\n // emit its event object to the `change` output.\n event.stopPropagation();\n const groupValueChanged =\n this.radioGroup && this.value !== this.radioGroup.value;\n this.checked = true;\n this._changeDetector.detectChanges();\n this._emitChangeEvent();\n\n if (this.radioGroup) {\n this.radioGroup._controlValueAccessorChangeFn(this.value);\n this.radioGroup._touch();\n if (groupValueChanged) {\n this.radioGroup._emitChangeEvent();\n }\n }\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": "$width: 16px;\r\n$height: 16px;\r\n$circle-color: #333;\r\n$disable-circle-color: #9b9b9b;\r\n\r\n.oui-radio-label {\r\n cursor: pointer;\r\n display: inline-flex;\r\n align-items: center;\r\n white-space: nowrap;\r\n vertical-align: middle;\r\n font-size: 14px;\r\n}\r\n.oui-radio-container {\r\n box-sizing: border-box;\r\n display: inline-block;\r\n position: relative;\r\n width: $width;\r\n height: $height;\r\n flex-shrink: 0;\r\n}\r\n.oui-radio-outer-circle {\r\n box-sizing: border-box;\r\n height: $height;\r\n left: 0;\r\n position: absolute;\r\n top: 0;\r\n transition: border-color ease 280ms;\r\n width: $width;\r\n border-width: 1px;\r\n border-style: solid;\r\n border-radius: 50%;\r\n}\r\n.oui-radio-outer-circle {\r\n border-color: #9c9c9c;\r\n}\r\n.oui-radio-inner-circle {\r\n border-radius: 50%;\r\n box-sizing: border-box;\r\n height: $height;\r\n left: 0;\r\n position: absolute;\r\n top: 0;\r\n transition: transform ease 280ms, background-color ease 280ms;\r\n width: $width;\r\n transform: scale(0.001);\r\n background-color: $circle-color;\r\n}\r\n.oui-radio-checked .oui-radio-inner-circle {\r\n transform: scale(0.5);\r\n}\r\n.oui-radio-input {\r\n bottom: 0;\r\n left: 50%;\r\n}\r\n.oui-visually-hidden {\r\n border: 0;\r\n clip: rect(0 0 0 0);\r\n height: 1px;\r\n margin: -1px;\r\n overflow: hidden;\r\n padding: 0;\r\n position: absolute;\r\n width: 1px;\r\n outline: 0;\r\n -webkit-appearance: none;\r\n -moz-appearance: none;\r\n}\r\n.oui-radio-label-content {\r\n display: inline-block;\r\n order: 0;\r\n line-height: inherit;\r\n padding-left: 8px;\r\n padding-right: 0;\r\n}\r\n.oui-radio-disabled {\r\n .oui-radio-label {\r\n cursor: default;\r\n }\r\n .oui-radio-outer-circle {\r\n background-color: #f9f9f9;\r\n border-color: #e4e4e4;\r\n }\r\n .oui-radio-inner-circle {\r\n background-color: $disable-circle-color;\r\n }\r\n}\r\n\r\n.oui-radio-label-before {\r\n order: -1;\r\n padding-left: 0;\r\n padding-right: 8px;\r\n}\r\n.cdk-keyboard-focused {\r\n .oui-radio-outer-circle {\r\n border-width: 2px;\r\n }\r\n}\r\n", + "data": "$width: 16px;\n$height: 16px;\n$circle-color: #333;\n$disable-circle-color: #9b9b9b;\n\n.oui-radio-label {\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n white-space: nowrap;\n vertical-align: middle;\n font-size: 14px;\n}\n.oui-radio-container {\n box-sizing: border-box;\n display: inline-block;\n position: relative;\n width: $width;\n height: $height;\n flex-shrink: 0;\n}\n.oui-radio-outer-circle {\n box-sizing: border-box;\n height: $height;\n left: 0;\n position: absolute;\n top: 0;\n transition: border-color ease 280ms;\n width: $width;\n border-width: 1px;\n border-style: solid;\n border-radius: 50%;\n}\n.oui-radio-outer-circle {\n border-color: #9c9c9c;\n}\n.oui-radio-inner-circle {\n border-radius: 50%;\n box-sizing: border-box;\n height: $height;\n left: 0;\n position: absolute;\n top: 0;\n transition: transform ease 280ms, background-color ease 280ms;\n width: $width;\n transform: scale(0.001);\n background-color: $circle-color;\n}\n.oui-radio-checked .oui-radio-inner-circle {\n transform: scale(0.5);\n}\n.oui-radio-input {\n bottom: 0;\n left: 50%;\n}\n.oui-visually-hidden {\n border: 0;\n clip: rect(0 0 0 0);\n height: 1px;\n margin: -1px;\n overflow: hidden;\n padding: 0;\n position: absolute;\n width: 1px;\n outline: 0;\n -webkit-appearance: none;\n -moz-appearance: none;\n}\n.oui-radio-label-content {\n display: inline-block;\n order: 0;\n line-height: inherit;\n padding-left: 8px;\n padding-right: 0;\n}\n.oui-radio-disabled {\n .oui-radio-label {\n cursor: default;\n }\n .oui-radio-outer-circle {\n background-color: #f9f9f9;\n border-color: #e4e4e4;\n }\n .oui-radio-inner-circle {\n background-color: $disable-circle-color;\n }\n}\n\n.oui-radio-label-before {\n order: -1;\n padding-left: 0;\n padding-right: 8px;\n}\n.cdk-keyboard-focused {\n .oui-radio-outer-circle {\n border-width: 2px;\n }\n}\n", "styleUrl": "radio.scss" } ], @@ -33581,7 +43439,7 @@ "optional": true } ], - "line": 483, + "line": 485, "jsdoctags": [ { "name": "radioGroup", @@ -33663,7 +43521,7 @@ } ], "returnType": "void", - "line": 365, + "line": 366, "jsdoctags": [ { "name": "value", @@ -33680,7 +43538,7 @@ "name": "checked", "type": "boolean", "returnType": "boolean", - "line": 362, + "line": 363, "rawdescription": "\nWhether this radio button is checked.", "description": "

Whether this radio button is checked.

\n" } @@ -33701,7 +43559,7 @@ } ], "returnType": "void", - "line": 398, + "line": 399, "jsdoctags": [ { "name": "value", @@ -33718,7 +43576,7 @@ "name": "value", "type": "any", "returnType": "any", - "line": 395, + "line": 396, "rawdescription": "\nThe value of this radio button.", "description": "

The value of this radio button.

\n" } @@ -33739,7 +43597,7 @@ } ], "returnType": "void", - "line": 422, + "line": 423, "jsdoctags": [ { "name": "value", @@ -33756,7 +43614,7 @@ "name": "labelPosition", "type": "", "returnType": "\"before\" | \"after\"", - "line": 415, + "line": 416, "rawdescription": "\nWhether the label should appear after or before the radio button. Defaults to `after`", "description": "

Whether the label should appear after or before the radio button. Defaults to after

\n" } @@ -33777,7 +43635,7 @@ } ], "returnType": "void", - "line": 434, + "line": 435, "jsdoctags": [ { "name": "value", @@ -33794,7 +43652,7 @@ "name": "disabled", "type": "boolean", "returnType": "boolean", - "line": 429, + "line": 430, "rawdescription": "\nWhether the radio button is disabled.", "description": "

Whether the radio button is disabled.

\n" } @@ -33815,7 +43673,7 @@ } ], "returnType": "void", - "line": 447, + "line": 448, "jsdoctags": [ { "name": "value", @@ -33832,7 +43690,7 @@ "name": "required", "type": "boolean", "returnType": "boolean", - "line": 444, + "line": 445, "rawdescription": "\nWhether the radio button is required.", "description": "

Whether the radio button is required.

\n" } @@ -33843,17 +43701,17 @@ "name": "inputId", "type": "string", "returnType": "string", - "line": 466, + "line": 468, "rawdescription": "\nID of the native input element inside ``", "description": "

ID of the native input element inside <oui-radio-button>

\n" } } }, - "templateData": "\r\n" + "templateData": "\n" }, { "name": "OuiRadioGroup", - "id": "component-OuiRadioGroup-7d11d4690b55d1942745ff1167a94fe08c4f452dd5c295a646dde09294239e4d0fc2fe722e202b13579ab34b7f46211cf0d265ab37f0b73ce63cb116c8f731e3", + "id": "component-OuiRadioGroup-8c1800186b5ff980f973d7d600dcd7b5a64f6b86f979b39ddfc305e1e942a8a2d8512bbb5dd25fe4c0b0e1eaa769127c104157bd1cda506c02c0e12b36b7f8ce", "file": "ui/src/components/radio/radio.ts", "encapsulation": [], "entryComponents": [], @@ -33879,7 +43737,7 @@ "deprecationMessage": "", "rawdescription": "\nWhether the radio group is disabled", "description": "

Whether the radio group is disabled

\n", - "line": 177, + "line": 178, "type": "boolean", "decorators": [] }, @@ -33889,7 +43747,7 @@ "deprecationMessage": "", "rawdescription": "\nWhether the labels should appear after or before the radio-buttons. Defaults to `after`", "description": "

Whether the labels should appear after or before the radio-buttons. Defaults to after

\n", - "line": 127, + "line": 128, "type": "\"before\" | \"after\"", "decorators": [] }, @@ -33899,7 +43757,7 @@ "deprecationMessage": "", "rawdescription": "\nName of the radio button group. All radio buttons inside this group will use this name.", "description": "

Name of the radio button group. All radio buttons inside this group will use this name.

\n", - "line": 117, + "line": 118, "type": "string", "decorators": [] }, @@ -33909,7 +43767,7 @@ "deprecationMessage": "", "rawdescription": "\nWhether the radio group is required", "description": "

Whether the radio group is required

\n", - "line": 187, + "line": 188, "type": "boolean", "decorators": [] }, @@ -33919,7 +43777,7 @@ "deprecationMessage": "", "rawdescription": "\n\nThe currently selected radio button. If set to a new radio button, the radio group value\nwill be updated to match the new selected button.\n", "description": "

The currently selected radio button. If set to a new radio button, the radio group value\nwill be updated to match the new selected button.

\n", - "line": 166, + "line": 167, "type": "OuiRadioButton", "decorators": [] }, @@ -33929,7 +43787,7 @@ "deprecationMessage": "", "rawdescription": "\n\nValue for the radio-group. Should equal the value of the selected radio button if there is\na corresponding radio button with a matching value. If there is not such a corresponding\nradio button, this value persists to be applied in case a new radio button is added with a\nmatching value.\n", "description": "

Value for the radio-group. Should equal the value of the selected radio button if there is\na corresponding radio button with a matching value. If there is not such a corresponding\nradio button, this value persists to be applied in case a new radio button is added with a\nmatching value.

\n", - "line": 142, + "line": 143, "type": "any", "decorators": [] } @@ -33955,7 +43813,7 @@ "type": "function", "optional": false, "description": "

The method to be called in order to update ngModel

\n", - "line": 106, + "line": 107, "rawdescription": "\nThe method to be called in order to update ngModel" }, { @@ -33966,7 +43824,7 @@ "type": "", "optional": false, "description": "

Whether the radio group is disabled.

\n", - "line": 100, + "line": 101, "rawdescription": "\nWhether the radio group is disabled.", "modifierKind": [ 121 @@ -33980,7 +43838,7 @@ "type": "", "optional": false, "description": "

Whether the value has been set to its initial value.

\n", - "line": 94, + "line": 95, "rawdescription": "\nWhether the `value` has been set to its initial value.", "modifierKind": [ 121 @@ -33994,7 +43852,7 @@ "type": "\"before\" | \"after\"", "optional": false, "description": "

Whether the labels should appear after or before the radio-buttons. Defaults to 'after'

\n", - "line": 97, + "line": 98, "rawdescription": "\nWhether the labels should appear after or before the radio-buttons. Defaults to 'after'", "modifierKind": [ 121 @@ -34008,7 +43866,7 @@ "type": "", "optional": false, "description": "

The HTML name attribute applied to radio buttons in this group.

\n", - "line": 88, + "line": 89, "rawdescription": "\nThe HTML name attribute applied to radio buttons in this group.", "modifierKind": [ 121 @@ -34021,7 +43879,7 @@ "type": "QueryList", "optional": false, "description": "

Child radio buttons.

\n", - "line": 83, + "line": 84, "rawdescription": "\nChild radio buttons.", "decorators": [ { @@ -34038,7 +43896,7 @@ "type": "", "optional": false, "description": "

Whether the radio group is required.

\n", - "line": 103, + "line": 104, "rawdescription": "\nWhether the radio group is required.", "modifierKind": [ 121 @@ -34052,7 +43910,7 @@ "type": "OuiRadioButton | null", "optional": false, "description": "

The currently selected radio button. Should match value.

\n", - "line": 91, + "line": 92, "rawdescription": "\nThe currently selected radio button. Should match value.", "modifierKind": [ 121 @@ -34066,7 +43924,7 @@ "type": "any", "optional": false, "description": "

Selected value for the radio group.

\n", - "line": 85, + "line": 86, "rawdescription": "\nSelected value for the radio group.", "modifierKind": [ 121 @@ -34080,19 +43938,19 @@ "type": "function", "optional": false, "description": "

onTouch function registered via registerOnTouch (ControlValueAccessor).

\n", - "line": 113, + "line": 114, "rawdescription": "\n\nonTouch function registered via registerOnTouch (ControlValueAccessor).\n\n", "jsdoctags": [ { - "pos": 3534, - "end": 3552, + "pos": 3428, + "end": 3445, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, "kind": 325, "tagName": { - "pos": 3535, - "end": 3547, + "pos": 3429, + "end": 3441, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -34111,7 +43969,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 155, + "line": 156, "deprecated": false, "deprecationMessage": "" }, @@ -34121,7 +43979,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 244, + "line": 245, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nDispatch change event with current selection and group value.", @@ -34133,7 +43991,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 250, + "line": 251, "deprecated": false, "deprecationMessage": "" }, @@ -34143,7 +44001,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 212, + "line": 213, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nMark this group as being \"touched\" (for ngModel). Meant to be called by the contained\nradio buttons upon their blur.\n", @@ -34155,7 +44013,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 218, + "line": 219, "deprecated": false, "deprecationMessage": "", "modifierKind": [ @@ -34168,7 +44026,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 227, + "line": 228, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nUpdates the `selected` radio button from the internal _value state.", @@ -34183,7 +44041,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 201, + "line": 202, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nInitialize properties once content children are available.\nThis allows us to propagate relevant attributes to associated buttons.\n", @@ -34210,7 +44068,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 272, + "line": 273, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nRegisters a callback to be triggered when the model value changes.\nImplemented as part of ControlValueAccessor.\n\n", @@ -34218,8 +44076,8 @@ "jsdoctags": [ { "name": { - "pos": 8084, - "end": 8086, + "pos": 7819, + "end": 7821, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -34238,8 +44096,8 @@ } ], "tagName": { - "pos": 8078, - "end": 8083, + "pos": 7813, + "end": 7818, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -34263,7 +44121,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 282, + "line": 283, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nRegisters a callback to be triggered when the control is touched.\nImplemented as part of ControlValueAccessor.\n\n", @@ -34271,8 +44129,8 @@ "jsdoctags": [ { "name": { - "pos": 8371, - "end": 8373, + "pos": 8096, + "end": 8098, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -34283,8 +44141,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 8365, - "end": 8370, + "pos": 8090, + "end": 8095, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -34308,7 +44166,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 291, + "line": 292, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nSets the disabled state of the control. Implemented as a part of ControlValueAccessor.\n\n", @@ -34316,8 +44174,8 @@ "jsdoctags": [ { "name": { - "pos": 8592, - "end": 8602, + "pos": 8308, + "end": 8318, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -34328,8 +44186,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 8586, - "end": 8591, + "pos": 8302, + "end": 8307, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -34353,7 +44211,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 261, + "line": 262, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nSets the model value. Implemented as part of ControlValueAccessor.\n\n", @@ -34361,8 +44219,8 @@ "jsdoctags": [ { "name": { - "pos": 7819, - "end": 7824, + "pos": 7565, + "end": 7570, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -34373,8 +44231,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 7813, - "end": 7818, + "pos": 7559, + "end": 7564, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -34393,7 +44251,7 @@ "description": "

A group of radio buttons. May contain one or more <oui-radio-button> elements.

\n", "rawdescription": "\n\nA group of radio buttons. May contain one or more `` elements.\n", "type": "component", - "sourceCode": "import { FocusMonitor } from '@angular/cdk/a11y';\r\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\r\nimport { UniqueSelectionDispatcher } from '@angular/cdk/collections';\r\nimport {\r\n AfterContentInit,\r\n AfterViewInit,\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n ContentChildren,\r\n ElementRef,\r\n EventEmitter,\r\n forwardRef,\r\n Inject,\r\n Input,\r\n OnDestroy,\r\n OnInit,\r\n Optional,\r\n Output,\r\n QueryList,\r\n ViewChild,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\n\r\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\r\nimport { ANIMATION_MODULE_TYPE } from '@angular/platform-browser/animations';\r\nimport { mixinColor } from '../core';\r\nimport { Subscription } from 'rxjs';\r\n// Increasing integer for generating unique ids for radio components.\r\nlet nextUniqueId = 0;\r\n\r\n/**\r\n * Provider Expression that allows oui-radio-group to register as a ControlValueAccessor. This\r\n * allows it to support [(ngModel)] and ngControl.\r\n *\r\n * @docs-private\r\n */\r\nexport const OUI_RADIO_GROUP_CONTROL_VALUE_ACCESSOR: any = {\r\n provide: NG_VALUE_ACCESSOR,\r\n useExisting: forwardRef(() => OuiRadioGroup),\r\n multi: true,\r\n};\r\n\r\n/** Change event object emitted by OuiRadio and OuiRadioGroup. */\r\nexport class OuiRadioChange {\r\n constructor(\r\n /** The OuiRadioButton that emits the change event. */\r\n public source: OuiRadioButton,\r\n /** The value of the OuiRadioButton. */\r\n public value: any\r\n ) {}\r\n}\r\n\r\n// Boilerplate for applying mixins to OuiRadioGroup.\r\n/** @docs-private */\r\nexport class OuiRadioGroupBase {}\r\n\r\n/**\r\n * A group of radio buttons. May contain one or more `` elements.\r\n */\r\n@Component({\r\n selector: 'oui-radio-group',\r\n exportAs: 'ouiRadioGroup',\r\n template: ` `,\r\n providers: [OUI_RADIO_GROUP_CONTROL_VALUE_ACCESSOR],\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n role: 'radiogroup',\r\n class: 'oui-radio-group',\r\n },\r\n})\r\nexport class OuiRadioGroup implements AfterContentInit, ControlValueAccessor {\r\n /**\r\n * Event emitted when the group value changes.\r\n * Change events are only emitted when the value changes due to user interaction with\r\n * a radio button (the same behavior as ``).\r\n */\r\n @Output()\r\n readonly change: EventEmitter = new EventEmitter();\r\n\r\n /** Child radio buttons. */\r\n @ContentChildren(forwardRef(() => OuiRadioButton), { descendants: true })\r\n _radios: QueryList;\r\n /** Selected value for the radio group. */\r\n private _value: any = null;\r\n\r\n /** The HTML name attribute applied to radio buttons in this group. */\r\n private _name = `oui-radio-group-${nextUniqueId++}`;\r\n\r\n /** The currently selected radio button. Should match value. */\r\n private _selected: OuiRadioButton | null = null;\r\n\r\n /** Whether the `value` has been set to its initial value. */\r\n private _isInitialized = false;\r\n\r\n /** Whether the labels should appear after or before the radio-buttons. Defaults to 'after' */\r\n private _labelPosition: 'before' | 'after' = 'after';\r\n\r\n /** Whether the radio group is disabled. */\r\n private _disabled = false;\r\n\r\n /** Whether the radio group is required. */\r\n private _required = false;\r\n\r\n /** The method to be called in order to update ngModel */\r\n _controlValueAccessorChangeFn: (value: any) => void = () => {};\r\n\r\n /**\r\n * onTouch function registered via registerOnTouch (ControlValueAccessor).\r\n *\r\n * @docs-private\r\n */\r\n onTouched: () => any = () => {};\r\n\r\n /** Name of the radio button group. All radio buttons inside this group will use this name. */\r\n @Input()\r\n get name(): string {\r\n return this._name;\r\n }\r\n set name(value: string) {\r\n this._name = value;\r\n this._updateRadioButtonNames();\r\n }\r\n\r\n /** Whether the labels should appear after or before the radio-buttons. Defaults to `after` */\r\n @Input()\r\n get labelPosition(): 'before' | 'after' {\r\n return this._labelPosition;\r\n }\r\n set labelPosition(v) {\r\n this._labelPosition = v === 'before' ? 'before' : 'after';\r\n this._markRadiosForCheck();\r\n }\r\n\r\n /**\r\n * Value for the radio-group. Should equal the value of the selected radio button if there is\r\n * a corresponding radio button with a matching value. If there is not such a corresponding\r\n * radio button, this value persists to be applied in case a new radio button is added with a\r\n * matching value.\r\n */\r\n @Input()\r\n get value(): any {\r\n return this._value;\r\n }\r\n set value(newValue: any) {\r\n if (this._value !== newValue) {\r\n // Set this before proceeding to ensure no circular loop occurs with selection.\r\n this._value = newValue;\r\n\r\n this._updateSelectedRadioFromValue();\r\n this._checkSelectedRadioButton();\r\n }\r\n }\r\n\r\n _checkSelectedRadioButton() {\r\n if (this._selected && !this._selected.checked) {\r\n this._selected.checked = true;\r\n }\r\n }\r\n\r\n /**\r\n * The currently selected radio button. If set to a new radio button, the radio group value\r\n * will be updated to match the new selected button.\r\n */\r\n @Input()\r\n get selected() {\r\n return this._selected;\r\n }\r\n set selected(selected) {\r\n this._selected = selected;\r\n this.value = selected ? selected.value : null;\r\n this._checkSelectedRadioButton();\r\n }\r\n\r\n /** Whether the radio group is disabled */\r\n @Input()\r\n get disabled(): boolean {\r\n return this._disabled;\r\n }\r\n set disabled(value) {\r\n this._disabled = coerceBooleanProperty(value);\r\n this._markRadiosForCheck();\r\n }\r\n\r\n /** Whether the radio group is required */\r\n @Input()\r\n get required(): boolean {\r\n return this._required;\r\n }\r\n set required(value: boolean) {\r\n this._required = coerceBooleanProperty(value);\r\n this._markRadiosForCheck();\r\n }\r\n\r\n constructor(private _changeDetector: ChangeDetectorRef) {}\r\n\r\n /**\r\n * Initialize properties once content children are available.\r\n * This allows us to propagate relevant attributes to associated buttons.\r\n */\r\n ngAfterContentInit() {\r\n // Mark this component as initialized in AfterContentInit because the initial value can\r\n // possibly be set by NgModel on OuiRadioGroup, and it is possible that the OnInit of the\r\n // NgModel occurs *after* the OnInit of the OuiRadioGroup.\r\n this._isInitialized = true;\r\n }\r\n\r\n /**\r\n * Mark this group as being \"touched\" (for ngModel). Meant to be called by the contained\r\n * radio buttons upon their blur.\r\n */\r\n _touch() {\r\n if (this.onTouched) {\r\n this.onTouched();\r\n }\r\n }\r\n\r\n private _updateRadioButtonNames(): void {\r\n if (this._radios) {\r\n this._radios.forEach((radio) => {\r\n radio.name = this.name;\r\n });\r\n }\r\n }\r\n\r\n /** Updates the `selected` radio button from the internal _value state. */\r\n private _updateSelectedRadioFromValue(): void {\r\n // If the value already matches the selected radio, do nothing.\r\n const isAlreadySelected =\r\n this._selected !== null && this._selected.value === this._value;\r\n\r\n if (this._radios && !isAlreadySelected) {\r\n this._selected = null;\r\n this._radios.forEach((radio) => {\r\n radio.checked = this.value === radio.value;\r\n if (radio.checked) {\r\n this._selected = radio;\r\n }\r\n });\r\n }\r\n }\r\n\r\n /** Dispatch change event with current selection and group value. */\r\n _emitChangeEvent(): void {\r\n if (this._isInitialized) {\r\n this.change.emit(new OuiRadioChange(this._selected!, this._value));\r\n }\r\n }\r\n\r\n _markRadiosForCheck() {\r\n if (this._radios) {\r\n this._radios.forEach((radio) => radio._markForCheck());\r\n }\r\n }\r\n\r\n /**\r\n * Sets the model value. Implemented as part of ControlValueAccessor.\r\n *\r\n * @param value\r\n */\r\n writeValue(value: any) {\r\n this.value = value;\r\n this._changeDetector.markForCheck();\r\n }\r\n\r\n /**\r\n * Registers a callback to be triggered when the model value changes.\r\n * Implemented as part of ControlValueAccessor.\r\n *\r\n * @param fn Callback to be registered.\r\n */\r\n registerOnChange(fn: (value: any) => void) {\r\n this._controlValueAccessorChangeFn = fn;\r\n }\r\n\r\n /**\r\n * Registers a callback to be triggered when the control is touched.\r\n * Implemented as part of ControlValueAccessor.\r\n *\r\n * @param fn Callback to be registered.\r\n */\r\n registerOnTouched(fn: any) {\r\n this.onTouched = fn;\r\n }\r\n\r\n /**\r\n * Sets the disabled state of the control. Implemented as a part of ControlValueAccessor.\r\n *\r\n * @param isDisabled Whether the control should be disabled.\r\n */\r\n setDisabledState(isDisabled: boolean) {\r\n this.disabled = isDisabled;\r\n this._changeDetector.markForCheck();\r\n }\r\n}\r\n\r\n// Boilerplate for applying mixins to OuiRadioButton.\r\n/** @docs-private */\r\nexport class OuiRadioButtonBase {\r\n // Since the disabled property is manually defined for the OuiRadioButton and isn't set up in\r\n // the mixin base class. To be able to use the tabindex mixin, a disabled property must be\r\n // defined to properly work.\r\n // disabled: boolean;\r\n\r\n constructor(public _elementRef: ElementRef) {}\r\n}\r\n\r\nexport const OuiRadioButtonMixinBase: typeof OuiRadioButtonBase =\r\n mixinColor(OuiRadioButtonBase);\r\n\r\n@Component({\r\n selector: 'oui-radio-button',\r\n templateUrl: 'radio.html',\r\n styleUrls: ['radio.scss'],\r\n encapsulation: ViewEncapsulation.None,\r\n exportAs: 'ouiRadioButton',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-radio-button',\r\n '[class.oui-radio-checked]': 'checked',\r\n '[class.oui-radio-disabled]': 'disabled',\r\n '[class._oui-animation-noopable]': '_animationMode === \"NoopAnimations\"',\r\n '[attr.tabindex]': 'null',\r\n '[attr.id]': 'id',\r\n // Note: under normal conditions focus shouldn't land on this element, however it may be\r\n // programmatically set, for example inside of a focus trap, in this case we want to forward\r\n // the focus to the native element.\r\n '(focus)': '_inputElement.nativeElement.focus()',\r\n '(click)': '_fireInputChange()',\r\n },\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class OuiRadioButton\r\n extends OuiRadioButtonMixinBase\r\n implements OnInit, AfterViewInit, OnDestroy\r\n{\r\n private _uniqueId = `oui-radio-${++nextUniqueId}`;\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n /**\r\n * Implemented as part of CanColor.\r\n */\r\n @Input() color = 'primary';\r\n /** The unique ID for the radio button. */\r\n @Input() id: string = this._uniqueId;\r\n\r\n /** Analog to HTML `name` attribute used to group radios for unique selection. */\r\n @Input() name: string;\r\n\r\n /** Used to set the `aria-label` attribute on the underlying input element. */\r\n @Input('aria-label') ariaLabel: string;\r\n\r\n /** The `aria-labelledby` attribute takes precedence as the element text alternative. */\r\n @Input('aria-labelledby') ariaLabelledby: string;\r\n /** The `aria-labelledby` attribute takes precedence as the element text alternative. */\r\n @Input() tabIndex: number;\r\n\r\n /** The `aria-describedby` attribute is read after the element label and field type. */\r\n @Input('aria-describedby') ariaDescribedby: string;\r\n\r\n /** Whether this radio button is checked. */\r\n @Input()\r\n get checked(): boolean {\r\n return this._checked;\r\n }\r\n set checked(value: boolean) {\r\n const newCheckedState = coerceBooleanProperty(value);\r\n if (this._checked !== newCheckedState) {\r\n this._checked = newCheckedState;\r\n if (\r\n newCheckedState &&\r\n this.radioGroup &&\r\n this.radioGroup.value !== this.value\r\n ) {\r\n this.radioGroup.selected = this;\r\n } else if (\r\n !newCheckedState &&\r\n this.radioGroup &&\r\n this.radioGroup.value === this.value\r\n ) {\r\n // When unchecking the selected radio button, update the selected radio\r\n // property on the group.\r\n this.radioGroup.selected = null;\r\n }\r\n\r\n if (newCheckedState) {\r\n // Notify all radio buttons with the same name to un-check.\r\n this._radioDispatcher.notify(this.id, this.name);\r\n }\r\n this._changeDetector.markForCheck();\r\n }\r\n }\r\n\r\n /** The value of this radio button. */\r\n @Input()\r\n get value(): any {\r\n return this._value;\r\n }\r\n set value(value: any) {\r\n if (this._value !== value) {\r\n this._value = value;\r\n if (this.radioGroup !== null) {\r\n if (!this.checked) {\r\n // Update checked when the value changed to match the radio group's value\r\n this.checked = this.radioGroup.value === value;\r\n }\r\n if (this.checked) {\r\n this.radioGroup.selected = this;\r\n }\r\n }\r\n }\r\n }\r\n\r\n /** Whether the label should appear after or before the radio button. Defaults to `after` */\r\n @Input()\r\n get labelPosition(): 'before' | 'after' {\r\n return (\r\n this._labelPosition ||\r\n (this.radioGroup && this.radioGroup.labelPosition) ||\r\n 'after'\r\n );\r\n }\r\n set labelPosition(value) {\r\n this._labelPosition = value;\r\n }\r\n private _labelPosition: 'before' | 'after';\r\n\r\n /** Whether the radio button is disabled. */\r\n @Input()\r\n get disabled(): boolean {\r\n return (\r\n this._disabled || (this.radioGroup !== null && this.radioGroup.disabled)\r\n );\r\n }\r\n set disabled(value: boolean) {\r\n const newDisabledState = coerceBooleanProperty(value);\r\n if (this._disabled !== newDisabledState) {\r\n this._disabled = newDisabledState;\r\n this._changeDetector.markForCheck();\r\n }\r\n }\r\n\r\n /** Whether the radio button is required. */\r\n @Input()\r\n get required(): boolean {\r\n return this._required || (this.radioGroup && this.radioGroup.required);\r\n }\r\n set required(value: boolean) {\r\n this._required = coerceBooleanProperty(value);\r\n }\r\n\r\n /**\r\n * Event emitted when the checked state of this radio button changes.\r\n * Change events are only emitted when the value changes due to user interaction with\r\n * the radio button (the same behavior as ``).\r\n */\r\n @Output()\r\n readonly change: EventEmitter = new EventEmitter();\r\n\r\n /** The native `` element */\r\n @ViewChild('input') _inputElement: ElementRef;\r\n\r\n /** The parent radio group. May or may not be present. */\r\n radioGroup: OuiRadioGroup;\r\n\r\n /** ID of the native input element inside `` */\r\n get inputId(): string {\r\n return `${this.id || this._uniqueId}-input`;\r\n }\r\n\r\n /** Whether this radio is checked. */\r\n private _checked = false;\r\n\r\n /** Whether this radio is disabled. */\r\n private _disabled: boolean;\r\n\r\n /** Whether this radio is required. */\r\n private _required: boolean;\r\n\r\n /** Value assigned to this radio. */\r\n private _value: any = null;\r\n\r\n /** Unregister function for _radioDispatcher */\r\n private _removeUniqueSelectionListener: () => void = () => {};\r\n\r\n constructor(\r\n @Optional() radioGroup: OuiRadioGroup,\r\n elementRef: ElementRef,\r\n private _changeDetector: ChangeDetectorRef,\r\n private _focusMonitor: FocusMonitor,\r\n private _radioDispatcher: UniqueSelectionDispatcher,\r\n @Optional() @Inject(ANIMATION_MODULE_TYPE) public _animationMode?: string\r\n ) {\r\n super(elementRef);\r\n // Assertions. Ideally these should be stripped out by the compiler.\r\n // TODO(jelbourn): Assert that there's no name binding AND a parent radio group.\r\n this.radioGroup = radioGroup;\r\n\r\n this._removeUniqueSelectionListener = _radioDispatcher.listen(\r\n (id: string, name: string) => {\r\n if (id !== this.id && name === this.name) {\r\n this.checked = false;\r\n }\r\n }\r\n );\r\n }\r\n\r\n /** Focuses the radio button. */\r\n focus(): void {\r\n this._focusMonitor.focusVia(this._inputElement, 'keyboard');\r\n }\r\n\r\n _fireInputChange(): void {\r\n if (!this.disabled) {\r\n const event = document.createEvent('Event');\r\n event.initEvent('change', false, true);\r\n this._inputElement.nativeElement.dispatchEvent(event);\r\n }\r\n }\r\n\r\n /**\r\n * Marks the radio button as needing checking for change detection.\r\n * This method is exposed because the parent radio group will directly\r\n * update bound properties of the radio button.\r\n */\r\n _markForCheck() {\r\n // When group value changes, the button will not be notified. Use `markForCheck` to explicit\r\n // update radio button's status\r\n this._changeDetector.markForCheck();\r\n }\r\n\r\n ngOnInit() {\r\n if (this.radioGroup) {\r\n // If the radio is inside a radio group, determine if it should be checked\r\n this.checked = this.radioGroup.value === this._value;\r\n // Copy name from parent radio group\r\n this.name = this.radioGroup.name;\r\n }\r\n }\r\n\r\n ngAfterViewInit() {\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this._elementRef, true)\r\n .subscribe((focusOrigin) => {\r\n if (!focusOrigin && this.radioGroup) {\r\n this.radioGroup._touch();\r\n }\r\n });\r\n }\r\n\r\n ngOnDestroy() {\r\n this._focusMonitor.stopMonitoring(this._elementRef);\r\n this._removeUniqueSelectionListener();\r\n this._monitorSubscription.unsubscribe();\r\n }\r\n\r\n /** Dispatch change event with current value. */\r\n private _emitChangeEvent(): void {\r\n this.change.emit(new OuiRadioChange(this, this._value));\r\n }\r\n\r\n _onInputClick(event: Event) {\r\n // We have to stop propagation for click events on the visual hidden input element.\r\n // By default, when a user clicks on a label element, a generated click event will be\r\n // dispatched on the associated input element. Since we are using a label element as our\r\n // root container, the click event on the `radio-button` will be executed twice.\r\n // The real click event will bubble up, and the generated click event also tries to bubble up.\r\n // This will lead to multiple click events.\r\n // Preventing bubbling for the second event will solve that issue.\r\n event.stopPropagation();\r\n }\r\n\r\n /**\r\n * Triggered when the radio button received a click or the input recognized any change.\r\n * Clicking on a label element, will trigger a change event on the associated input.\r\n */\r\n _onInputChange(event: Event) {\r\n // We always have to stop propagation on the change event.\r\n // Otherwise the change event, from the input element, will bubble up and\r\n // emit its event object to the `change` output.\r\n event.stopPropagation();\r\n const groupValueChanged =\r\n this.radioGroup && this.value !== this.radioGroup.value;\r\n this.checked = true;\r\n this._changeDetector.detectChanges();\r\n this._emitChangeEvent();\r\n\r\n if (this.radioGroup) {\r\n this.radioGroup._controlValueAccessorChangeFn(this.value);\r\n this.radioGroup._touch();\r\n if (groupValueChanged) {\r\n this.radioGroup._emitChangeEvent();\r\n }\r\n }\r\n }\r\n}\r\n", + "sourceCode": "import { FocusMonitor } from '@angular/cdk/a11y';\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { UniqueSelectionDispatcher } from '@angular/cdk/collections';\nimport {\n AfterContentInit,\n AfterViewInit,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n ContentChildren,\n ElementRef,\n EventEmitter,\n forwardRef,\n Inject,\n Input,\n OnDestroy,\n OnInit,\n Optional,\n Output,\n QueryList,\n ViewChild,\n ViewEncapsulation,\n} from '@angular/core';\n\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { ANIMATION_MODULE_TYPE } from '@angular/platform-browser/animations';\nimport { mixinColor } from '../core';\nimport { Subscription } from 'rxjs';\n// Increasing integer for generating unique ids for radio components.\nlet nextUniqueId = 0;\n\n/**\n * Provider Expression that allows oui-radio-group to register as a ControlValueAccessor. This\n * allows it to support [(ngModel)] and ngControl.\n *\n * @docs-private\n */\nexport const OUI_RADIO_GROUP_CONTROL_VALUE_ACCESSOR: any = {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => OuiRadioGroup),\n multi: true,\n};\n\n/** Change event object emitted by OuiRadio and OuiRadioGroup. */\nexport class OuiRadioChange {\n constructor(\n /** The OuiRadioButton that emits the change event. */\n public source: OuiRadioButton,\n /** The value of the OuiRadioButton. */\n public value: any\n ) {}\n}\n\n// Boilerplate for applying mixins to OuiRadioGroup.\n/** @docs-private */\nexport class OuiRadioGroupBase {}\n\n/**\n * A group of radio buttons. May contain one or more `` elements.\n */\n@Component({\n selector: 'oui-radio-group',\n exportAs: 'ouiRadioGroup',\n template: ` `,\n providers: [OUI_RADIO_GROUP_CONTROL_VALUE_ACCESSOR],\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n role: 'radiogroup',\n class: 'oui-radio-group',\n },\n})\nexport class OuiRadioGroup implements AfterContentInit, ControlValueAccessor {\n /**\n * Event emitted when the group value changes.\n * Change events are only emitted when the value changes due to user interaction with\n * a radio button (the same behavior as ``).\n */\n @Output()\n readonly change: EventEmitter =\n new EventEmitter();\n\n /** Child radio buttons. */\n @ContentChildren(forwardRef(() => OuiRadioButton), { descendants: true })\n _radios: QueryList;\n /** Selected value for the radio group. */\n private _value: any = null;\n\n /** The HTML name attribute applied to radio buttons in this group. */\n private _name = `oui-radio-group-${nextUniqueId++}`;\n\n /** The currently selected radio button. Should match value. */\n private _selected: OuiRadioButton | null = null;\n\n /** Whether the `value` has been set to its initial value. */\n private _isInitialized = false;\n\n /** Whether the labels should appear after or before the radio-buttons. Defaults to 'after' */\n private _labelPosition: 'before' | 'after' = 'after';\n\n /** Whether the radio group is disabled. */\n private _disabled = false;\n\n /** Whether the radio group is required. */\n private _required = false;\n\n /** The method to be called in order to update ngModel */\n _controlValueAccessorChangeFn: (value: any) => void = () => {};\n\n /**\n * onTouch function registered via registerOnTouch (ControlValueAccessor).\n *\n * @docs-private\n */\n onTouched: () => any = () => {};\n\n /** Name of the radio button group. All radio buttons inside this group will use this name. */\n @Input()\n get name(): string {\n return this._name;\n }\n set name(value: string) {\n this._name = value;\n this._updateRadioButtonNames();\n }\n\n /** Whether the labels should appear after or before the radio-buttons. Defaults to `after` */\n @Input()\n get labelPosition(): 'before' | 'after' {\n return this._labelPosition;\n }\n set labelPosition(v) {\n this._labelPosition = v === 'before' ? 'before' : 'after';\n this._markRadiosForCheck();\n }\n\n /**\n * Value for the radio-group. Should equal the value of the selected radio button if there is\n * a corresponding radio button with a matching value. If there is not such a corresponding\n * radio button, this value persists to be applied in case a new radio button is added with a\n * matching value.\n */\n @Input()\n get value(): any {\n return this._value;\n }\n set value(newValue: any) {\n if (this._value !== newValue) {\n // Set this before proceeding to ensure no circular loop occurs with selection.\n this._value = newValue;\n\n this._updateSelectedRadioFromValue();\n this._checkSelectedRadioButton();\n }\n }\n\n _checkSelectedRadioButton() {\n if (this._selected && !this._selected.checked) {\n this._selected.checked = true;\n }\n }\n\n /**\n * The currently selected radio button. If set to a new radio button, the radio group value\n * will be updated to match the new selected button.\n */\n @Input()\n get selected() {\n return this._selected;\n }\n set selected(selected) {\n this._selected = selected;\n this.value = selected ? selected.value : null;\n this._checkSelectedRadioButton();\n }\n\n /** Whether the radio group is disabled */\n @Input()\n get disabled(): boolean {\n return this._disabled;\n }\n set disabled(value) {\n this._disabled = coerceBooleanProperty(value);\n this._markRadiosForCheck();\n }\n\n /** Whether the radio group is required */\n @Input()\n get required(): boolean {\n return this._required;\n }\n set required(value: boolean) {\n this._required = coerceBooleanProperty(value);\n this._markRadiosForCheck();\n }\n\n constructor(private _changeDetector: ChangeDetectorRef) {}\n\n /**\n * Initialize properties once content children are available.\n * This allows us to propagate relevant attributes to associated buttons.\n */\n ngAfterContentInit() {\n // Mark this component as initialized in AfterContentInit because the initial value can\n // possibly be set by NgModel on OuiRadioGroup, and it is possible that the OnInit of the\n // NgModel occurs *after* the OnInit of the OuiRadioGroup.\n this._isInitialized = true;\n }\n\n /**\n * Mark this group as being \"touched\" (for ngModel). Meant to be called by the contained\n * radio buttons upon their blur.\n */\n _touch() {\n if (this.onTouched) {\n this.onTouched();\n }\n }\n\n private _updateRadioButtonNames(): void {\n if (this._radios) {\n this._radios.forEach((radio) => {\n radio.name = this.name;\n });\n }\n }\n\n /** Updates the `selected` radio button from the internal _value state. */\n private _updateSelectedRadioFromValue(): void {\n // If the value already matches the selected radio, do nothing.\n const isAlreadySelected =\n this._selected !== null && this._selected.value === this._value;\n\n if (this._radios && !isAlreadySelected) {\n this._selected = null;\n this._radios.forEach((radio) => {\n radio.checked = this.value === radio.value;\n if (radio.checked) {\n this._selected = radio;\n }\n });\n }\n }\n\n /** Dispatch change event with current selection and group value. */\n _emitChangeEvent(): void {\n if (this._isInitialized) {\n this.change.emit(new OuiRadioChange(this._selected!, this._value));\n }\n }\n\n _markRadiosForCheck() {\n if (this._radios) {\n this._radios.forEach((radio) => radio._markForCheck());\n }\n }\n\n /**\n * Sets the model value. Implemented as part of ControlValueAccessor.\n *\n * @param value\n */\n writeValue(value: any) {\n this.value = value;\n this._changeDetector.markForCheck();\n }\n\n /**\n * Registers a callback to be triggered when the model value changes.\n * Implemented as part of ControlValueAccessor.\n *\n * @param fn Callback to be registered.\n */\n registerOnChange(fn: (value: any) => void) {\n this._controlValueAccessorChangeFn = fn;\n }\n\n /**\n * Registers a callback to be triggered when the control is touched.\n * Implemented as part of ControlValueAccessor.\n *\n * @param fn Callback to be registered.\n */\n registerOnTouched(fn: any) {\n this.onTouched = fn;\n }\n\n /**\n * Sets the disabled state of the control. Implemented as a part of ControlValueAccessor.\n *\n * @param isDisabled Whether the control should be disabled.\n */\n setDisabledState(isDisabled: boolean) {\n this.disabled = isDisabled;\n this._changeDetector.markForCheck();\n }\n}\n\n// Boilerplate for applying mixins to OuiRadioButton.\n/** @docs-private */\nexport class OuiRadioButtonBase {\n // Since the disabled property is manually defined for the OuiRadioButton and isn't set up in\n // the mixin base class. To be able to use the tabindex mixin, a disabled property must be\n // defined to properly work.\n // disabled: boolean;\n\n constructor(public _elementRef: ElementRef) {}\n}\n\nexport const OuiRadioButtonMixinBase: typeof OuiRadioButtonBase =\n mixinColor(OuiRadioButtonBase);\n\n@Component({\n selector: 'oui-radio-button',\n templateUrl: 'radio.html',\n styleUrls: ['radio.scss'],\n encapsulation: ViewEncapsulation.None,\n exportAs: 'ouiRadioButton',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-radio-button',\n '[class.oui-radio-checked]': 'checked',\n '[class.oui-radio-disabled]': 'disabled',\n '[class._oui-animation-noopable]': '_animationMode === \"NoopAnimations\"',\n '[attr.tabindex]': 'null',\n '[attr.id]': 'id',\n // Note: under normal conditions focus shouldn't land on this element, however it may be\n // programmatically set, for example inside of a focus trap, in this case we want to forward\n // the focus to the native element.\n '(focus)': '_inputElement.nativeElement.focus()',\n '(click)': '_fireInputChange()',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class OuiRadioButton\n extends OuiRadioButtonMixinBase\n implements OnInit, AfterViewInit, OnDestroy\n{\n private _uniqueId = `oui-radio-${++nextUniqueId}`;\n private _monitorSubscription: Subscription = Subscription.EMPTY;\n /**\n * Implemented as part of CanColor.\n */\n @Input() color = 'primary';\n /** The unique ID for the radio button. */\n @Input() id: string = this._uniqueId;\n\n /** Analog to HTML `name` attribute used to group radios for unique selection. */\n @Input() name: string;\n\n /** Used to set the `aria-label` attribute on the underlying input element. */\n @Input('aria-label') ariaLabel: string;\n\n /** The `aria-labelledby` attribute takes precedence as the element text alternative. */\n @Input('aria-labelledby') ariaLabelledby: string;\n /** The `aria-labelledby` attribute takes precedence as the element text alternative. */\n @Input() tabIndex: number;\n\n /** The `aria-describedby` attribute is read after the element label and field type. */\n @Input('aria-describedby') ariaDescribedby: string;\n\n /** Whether this radio button is checked. */\n @Input()\n get checked(): boolean {\n return this._checked;\n }\n set checked(value: boolean) {\n const newCheckedState = coerceBooleanProperty(value);\n if (this._checked !== newCheckedState) {\n this._checked = newCheckedState;\n if (\n newCheckedState &&\n this.radioGroup &&\n this.radioGroup.value !== this.value\n ) {\n this.radioGroup.selected = this;\n } else if (\n !newCheckedState &&\n this.radioGroup &&\n this.radioGroup.value === this.value\n ) {\n // When unchecking the selected radio button, update the selected radio\n // property on the group.\n this.radioGroup.selected = null;\n }\n\n if (newCheckedState) {\n // Notify all radio buttons with the same name to un-check.\n this._radioDispatcher.notify(this.id, this.name);\n }\n this._changeDetector.markForCheck();\n }\n }\n\n /** The value of this radio button. */\n @Input()\n get value(): any {\n return this._value;\n }\n set value(value: any) {\n if (this._value !== value) {\n this._value = value;\n if (this.radioGroup !== null) {\n if (!this.checked) {\n // Update checked when the value changed to match the radio group's value\n this.checked = this.radioGroup.value === value;\n }\n if (this.checked) {\n this.radioGroup.selected = this;\n }\n }\n }\n }\n\n /** Whether the label should appear after or before the radio button. Defaults to `after` */\n @Input()\n get labelPosition(): 'before' | 'after' {\n return (\n this._labelPosition ||\n (this.radioGroup && this.radioGroup.labelPosition) ||\n 'after'\n );\n }\n set labelPosition(value) {\n this._labelPosition = value;\n }\n private _labelPosition: 'before' | 'after';\n\n /** Whether the radio button is disabled. */\n @Input()\n get disabled(): boolean {\n return (\n this._disabled || (this.radioGroup !== null && this.radioGroup.disabled)\n );\n }\n set disabled(value: boolean) {\n const newDisabledState = coerceBooleanProperty(value);\n if (this._disabled !== newDisabledState) {\n this._disabled = newDisabledState;\n this._changeDetector.markForCheck();\n }\n }\n\n /** Whether the radio button is required. */\n @Input()\n get required(): boolean {\n return this._required || (this.radioGroup && this.radioGroup.required);\n }\n set required(value: boolean) {\n this._required = coerceBooleanProperty(value);\n }\n\n /**\n * Event emitted when the checked state of this radio button changes.\n * Change events are only emitted when the value changes due to user interaction with\n * the radio button (the same behavior as ``).\n */\n @Output()\n readonly change: EventEmitter =\n new EventEmitter();\n\n /** The native `` element */\n @ViewChild('input') _inputElement: ElementRef;\n\n /** The parent radio group. May or may not be present. */\n radioGroup: OuiRadioGroup;\n\n /** ID of the native input element inside `` */\n get inputId(): string {\n return `${this.id || this._uniqueId}-input`;\n }\n\n /** Whether this radio is checked. */\n private _checked = false;\n\n /** Whether this radio is disabled. */\n private _disabled: boolean;\n\n /** Whether this radio is required. */\n private _required: boolean;\n\n /** Value assigned to this radio. */\n private _value: any = null;\n\n /** Unregister function for _radioDispatcher */\n private _removeUniqueSelectionListener: () => void = () => {};\n\n constructor(\n @Optional() radioGroup: OuiRadioGroup,\n elementRef: ElementRef,\n private _changeDetector: ChangeDetectorRef,\n private _focusMonitor: FocusMonitor,\n private _radioDispatcher: UniqueSelectionDispatcher,\n @Optional() @Inject(ANIMATION_MODULE_TYPE) public _animationMode?: string\n ) {\n super(elementRef);\n // Assertions. Ideally these should be stripped out by the compiler.\n // TODO(jelbourn): Assert that there's no name binding AND a parent radio group.\n this.radioGroup = radioGroup;\n\n this._removeUniqueSelectionListener = _radioDispatcher.listen(\n (id: string, name: string) => {\n if (id !== this.id && name === this.name) {\n this.checked = false;\n }\n }\n );\n }\n\n /** Focuses the radio button. */\n focus(): void {\n this._focusMonitor.focusVia(this._inputElement, 'keyboard');\n }\n\n _fireInputChange(): void {\n if (!this.disabled) {\n const event = document.createEvent('Event');\n event.initEvent('change', false, true);\n this._inputElement.nativeElement.dispatchEvent(event);\n }\n }\n\n /**\n * Marks the radio button as needing checking for change detection.\n * This method is exposed because the parent radio group will directly\n * update bound properties of the radio button.\n */\n _markForCheck() {\n // When group value changes, the button will not be notified. Use `markForCheck` to explicit\n // update radio button's status\n this._changeDetector.markForCheck();\n }\n\n ngOnInit() {\n if (this.radioGroup) {\n // If the radio is inside a radio group, determine if it should be checked\n this.checked = this.radioGroup.value === this._value;\n // Copy name from parent radio group\n this.name = this.radioGroup.name;\n }\n }\n\n ngAfterViewInit() {\n this._monitorSubscription = this._focusMonitor\n .monitor(this._elementRef, true)\n .subscribe((focusOrigin) => {\n if (!focusOrigin && this.radioGroup) {\n this.radioGroup._touch();\n }\n });\n }\n\n ngOnDestroy() {\n this._focusMonitor.stopMonitoring(this._elementRef);\n this._removeUniqueSelectionListener();\n this._monitorSubscription.unsubscribe();\n }\n\n /** Dispatch change event with current value. */\n private _emitChangeEvent(): void {\n this.change.emit(new OuiRadioChange(this, this._value));\n }\n\n _onInputClick(event: Event) {\n // We have to stop propagation for click events on the visual hidden input element.\n // By default, when a user clicks on a label element, a generated click event will be\n // dispatched on the associated input element. Since we are using a label element as our\n // root container, the click event on the `radio-button` will be executed twice.\n // The real click event will bubble up, and the generated click event also tries to bubble up.\n // This will lead to multiple click events.\n // Preventing bubbling for the second event will solve that issue.\n event.stopPropagation();\n }\n\n /**\n * Triggered when the radio button received a click or the input recognized any change.\n * Clicking on a label element, will trigger a change event on the associated input.\n */\n _onInputChange(event: Event) {\n // We always have to stop propagation on the change event.\n // Otherwise the change event, from the input element, will bubble up and\n // emit its event object to the `change` output.\n event.stopPropagation();\n const groupValueChanged =\n this.radioGroup && this.value !== this.radioGroup.value;\n this.checked = true;\n this._changeDetector.detectChanges();\n this._emitChangeEvent();\n\n if (this.radioGroup) {\n this.radioGroup._controlValueAccessorChangeFn(this.value);\n this.radioGroup._touch();\n if (groupValueChanged) {\n this.radioGroup._emitChangeEvent();\n }\n }\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -34410,7 +44268,7 @@ "deprecationMessage": "" } ], - "line": 193, + "line": 194, "jsdoctags": [ { "name": "_changeDetector", @@ -34444,7 +44302,7 @@ } ], "returnType": "void", - "line": 120, + "line": 121, "jsdoctags": [ { "name": "value", @@ -34461,7 +44319,7 @@ "name": "name", "type": "string", "returnType": "string", - "line": 117, + "line": 118, "rawdescription": "\nName of the radio button group. All radio buttons inside this group will use this name.", "description": "

Name of the radio button group. All radio buttons inside this group will use this name.

\n" } @@ -34482,7 +44340,7 @@ } ], "returnType": "void", - "line": 130, + "line": 131, "jsdoctags": [ { "name": "v", @@ -34499,7 +44357,7 @@ "name": "labelPosition", "type": "", "returnType": "\"before\" | \"after\"", - "line": 127, + "line": 128, "rawdescription": "\nWhether the labels should appear after or before the radio-buttons. Defaults to `after`", "description": "

Whether the labels should appear after or before the radio-buttons. Defaults to after

\n" } @@ -34520,7 +44378,7 @@ } ], "returnType": "void", - "line": 145, + "line": 146, "jsdoctags": [ { "name": "newValue", @@ -34537,7 +44395,7 @@ "name": "value", "type": "any", "returnType": "any", - "line": 142, + "line": 143, "rawdescription": "\n\nValue for the radio-group. Should equal the value of the selected radio button if there is\na corresponding radio button with a matching value. If there is not such a corresponding\nradio button, this value persists to be applied in case a new radio button is added with a\nmatching value.\n", "description": "

Value for the radio-group. Should equal the value of the selected radio button if there is\na corresponding radio button with a matching value. If there is not such a corresponding\nradio button, this value persists to be applied in case a new radio button is added with a\nmatching value.

\n" } @@ -34558,7 +44416,7 @@ } ], "returnType": "void", - "line": 169, + "line": 170, "jsdoctags": [ { "name": "selected", @@ -34575,7 +44433,7 @@ "name": "selected", "type": "", "returnType": "", - "line": 166, + "line": 167, "rawdescription": "\n\nThe currently selected radio button. If set to a new radio button, the radio group value\nwill be updated to match the new selected button.\n", "description": "

The currently selected radio button. If set to a new radio button, the radio group value\nwill be updated to match the new selected button.

\n" } @@ -34596,7 +44454,7 @@ } ], "returnType": "void", - "line": 180, + "line": 181, "jsdoctags": [ { "name": "value", @@ -34613,7 +44471,7 @@ "name": "disabled", "type": "boolean", "returnType": "boolean", - "line": 177, + "line": 178, "rawdescription": "\nWhether the radio group is disabled", "description": "

Whether the radio group is disabled

\n" } @@ -34634,7 +44492,7 @@ } ], "returnType": "void", - "line": 190, + "line": 191, "jsdoctags": [ { "name": "value", @@ -34651,7 +44509,7 @@ "name": "required", "type": "boolean", "returnType": "boolean", - "line": 187, + "line": 188, "rawdescription": "\nWhether the radio group is required", "description": "

Whether the radio group is required

\n" } @@ -34660,7 +44518,7 @@ }, { "name": "OuiRow", - "id": "component-OuiRow-2c11beafcdd8c12a01329de8f4f053505e5dbbd179540cefba04203174c4dbaef3f70b9edd0846497b3f172b61f71885e2ae7d091d3b9bd6ed01366b24783bc2", + "id": "component-OuiRow-231d97eaa7f9a073f68bb47891eb7839f7a6610df3284b8b1e98593fcac5b8a47362c5920e2b3f0a60958ecee910f5d4d98d6ac257b31323cdd32c67e5768b7b", "file": "ui/src/components/table/row.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -34718,7 +44576,7 @@ "description": "

Data row template container that contains the cell outlet. Adds the right class and role.

\n", "rawdescription": "\nData row template container that contains the cell outlet. Adds the right class and role.", "type": "component", - "sourceCode": "import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n Directive,\r\n ViewEncapsulation,\r\n OnDestroy,\r\n ElementRef,\r\n NgZone,\r\n IterableDiffers,\r\n} from '@angular/core';\r\nimport {\r\n CDK_ROW_TEMPLATE,\r\n CdkFooterRow,\r\n CdkFooterRowDef,\r\n CdkHeaderRow,\r\n CdkHeaderRowDef,\r\n CdkRow,\r\n CdkRowDef,\r\n} from '@angular/cdk/table';\r\nimport { FocusMonitor } from '@angular/cdk/a11y';\r\nimport { Subscription } from 'rxjs';\r\n\r\n/**\r\n * Header row definition for the oui-table.\r\n * Captures the header row's template and other header properties such as the columns to display.\r\n */\r\n@Directive({\r\n selector: '[ouiHeaderRowDef]',\r\n providers: [{ provide: CdkHeaderRowDef, useExisting: OuiHeaderRowDef }],\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['columns: ouiHeaderRowDef'],\r\n})\r\nexport class OuiHeaderRowDef extends CdkHeaderRowDef {}\r\n\r\n/**\r\n * Footer row definition for the oui-table.\r\n * Captures the footer row's template and other footer properties such as the columns to display.\r\n */\r\n@Directive({\r\n selector: '[ouiFooterRowDef]',\r\n providers: [{ provide: CdkFooterRowDef, useExisting: OuiFooterRowDef }],\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['columns: ouiFooterRowDef'],\r\n})\r\nexport class OuiFooterRowDef extends CdkFooterRowDef {}\r\n\r\n/**\r\n * Data row definition for the oui-table.\r\n * Captures the data row's template and other properties such as the columns to display and\r\n * a when predicate that describes when this row should be used.\r\n */\r\n@Directive({\r\n selector: '[ouiRowDef]',\r\n providers: [{ provide: CdkRowDef, useExisting: OuiRowDef }],\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['columns: ouiRowDefColumns', 'when: ouiRowDefWhen'],\r\n})\r\nexport class OuiRowDef extends CdkRowDef {}\r\n\r\n/** Footer template container that contains the cell outlet. Adds the right class and role. */\r\n@Component({\r\n selector: 'oui-header-row, tr[oui-header-row]',\r\n template: CDK_ROW_TEMPLATE,\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-header-row',\r\n role: 'row',\r\n },\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n exportAs: 'ouiHeaderRow',\r\n providers: [{ provide: CdkHeaderRow, useExisting: OuiHeaderRow }],\r\n})\r\nexport class OuiHeaderRow extends CdkHeaderRow {}\r\n\r\n/** Footer template container that contains the cell outlet. Adds the right class and role. */\r\n@Component({\r\n selector: 'oui-footer-row, tr[oui-footer-row]',\r\n template: CDK_ROW_TEMPLATE,\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-footer-row',\r\n role: 'row',\r\n },\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n exportAs: 'ouiFooterRow',\r\n providers: [{ provide: CdkFooterRow, useExisting: OuiFooterRow }],\r\n})\r\nexport class OuiFooterRow extends CdkFooterRow {}\r\n\r\n/** Data row template container that contains the cell outlet. Adds the right class and role. */\r\n@Component({\r\n selector: 'oui-row, tr[oui-row]',\r\n template: CDK_ROW_TEMPLATE,\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-row',\r\n role: 'row',\r\n tabindex: '0',\r\n },\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n exportAs: 'ouiRow',\r\n providers: [{ provide: CdkRow, useExisting: OuiRow }],\r\n})\r\nexport class OuiRow extends CdkRow implements OnDestroy {\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n constructor(\r\n protected elementRef: ElementRef,\r\n protected _differs: IterableDiffers,\r\n private _focusMonitor: FocusMonitor,\r\n private _ngZone: NgZone\r\n ) {\r\n super();\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this.elementRef, true)\r\n .subscribe(() => this._ngZone.run(() => {}));\r\n }\r\n ngOnDestroy(): void {\r\n this._focusMonitor.stopMonitoring(this.elementRef);\r\n this._monitorSubscription.unsubscribe();\r\n }\r\n}\r\n", + "sourceCode": "import {\n ChangeDetectionStrategy,\n Component,\n Directive,\n ViewEncapsulation,\n OnDestroy,\n ElementRef,\n NgZone,\n IterableDiffers,\n} from '@angular/core';\nimport {\n CDK_ROW_TEMPLATE,\n CdkFooterRow,\n CdkFooterRowDef,\n CdkHeaderRow,\n CdkHeaderRowDef,\n CdkRow,\n CdkRowDef,\n} from '@angular/cdk/table';\nimport { FocusMonitor } from '@angular/cdk/a11y';\nimport { Subscription } from 'rxjs';\n\n/**\n * Header row definition for the oui-table.\n * Captures the header row's template and other header properties such as the columns to display.\n */\n@Directive({\n selector: '[ouiHeaderRowDef]',\n providers: [{ provide: CdkHeaderRowDef, useExisting: OuiHeaderRowDef }],\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\n inputs: ['columns: ouiHeaderRowDef'],\n})\nexport class OuiHeaderRowDef extends CdkHeaderRowDef {}\n\n/**\n * Footer row definition for the oui-table.\n * Captures the footer row's template and other footer properties such as the columns to display.\n */\n@Directive({\n selector: '[ouiFooterRowDef]',\n providers: [{ provide: CdkFooterRowDef, useExisting: OuiFooterRowDef }],\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\n inputs: ['columns: ouiFooterRowDef'],\n})\nexport class OuiFooterRowDef extends CdkFooterRowDef {}\n\n/**\n * Data row definition for the oui-table.\n * Captures the data row's template and other properties such as the columns to display and\n * a when predicate that describes when this row should be used.\n */\n@Directive({\n selector: '[ouiRowDef]',\n providers: [{ provide: CdkRowDef, useExisting: OuiRowDef }],\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\n inputs: ['columns: ouiRowDefColumns', 'when: ouiRowDefWhen'],\n})\nexport class OuiRowDef extends CdkRowDef {}\n\n/** Footer template container that contains the cell outlet. Adds the right class and role. */\n@Component({\n selector: 'oui-header-row, tr[oui-header-row]',\n template: CDK_ROW_TEMPLATE,\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-header-row',\n role: 'row',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n exportAs: 'ouiHeaderRow',\n providers: [{ provide: CdkHeaderRow, useExisting: OuiHeaderRow }],\n})\nexport class OuiHeaderRow extends CdkHeaderRow {}\n\n/** Footer template container that contains the cell outlet. Adds the right class and role. */\n@Component({\n selector: 'oui-footer-row, tr[oui-footer-row]',\n template: CDK_ROW_TEMPLATE,\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-footer-row',\n role: 'row',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n exportAs: 'ouiFooterRow',\n providers: [{ provide: CdkFooterRow, useExisting: OuiFooterRow }],\n})\nexport class OuiFooterRow extends CdkFooterRow {}\n\n/** Data row template container that contains the cell outlet. Adds the right class and role. */\n@Component({\n selector: 'oui-row, tr[oui-row]',\n template: CDK_ROW_TEMPLATE,\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-row',\n role: 'row',\n tabindex: '0',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n exportAs: 'ouiRow',\n providers: [{ provide: CdkRow, useExisting: OuiRow }],\n})\nexport class OuiRow extends CdkRow implements OnDestroy {\n private _monitorSubscription: Subscription = Subscription.EMPTY;\n constructor(\n protected elementRef: ElementRef,\n protected _differs: IterableDiffers,\n private _focusMonitor: FocusMonitor,\n private _ngZone: NgZone\n ) {\n super();\n this._monitorSubscription = this._focusMonitor\n .monitor(this.elementRef, true)\n .subscribe(() => this._ngZone.run(() => {}));\n }\n ngOnDestroy(): void {\n this._focusMonitor.stopMonitoring(this.elementRef);\n this._monitorSubscription.unsubscribe();\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -34800,7 +44658,7 @@ }, { "name": "OuiScrollbar", - "id": "component-OuiScrollbar-6f231a259fa736beb5c105d70f8d0986a2c351b97f236cab9ef2705a27074b5cc2b958170acfdb32f3b4642e0343cbe7b22ea716097736d4bd16f1917872bda2", + "id": "component-OuiScrollbar-8606338c46329d49aa1789386d902841bf40443b13b264952f1a11f5d6a0cae891bd92dde794b6a55e639b547c72297cc20e2ae46f6272d4c06be72a4059c7c3", "file": "ui/src/components/scrollbar/scrollbar.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -34856,11 +44714,11 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import {\r\n Component,\r\n ChangeDetectionStrategy,\r\n ViewEncapsulation,\r\n Input,\r\n} from '@angular/core';\r\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\r\n\r\n@Component({\r\n template: ` `,\r\n // eslint-disable-next-line @angular-eslint/component-selector\r\n selector: '[oui-scrollbar]',\r\n exportAs: 'OuiScrollbar',\r\n styleUrls: ['scrollbar.scss'],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-scrollbar-container',\r\n '[class.oui-scrollbar-container-large]': 'large',\r\n },\r\n})\r\nexport class OuiScrollbar {\r\n private _large = false;\r\n\r\n /** Whether the oui-select is of large size. */\r\n @Input('oui-scrollbar-large')\r\n get large(): boolean {\r\n return this._large;\r\n }\r\n set large(value) {\r\n this._large = coerceBooleanProperty(value);\r\n }\r\n constructor() {}\r\n}\r\n", + "sourceCode": "import {\n Component,\n ChangeDetectionStrategy,\n ViewEncapsulation,\n Input,\n} from '@angular/core';\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\n\n@Component({\n template: ` `,\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: '[oui-scrollbar]',\n exportAs: 'OuiScrollbar',\n styleUrls: ['scrollbar.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-scrollbar-container',\n '[class.oui-scrollbar-container-large]': 'large',\n },\n})\nexport class OuiScrollbar {\n private _large = false;\n\n /** Whether the oui-select is of large size. */\n @Input('oui-scrollbar-large')\n get large(): boolean {\n return this._large;\n }\n set large(value) {\n this._large = coerceBooleanProperty(value);\n }\n constructor() {}\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": ".oui-scrollbar-container::-webkit-scrollbar {\r\n width: 12px;\r\n}\r\n.oui-scrollbar-container::-webkit-scrollbar-track {\r\n display: none;\r\n}\r\n.oui-scrollbar-container::-webkit-scrollbar-thumb {\r\n background-color: #ccc !important;\r\n border: 3px #fff solid;\r\n border-radius: 15px;\r\n}\r\n.oui-scrollbar-container-large::-webkit-scrollbar {\r\n width: 16px;\r\n}\r\n.oui-scrollbar-container-large::-webkit-scrollbar-thumb {\r\n border: 4px #fff solid !important;\r\n}\r\n\r\n.oui-scrollbar-container {\r\n overflow-y: scroll;\r\n scrollbar-color: #ccc transparent;\r\n scrollbar-width: thin;\r\n}\r\n", + "data": ".oui-scrollbar-container::-webkit-scrollbar {\n width: 12px;\n}\n.oui-scrollbar-container::-webkit-scrollbar-track {\n display: none;\n}\n.oui-scrollbar-container::-webkit-scrollbar-thumb {\n background-color: #ccc !important;\n border: 3px #fff solid;\n border-radius: 15px;\n}\n.oui-scrollbar-container-large::-webkit-scrollbar {\n width: 16px;\n}\n.oui-scrollbar-container-large::-webkit-scrollbar-thumb {\n border: 4px #fff solid !important;\n}\n\n.oui-scrollbar-container {\n overflow-y: scroll;\n scrollbar-color: #ccc transparent;\n scrollbar-width: thin;\n}\n", "styleUrl": "scrollbar.scss" } ], @@ -35072,7 +44930,7 @@ }, { "name": "OuiSelect", - "id": "component-OuiSelect-c31c47d801410216b9011bcf6d1f92b501c90e5c5ad2b6ed2698d000df2b61d44f5d16435aa84af435a6f974ecdd41804312d09195c0e468908788e414878869", + "id": "component-OuiSelect-b55012efc12375ebc3dfddf82a12f3c93d79e2444e310d87c1b2851af03e58945c24688be5227f215f1c0cb64177c88837c083862f3cb4ffcf9e5bb074c7208b", "file": "ui/src/components/select/select.component.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -35111,7 +44969,7 @@ "deprecationMessage": "", "rawdescription": "\nWhether the action items are required and use saveSelectionChange instead of selectionChange.", "description": "

Whether the action items are required and use saveSelectionChange instead of selectionChange.

\n", - "line": 480, + "line": 482, "type": "boolean", "decorators": [] }, @@ -35142,7 +45000,7 @@ "deprecationMessage": "", "rawdescription": "\n\nFunction to compare the option values with the selected values. The first argument\nis a value from an option. The second is a value from the selection. A boolean\nshould be returned.\n", "description": "

Function to compare the option values with the selected values. The first argument\nis a value from an option. The second is a value from the selection. A boolean\nshould be returned.

\n", - "line": 505, + "line": 507, "type": "(o1: any, o2: any) => boolean", "decorators": [] }, @@ -35152,7 +45010,7 @@ "deprecationMessage": "", "rawdescription": "\nWhether to center the active option over the trigger.", "description": "

Whether to center the active option over the trigger.

\n", - "line": 492, + "line": 494, "type": "boolean", "decorators": [] }, @@ -35172,7 +45030,7 @@ "deprecationMessage": "", "rawdescription": "\nUnique id of the element.", "description": "

Unique id of the element.

\n", - "line": 533, + "line": 535, "type": "string", "decorators": [] }, @@ -35192,7 +45050,7 @@ "deprecationMessage": "", "rawdescription": "\nWhether the user should be allowed to select multiple options.", "description": "

Whether the user should be allowed to select multiple options.

\n", - "line": 467, + "line": 469, "type": "boolean", "decorators": [] }, @@ -35202,7 +45060,7 @@ "deprecationMessage": "", "rawdescription": "\nClasses to be passed to the select panel. Supports the same syntax as `ngClass`.", "description": "

Classes to be passed to the select panel. Supports the same syntax as ngClass.

\n", - "line": 417, + "line": 419, "type": "string | string[] | Set | literal type", "decorators": [] }, @@ -35212,7 +45070,7 @@ "deprecationMessage": "", "rawdescription": "\nPlaceholder to be shown if no value has been selected.", "description": "

Placeholder to be shown if no value has been selected.

\n", - "line": 447, + "line": 449, "type": "string", "decorators": [] }, @@ -35222,7 +45080,7 @@ "deprecationMessage": "", "rawdescription": "\nWhether the component is required.", "description": "

Whether the component is required.

\n", - "line": 457, + "line": 459, "type": "boolean", "decorators": [] }, @@ -35253,7 +45111,7 @@ "deprecationMessage": "", "rawdescription": "\nValue of the select control.", "description": "

Value of the select control.

\n", - "line": 521, + "line": 523, "type": "any", "decorators": [] } @@ -35261,7 +45119,7 @@ "outputsClass": [ { "name": "closed", - "defaultValue": "this.openedChange.pipe(\r\n filter((o) => !o),\r\n map(() => {\r\n this.isSearchFieldPresent = false;\r\n })\r\n )", + "defaultValue": "this.openedChange.pipe(\n filter((o) => !o),\n map(() => {\n this.isSearchFieldPresent = false;\n })\n )", "deprecated": false, "deprecationMessage": "", "rawdescription": "\nEvent emitted when the select has been closed.", @@ -35271,7 +45129,7 @@ }, { "name": "opened", - "defaultValue": "this.openedChange.pipe(\r\n filter((o) => o),\r\n map(() => {})\r\n )", + "defaultValue": "this.openedChange.pipe(\n filter((o) => o),\n map(() => {})\n )", "deprecated": false, "deprecationMessage": "", "rawdescription": "\nEvent emitted when the select has been opened.", @@ -35296,7 +45154,7 @@ "deprecationMessage": "", "rawdescription": "\nEvent emitted when the selected value has been changed and saved by the user.", "description": "

Event emitted when the selected value has been changed and saved by the user.

\n", - "line": 407, + "line": 408, "type": "EventEmitter" }, { @@ -35318,15 +45176,15 @@ "description": "

Event that emits whenever the raw value of the select changes. This is here primarily\nto facilitate the two-way binding for the value input.

\n", "jsdoctags": [ { - "pos": 11284, - "end": 11302, + "pos": 10913, + "end": 10930, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, "kind": 325, "tagName": { - "pos": 11285, - "end": 11297, + "pos": 10914, + "end": 10926, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -35373,7 +45231,7 @@ "type": "", "optional": false, "description": "

Comparison function to specify which option is displayed. Defaults to object equality.

\n", - "line": 427, + "line": 429, "rawdescription": "\nComparison function to specify which option is displayed. Defaults to object equality.", "modifierKind": [ 121 @@ -35415,7 +45273,7 @@ "type": "ElementRef", "optional": false, "description": "", - "line": 554, + "line": 556, "modifierKind": [ 123 ] @@ -35510,7 +45368,7 @@ "type": "function", "optional": false, "description": "

View -> model callback called when value changes

\n", - "line": 440, + "line": 442, "rawdescription": "\n`View -> model callback called when value changes`" }, { @@ -35521,7 +45379,7 @@ "type": "", "optional": false, "description": "

View -> model callback called when select has been touched

\n", - "line": 443, + "line": 445, "rawdescription": "\n`View -> model callback called when select has been touched`" }, { @@ -35542,7 +45400,7 @@ "type": "OuiIconRegistry", "optional": false, "description": "", - "line": 555, + "line": 557, "modifierKind": [ 123 ] @@ -35555,7 +45413,7 @@ "type": "", "optional": false, "description": "

Emits when the panel element is finished transforming in.

\n", - "line": 424, + "line": 426, "rawdescription": "\nEmits when the panel element is finished transforming in." }, { @@ -35587,7 +45445,7 @@ }, { "name": "_positions", - "defaultValue": "[\r\n {\r\n originX: 'start',\r\n originY: 'top',\r\n overlayX: 'start',\r\n overlayY: 'top',\r\n },\r\n {\r\n originX: 'start',\r\n originY: 'bottom',\r\n overlayX: 'start',\r\n overlayY: 'bottom',\r\n },\r\n ]", + "defaultValue": "[\n {\n originX: 'start',\n originY: 'top',\n overlayX: 'start',\n overlayY: 'top',\n },\n {\n originX: 'start',\n originY: 'bottom',\n overlayX: 'start',\n overlayY: 'bottom',\n },\n ]", "deprecated": false, "deprecationMessage": "", "type": "[]", @@ -35721,7 +45579,7 @@ "type": "OuiSelectTrigger", "optional": false, "description": "

User-supplied override of the trigger element.

\n", - "line": 414, + "line": 416, "rawdescription": "\nUser-supplied override of the trigger element.", "decorators": [ { @@ -35758,7 +45616,7 @@ "type": "NgControl", "optional": false, "description": "", - "line": 551, + "line": 553, "decorators": [ { "name": "Self", @@ -35780,7 +45638,7 @@ "type": "QueryList", "optional": false, "description": "

All of the defined groups of options.

\n", - "line": 410, + "line": 412, "rawdescription": "\nAll of the defined groups of options.", "decorators": [ { @@ -35807,7 +45665,7 @@ }, { "name": "optionSelectionChanges", - "defaultValue": "defer(\r\n (): Observable => {\r\n if (this.options) {\r\n return merge(...this.options.map((option) => option.onSelectionChange));\r\n }\r\n\r\n return this._ngZone.onStable.asObservable().pipe(\r\n take(1),\r\n switchMap(() => this.optionSelectionChanges)\r\n );\r\n }\r\n )", + "defaultValue": "defer(\n (): Observable => {\n if (this.options) {\n return merge(...this.options.map((option) => option.onSelectionChange));\n }\n\n return this._ngZone.onStable.asObservable().pipe(\n take(1),\n switchMap(() => this.optionSelectionChanges)\n );\n }\n )", "deprecated": false, "deprecationMessage": "", "type": "Observable", @@ -35836,7 +45694,7 @@ "type": "CdkConnectedOverlay", "optional": false, "description": "

Overlay pane containing the options.

\n", - "line": 421, + "line": 423, "rawdescription": "\nOverlay pane containing the options.", "decorators": [ { @@ -35885,7 +45743,7 @@ "optional": false, "returnType": "string | null", "typeParameters": [], - "line": 1208, + "line": 1229, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nDetermines the `aria-activedescendant` to be set on the host.", @@ -35897,7 +45755,7 @@ "optional": false, "returnType": "string | null", "typeParameters": [], - "line": 1192, + "line": 1213, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nReturns the aria-label of the select component.", @@ -35909,7 +45767,7 @@ "optional": false, "returnType": "string | null", "typeParameters": [], - "line": 1199, + "line": 1220, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nReturns the aria-labelledby of the select component.", @@ -35921,7 +45779,7 @@ "optional": false, "returnType": "string", "typeParameters": [], - "line": 943, + "line": 945, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nReturns the theme to be used on the panel.", @@ -35933,7 +45791,7 @@ "optional": false, "returnType": "number", "typeParameters": [], - "line": 1334, + "line": 1355, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nReturns the panel's scrollTop.", @@ -35952,7 +45810,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 781, + "line": 783, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nHandles keyboard events while the select is closed.", @@ -35985,7 +45843,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 772, + "line": 774, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nHandles all keydown events on the select.", @@ -36015,7 +45873,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 811, + "line": 813, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nHandles keyboard events when the selected is open.", @@ -36041,7 +45899,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 1176, + "line": 1181, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nHighlights the selected item. If no option is selected, it will highlight\nthe first item instead.\n", @@ -36050,13 +45908,28 @@ 121 ] }, + { + "name": "_highlightFirstFilteredOption", + "args": [], + "optional": false, + "returnType": "void", + "typeParameters": [], + "line": 1196, + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\n\nHighlights the first of the filtered options if no element is currently highlighted\n", + "description": "

Highlights the first of the filtered options if no element is currently highlighted

\n", + "modifierKind": [ + 121 + ] + }, { "name": "_initializeSelection", "args": [], "optional": false, "returnType": "void", "typeParameters": [], - "line": 966, + "line": 968, "deprecated": false, "deprecationMessage": "", "modifierKind": [ @@ -36069,7 +45942,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 1029, + "line": 1034, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nSets up a key manager to listen to keyboard events on the overlay panel.", @@ -36084,7 +45957,7 @@ "optional": false, "returnType": "boolean", "typeParameters": [], - "line": 767, + "line": 769, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nWhether the element is in RTL mode.", @@ -36096,7 +45969,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 934, + "line": 936, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nCallback that is invoked when the overlay panel has been attached.\n", @@ -36108,7 +45981,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 920, + "line": 922, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nCalls the touched callback only if the panel is closed. Otherwise, the trigger will\n\"blur\" to the panel when it opens, causing a false positive.\n", @@ -36120,7 +45993,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 909, + "line": 911, "deprecated": false, "deprecationMessage": "" }, @@ -36143,7 +46016,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 1085, + "line": 1090, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nInvoked when an option is clicked.", @@ -36186,7 +46059,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 1147, + "line": 1152, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nEmits change event to set the model value.", @@ -36213,7 +46086,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 1058, + "line": 1063, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nDrops current option subscriptions and IDs and resets from scratch.", @@ -36228,7 +46101,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 1305, + "line": 1326, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nGiven that we are not actually focusing active options, we must manually adjust scroll\nto reveal options below the fold. First, we find the offset of the option from the top\nof the panel. If that offset is below the fold, the new scrollTop will be the offset -\nthe panel height + the option height, so the active option will be just visible at the\nbottom of the panel. If that offset is above the top of the visible panel, the new scrollTop\nwill become the offset. If that offset is visible within the panel already, the scrollTop is\nnot adjusted.\n", @@ -36250,7 +46123,7 @@ "optional": false, "returnType": "OuiOption | undefined", "typeParameters": [], - "line": 1007, + "line": 1012, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nFinds and selects and option based on its value.\n\n", @@ -36270,8 +46143,8 @@ }, { "tagName": { - "pos": 30668, - "end": 30675, + "pos": 29753, + "end": 29760, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -36288,7 +46161,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 1168, + "line": 1173, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nRecords option IDs to pass to the aria-owns property.", @@ -36303,7 +46176,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 949, + "line": 951, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nSets the pseudo checkbox padding size based on the width of the pseudo checkbox.", @@ -36325,7 +46198,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 1327, + "line": 1348, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nSets the panel scrollTop. This allows us to manually scroll to display options\nabove or below the fold, as they are not actually being focused when active.\n", @@ -36355,7 +46228,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 981, + "line": 986, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nSets the selected option based on a value. If no option can be\nfound with the designated value, the select trigger is cleared.\n", @@ -36381,7 +46254,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 1133, + "line": 1138, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nSorts the selected values in the selected based on their order in the panel.", @@ -36396,7 +46269,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 676, + "line": 678, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nCloses the overlay panel and focuses the host element.", @@ -36408,7 +46281,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 1120, + "line": 1125, "deprecated": false, "deprecationMessage": "" }, @@ -36418,7 +46291,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 1126, + "line": 1131, "deprecated": false, "deprecationMessage": "" }, @@ -36428,7 +46301,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 1187, + "line": 1208, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nFocuses the select element.", @@ -36440,7 +46313,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 857, + "line": 859, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nHandle ctrl key\n", @@ -36480,7 +46353,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 875, + "line": 877, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\n", @@ -36491,8 +46364,8 @@ "jsdoctags": [ { "name": { - "pos": 26531, - "end": 26538, + "pos": 25670, + "end": 25677, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -36503,8 +46376,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 26525, - "end": 26530, + "pos": 25664, + "end": 25669, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -36515,8 +46388,8 @@ }, { "name": { - "pos": 26552, - "end": 26557, + "pos": 25690, + "end": 25695, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -36527,8 +46400,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 26546, - "end": 26551, + "pos": 25684, + "end": 25689, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -36539,8 +46412,8 @@ }, { "name": { - "pos": 26571, - "end": 26581, + "pos": 25708, + "end": 25718, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -36551,8 +46424,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 26565, - "end": 26570, + "pos": 25702, + "end": 25707, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -36563,8 +46436,8 @@ }, { "name": { - "pos": 26595, - "end": 26602, + "pos": 25731, + "end": 25738, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -36575,8 +46448,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 26589, - "end": 26594, + "pos": 25725, + "end": 25730, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -36593,7 +46466,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 606, + "line": 608, "deprecated": false, "deprecationMessage": "" }, @@ -36603,7 +46476,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 624, + "line": 626, "deprecated": false, "deprecationMessage": "" }, @@ -36620,7 +46493,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 630, + "line": 632, "deprecated": false, "deprecationMessage": "", "jsdoctags": [ @@ -36641,7 +46514,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 638, + "line": 640, "deprecated": false, "deprecationMessage": "" }, @@ -36651,7 +46524,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 585, + "line": 587, "deprecated": false, "deprecationMessage": "" }, @@ -36661,7 +46534,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 1230, + "line": 1251, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nImplemented as part of OuiFormFieldControl.\n\n", @@ -36674,7 +46547,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 652, + "line": 654, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nOpens the overlay panel.", @@ -36686,7 +46559,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 1255, + "line": 1276, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nCustom overlay class for cdk overlay container\n", @@ -36698,7 +46571,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 1248, + "line": 1269, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nAdd outer class to perfect scrollbar\nThis is added only when there is a search field\n", @@ -36725,7 +46598,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 709, + "line": 711, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nSaves a callback function to be invoked when the select's value\nchanges from user input. Part of the ControlValueAccessor interface\nrequired to integrate with Angular's core forms API.\n\n", @@ -36733,8 +46606,8 @@ "jsdoctags": [ { "name": { - "pos": 21611, - "end": 21613, + "pos": 20913, + "end": 20915, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -36753,8 +46626,8 @@ } ], "tagName": { - "pos": 21605, - "end": 21610, + "pos": 20907, + "end": 20912, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -36779,7 +46652,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 720, + "line": 722, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nSaves a callback function to be invoked when the select is blurred\nby the user. Part of the ControlValueAccessor interface required\nto integrate with Angular's core forms API.\n\n", @@ -36787,8 +46660,8 @@ "jsdoctags": [ { "name": { - "pos": 21977, - "end": 21979, + "pos": 21268, + "end": 21270, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -36800,8 +46673,8 @@ "deprecationMessage": "", "function": [], "tagName": { - "pos": 21971, - "end": 21976, + "pos": 21262, + "end": 21267, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -36825,7 +46698,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 1281, + "line": 1302, "deprecated": false, "deprecationMessage": "", "jsdoctags": [ @@ -36853,7 +46726,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 1221, + "line": 1242, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nImplemented as part of OuiFormFieldControl.\n\n", @@ -36883,7 +46756,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 730, + "line": 732, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nDisables the select. Part of the ControlValueAccessor interface required\nto integrate with Angular's core forms API.\n\n", @@ -36891,8 +46764,8 @@ "jsdoctags": [ { "name": { - "pos": 22281, - "end": 22291, + "pos": 21562, + "end": 21572, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -36903,8 +46776,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 22275, - "end": 22280, + "pos": 21556, + "end": 21561, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -36921,7 +46794,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 647, + "line": 649, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nToggles the overlay panel open or closed.", @@ -36940,7 +46813,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 696, + "line": 698, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nSets the select's value. Part of the ControlValueAccessor interface\nrequired to integrate with Angular's core forms API.\n\n", @@ -36948,8 +46821,8 @@ "jsdoctags": [ { "name": { - "pos": 21217, - "end": 21222, + "pos": 20532, + "end": 20537, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -36960,8 +46833,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 21211, - "end": 21216, + "pos": 20526, + "end": 20531, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -36980,11 +46853,11 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import { ActiveDescendantKeyManager, FocusMonitor } from '@angular/cdk/a11y';\r\nimport { Directionality } from '@angular/cdk/bidi';\r\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\r\nimport { SelectionModel } from '@angular/cdk/collections';\r\nimport {\r\n A,\r\n DOWN_ARROW,\r\n END,\r\n ENTER,\r\n HOME,\r\n LEFT_ARROW,\r\n RIGHT_ARROW,\r\n SPACE,\r\n UP_ARROW,\r\n hasModifierKey,\r\n} from '@angular/cdk/keycodes';\r\nimport { CdkConnectedOverlay } from '@angular/cdk/overlay';\r\nimport {\r\n AfterContentInit,\r\n Attribute,\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n ContentChild,\r\n ContentChildren,\r\n Directive,\r\n DoCheck,\r\n ElementRef,\r\n EventEmitter,\r\n Input,\r\n isDevMode,\r\n NgZone,\r\n OnChanges,\r\n OnDestroy,\r\n OnInit,\r\n Optional,\r\n Output,\r\n QueryList,\r\n Self,\r\n SimpleChanges,\r\n ViewChild,\r\n ViewEncapsulation,\r\n Inject,\r\n} from '@angular/core';\r\nimport {\r\n ControlValueAccessor,\r\n FormGroupDirective,\r\n NgControl,\r\n NgForm,\r\n} from '@angular/forms';\r\nimport {\r\n _countGroupLabelsBeforeOption,\r\n _getOptionScrollPosition,\r\n CanDisable,\r\n CanDisableCtor,\r\n CanUpdateErrorState,\r\n CanUpdateErrorStateCtor,\r\n HasTabIndex,\r\n HasTabIndexCtor,\r\n OuiOptionSelectionChange,\r\n mixinErrorState,\r\n mixinTabIndex,\r\n mixinDisabled,\r\n} from '../core';\r\nimport { OuiFormField, OuiFormFieldControl } from '../form-field/public-api';\r\nimport { DOCUMENT } from '@angular/common';\r\nimport { OUI_OPTION_PARENT_COMPONENT, OuiOption } from '../core/option/option';\r\nimport { OuiOptgroup } from '../core/option/optgroup';\r\nimport { ErrorStateMatcher } from '../core/error/error-options';\r\nimport { defer, merge, Observable, Subject } from 'rxjs';\r\nimport {\r\n distinctUntilChanged,\r\n filter,\r\n map,\r\n startWith,\r\n switchMap,\r\n take,\r\n takeUntil,\r\n} from 'rxjs/operators';\r\nimport {\r\n getOuiSelectDynamicMultipleError,\r\n getOuiSelectNonArrayValueError,\r\n getOuiSelectNonFunctionValueError,\r\n} from './select-errors';\r\nimport { OuiIconRegistry } from '../icon/icon-registery';\r\nimport { DomSanitizer } from '@angular/platform-browser';\r\nimport { ICONS } from '../core/shared/icons';\r\n\r\nlet nextUniqueId = 0;\r\n\r\n/**\r\n * The following style constants are necessary to save here in order\r\n * to properly calculate the alignment of the selected option over\r\n * the trigger element.\r\n */\r\n\r\n/** The height of each select option. */\r\nexport const SELECT_OPTION_HEIGHT = 40;\r\n\r\n/** The panel's padding on the x-axis */\r\nexport const SELECT_PANEL_PADDING_X = 16;\r\n\r\n/** The panel's x axis padding if it is indented (e.g. there is an option group). */\r\nexport const SELECT_PANEL_INDENT_PADDING_X = SELECT_PANEL_PADDING_X * 2;\r\n\r\n/** The height of the select items in `em` units. */\r\nexport const SELECT_ITEM_HEIGHT_EM = 3;\r\n\r\n/** The total height of the select panel. */\r\nexport const SELECT_PANEL_HEIGHT = 200;\r\n\r\n// TODO(josephperrott): Revert to a constant after 2018 spec updates are fully merged.\r\n/**\r\n * Distance between the panel edge and the option text in\r\n * multi-selection mode.\r\n *\r\n * Calculated as:\r\n * (SELECT_PANEL_PADDING_X * 1.5) + 20 = 44\r\n * The padding is multiplied by 1.5 because the checkbox's margin is half the padding.\r\n * The checkbox width is 16px.\r\n */\r\nexport let SELECT_MULTIPLE_PANEL_PADDING_X = 0;\r\n\r\n/**\r\n * The select panel will only \"fit\" inside the viewport if it is positioned at\r\n * this value or more away from the viewport boundary.\r\n */\r\nexport const SELECT_PANEL_VIEWPORT_PADDING = 8;\r\n\r\n/** Change event object that is emitted when the select value has changed. */\r\nexport class OuiSelectChange {\r\n constructor(\r\n /** Reference to the select that emitted the change event. */\r\n public source: OuiSelect,\r\n /** Current value of the select that emitted the event. */\r\n public value: any\r\n ) {}\r\n}\r\n\r\n// Boilerplate for applying mixins to OuiSelect.\r\n/** @docs-private */\r\nexport class OuiSelectBase {\r\n constructor(\r\n public _elementRef: ElementRef,\r\n public _defaultErrorStateMatcher: ErrorStateMatcher,\r\n public _parentForm: NgForm,\r\n public _parentFormGroup: FormGroupDirective,\r\n public ngControl: NgControl\r\n ) {}\r\n}\r\n\r\nexport const _OuiSelectMixinBase: CanDisableCtor &\r\n HasTabIndexCtor &\r\n CanUpdateErrorStateCtor &\r\n typeof OuiSelectBase = mixinTabIndex(\r\n mixinDisabled(mixinErrorState(OuiSelectBase))\r\n);\r\n\r\n/**\r\n * Allows the user to customize the trigger that is displayed when the select has a value.\r\n */\r\n@Directive({\r\n // eslint-disable-next-line @angular-eslint/directive-selector\r\n selector: 'oui-select-trigger',\r\n})\r\nexport class OuiSelectTrigger {}\r\n\r\n@Component({\r\n selector: 'oui-select',\r\n exportAs: 'ouiSelect',\r\n templateUrl: 'select.html',\r\n styleUrls: ['select.scss'],\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['disabled', 'tabIndex'],\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n role: 'listbox',\r\n '[attr.id]': 'id',\r\n '[attr.tabindex]': 'tabIndex',\r\n '[attr.aria-label]': '_getAriaLabel()',\r\n '[attr.aria-labelledby]': '_getAriaLabelledby()',\r\n '[attr.aria-required]': 'required.toString()',\r\n '[attr.aria-disabled]': 'disabled.toString()',\r\n '[attr.aria-invalid]': 'errorState',\r\n '[attr.aria-owns]': 'panelOpen ? _optionIds : null',\r\n '[attr.aria-multiselectable]': 'multiple',\r\n '[attr.aria-describedby]': '_ariaDescribedby || null',\r\n '[attr.aria-activedescendant]': '_getAriaActiveDescendant()',\r\n '[class.oui-select-disabled]': 'disabled',\r\n '[class.oui-select-invalid]': 'errorState',\r\n '[class.oui-select-required]': 'required',\r\n '[class.oui-select-empty]': 'empty',\r\n class: 'oui-select oui-input',\r\n '(keydown)': '_handleKeydown($event)',\r\n '(focus)': '_onFocus()',\r\n '(blur)': '_onBlur()',\r\n },\r\n providers: [\r\n { provide: OuiFormFieldControl, useExisting: OuiSelect },\r\n { provide: OUI_OPTION_PARENT_COMPONENT, useExisting: OuiSelect },\r\n ],\r\n})\r\nexport class OuiSelect\r\n extends _OuiSelectMixinBase\r\n implements\r\n AfterContentInit,\r\n OnChanges,\r\n OnDestroy,\r\n OnInit,\r\n DoCheck,\r\n ControlValueAccessor,\r\n CanDisable,\r\n HasTabIndex,\r\n OuiFormFieldControl,\r\n CanUpdateErrorState\r\n{\r\n /**Holds selected values after done */\r\n @Input() savedValues = [];\r\n /**Done button disabled until dropdown is dirty */\r\n disableDoneButton = true;\r\n /** Whether or not the overlay panel is open. */\r\n private _panelOpen = false;\r\n\r\n /** Whether filling out the select is required in the form. */\r\n private _required = false;\r\n\r\n /** Whether filling out the select is required in the form. */\r\n private _actionItems = false;\r\n\r\n /** The scroll position of the overlay panel, calculated to center the selected option. */\r\n private _scrollTop = 0;\r\n\r\n /** The placeholder displayed in the trigger of the select. */\r\n private _placeholder: string;\r\n\r\n /** Whether the component is in multiple selection mode. */\r\n private _multiple = false;\r\n\r\n /** Search input field **/\r\n isSearchFieldPresent: boolean;\r\n\r\n /** Unique id for this input. */\r\n private _uid = `oui-select-${nextUniqueId++}`;\r\n\r\n /** The last measured value for the trigger's client bounding rect. */\r\n _triggerRect: ClientRect;\r\n\r\n /** The aria-describedby attribute on the select for improved a11y. */\r\n _ariaDescribedby: string;\r\n\r\n /** The cached font-size of the trigger element. */\r\n _triggerFontSize = 0;\r\n\r\n /** Deals with the selection logic. */\r\n _selectionModel: SelectionModel;\r\n\r\n /** Manages keyboard events for options in the panel. */\r\n _keyManager: ActiveDescendantKeyManager;\r\n\r\n /** The IDs of child options to be passed to the aria-owns attribute. */\r\n _optionIds = '';\r\n\r\n /** The value of the select panel's transform-origin property. */\r\n _transformOrigin = 'top';\r\n\r\n /** If there is search input field a class is added dynamically to the perfect scrollbar **/\r\n ouiSelectInputOuterClassName: string;\r\n\r\n /** Adding top class to overlay panel */\r\n cdkConnectionOverlayPanel = '';\r\n\r\n /**\r\n * The y-offset of the overlay panel in relation to the trigger's top start corner.\r\n * This must be adjusted to align the selected option text over the trigger text.\r\n * when the panel opens. Will change based on the y-position of the selected option.\r\n */\r\n _offsetY = 0;\r\n\r\n /**\r\n * This position config ensures that the top \"start\" corner of the overlay\r\n * is aligned with with the top \"start\" of the origin by default (overlapping\r\n * the trigger completely). If the panel cannot fit below the trigger, it\r\n * will fall back to a position above the trigger.\r\n */\r\n _positions = [\r\n {\r\n originX: 'start',\r\n originY: 'top',\r\n overlayX: 'start',\r\n overlayY: 'top',\r\n },\r\n {\r\n originX: 'start',\r\n originY: 'bottom',\r\n overlayX: 'start',\r\n overlayY: 'bottom',\r\n },\r\n ];\r\n /** Emits whenever the component is destroyed. */\r\n private readonly _destroy = new Subject();\r\n\r\n /** Whether the component is disabling centering of the active option over the trigger. */\r\n private _disableOptionCentering = false;\r\n\r\n private _focused = false;\r\n\r\n /** A name for this control that can be used by `oui-form-field`. */\r\n controlType = 'oui-select';\r\n\r\n /** Trigger that opens the select. */\r\n @ViewChild('trigger') trigger: ElementRef;\r\n\r\n /** Panel containing the select options. */\r\n @ViewChild('panel', { read: ElementRef }) panel: ElementRef;\r\n\r\n private _value: any;\r\n\r\n /**\r\n * Function used to sort the values in a select in multiple mode.\r\n * Follows the same logic as `Array.prototype.sort`.\r\n */\r\n @Input() sortComparator: (\r\n a: OuiOption,\r\n b: OuiOption,\r\n options: OuiOption[]\r\n ) => number;\r\n\r\n /** Aria label of the select. If not specified, the placeholder will be used as label. */\r\n @Input('aria-label') ariaLabel = '';\r\n\r\n /** Input that can be used to specify the `aria-labelledby` attribute. */\r\n @Input('aria-labelledby') ariaLabelledby: string;\r\n private _large = false;\r\n _monitorSubscription: any;\r\n\r\n /** Whether the oui-select is of large size. */\r\n @Input()\r\n get large(): boolean {\r\n return this._large;\r\n }\r\n set large(value) {\r\n this._large = coerceBooleanProperty(value);\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n\r\n private _id: string;\r\n\r\n /** Event emitted when the select panel has been toggled. */\r\n @Output()\r\n readonly openedChange: EventEmitter = new EventEmitter();\r\n\r\n /** Combined stream of all of the child options' change events. */\r\n readonly optionSelectionChanges: Observable = defer(\r\n (): Observable => {\r\n if (this.options) {\r\n return merge(...this.options.map((option) => option.onSelectionChange));\r\n }\r\n\r\n return this._ngZone.onStable.asObservable().pipe(\r\n take(1),\r\n switchMap(() => this.optionSelectionChanges)\r\n );\r\n }\r\n );\r\n\r\n /**\r\n * Event that emits whenever the raw value of the select changes. This is here primarily\r\n * to facilitate the two-way binding for the `value` input.\r\n *\r\n * @docs-private\r\n */\r\n @Output() readonly valueChange: EventEmitter = new EventEmitter();\r\n\r\n /** Object used to control when error messages are shown. */\r\n @Input() errorStateMatcher: ErrorStateMatcher;\r\n\r\n /** All of the defined select options. */\r\n @ContentChildren(OuiOption, { descendants: true })\r\n options: QueryList;\r\n\r\n /** Event emitted when the select has been opened. */\r\n // eslint-disable-next-line @angular-eslint/no-output-rename\r\n @Output('opened')\r\n readonly _openedStream: Observable = this.openedChange.pipe(\r\n filter((o) => o),\r\n map(() => {})\r\n );\r\n\r\n /** Event emitted when the select has been closed. */\r\n // eslint-disable-next-line @angular-eslint/no-output-rename\r\n @Output('closed')\r\n readonly _closedStream: Observable = this.openedChange.pipe(\r\n filter((o) => !o),\r\n map(() => {\r\n this.isSearchFieldPresent = false;\r\n })\r\n );\r\n\r\n /** Event emitted when the selected value has been changed by the user. */\r\n @Output()\r\n readonly selectionChange: EventEmitter = new EventEmitter();\r\n\r\n /** Event emitted when the selected value has been changed and saved by the user. */\r\n @Output()\r\n readonly saveSelectionChange: EventEmitter = new EventEmitter();\r\n\r\n /** All of the defined groups of options. */\r\n @ContentChildren(OuiOptgroup) optionGroups: QueryList;\r\n\r\n /** User-supplied override of the trigger element. */\r\n @ContentChild(OuiSelectTrigger)\r\n customTrigger: OuiSelectTrigger;\r\n\r\n /** Classes to be passed to the select panel. Supports the same syntax as `ngClass`. */\r\n @Input() panelClass: string | string[] | Set | { [key: string]: any };\r\n\r\n /** Overlay pane containing the options. */\r\n @ViewChild(CdkConnectedOverlay)\r\n overlayDir: CdkConnectedOverlay;\r\n\r\n /** Emits when the panel element is finished transforming in. */\r\n _panelDoneAnimatingStream = new Subject();\r\n\r\n /** Comparison function to specify which option is displayed. Defaults to object equality. */\r\n private _compareWith = (o1: any, o2: any) => o1 === o2;\r\n\r\n /** Whether the select is focused. */\r\n get focused(): boolean {\r\n return this._focused || this._panelOpen;\r\n }\r\n /**\r\n * @deprecated Setter to be removed as this property is intended to be readonly.\r\n */\r\n set focused(value: boolean) {\r\n this._focused = value;\r\n }\r\n /** `View -> model callback called when value changes` */\r\n _onChange: (value: any) => void = () => {};\r\n\r\n /** `View -> model callback called when select has been touched` */\r\n _onTouched = () => {};\r\n\r\n /** Placeholder to be shown if no value has been selected. */\r\n @Input()\r\n get placeholder(): string {\r\n return this._placeholder;\r\n }\r\n set placeholder(value: string) {\r\n this._placeholder = value;\r\n this.stateChanges.next();\r\n }\r\n\r\n /** Whether the component is required. */\r\n @Input()\r\n get required(): boolean {\r\n return this._required;\r\n }\r\n set required(value: boolean) {\r\n this._required = coerceBooleanProperty(value);\r\n this.stateChanges.next();\r\n }\r\n\r\n /** Whether the user should be allowed to select multiple options. */\r\n @Input()\r\n get multiple(): boolean {\r\n return this._multiple;\r\n }\r\n set multiple(value: boolean) {\r\n if (this._selectionModel) {\r\n throw getOuiSelectDynamicMultipleError();\r\n }\r\n\r\n this._multiple = coerceBooleanProperty(value);\r\n }\r\n\r\n /** Whether the action items are required and use saveSelectionChange instead of selectionChange. */\r\n @Input()\r\n get actionItems(): boolean {\r\n return this._actionItems;\r\n }\r\n set actionItems(value: boolean) {\r\n if (this._multiple) {\r\n this._actionItems = coerceBooleanProperty(value);\r\n this.stateChanges.next();\r\n }\r\n }\r\n\r\n /** Whether to center the active option over the trigger. */\r\n @Input()\r\n get disableOptionCentering(): boolean {\r\n return this._disableOptionCentering;\r\n }\r\n set disableOptionCentering(value: boolean) {\r\n this._disableOptionCentering = coerceBooleanProperty(value);\r\n }\r\n\r\n /**\r\n * Function to compare the option values with the selected values. The first argument\r\n * is a value from an option. The second is a value from the selection. A boolean\r\n * should be returned.\r\n */\r\n @Input()\r\n get compareWith() {\r\n return this._compareWith;\r\n }\r\n set compareWith(fn: (o1: any, o2: any) => boolean) {\r\n if (typeof fn !== 'function') {\r\n throw getOuiSelectNonFunctionValueError();\r\n }\r\n this._compareWith = fn;\r\n if (this._selectionModel) {\r\n // A different comparator means the selection could change.\r\n this._initializeSelection();\r\n }\r\n }\r\n\r\n /** Value of the select control. */\r\n @Input()\r\n get value(): any {\r\n return this._value;\r\n }\r\n set value(newValue: any) {\r\n if (newValue !== this._value) {\r\n this.writeValue(newValue);\r\n this._value = newValue;\r\n }\r\n }\r\n\r\n /** Unique id of the element. */\r\n @Input()\r\n get id(): string {\r\n return this._id;\r\n }\r\n set id(value: string) {\r\n this._id = value || this._uid;\r\n this.stateChanges.next();\r\n }\r\n\r\n constructor(\r\n private _changeDetectorRef: ChangeDetectorRef,\r\n private _ngZone: NgZone,\r\n _defaultErrorStateMatcher: ErrorStateMatcher,\r\n elementRef: ElementRef,\r\n private _focusMonitor: FocusMonitor,\r\n @Optional() private _dir: Directionality,\r\n @Optional() _parentForm: NgForm,\r\n @Optional() _parentFormGroup: FormGroupDirective,\r\n @Optional() private _parentFormField: OuiFormField,\r\n @Self() @Optional() public ngControl: NgControl,\r\n @Attribute('tabindex') tabIndex: string,\r\n @Optional() @Inject(DOCUMENT) private _document: any,\r\n public _elementRef: ElementRef,\r\n public _ouiIconRegistry: OuiIconRegistry,\r\n private _domSanitizer: DomSanitizer\r\n ) {\r\n super(\r\n elementRef,\r\n _defaultErrorStateMatcher,\r\n _parentForm,\r\n _parentFormGroup,\r\n ngControl\r\n );\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this._elementRef, true)\r\n .subscribe(() => this._ngZone.run(() => {}));\r\n this._ouiIconRegistry.addSvgIconLiteral(\r\n `select-arrow-icon`,\r\n this._domSanitizer.bypassSecurityTrustHtml(ICONS.SELECT_ARROW_ICON)\r\n );\r\n\r\n if (this.ngControl) {\r\n // Note: we provide the value accessor through here, instead of\r\n // the `providers` to avoid running into a circular import.\r\n this.ngControl.valueAccessor = this;\r\n }\r\n\r\n this.tabIndex = parseInt(tabIndex, 10) || 0;\r\n\r\n // Force setter to be called in case id was not specified.\r\n this.id = this.id;\r\n }\r\n\r\n ngOnInit() {\r\n this._selectionModel = new SelectionModel(this.multiple);\r\n this.stateChanges.next();\r\n\r\n // We need `distinctUntilChanged` here, because some browsers will\r\n // fire the animation end event twice for the same animation. See:\r\n // https://github.com/angular/angular/issues/24084\r\n this._panelDoneAnimatingStream\r\n .pipe(distinctUntilChanged(), takeUntil(this._destroy))\r\n .subscribe(() => {\r\n if (this.panelOpen) {\r\n this._scrollTop = 0;\r\n this.openedChange.emit(true);\r\n } else {\r\n this.openedChange.emit(false);\r\n this.overlayDir.offsetX = 0;\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n });\r\n }\r\n\r\n ngAfterContentInit() {\r\n this._initKeyManager();\r\n\r\n this._selectionModel.changed\r\n .pipe(takeUntil(this._destroy))\r\n .subscribe((event) => {\r\n event.added.forEach((option) => option.select());\r\n event.removed.forEach((option) => option.deselect());\r\n });\r\n\r\n this.options.changes\r\n .pipe(startWith(null), takeUntil(this._destroy))\r\n .subscribe(() => {\r\n this._resetOptions();\r\n this._initializeSelection();\r\n });\r\n }\r\n\r\n ngDoCheck() {\r\n if (this.ngControl) {\r\n this.updateErrorState();\r\n }\r\n }\r\n\r\n ngOnChanges(changes: SimpleChanges) {\r\n // Updating the disabled state is handled by `mixinDisabled`, but we need to additionally let\r\n // the parent form field know to run change detection when the disabled state changes.\r\n if (changes.disabled) {\r\n this.stateChanges.next();\r\n }\r\n }\r\n\r\n ngOnDestroy() {\r\n this._monitorSubscription.unsubscribe();\r\n this._focusMonitor.stopMonitoring(this._elementRef);\r\n this._destroy.next();\r\n this._destroy.complete();\r\n this.stateChanges.complete();\r\n }\r\n\r\n /** Toggles the overlay panel open or closed. */\r\n toggle(): void {\r\n this.panelOpen ? this.close() : this.open();\r\n }\r\n\r\n /** Opens the overlay panel. */\r\n open(): void {\r\n if (\r\n this.disabled ||\r\n !this.options ||\r\n !this.options.length ||\r\n this._panelOpen\r\n ) {\r\n return;\r\n }\r\n\r\n this._triggerRect = this.trigger.nativeElement.getBoundingClientRect();\r\n\r\n this._panelOpen = true;\r\n this._keyManager.withHorizontalOrientation(null);\r\n\r\n this._highlightCorrectOption();\r\n this._changeDetectorRef.markForCheck();\r\n this.openedChange.emit(true);\r\n this._elementRef.nativeElement.classList.add(\r\n 'oui-select-list-options-opened'\r\n );\r\n }\r\n\r\n /** Closes the overlay panel and focuses the host element. */\r\n close(): void {\r\n if (this._panelOpen) {\r\n this._panelOpen = false;\r\n this._keyManager.withHorizontalOrientation(this._isRtl() ? 'rtl' : 'ltr');\r\n this._changeDetectorRef.markForCheck();\r\n this._onTouched();\r\n this.openedChange.emit(false);\r\n this._elementRef.nativeElement.classList.remove(\r\n 'oui-select-list-options-opened'\r\n );\r\n setTimeout((_) => this._document.activeElement.blur());\r\n }\r\n }\r\n\r\n /**\r\n * Sets the select's value. Part of the ControlValueAccessor interface\r\n * required to integrate with Angular's core forms API.\r\n *\r\n * @param value New value to be written to the model.\r\n */\r\n writeValue(value: any): void {\r\n if (this.options) {\r\n this._setSelectionByValue(value);\r\n }\r\n }\r\n\r\n /**\r\n * Saves a callback function to be invoked when the select's value\r\n * changes from user input. Part of the ControlValueAccessor interface\r\n * required to integrate with Angular's core forms API.\r\n *\r\n * @param fn Callback to be triggered when the value changes.\r\n */\r\n registerOnChange(fn: (value: any) => void): void {\r\n this._onChange = fn;\r\n }\r\n\r\n /**\r\n * Saves a callback function to be invoked when the select is blurred\r\n * by the user. Part of the ControlValueAccessor interface required\r\n * to integrate with Angular's core forms API.\r\n *\r\n * @param fn Callback to be triggered when the component has been touched.\r\n */\r\n registerOnTouched(fn: () => {}): void {\r\n this._onTouched = fn;\r\n }\r\n\r\n /**\r\n * Disables the select. Part of the ControlValueAccessor interface required\r\n * to integrate with Angular's core forms API.\r\n *\r\n * @param isDisabled Sets whether the component is disabled.\r\n */\r\n setDisabledState(isDisabled: boolean): void {\r\n this.disabled = isDisabled;\r\n this._changeDetectorRef.markForCheck();\r\n this.stateChanges.next();\r\n }\r\n\r\n /** Whether or not the overlay panel is open. */\r\n get panelOpen(): boolean {\r\n return this._panelOpen;\r\n }\r\n\r\n /** The currently selected option. */\r\n get selected(): OuiOption | OuiOption[] {\r\n return this.multiple\r\n ? this._selectionModel.selected\r\n : this._selectionModel.selected[0];\r\n }\r\n\r\n /** The value displayed in the trigger. */\r\n get triggerValue(): string {\r\n if (this.empty) {\r\n return '';\r\n }\r\n if (this._multiple) {\r\n const selectedOptions = this._selectionModel.selected.map(\r\n (option) => option.viewValueForSelect\r\n );\r\n\r\n if (this._isRtl()) {\r\n selectedOptions.reverse();\r\n }\r\n return selectedOptions.join(', ');\r\n }\r\n return this._selectionModel.selected[0].viewValueForSelect;\r\n }\r\n\r\n /** Whether the element is in RTL mode. */\r\n _isRtl(): boolean {\r\n return this._dir ? this._dir.value === 'rtl' : false;\r\n }\r\n\r\n /** Handles all keydown events on the select. */\r\n _handleKeydown(event: KeyboardEvent): void {\r\n if (!this.disabled) {\r\n this.panelOpen\r\n ? this._handleOpenKeydown(event)\r\n : this._handleClosedKeydown(event);\r\n }\r\n }\r\n\r\n /** Handles keyboard events while the select is closed. */\r\n private _handleClosedKeydown(event: KeyboardEvent): void {\r\n const keyCode = event.keyCode;\r\n const isArrowKey =\r\n keyCode === DOWN_ARROW ||\r\n keyCode === UP_ARROW ||\r\n keyCode === LEFT_ARROW ||\r\n keyCode === RIGHT_ARROW;\r\n const isOpenKey = keyCode === ENTER || keyCode === SPACE;\r\n const manager = this._keyManager;\r\n\r\n // Open the select on ALT + arrow key to match the native \r\n event.preventDefault();\r\n this.close();\r\n } else if (\r\n (keyCode === ENTER || keyCode === SPACE) &&\r\n manager.activeItem &&\r\n !hasModifierKey(event)\r\n ) {\r\n event.preventDefault();\r\n manager.activeItem._selectViaInteraction();\r\n } else if (this._multiple && keyCode === A && event.ctrlKey) {\r\n event.preventDefault();\r\n this.handleCtrlKey();\r\n } else {\r\n this.handleScrolling(manager, event, isArrowKey, keyCode);\r\n }\r\n }\r\n\r\n /**\r\n * Handle ctrl key\r\n */\r\n private handleCtrlKey() {\r\n const hasDeselectedOptions = this.options.some(\r\n (opt) => !opt.disabled && !opt.selected\r\n );\r\n\r\n this.options.forEach((option) => {\r\n if (!option.disabled) {\r\n hasDeselectedOptions ? option.select() : option.deselect();\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * @param manager\r\n * @param event\r\n * @param isArrowKey\r\n * @param keyCode\r\n */\r\n private handleScrolling(\r\n manager: ActiveDescendantKeyManager,\r\n event: KeyboardEvent,\r\n isArrowKey: boolean,\r\n keyCode: number\r\n ) {\r\n const previouslyFocusedIndex = manager.activeItemIndex;\r\n\r\n manager.onKeydown(event);\r\n\r\n if (\r\n this._multiple &&\r\n isArrowKey &&\r\n event.shiftKey &&\r\n manager.activeItem &&\r\n manager.activeItemIndex !== previouslyFocusedIndex\r\n ) {\r\n manager.activeItem._selectViaInteraction();\r\n }\r\n if (isArrowKey && manager.activeItemIndex !== previouslyFocusedIndex) {\r\n this._scrollToOption();\r\n } else {\r\n // First or last\r\n if (keyCode === DOWN_ARROW) {\r\n manager.setFirstItemActive();\r\n this._setScrollTop(0);\r\n }\r\n if (keyCode === UP_ARROW) {\r\n manager.setLastItemActive();\r\n this._scrollToOption();\r\n }\r\n }\r\n }\r\n\r\n _onFocus() {\r\n if (!this.disabled) {\r\n this._focused = true;\r\n this.stateChanges.next();\r\n }\r\n }\r\n\r\n /**\r\n * Calls the touched callback only if the panel is closed. Otherwise, the trigger will\r\n * \"blur\" to the panel when it opens, causing a false positive.\r\n */\r\n _onBlur() {\r\n this._focused = false;\r\n this.isSearchFieldPresent = false;\r\n\r\n if (!this.disabled && !this.panelOpen) {\r\n this._onTouched();\r\n this._changeDetectorRef.markForCheck();\r\n this.stateChanges.next();\r\n }\r\n }\r\n\r\n /**\r\n * Callback that is invoked when the overlay panel has been attached.\r\n */\r\n _onAttached(): void {\r\n this.overlayDir.positionChange.pipe(take(1)).subscribe(() => {\r\n this._setPseudoCheckboxPaddingSize();\r\n this._changeDetectorRef.detectChanges();\r\n this.panel.nativeElement.scrollTop = this._scrollTop;\r\n });\r\n }\r\n\r\n /** Returns the theme to be used on the panel. */\r\n _getPanelTheme(): string {\r\n return this._parentFormField ? `oui-${this._parentFormField.color}` : '';\r\n }\r\n\r\n // TODO(josephperrott): Remove after 2018 spec updates are fully merged.\r\n /** Sets the pseudo checkbox padding size based on the width of the pseudo checkbox. */\r\n private _setPseudoCheckboxPaddingSize() {\r\n if (!SELECT_MULTIPLE_PANEL_PADDING_X && this.multiple) {\r\n const pseudoCheckbox = this.panel.nativeElement.querySelector(\r\n '.oui-pseudo-checkbox'\r\n );\r\n if (pseudoCheckbox) {\r\n SELECT_MULTIPLE_PANEL_PADDING_X =\r\n SELECT_PANEL_PADDING_X * 1.5 + pseudoCheckbox.offsetWidth;\r\n }\r\n }\r\n }\r\n\r\n /** Whether the select has a value. */\r\n get empty(): boolean {\r\n return !this._selectionModel || this._selectionModel.isEmpty();\r\n }\r\n\r\n private _initializeSelection(): void {\r\n // Defer setting the value in order to avoid the \"Expression\r\n // has changed after it was checked\" errors from Angular.\r\n Promise.resolve().then(() => {\r\n this._setSelectionByValue(\r\n this.ngControl ? this.ngControl.value : this._value\r\n );\r\n this.savedValues = this.ngControl ? this.ngControl.value : this._value;\r\n });\r\n }\r\n\r\n /**\r\n * Sets the selected option based on a value. If no option can be\r\n * found with the designated value, the select trigger is cleared.\r\n */\r\n private _setSelectionByValue(value: any | any[]): void {\r\n if (this.multiple && value) {\r\n if (!Array.isArray(value)) {\r\n throw getOuiSelectNonArrayValueError();\r\n }\r\n\r\n this._selectionModel.clear();\r\n value.forEach((currentValue: any) => this._selectValue(currentValue));\r\n this._sortValues();\r\n } else {\r\n this._selectionModel.clear();\r\n const correspondingOption = this._selectValue(value);\r\n // Shift focus to the active item. Note that we shouldn't do this in multiple\r\n // mode, because we don't know what option the user interacted with last.\r\n if (correspondingOption) {\r\n this._keyManager.setActiveItem(correspondingOption);\r\n }\r\n }\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n\r\n /**\r\n * Finds and selects and option based on its value.\r\n *\r\n * @returns Option that has the corresponding value.\r\n */\r\n private _selectValue(value: any): OuiOption | undefined {\r\n const correspondingOption = this.options.find((option: OuiOption) => {\r\n try {\r\n // Treat null as a special reset value.\r\n return option.value != null && this._compareWith(option.value, value);\r\n } catch (error) {\r\n if (isDevMode()) {\r\n // Notify developers of errors in their comparator.\r\n console.warn(error);\r\n }\r\n return false;\r\n }\r\n });\r\n\r\n if (correspondingOption) {\r\n this._selectionModel.select(correspondingOption);\r\n }\r\n\r\n return correspondingOption;\r\n }\r\n\r\n /** Sets up a key manager to listen to keyboard events on the overlay panel. */\r\n private _initKeyManager() {\r\n this._keyManager = new ActiveDescendantKeyManager(this.options)\r\n .withTypeAhead()\r\n .withVerticalOrientation()\r\n .withHorizontalOrientation(this._isRtl() ? 'rtl' : 'ltr')\r\n .withAllowedModifierKeys(['shiftKey']);\r\n\r\n this._keyManager.tabOut.pipe(takeUntil(this._destroy)).subscribe(() => {\r\n // Restore focus to the trigger before closing. Ensures that the focus\r\n // position won't be lost if the user got focus into the overlay.\r\n this.focus();\r\n this.close();\r\n });\r\n\r\n this._keyManager.change.pipe(takeUntil(this._destroy)).subscribe(() => {\r\n if (this._panelOpen && this.panel) {\r\n // Panel is opened\r\n // Need not to scroll\r\n } else if (\r\n !this._panelOpen &&\r\n !this.multiple &&\r\n this._keyManager.activeItem\r\n ) {\r\n this._keyManager.activeItem._selectViaInteraction();\r\n }\r\n });\r\n }\r\n\r\n /** Drops current option subscriptions and IDs and resets from scratch. */\r\n private _resetOptions(): void {\r\n const changedOrDestroyed = merge(this.options.changes, this._destroy);\r\n\r\n this.optionSelectionChanges\r\n .pipe(takeUntil(changedOrDestroyed))\r\n .subscribe((event) => {\r\n this._onSelect(event.source, event.isUserInput);\r\n\r\n if (event.isUserInput && !this.multiple && this._panelOpen) {\r\n this.close();\r\n this.focus();\r\n }\r\n });\r\n\r\n // Listen to changes in the internal state of the options and react accordingly.\r\n // Handles cases like the labels of the selected options changing.\r\n merge(...this.options.map((option) => option._stateChanges))\r\n .pipe(takeUntil(changedOrDestroyed))\r\n .subscribe(() => {\r\n this._changeDetectorRef.markForCheck();\r\n this.stateChanges.next();\r\n });\r\n\r\n this._setOptionIds();\r\n }\r\n\r\n /** Invoked when an option is clicked. */\r\n private _onSelect(option: OuiOption, isUserInput: boolean): void {\r\n const wasSelected = this._selectionModel.isSelected(option);\r\n\r\n if (option.value == null && !this._multiple) {\r\n option.deselect();\r\n this._selectionModel.clear();\r\n this._propagateChanges(option.value);\r\n } else {\r\n option.selected\r\n ? this._selectionModel.select(option)\r\n : this._selectionModel.deselect(option);\r\n\r\n if (isUserInput) {\r\n this._keyManager.setActiveItem(option);\r\n }\r\n\r\n if (this.multiple) {\r\n this._sortValues();\r\n\r\n if (isUserInput) {\r\n // In case the user selected the option with their mouse, we\r\n // want to restore focus back to the trigger, in order to\r\n // prevent the select keyboard controls from clashing with\r\n // the ones from `oui-option`.\r\n this.focus();\r\n }\r\n }\r\n }\r\n\r\n if (wasSelected !== this._selectionModel.isSelected(option)) {\r\n this._propagateChanges();\r\n }\r\n this.disableDoneButton = false;\r\n this.stateChanges.next();\r\n }\r\n discardRecentChanges() {\r\n this.value = this.savedValues;\r\n this._setSelectionByValue(this.value);\r\n this.disableDoneButton = true;\r\n this.close();\r\n }\r\n doneRecentChanges() {\r\n this.savedValues = this.value;\r\n this.disableDoneButton = true;\r\n this.saveSelectionChange.emit(new OuiSelectChange(this, this.value));\r\n this.close();\r\n }\r\n /** Sorts the selected values in the selected based on their order in the panel. */\r\n private _sortValues() {\r\n if (this.multiple) {\r\n const options = this.options.toArray();\r\n\r\n this._selectionModel.sort((a, b) =>\r\n this.sortComparator\r\n ? this.sortComparator(a, b, options)\r\n : options.indexOf(a) - options.indexOf(b)\r\n );\r\n this.stateChanges.next();\r\n }\r\n }\r\n\r\n /** Emits change event to set the model value. */\r\n private _propagateChanges(fallbackValue?: any): void {\r\n let valueToEmit: any = null;\r\n\r\n if (this.multiple) {\r\n valueToEmit = (this.selected as OuiOption[]).map(\r\n (option) => option.value\r\n );\r\n } else {\r\n valueToEmit = this.selected\r\n ? (this.selected as OuiOption).value\r\n : fallbackValue;\r\n }\r\n\r\n this._value = valueToEmit;\r\n this.valueChange.emit(valueToEmit);\r\n this._onChange(valueToEmit);\r\n this.selectionChange.emit(new OuiSelectChange(this, valueToEmit));\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n\r\n /** Records option IDs to pass to the aria-owns property. */\r\n private _setOptionIds() {\r\n this._optionIds = this.options.map((option) => option.id).join(' ');\r\n }\r\n\r\n /**\r\n * Highlights the selected item. If no option is selected, it will highlight\r\n * the first item instead.\r\n */\r\n private _highlightCorrectOption(): void {\r\n if (this._keyManager) {\r\n if (this.empty) {\r\n this._keyManager.setFirstItemActive();\r\n } else {\r\n this._keyManager.setActiveItem(this._selectionModel.selected[0]);\r\n }\r\n }\r\n }\r\n\r\n /** Focuses the select element. */\r\n focus(): void {\r\n this._elementRef.nativeElement.focus();\r\n }\r\n\r\n /** Returns the aria-label of the select component. */\r\n _getAriaLabel(): string | null {\r\n // If an ariaLabelledby value has been set by the consumer, the select should not overwrite the\r\n // `aria-labelledby` value by setting the ariaLabel to the placeholder.\r\n return this.ariaLabelledby ? null : this.ariaLabel || this.placeholder;\r\n }\r\n\r\n /** Returns the aria-labelledby of the select component. */\r\n _getAriaLabelledby(): string | null {\r\n if (this.ariaLabelledby) {\r\n return this.ariaLabelledby;\r\n }\r\n\r\n return null;\r\n }\r\n\r\n /** Determines the `aria-activedescendant` to be set on the host. */\r\n _getAriaActiveDescendant(): string | null {\r\n if (this.panelOpen && this._keyManager && this._keyManager.activeItem) {\r\n return this._keyManager.activeItem.id;\r\n }\r\n\r\n return null;\r\n }\r\n\r\n /**\r\n * Implemented as part of OuiFormFieldControl.\r\n *\r\n * @docs-private\r\n */\r\n setDescribedByIds(ids: string[]) {\r\n this._ariaDescribedby = ids.join(' ');\r\n }\r\n\r\n /**\r\n * Implemented as part of OuiFormFieldControl.\r\n *\r\n * @docs-private\r\n */\r\n onContainerClick() {\r\n this.focus();\r\n this.open();\r\n }\r\n\r\n /**\r\n * Implemented as part of OuiFormFieldControl.\r\n *\r\n * @docs-private\r\n */\r\n get shouldLabelFloat(): boolean {\r\n return this._panelOpen || !this.empty;\r\n }\r\n\r\n /**\r\n * Add outer class to perfect scrollbar\r\n * This is added only when there is a search field\r\n */\r\n ouiSelectInputOuter() {\r\n this.ouiSelectInputOuterClassName = 'oui-select-input-outer';\r\n }\r\n\r\n /**\r\n * Custom overlay class for cdk overlay container\r\n */\r\n openCdk() {\r\n this.overlayDir.positionChange.pipe(take(1)).subscribe((e) => {\r\n this.cdkConnectionOverlayPanel = '';\r\n if (e.connectionPair.originY === 'top') {\r\n this.cdkConnectionOverlayPanel = 'select-overlay-top';\r\n }\r\n this._changeDetectorRef.detectChanges();\r\n setTimeout((_) => this._scrollToOption());\r\n });\r\n\r\n const cdkOverLayContainer = this._document.querySelector(\r\n '.cdk-overlay-container'\r\n );\r\n const ouiSelectPanel = this._document.querySelector('.oui-select-panel');\r\n cdkOverLayContainer.classList.add('oui-select-overlay-container');\r\n const containerWidth = this._elementRef.nativeElement.offsetWidth;\r\n ouiSelectPanel.style.width = `${containerWidth}px`;\r\n const searchQueryString = '.oui-select-search-inner';\r\n if (this._document.querySelector(searchQueryString)) {\r\n this.scrollCalc(searchQueryString);\r\n }\r\n const actionItemsQueryString = '.oui-select-action-items';\r\n if (this._document.querySelector(actionItemsQueryString)) {\r\n this.scrollCalc(actionItemsQueryString);\r\n }\r\n }\r\n scrollCalc(selectQueryString: string) {\r\n const searchInput = this._document.querySelector(selectQueryString);\r\n const outter = this._document.querySelector('.oui-select-panel');\r\n let inner = this._document.querySelector('.oui-option');\r\n if (inner === null) {\r\n inner = 0;\r\n }\r\n const scrollbarWidth = outter.offsetWidth - inner.offsetWidth;\r\n if (scrollbarWidth > 5) {\r\n searchInput.style.width = `${inner.offsetWidth}px`;\r\n } else {\r\n searchInput.style.width = `calc(100% + 8px)`;\r\n }\r\n }\r\n\r\n /**\r\n * Given that we are not actually focusing active options, we must manually adjust scroll\r\n * to reveal options below the fold. First, we find the offset of the option from the top\r\n * of the panel. If that offset is below the fold, the new scrollTop will be the offset -\r\n * the panel height + the option height, so the active option will be just visible at the\r\n * bottom of the panel. If that offset is above the top of the visible panel, the new scrollTop\r\n * will become the offset. If that offset is visible within the panel already, the scrollTop is\r\n * not adjusted.\r\n */\r\n private _scrollToOption(): void {\r\n const manager = this._keyManager;\r\n const index = manager.activeItemIndex || 0;\r\n const labelCount = _countGroupLabelsBeforeOption(\r\n index,\r\n this.options,\r\n this.optionGroups\r\n );\r\n const scrollTop = this._getScrollTop();\r\n const newScrollPosition = _getOptionScrollPosition(\r\n index + labelCount,\r\n SELECT_OPTION_HEIGHT,\r\n scrollTop,\r\n SELECT_PANEL_HEIGHT\r\n );\r\n this._setScrollTop(newScrollPosition);\r\n }\r\n\r\n /**\r\n * Sets the panel scrollTop. This allows us to manually scroll to display options\r\n * above or below the fold, as they are not actually being focused when active.\r\n */\r\n _setScrollTop(scrollTop: number): void {\r\n if (this.panel) {\r\n this.panel.nativeElement.scrollTop = scrollTop;\r\n }\r\n }\r\n\r\n /** Returns the panel's scrollTop. */\r\n _getScrollTop(): number {\r\n return this.panel ? this.panel.nativeElement.scrollTop : 0;\r\n }\r\n}\r\n", + "sourceCode": "import { ActiveDescendantKeyManager, FocusMonitor } from '@angular/cdk/a11y';\nimport { Directionality } from '@angular/cdk/bidi';\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { SelectionModel } from '@angular/cdk/collections';\nimport {\n A,\n DOWN_ARROW,\n END,\n ENTER,\n HOME,\n LEFT_ARROW,\n RIGHT_ARROW,\n SPACE,\n UP_ARROW,\n hasModifierKey,\n} from '@angular/cdk/keycodes';\nimport { CdkConnectedOverlay } from '@angular/cdk/overlay';\nimport {\n AfterContentInit,\n Attribute,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n ContentChild,\n ContentChildren,\n Directive,\n DoCheck,\n ElementRef,\n EventEmitter,\n Input,\n isDevMode,\n NgZone,\n OnChanges,\n OnDestroy,\n OnInit,\n Optional,\n Output,\n QueryList,\n Self,\n SimpleChanges,\n ViewChild,\n ViewEncapsulation,\n Inject,\n} from '@angular/core';\nimport {\n ControlValueAccessor,\n FormGroupDirective,\n NgControl,\n NgForm,\n} from '@angular/forms';\nimport {\n _countGroupLabelsBeforeOption,\n _getOptionScrollPosition,\n CanDisable,\n CanDisableCtor,\n CanUpdateErrorState,\n CanUpdateErrorStateCtor,\n HasTabIndex,\n HasTabIndexCtor,\n OuiOptionSelectionChange,\n mixinErrorState,\n mixinTabIndex,\n mixinDisabled,\n} from '../core';\nimport { OuiFormField, OuiFormFieldControl } from '../form-field/public-api';\nimport { DOCUMENT } from '@angular/common';\nimport { OUI_OPTION_PARENT_COMPONENT, OuiOption } from '../core/option/option';\nimport { OuiOptgroup } from '../core/option/optgroup';\nimport { ErrorStateMatcher } from '../core/error/error-options';\nimport { defer, merge, Observable, Subject } from 'rxjs';\nimport {\n distinctUntilChanged,\n filter,\n map,\n startWith,\n switchMap,\n take,\n takeUntil,\n} from 'rxjs/operators';\nimport {\n getOuiSelectDynamicMultipleError,\n getOuiSelectNonArrayValueError,\n getOuiSelectNonFunctionValueError,\n} from './select-errors';\nimport { OuiIconRegistry } from '../icon/icon-registery';\nimport { DomSanitizer } from '@angular/platform-browser';\nimport { ICONS } from '../core/shared/icons';\n\nlet nextUniqueId = 0;\n\n/**\n * The following style constants are necessary to save here in order\n * to properly calculate the alignment of the selected option over\n * the trigger element.\n */\n\n/** The height of each select option. */\nexport const SELECT_OPTION_HEIGHT = 40;\n\n/** The panel's padding on the x-axis */\nexport const SELECT_PANEL_PADDING_X = 16;\n\n/** The panel's x axis padding if it is indented (e.g. there is an option group). */\nexport const SELECT_PANEL_INDENT_PADDING_X = SELECT_PANEL_PADDING_X * 2;\n\n/** The height of the select items in `em` units. */\nexport const SELECT_ITEM_HEIGHT_EM = 3;\n\n/** The total height of the select panel. */\nexport const SELECT_PANEL_HEIGHT = 200;\n\n// TODO(josephperrott): Revert to a constant after 2018 spec updates are fully merged.\n/**\n * Distance between the panel edge and the option text in\n * multi-selection mode.\n *\n * Calculated as:\n * (SELECT_PANEL_PADDING_X * 1.5) + 20 = 44\n * The padding is multiplied by 1.5 because the checkbox's margin is half the padding.\n * The checkbox width is 16px.\n */\nexport let SELECT_MULTIPLE_PANEL_PADDING_X = 0;\n\n/**\n * The select panel will only \"fit\" inside the viewport if it is positioned at\n * this value or more away from the viewport boundary.\n */\nexport const SELECT_PANEL_VIEWPORT_PADDING = 8;\n\n/** Change event object that is emitted when the select value has changed. */\nexport class OuiSelectChange {\n constructor(\n /** Reference to the select that emitted the change event. */\n public source: OuiSelect,\n /** Current value of the select that emitted the event. */\n public value: any\n ) {}\n}\n\n// Boilerplate for applying mixins to OuiSelect.\n/** @docs-private */\nexport class OuiSelectBase {\n constructor(\n public _elementRef: ElementRef,\n public _defaultErrorStateMatcher: ErrorStateMatcher,\n public _parentForm: NgForm,\n public _parentFormGroup: FormGroupDirective,\n public ngControl: NgControl\n ) {}\n}\n\nexport const _OuiSelectMixinBase: CanDisableCtor &\n HasTabIndexCtor &\n CanUpdateErrorStateCtor &\n typeof OuiSelectBase = mixinTabIndex(\n mixinDisabled(mixinErrorState(OuiSelectBase))\n);\n\n/**\n * Allows the user to customize the trigger that is displayed when the select has a value.\n */\n@Directive({\n // eslint-disable-next-line @angular-eslint/directive-selector\n selector: 'oui-select-trigger',\n})\nexport class OuiSelectTrigger {}\n\n@Component({\n selector: 'oui-select',\n exportAs: 'ouiSelect',\n templateUrl: 'select.html',\n styleUrls: ['select.scss'],\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\n inputs: ['disabled', 'tabIndex'],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n role: 'listbox',\n '[attr.id]': 'id',\n '[attr.tabindex]': 'tabIndex',\n '[attr.aria-label]': '_getAriaLabel()',\n '[attr.aria-labelledby]': '_getAriaLabelledby()',\n '[attr.aria-required]': 'required.toString()',\n '[attr.aria-disabled]': 'disabled.toString()',\n '[attr.aria-invalid]': 'errorState',\n '[attr.aria-owns]': 'panelOpen ? _optionIds : null',\n '[attr.aria-multiselectable]': 'multiple',\n '[attr.aria-describedby]': '_ariaDescribedby || null',\n '[attr.aria-activedescendant]': '_getAriaActiveDescendant()',\n '[class.oui-select-disabled]': 'disabled',\n '[class.oui-select-invalid]': 'errorState',\n '[class.oui-select-required]': 'required',\n '[class.oui-select-empty]': 'empty',\n class: 'oui-select oui-input',\n '(keydown)': '_handleKeydown($event)',\n '(focus)': '_onFocus()',\n '(blur)': '_onBlur()',\n },\n providers: [\n { provide: OuiFormFieldControl, useExisting: OuiSelect },\n { provide: OUI_OPTION_PARENT_COMPONENT, useExisting: OuiSelect },\n ],\n})\nexport class OuiSelect\n extends _OuiSelectMixinBase\n implements\n AfterContentInit,\n OnChanges,\n OnDestroy,\n OnInit,\n DoCheck,\n ControlValueAccessor,\n CanDisable,\n HasTabIndex,\n OuiFormFieldControl,\n CanUpdateErrorState\n{\n /**Holds selected values after done */\n @Input() savedValues = [];\n /**Done button disabled until dropdown is dirty */\n disableDoneButton = true;\n /** Whether or not the overlay panel is open. */\n private _panelOpen = false;\n\n /** Whether filling out the select is required in the form. */\n private _required = false;\n\n /** Whether filling out the select is required in the form. */\n private _actionItems = false;\n\n /** The scroll position of the overlay panel, calculated to center the selected option. */\n private _scrollTop = 0;\n\n /** The placeholder displayed in the trigger of the select. */\n private _placeholder: string;\n\n /** Whether the component is in multiple selection mode. */\n private _multiple = false;\n\n /** Search input field **/\n isSearchFieldPresent: boolean;\n\n /** Unique id for this input. */\n private _uid = `oui-select-${nextUniqueId++}`;\n\n /** The last measured value for the trigger's client bounding rect. */\n _triggerRect: ClientRect;\n\n /** The aria-describedby attribute on the select for improved a11y. */\n _ariaDescribedby: string;\n\n /** The cached font-size of the trigger element. */\n _triggerFontSize = 0;\n\n /** Deals with the selection logic. */\n _selectionModel: SelectionModel;\n\n /** Manages keyboard events for options in the panel. */\n _keyManager: ActiveDescendantKeyManager;\n\n /** The IDs of child options to be passed to the aria-owns attribute. */\n _optionIds = '';\n\n /** The value of the select panel's transform-origin property. */\n _transformOrigin = 'top';\n\n /** If there is search input field a class is added dynamically to the perfect scrollbar **/\n ouiSelectInputOuterClassName: string;\n\n /** Adding top class to overlay panel */\n cdkConnectionOverlayPanel = '';\n\n /**\n * The y-offset of the overlay panel in relation to the trigger's top start corner.\n * This must be adjusted to align the selected option text over the trigger text.\n * when the panel opens. Will change based on the y-position of the selected option.\n */\n _offsetY = 0;\n\n /**\n * This position config ensures that the top \"start\" corner of the overlay\n * is aligned with with the top \"start\" of the origin by default (overlapping\n * the trigger completely). If the panel cannot fit below the trigger, it\n * will fall back to a position above the trigger.\n */\n _positions = [\n {\n originX: 'start',\n originY: 'top',\n overlayX: 'start',\n overlayY: 'top',\n },\n {\n originX: 'start',\n originY: 'bottom',\n overlayX: 'start',\n overlayY: 'bottom',\n },\n ];\n /** Emits whenever the component is destroyed. */\n private readonly _destroy = new Subject();\n\n /** Whether the component is disabling centering of the active option over the trigger. */\n private _disableOptionCentering = false;\n\n private _focused = false;\n\n /** A name for this control that can be used by `oui-form-field`. */\n controlType = 'oui-select';\n\n /** Trigger that opens the select. */\n @ViewChild('trigger') trigger: ElementRef;\n\n /** Panel containing the select options. */\n @ViewChild('panel', { read: ElementRef }) panel: ElementRef;\n\n private _value: any;\n\n /**\n * Function used to sort the values in a select in multiple mode.\n * Follows the same logic as `Array.prototype.sort`.\n */\n @Input() sortComparator: (\n a: OuiOption,\n b: OuiOption,\n options: OuiOption[]\n ) => number;\n\n /** Aria label of the select. If not specified, the placeholder will be used as label. */\n @Input('aria-label') ariaLabel = '';\n\n /** Input that can be used to specify the `aria-labelledby` attribute. */\n @Input('aria-labelledby') ariaLabelledby: string;\n private _large = false;\n _monitorSubscription: any;\n\n /** Whether the oui-select is of large size. */\n @Input()\n get large(): boolean {\n return this._large;\n }\n set large(value) {\n this._large = coerceBooleanProperty(value);\n this._changeDetectorRef.markForCheck();\n }\n\n private _id: string;\n\n /** Event emitted when the select panel has been toggled. */\n @Output()\n readonly openedChange: EventEmitter = new EventEmitter();\n\n /** Combined stream of all of the child options' change events. */\n readonly optionSelectionChanges: Observable = defer(\n (): Observable => {\n if (this.options) {\n return merge(...this.options.map((option) => option.onSelectionChange));\n }\n\n return this._ngZone.onStable.asObservable().pipe(\n take(1),\n switchMap(() => this.optionSelectionChanges)\n );\n }\n );\n\n /**\n * Event that emits whenever the raw value of the select changes. This is here primarily\n * to facilitate the two-way binding for the `value` input.\n *\n * @docs-private\n */\n @Output() readonly valueChange: EventEmitter = new EventEmitter();\n\n /** Object used to control when error messages are shown. */\n @Input() errorStateMatcher: ErrorStateMatcher;\n\n /** All of the defined select options. */\n @ContentChildren(OuiOption, { descendants: true })\n options: QueryList;\n\n /** Event emitted when the select has been opened. */\n // eslint-disable-next-line @angular-eslint/no-output-rename\n @Output('opened')\n readonly _openedStream: Observable = this.openedChange.pipe(\n filter((o) => o),\n map(() => {})\n );\n\n /** Event emitted when the select has been closed. */\n // eslint-disable-next-line @angular-eslint/no-output-rename\n @Output('closed')\n readonly _closedStream: Observable = this.openedChange.pipe(\n filter((o) => !o),\n map(() => {\n this.isSearchFieldPresent = false;\n })\n );\n\n /** Event emitted when the selected value has been changed by the user. */\n @Output()\n readonly selectionChange: EventEmitter =\n new EventEmitter();\n\n /** Event emitted when the selected value has been changed and saved by the user. */\n @Output()\n readonly saveSelectionChange: EventEmitter =\n new EventEmitter();\n\n /** All of the defined groups of options. */\n @ContentChildren(OuiOptgroup) optionGroups: QueryList;\n\n /** User-supplied override of the trigger element. */\n @ContentChild(OuiSelectTrigger)\n customTrigger: OuiSelectTrigger;\n\n /** Classes to be passed to the select panel. Supports the same syntax as `ngClass`. */\n @Input() panelClass: string | string[] | Set | { [key: string]: any };\n\n /** Overlay pane containing the options. */\n @ViewChild(CdkConnectedOverlay)\n overlayDir: CdkConnectedOverlay;\n\n /** Emits when the panel element is finished transforming in. */\n _panelDoneAnimatingStream = new Subject();\n\n /** Comparison function to specify which option is displayed. Defaults to object equality. */\n private _compareWith = (o1: any, o2: any) => o1 === o2;\n\n /** Whether the select is focused. */\n get focused(): boolean {\n return this._focused || this._panelOpen;\n }\n /**\n * @deprecated Setter to be removed as this property is intended to be readonly.\n */\n set focused(value: boolean) {\n this._focused = value;\n }\n /** `View -> model callback called when value changes` */\n _onChange: (value: any) => void = () => {};\n\n /** `View -> model callback called when select has been touched` */\n _onTouched = () => {};\n\n /** Placeholder to be shown if no value has been selected. */\n @Input()\n get placeholder(): string {\n return this._placeholder;\n }\n set placeholder(value: string) {\n this._placeholder = value;\n this.stateChanges.next();\n }\n\n /** Whether the component is required. */\n @Input()\n get required(): boolean {\n return this._required;\n }\n set required(value: boolean) {\n this._required = coerceBooleanProperty(value);\n this.stateChanges.next();\n }\n\n /** Whether the user should be allowed to select multiple options. */\n @Input()\n get multiple(): boolean {\n return this._multiple;\n }\n set multiple(value: boolean) {\n if (this._selectionModel) {\n throw getOuiSelectDynamicMultipleError();\n }\n\n this._multiple = coerceBooleanProperty(value);\n }\n\n /** Whether the action items are required and use saveSelectionChange instead of selectionChange. */\n @Input()\n get actionItems(): boolean {\n return this._actionItems;\n }\n set actionItems(value: boolean) {\n if (this._multiple) {\n this._actionItems = coerceBooleanProperty(value);\n this.stateChanges.next();\n }\n }\n\n /** Whether to center the active option over the trigger. */\n @Input()\n get disableOptionCentering(): boolean {\n return this._disableOptionCentering;\n }\n set disableOptionCentering(value: boolean) {\n this._disableOptionCentering = coerceBooleanProperty(value);\n }\n\n /**\n * Function to compare the option values with the selected values. The first argument\n * is a value from an option. The second is a value from the selection. A boolean\n * should be returned.\n */\n @Input()\n get compareWith() {\n return this._compareWith;\n }\n set compareWith(fn: (o1: any, o2: any) => boolean) {\n if (typeof fn !== 'function') {\n throw getOuiSelectNonFunctionValueError();\n }\n this._compareWith = fn;\n if (this._selectionModel) {\n // A different comparator means the selection could change.\n this._initializeSelection();\n }\n }\n\n /** Value of the select control. */\n @Input()\n get value(): any {\n return this._value;\n }\n set value(newValue: any) {\n if (newValue !== this._value) {\n this.writeValue(newValue);\n this._value = newValue;\n }\n }\n\n /** Unique id of the element. */\n @Input()\n get id(): string {\n return this._id;\n }\n set id(value: string) {\n this._id = value || this._uid;\n this.stateChanges.next();\n }\n\n constructor(\n private _changeDetectorRef: ChangeDetectorRef,\n private _ngZone: NgZone,\n _defaultErrorStateMatcher: ErrorStateMatcher,\n elementRef: ElementRef,\n private _focusMonitor: FocusMonitor,\n @Optional() private _dir: Directionality,\n @Optional() _parentForm: NgForm,\n @Optional() _parentFormGroup: FormGroupDirective,\n @Optional() private _parentFormField: OuiFormField,\n @Self() @Optional() public ngControl: NgControl,\n @Attribute('tabindex') tabIndex: string,\n @Optional() @Inject(DOCUMENT) private _document: any,\n public _elementRef: ElementRef,\n public _ouiIconRegistry: OuiIconRegistry,\n private _domSanitizer: DomSanitizer\n ) {\n super(\n elementRef,\n _defaultErrorStateMatcher,\n _parentForm,\n _parentFormGroup,\n ngControl\n );\n this._monitorSubscription = this._focusMonitor\n .monitor(this._elementRef, true)\n .subscribe(() => this._ngZone.run(() => {}));\n this._ouiIconRegistry.addSvgIconLiteral(\n `select-arrow-icon`,\n this._domSanitizer.bypassSecurityTrustHtml(ICONS.SELECT_ARROW_ICON)\n );\n\n if (this.ngControl) {\n // Note: we provide the value accessor through here, instead of\n // the `providers` to avoid running into a circular import.\n this.ngControl.valueAccessor = this;\n }\n\n this.tabIndex = parseInt(tabIndex, 10) || 0;\n\n // Force setter to be called in case id was not specified.\n this.id = this.id;\n }\n\n ngOnInit() {\n this._selectionModel = new SelectionModel(this.multiple);\n this.stateChanges.next();\n\n // We need `distinctUntilChanged` here, because some browsers will\n // fire the animation end event twice for the same animation. See:\n // https://github.com/angular/angular/issues/24084\n this._panelDoneAnimatingStream\n .pipe(distinctUntilChanged(), takeUntil(this._destroy))\n .subscribe(() => {\n if (this.panelOpen) {\n this._scrollTop = 0;\n this.openedChange.emit(true);\n } else {\n this.openedChange.emit(false);\n this.overlayDir.offsetX = 0;\n this._changeDetectorRef.markForCheck();\n }\n });\n }\n\n ngAfterContentInit() {\n this._initKeyManager();\n\n this._selectionModel.changed\n .pipe(takeUntil(this._destroy))\n .subscribe((event) => {\n event.added.forEach((option) => option.select());\n event.removed.forEach((option) => option.deselect());\n });\n\n this.options.changes\n .pipe(startWith(null), takeUntil(this._destroy))\n .subscribe(() => {\n this._resetOptions();\n this._initializeSelection();\n });\n }\n\n ngDoCheck() {\n if (this.ngControl) {\n this.updateErrorState();\n }\n }\n\n ngOnChanges(changes: SimpleChanges) {\n // Updating the disabled state is handled by `mixinDisabled`, but we need to additionally let\n // the parent form field know to run change detection when the disabled state changes.\n if (changes.disabled) {\n this.stateChanges.next();\n }\n }\n\n ngOnDestroy() {\n this._monitorSubscription.unsubscribe();\n this._focusMonitor.stopMonitoring(this._elementRef);\n this._destroy.next();\n this._destroy.complete();\n this.stateChanges.complete();\n }\n\n /** Toggles the overlay panel open or closed. */\n toggle(): void {\n this.panelOpen ? this.close() : this.open();\n }\n\n /** Opens the overlay panel. */\n open(): void {\n if (\n this.disabled ||\n !this.options ||\n !this.options.length ||\n this._panelOpen\n ) {\n return;\n }\n\n this._triggerRect = this.trigger.nativeElement.getBoundingClientRect();\n\n this._panelOpen = true;\n this._keyManager.withHorizontalOrientation(null);\n\n this._highlightCorrectOption();\n this._changeDetectorRef.markForCheck();\n this.openedChange.emit(true);\n this._elementRef.nativeElement.classList.add(\n 'oui-select-list-options-opened'\n );\n }\n\n /** Closes the overlay panel and focuses the host element. */\n close(): void {\n if (this._panelOpen) {\n this._panelOpen = false;\n this._keyManager.withHorizontalOrientation(this._isRtl() ? 'rtl' : 'ltr');\n this._changeDetectorRef.markForCheck();\n this._onTouched();\n this.openedChange.emit(false);\n this._elementRef.nativeElement.classList.remove(\n 'oui-select-list-options-opened'\n );\n setTimeout((_) => this._document.activeElement.blur());\n }\n }\n\n /**\n * Sets the select's value. Part of the ControlValueAccessor interface\n * required to integrate with Angular's core forms API.\n *\n * @param value New value to be written to the model.\n */\n writeValue(value: any): void {\n if (this.options) {\n this._setSelectionByValue(value);\n }\n }\n\n /**\n * Saves a callback function to be invoked when the select's value\n * changes from user input. Part of the ControlValueAccessor interface\n * required to integrate with Angular's core forms API.\n *\n * @param fn Callback to be triggered when the value changes.\n */\n registerOnChange(fn: (value: any) => void): void {\n this._onChange = fn;\n }\n\n /**\n * Saves a callback function to be invoked when the select is blurred\n * by the user. Part of the ControlValueAccessor interface required\n * to integrate with Angular's core forms API.\n *\n * @param fn Callback to be triggered when the component has been touched.\n */\n registerOnTouched(fn: () => {}): void {\n this._onTouched = fn;\n }\n\n /**\n * Disables the select. Part of the ControlValueAccessor interface required\n * to integrate with Angular's core forms API.\n *\n * @param isDisabled Sets whether the component is disabled.\n */\n setDisabledState(isDisabled: boolean): void {\n this.disabled = isDisabled;\n this._changeDetectorRef.markForCheck();\n this.stateChanges.next();\n }\n\n /** Whether or not the overlay panel is open. */\n get panelOpen(): boolean {\n return this._panelOpen;\n }\n\n /** The currently selected option. */\n get selected(): OuiOption | OuiOption[] {\n return this.multiple\n ? this._selectionModel.selected\n : this._selectionModel.selected[0];\n }\n\n /** The value displayed in the trigger. */\n get triggerValue(): string {\n if (this.empty) {\n return '';\n }\n if (this._multiple) {\n const selectedOptions = this._selectionModel.selected.map(\n (option) => option.viewValueForSelect\n );\n\n if (this._isRtl()) {\n selectedOptions.reverse();\n }\n return selectedOptions.join(', ');\n }\n return this._selectionModel.selected[0].viewValueForSelect;\n }\n\n /** Whether the element is in RTL mode. */\n _isRtl(): boolean {\n return this._dir ? this._dir.value === 'rtl' : false;\n }\n\n /** Handles all keydown events on the select. */\n _handleKeydown(event: KeyboardEvent): void {\n if (!this.disabled) {\n this.panelOpen\n ? this._handleOpenKeydown(event)\n : this._handleClosedKeydown(event);\n }\n }\n\n /** Handles keyboard events while the select is closed. */\n private _handleClosedKeydown(event: KeyboardEvent): void {\n const keyCode = event.keyCode;\n const isArrowKey =\n keyCode === DOWN_ARROW ||\n keyCode === UP_ARROW ||\n keyCode === LEFT_ARROW ||\n keyCode === RIGHT_ARROW;\n const isOpenKey = keyCode === ENTER || keyCode === SPACE;\n const manager = this._keyManager;\n\n // Open the select on ALT + arrow key to match the native \n event.preventDefault();\n this.close();\n } else if (\n (keyCode === ENTER || keyCode === SPACE) &&\n manager.activeItem &&\n !hasModifierKey(event)\n ) {\n event.preventDefault();\n manager.activeItem._selectViaInteraction();\n } else if (this._multiple && keyCode === A && event.ctrlKey) {\n event.preventDefault();\n this.handleCtrlKey();\n } else {\n this.handleScrolling(manager, event, isArrowKey, keyCode);\n }\n }\n\n /**\n * Handle ctrl key\n */\n private handleCtrlKey() {\n const hasDeselectedOptions = this.options.some(\n (opt) => !opt.disabled && !opt.selected\n );\n\n this.options.forEach((option) => {\n if (!option.disabled) {\n hasDeselectedOptions ? option.select() : option.deselect();\n }\n });\n }\n\n /**\n * @param manager\n * @param event\n * @param isArrowKey\n * @param keyCode\n */\n private handleScrolling(\n manager: ActiveDescendantKeyManager,\n event: KeyboardEvent,\n isArrowKey: boolean,\n keyCode: number\n ) {\n const previouslyFocusedIndex = manager.activeItemIndex;\n\n manager.onKeydown(event);\n\n if (\n this._multiple &&\n isArrowKey &&\n event.shiftKey &&\n manager.activeItem &&\n manager.activeItemIndex !== previouslyFocusedIndex\n ) {\n manager.activeItem._selectViaInteraction();\n }\n if (isArrowKey && manager.activeItemIndex !== previouslyFocusedIndex) {\n this._scrollToOption();\n } else {\n // First or last\n if (keyCode === DOWN_ARROW) {\n manager.setFirstItemActive();\n this._setScrollTop(0);\n }\n if (keyCode === UP_ARROW) {\n manager.setLastItemActive();\n this._scrollToOption();\n }\n }\n }\n\n _onFocus() {\n if (!this.disabled) {\n this._focused = true;\n this.stateChanges.next();\n }\n }\n\n /**\n * Calls the touched callback only if the panel is closed. Otherwise, the trigger will\n * \"blur\" to the panel when it opens, causing a false positive.\n */\n _onBlur() {\n this._focused = false;\n this.isSearchFieldPresent = false;\n\n if (!this.disabled && !this.panelOpen) {\n this._onTouched();\n this._changeDetectorRef.markForCheck();\n this.stateChanges.next();\n }\n }\n\n /**\n * Callback that is invoked when the overlay panel has been attached.\n */\n _onAttached(): void {\n this.overlayDir.positionChange.pipe(take(1)).subscribe(() => {\n this._setPseudoCheckboxPaddingSize();\n this._changeDetectorRef.detectChanges();\n this.panel.nativeElement.scrollTop = this._scrollTop;\n });\n }\n\n /** Returns the theme to be used on the panel. */\n _getPanelTheme(): string {\n return this._parentFormField ? `oui-${this._parentFormField.color}` : '';\n }\n\n // TODO(josephperrott): Remove after 2018 spec updates are fully merged.\n /** Sets the pseudo checkbox padding size based on the width of the pseudo checkbox. */\n private _setPseudoCheckboxPaddingSize() {\n if (!SELECT_MULTIPLE_PANEL_PADDING_X && this.multiple) {\n const pseudoCheckbox = this.panel.nativeElement.querySelector(\n '.oui-pseudo-checkbox'\n );\n if (pseudoCheckbox) {\n SELECT_MULTIPLE_PANEL_PADDING_X =\n SELECT_PANEL_PADDING_X * 1.5 + pseudoCheckbox.offsetWidth;\n }\n }\n }\n\n /** Whether the select has a value. */\n get empty(): boolean {\n return !this._selectionModel || this._selectionModel.isEmpty();\n }\n\n private _initializeSelection(): void {\n // Defer setting the value in order to avoid the \"Expression\n // has changed after it was checked\" errors from Angular.\n Promise.resolve().then(() => {\n this._setSelectionByValue(\n this.ngControl ? this.ngControl.value : this._value\n );\n this.savedValues = this.ngControl ? this.ngControl.value : this._value;\n if (this.multiple) {\n this._highlightFirstFilteredOption();\n }\n });\n }\n\n /**\n * Sets the selected option based on a value. If no option can be\n * found with the designated value, the select trigger is cleared.\n */\n private _setSelectionByValue(value: any | any[]): void {\n if (this.multiple && value) {\n if (!Array.isArray(value)) {\n throw getOuiSelectNonArrayValueError();\n }\n\n this._selectionModel.clear();\n value.forEach((currentValue: any) => this._selectValue(currentValue));\n this._sortValues();\n } else {\n this._selectionModel.clear();\n const correspondingOption = this._selectValue(value);\n // Shift focus to the active item. Note that we shouldn't do this in multiple\n // mode, because we don't know what option the user interacted with last.\n if (correspondingOption) {\n this._keyManager.setActiveItem(correspondingOption);\n }\n }\n this._changeDetectorRef.markForCheck();\n }\n\n /**\n * Finds and selects and option based on its value.\n *\n * @returns Option that has the corresponding value.\n */\n private _selectValue(value: any): OuiOption | undefined {\n const correspondingOption = this.options.find((option: OuiOption) => {\n try {\n // Treat null as a special reset value.\n return option.value != null && this._compareWith(option.value, value);\n } catch (error) {\n if (isDevMode()) {\n // Notify developers of errors in their comparator.\n console.warn(error);\n }\n return false;\n }\n });\n\n if (correspondingOption) {\n this._selectionModel.select(correspondingOption);\n }\n\n return correspondingOption;\n }\n\n /** Sets up a key manager to listen to keyboard events on the overlay panel. */\n private _initKeyManager() {\n this._keyManager = new ActiveDescendantKeyManager(this.options)\n .withTypeAhead()\n .withVerticalOrientation()\n .withHorizontalOrientation(this._isRtl() ? 'rtl' : 'ltr')\n .withAllowedModifierKeys(['shiftKey']);\n\n this._keyManager.tabOut.pipe(takeUntil(this._destroy)).subscribe(() => {\n // Restore focus to the trigger before closing. Ensures that the focus\n // position won't be lost if the user got focus into the overlay.\n this.focus();\n this.close();\n });\n\n this._keyManager.change.pipe(takeUntil(this._destroy)).subscribe(() => {\n if (this._panelOpen && this.panel) {\n // Panel is opened\n // Need not to scroll\n } else if (\n !this._panelOpen &&\n !this.multiple &&\n this._keyManager.activeItem\n ) {\n this._keyManager.activeItem._selectViaInteraction();\n }\n });\n }\n\n /** Drops current option subscriptions and IDs and resets from scratch. */\n private _resetOptions(): void {\n const changedOrDestroyed = merge(this.options.changes, this._destroy);\n\n this.optionSelectionChanges\n .pipe(takeUntil(changedOrDestroyed))\n .subscribe((event) => {\n this._onSelect(event.source, event.isUserInput);\n\n if (event.isUserInput && !this.multiple && this._panelOpen) {\n this.close();\n this.focus();\n }\n });\n\n // Listen to changes in the internal state of the options and react accordingly.\n // Handles cases like the labels of the selected options changing.\n merge(...this.options.map((option) => option._stateChanges))\n .pipe(takeUntil(changedOrDestroyed))\n .subscribe(() => {\n this._changeDetectorRef.markForCheck();\n this.stateChanges.next();\n });\n\n this._setOptionIds();\n }\n\n /** Invoked when an option is clicked. */\n private _onSelect(option: OuiOption, isUserInput: boolean): void {\n const wasSelected = this._selectionModel.isSelected(option);\n\n if (option.value == null && !this._multiple) {\n option.deselect();\n this._selectionModel.clear();\n this._propagateChanges(option.value);\n } else {\n option.selected\n ? this._selectionModel.select(option)\n : this._selectionModel.deselect(option);\n\n if (isUserInput) {\n this._keyManager.setActiveItem(option);\n }\n\n if (this.multiple) {\n this._sortValues();\n\n if (isUserInput) {\n // In case the user selected the option with their mouse, we\n // want to restore focus back to the trigger, in order to\n // prevent the select keyboard controls from clashing with\n // the ones from `oui-option`.\n this.focus();\n }\n }\n }\n\n if (wasSelected !== this._selectionModel.isSelected(option)) {\n this._propagateChanges();\n }\n this.disableDoneButton = false;\n this.stateChanges.next();\n }\n discardRecentChanges() {\n this.value = this.savedValues;\n this._setSelectionByValue(this.value);\n this.disableDoneButton = true;\n this.close();\n }\n doneRecentChanges() {\n this.savedValues = this.value;\n this.disableDoneButton = true;\n this.saveSelectionChange.emit(new OuiSelectChange(this, this.value));\n this.close();\n }\n /** Sorts the selected values in the selected based on their order in the panel. */\n private _sortValues() {\n if (this.multiple) {\n const options = this.options.toArray();\n\n this._selectionModel.sort((a, b) =>\n this.sortComparator\n ? this.sortComparator(a, b, options)\n : options.indexOf(a) - options.indexOf(b)\n );\n this.stateChanges.next();\n }\n }\n\n /** Emits change event to set the model value. */\n private _propagateChanges(fallbackValue?: any): void {\n let valueToEmit: any = null;\n\n if (this.multiple) {\n valueToEmit = (this.selected as OuiOption[]).map(\n (option) => option.value\n );\n } else {\n valueToEmit = this.selected\n ? (this.selected as OuiOption).value\n : fallbackValue;\n }\n\n this._value = valueToEmit;\n this.valueChange.emit(valueToEmit);\n this._onChange(valueToEmit);\n this.selectionChange.emit(new OuiSelectChange(this, valueToEmit));\n this._changeDetectorRef.markForCheck();\n }\n\n /** Records option IDs to pass to the aria-owns property. */\n private _setOptionIds() {\n this._optionIds = this.options.map((option) => option.id).join(' ');\n }\n\n /**\n * Highlights the selected item. If no option is selected, it will highlight\n * the first item instead.\n */\n private _highlightCorrectOption(): void {\n if (this.multiple) {\n this._highlightFirstFilteredOption();\n } else if (this._keyManager) {\n if (this.empty) {\n this._keyManager.setFirstItemActive();\n } else {\n this._keyManager.setActiveItem(this._selectionModel.selected[0]);\n }\n }\n }\n\n /**\n * Highlights the first of the filtered options if no element is currently highlighted\n */\n private _highlightFirstFilteredOption(): void {\n if (this._keyManager) {\n const activeElement = this._keyManager.activeItem?._getHostElement();\n // activeElement is not part of DOM if there is no parent element\n if (!activeElement || !activeElement.parentElement) {\n // highlight first element if there is no active element or active element is not part of DOM\n this._keyManager.setFirstItemActive();\n }\n }\n }\n\n /** Focuses the select element. */\n focus(): void {\n this._elementRef.nativeElement.focus();\n }\n\n /** Returns the aria-label of the select component. */\n _getAriaLabel(): string | null {\n // If an ariaLabelledby value has been set by the consumer, the select should not overwrite the\n // `aria-labelledby` value by setting the ariaLabel to the placeholder.\n return this.ariaLabelledby ? null : this.ariaLabel || this.placeholder;\n }\n\n /** Returns the aria-labelledby of the select component. */\n _getAriaLabelledby(): string | null {\n if (this.ariaLabelledby) {\n return this.ariaLabelledby;\n }\n\n return null;\n }\n\n /** Determines the `aria-activedescendant` to be set on the host. */\n _getAriaActiveDescendant(): string | null {\n if (this.panelOpen && this._keyManager && this._keyManager.activeItem) {\n return this._keyManager.activeItem.id;\n }\n\n return null;\n }\n\n /**\n * Implemented as part of OuiFormFieldControl.\n *\n * @docs-private\n */\n setDescribedByIds(ids: string[]) {\n this._ariaDescribedby = ids.join(' ');\n }\n\n /**\n * Implemented as part of OuiFormFieldControl.\n *\n * @docs-private\n */\n onContainerClick() {\n this.focus();\n this.open();\n }\n\n /**\n * Implemented as part of OuiFormFieldControl.\n *\n * @docs-private\n */\n get shouldLabelFloat(): boolean {\n return this._panelOpen || !this.empty;\n }\n\n /**\n * Add outer class to perfect scrollbar\n * This is added only when there is a search field\n */\n ouiSelectInputOuter() {\n this.ouiSelectInputOuterClassName = 'oui-select-input-outer';\n }\n\n /**\n * Custom overlay class for cdk overlay container\n */\n openCdk() {\n this.overlayDir.positionChange.pipe(take(1)).subscribe((e) => {\n this.cdkConnectionOverlayPanel = '';\n if (e.connectionPair.originY === 'top') {\n this.cdkConnectionOverlayPanel = 'select-overlay-top';\n }\n this._changeDetectorRef.detectChanges();\n setTimeout((_) => this._scrollToOption());\n });\n\n const cdkOverLayContainer = this._document.querySelector(\n '.cdk-overlay-container'\n );\n const ouiSelectPanel = this._document.querySelector('.oui-select-panel');\n cdkOverLayContainer.classList.add('oui-select-overlay-container');\n const containerWidth = this._elementRef.nativeElement.offsetWidth;\n ouiSelectPanel.style.width = `${containerWidth}px`;\n const searchQueryString = '.oui-select-search-inner';\n if (this._document.querySelector(searchQueryString)) {\n this.scrollCalc(searchQueryString);\n }\n const actionItemsQueryString = '.oui-select-action-items';\n if (this._document.querySelector(actionItemsQueryString)) {\n this.scrollCalc(actionItemsQueryString);\n }\n }\n scrollCalc(selectQueryString: string) {\n const searchInput = this._document.querySelector(selectQueryString);\n const outter = this._document.querySelector('.oui-select-panel');\n let inner = this._document.querySelector('.oui-option');\n if (inner === null) {\n inner = 0;\n }\n const scrollbarWidth = outter.offsetWidth - inner.offsetWidth;\n if (scrollbarWidth > 5) {\n searchInput.style.width = `${inner.offsetWidth}px`;\n } else {\n searchInput.style.width = `calc(100% + 8px)`;\n }\n }\n\n /**\n * Given that we are not actually focusing active options, we must manually adjust scroll\n * to reveal options below the fold. First, we find the offset of the option from the top\n * of the panel. If that offset is below the fold, the new scrollTop will be the offset -\n * the panel height + the option height, so the active option will be just visible at the\n * bottom of the panel. If that offset is above the top of the visible panel, the new scrollTop\n * will become the offset. If that offset is visible within the panel already, the scrollTop is\n * not adjusted.\n */\n private _scrollToOption(): void {\n const manager = this._keyManager;\n const index = manager.activeItemIndex || 0;\n const labelCount = _countGroupLabelsBeforeOption(\n index,\n this.options,\n this.optionGroups\n );\n const scrollTop = this._getScrollTop();\n const newScrollPosition = _getOptionScrollPosition(\n index + labelCount,\n SELECT_OPTION_HEIGHT,\n scrollTop,\n SELECT_PANEL_HEIGHT\n );\n this._setScrollTop(newScrollPosition);\n }\n\n /**\n * Sets the panel scrollTop. This allows us to manually scroll to display options\n * above or below the fold, as they are not actually being focused when active.\n */\n _setScrollTop(scrollTop: number): void {\n if (this.panel) {\n this.panel.nativeElement.scrollTop = scrollTop;\n }\n }\n\n /** Returns the panel's scrollTop. */\n _getScrollTop(): number {\n return this.panel ? this.panel.nativeElement.scrollTop : 0;\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": "@use 'sass:math';\r\n\r\n@import '../core/style/menu-common';\r\n@import '../core/style/list-common';\r\n@import '../core/style/variables';\r\n@import '../core/style/vendor-prefixes';\r\n$oui-select-arrow-size: 5px !default;\r\n$oui-select-arrow-margin: 4px !default;\r\n$oui-select-panel-max-height: 256px !default;\r\n$oui-select-item-height: 40px !default;\r\n$oui-select-placeholder-arrow-space: 2 *\r\n ($oui-select-arrow-size + $oui-select-arrow-margin);\r\n$gray-color: #eee;\r\n\r\n%placeholder {\r\n font-size: 14px;\r\n}\r\n\r\n.oui-error {\r\n @extend %placeholder;\r\n}\r\n.oui-select {\r\n display: inline-block;\r\n width: 100%;\r\n outline: none;\r\n div {\r\n margin: 0;\r\n vertical-align: middle;\r\n }\r\n .oui-select-value {\r\n @extend %placeholder;\r\n line-height: 19px;\r\n padding: 2px 8px 2px 0;\r\n }\r\n}\r\n\r\n.oui-select-trigger {\r\n display: inline-table;\r\n cursor: pointer;\r\n position: relative;\r\n box-sizing: border-box;\r\n\r\n .oui-select-disabled & {\r\n @include user-select(none);\r\n cursor: default;\r\n }\r\n}\r\n\r\n.oui-select-value {\r\n display: table-cell;\r\n max-width: 0;\r\n width: 100%;\r\n}\r\n\r\n.oui-select-value-text {\r\n display: inline-block;\r\n width: 100%;\r\n @include oui-truncate-line();\r\n span img {\r\n max-width: 20px;\r\n max-height: 20px;\r\n }\r\n}\r\n\r\n.oui-select-arrow-wrapper {\r\n display: inline-block;\r\n vertical-align: middle;\r\n\r\n // When used in a box appearance form-field the arrow should be shifted up 50%.\r\n .oui-form-field-appearance-fill & {\r\n transform: translateY(-50%);\r\n }\r\n\r\n // When used in a outline form-field the arrow should be shifted up 25%.\r\n .oui-form-field-appearance-outline & {\r\n transform: translateY(-25%);\r\n }\r\n\r\n // Animate the arrow position, but only when the transitioning to empty (animate the arrow down)\r\n // This is in line with the oui-form-field label animation\r\n .oui-form-field-appearance-standard .oui-select.oui-select-empty & {\r\n transition: transform $swift-ease-out-duration\r\n $swift-ease-out-timing-function;\r\n }\r\n\r\n ._oui-animation-noopable.oui-form-field-appearance-standard\r\n .oui-select.oui-select-empty\r\n & {\r\n transition: none;\r\n }\r\n}\r\n\r\n.oui-select-arrow {\r\n width: 12px;\r\n}\r\n\r\n.oui-focused {\r\n .oui-select-arrow {\r\n transform: rotate(180deg);\r\n }\r\n .oui-select.oui-input:not(.oui-select-list-options-opened) {\r\n .oui-select-arrow {\r\n transform: rotate(0deg);\r\n }\r\n }\r\n}\r\n\r\n.oui-select-large {\r\n max-height: 392px !important;\r\n}\r\n\r\n// Override optgroup and option to scale based on font-size of the trigger.\r\n.oui-select-panel {\r\n color: #333;\r\n -webkit-overflow-scrolling: touch; // for momentum scroll on mobile\r\n overflow-x: hidden;\r\n padding-bottom: 0;\r\n border: 1px solid #c8c8c8;\r\n background-color: #ffffff;\r\n max-height: 272px;\r\n margin: 6px 0 0 -10px;\r\n box-sizing: border-box;\r\n min-width: 100px;\r\n font-family: 'Open Sans', Helvetica, Arial, sans-serif;\r\n box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14),\r\n 0 1px 10px 0 rgba(0, 0, 0, 0.12);\r\n padding-top: 10px;\r\n border-top: 0;\r\n &.oui-select-input-outer {\r\n padding-top: 0;\r\n }\r\n .oui-optgroup-label,\r\n .oui-option {\r\n line-height: $oui-select-item-height;\r\n height: $oui-select-item-height;\r\n padding: 0 10px;\r\n font-size: 14px;\r\n &.oui-active {\r\n background: $gray-color;\r\n color: #333;\r\n }\r\n &.oui-option-disabled {\r\n color: rgba(0, 0, 0, 0.38);\r\n }\r\n &:last-child {\r\n margin-bottom: 10px;\r\n }\r\n oui-checkbox {\r\n width: 100%;\r\n label {\r\n width: 100%;\r\n display: block;\r\n }\r\n }\r\n }\r\n .oui-option-multiple.oui-selected,\r\n .oui-option-multiple.oui-active {\r\n background: none;\r\n }\r\n .oui-optgroup-label {\r\n font-weight: 700;\r\n }\r\n}\r\n\r\n.oui-form-field-type-oui-select {\r\n &:not(.oui-form-field-disabled) .oui-form-field-flex {\r\n cursor: pointer;\r\n }\r\n\r\n .oui-form-field-label {\r\n width: calc(100% - #{$oui-select-placeholder-arrow-space});\r\n }\r\n}\r\n\r\n.oui-select-placeholder {\r\n // Delay the transition until the label has animated about a third of the way through, in\r\n // order to prevent the placeholder from overlapping for a split second.\r\n @include oui-truncate-line();\r\n display: inline-block;\r\n width: 100%;\r\n transition: color $swift-ease-out-duration\r\n math.div($swift-ease-out-duration, 3) $swift-ease-out-timing-function;\r\n color: #666;\r\n ._oui-animation-noopable & {\r\n transition: none;\r\n }\r\n\r\n .oui-form-field-hide-placeholder & {\r\n color: transparent;\r\n\r\n // Overwrite browser specific CSS properties that can overwrite the `color` property.\r\n // Some developers seem to use this approach to easily overwrite the placeholder / label color.\r\n -webkit-text-fill-color: transparent;\r\n\r\n // Remove the transition to prevent the placeholder\r\n // from overlapping when the label comes back down.\r\n transition: none;\r\n // Prevents the '...' from showing on the parent element.\r\n display: block;\r\n }\r\n}\r\n/*-- search dropdown --*/\r\n\r\n.oui-select-input-outer {\r\n box-sizing: border-box;\r\n}\r\n\r\n/*-- search dropdown --*/\r\n\r\n/*-- search ddropdown --*/\r\n.ui-select-overlay-container {\r\n position: absolute;\r\n}\r\n.oui-select-has-a-panel\r\n .oui-option.oui-selected:not(.oui-option-multiple):not(.oui-option-disabled) {\r\n background: rgba(0, 0, 0, 0.12);\r\n color: #3f51b5;\r\n font-weight: 500;\r\n}\r\n.oui-select-has-a-panel .oui-option:hover:not(.oui-option-disabled) {\r\n background: rgba(0, 0, 0, 0.04);\r\n}\r\n\r\n/*-- search ddropdown --*/\r\n\r\n/*-- search dropdown underline --*/\r\n.oui-form-field-appearance-underline {\r\n .oui-select-trigger {\r\n margin-left: 9px;\r\n .oui-select-value {\r\n overflow: visible;\r\n .oui-select-placeholder,\r\n .oui-select-value-text {\r\n width: 100%;\r\n @include oui-truncate-line();\r\n margin-left: -9px;\r\n display: block;\r\n height: 19px;\r\n }\r\n }\r\n }\r\n .oui-select.oui-input {\r\n border: 1px solid transparent;\r\n padding-left: 0;\r\n padding-right: 0;\r\n border-bottom: 1px solid #c8c8c8;\r\n > div {\r\n padding-right: 9px;\r\n }\r\n &:after {\r\n content: '';\r\n height: 1px;\r\n width: 100%;\r\n background: #c8c8c8;\r\n position: absolute;\r\n left: 0;\r\n bottom: 0;\r\n }\r\n }\r\n}\r\n\r\n.oui-form-field.oui-form-field-appearance-underline .oui-select.oui-input {\r\n &.oui-select-disabled {\r\n border: 1px solid transparent;\r\n border-bottom: 1px solid #e4e4e4;\r\n background: transparent;\r\n &:after {\r\n background: #e4e4e4;\r\n }\r\n }\r\n}\r\n.oui-form-field .oui-select.oui-input {\r\n &.oui-select-disabled {\r\n border: 1px solid #e4e4e4;\r\n background-color: #f9f9f9;\r\n color: #9b9b9b;\r\n cursor: default;\r\n .oui-select-arrow svg path {\r\n fill: #c8c8c8;\r\n }\r\n }\r\n &.oui-form-field-appearance-underline {\r\n .oui-select.oui-input {\r\n transition: border 0.5s, padding 0.5s;\r\n }\r\n }\r\n}\r\n\r\n/*-- search dropdown underline --*/\r\n\r\n.oui-option:focus:not(.oui-option-disabled),\r\n.oui-option:hover:not(.oui-option-disabled),\r\n.oui-option.oui-selected:not(.oui-option-multiple):not(.oui-option-disabled) {\r\n background: $gray-color;\r\n}\r\n\r\n.select-overlay-top.oui-select-panel {\r\n margin-bottom: 7px;\r\n border-top: 1px solid #c8c8c8;\r\n}\r\n\r\n.noResults {\r\n color: #333333;\r\n font-size: 14px;\r\n line-height: 22px;\r\n padding: 0 10px;\r\n margin: 17px 0 21px;\r\n word-break: break-all;\r\n}\r\n\r\noui-select-search {\r\n height: 60px;\r\n display: block;\r\n}\r\n.oui-select-disabled .oui-select-placeholder {\r\n color: #9b9b9b;\r\n}\r\n.oui-select-action-wrapper {\r\n height: 60px;\r\n display: block;\r\n margin-top: 10px;\r\n}\r\n\r\n.oui-select-action-items {\r\n position: absolute;\r\n background: #fff;\r\n bottom: 1px;\r\n border-top: 1px solid #ddd;\r\n padding: 12px 10px;\r\n z-index: 100;\r\n box-sizing: border-box;\r\n width: 100%;\r\n display: flex;\r\n justify-content: space-between;\r\n}\r\n", + "data": "@use 'sass:math';\n\n@import '../core/style/menu-common';\n@import '../core/style/list-common';\n@import '../core/style/variables';\n@import '../core/style/vendor-prefixes';\n$oui-select-arrow-size: 5px !default;\n$oui-select-arrow-margin: 4px !default;\n$oui-select-panel-max-height: 256px !default;\n$oui-select-item-height: 40px !default;\n$oui-select-placeholder-arrow-space: 2 *\n ($oui-select-arrow-size + $oui-select-arrow-margin);\n$gray-color: #eee;\n\n%placeholder {\n font-size: 14px;\n}\n\n.oui-error {\n @extend %placeholder;\n}\n.oui-select {\n display: inline-block;\n width: 100%;\n outline: none;\n div {\n margin: 0;\n vertical-align: middle;\n }\n .oui-select-value {\n @extend %placeholder;\n line-height: 19px;\n padding: 2px 8px 2px 0;\n }\n}\n\n.oui-select-trigger {\n display: inline-table;\n cursor: pointer;\n position: relative;\n box-sizing: border-box;\n\n .oui-select-disabled & {\n @include user-select(none);\n cursor: default;\n }\n}\n\n.oui-select-value {\n display: table-cell;\n max-width: 0;\n width: 100%;\n}\n\n.oui-select-value-text {\n display: inline-block;\n width: 100%;\n @include oui-truncate-line();\n span img {\n max-width: 20px;\n max-height: 20px;\n }\n}\n\n.oui-select-arrow-wrapper {\n display: inline-block;\n vertical-align: middle;\n\n // When used in a box appearance form-field the arrow should be shifted up 50%.\n .oui-form-field-appearance-fill & {\n transform: translateY(-50%);\n }\n\n // When used in a outline form-field the arrow should be shifted up 25%.\n .oui-form-field-appearance-outline & {\n transform: translateY(-25%);\n }\n\n // Animate the arrow position, but only when the transitioning to empty (animate the arrow down)\n // This is in line with the oui-form-field label animation\n .oui-form-field-appearance-standard .oui-select.oui-select-empty & {\n transition: transform $swift-ease-out-duration\n $swift-ease-out-timing-function;\n }\n\n ._oui-animation-noopable.oui-form-field-appearance-standard\n .oui-select.oui-select-empty\n & {\n transition: none;\n }\n}\n\n.oui-select-arrow {\n width: 12px;\n}\n\n.oui-focused {\n .oui-select-arrow {\n transform: rotate(180deg);\n }\n .oui-select.oui-input:not(.oui-select-list-options-opened) {\n .oui-select-arrow {\n transform: rotate(0deg);\n }\n }\n}\n\n.oui-select-large {\n max-height: 392px !important;\n}\n\n// Override optgroup and option to scale based on font-size of the trigger.\n.oui-select-panel {\n color: #333;\n -webkit-overflow-scrolling: touch; // for momentum scroll on mobile\n overflow-x: hidden;\n padding-bottom: 0;\n border: 1px solid #c8c8c8;\n background-color: #ffffff;\n max-height: 272px;\n margin: 6px 0 0 -10px;\n box-sizing: border-box;\n min-width: 100px;\n font-family: 'Open Sans', Helvetica, Arial, sans-serif;\n box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14),\n 0 1px 10px 0 rgba(0, 0, 0, 0.12);\n padding-top: 10px;\n border-top: 0;\n &.oui-select-input-outer {\n padding-top: 0;\n }\n .oui-optgroup-label,\n .oui-option {\n line-height: $oui-select-item-height;\n height: $oui-select-item-height;\n padding: 0 10px;\n font-size: 14px;\n &.oui-active {\n background: $gray-color;\n color: #333;\n }\n &.oui-option-disabled {\n color: rgba(0, 0, 0, 0.38);\n }\n &:last-child {\n margin-bottom: 10px;\n }\n oui-checkbox {\n width: 100%;\n label {\n width: 100%;\n display: block;\n }\n }\n }\n .oui-option-multiple.oui-selected,\n .oui-option-multiple.oui-active {\n background: none;\n }\n .oui-optgroup-label {\n font-weight: 700;\n }\n}\n\n.oui-form-field-type-oui-select {\n &:not(.oui-form-field-disabled) .oui-form-field-flex {\n cursor: pointer;\n }\n\n .oui-form-field-label {\n width: calc(100% - #{$oui-select-placeholder-arrow-space});\n }\n}\n\n.oui-select-placeholder {\n // Delay the transition until the label has animated about a third of the way through, in\n // order to prevent the placeholder from overlapping for a split second.\n @include oui-truncate-line();\n display: inline-block;\n width: 100%;\n transition: color $swift-ease-out-duration\n math.div($swift-ease-out-duration, 3) $swift-ease-out-timing-function;\n color: #666;\n ._oui-animation-noopable & {\n transition: none;\n }\n\n .oui-form-field-hide-placeholder & {\n color: transparent;\n\n // Overwrite browser specific CSS properties that can overwrite the `color` property.\n // Some developers seem to use this approach to easily overwrite the placeholder / label color.\n -webkit-text-fill-color: transparent;\n\n // Remove the transition to prevent the placeholder\n // from overlapping when the label comes back down.\n transition: none;\n // Prevents the '...' from showing on the parent element.\n display: block;\n }\n}\n/*-- search dropdown --*/\n\n.oui-select-input-outer {\n box-sizing: border-box;\n}\n\n/*-- search dropdown --*/\n\n/*-- search ddropdown --*/\n.ui-select-overlay-container {\n position: absolute;\n}\n.oui-select-has-a-panel\n .oui-option.oui-selected:not(.oui-option-multiple):not(.oui-option-disabled) {\n background: rgba(0, 0, 0, 0.12);\n color: #3f51b5;\n font-weight: 500;\n}\n.oui-select-has-a-panel .oui-option:hover:not(.oui-option-disabled) {\n background: rgba(0, 0, 0, 0.04);\n}\n\n/*-- search ddropdown --*/\n\n/*-- search dropdown underline --*/\n.oui-form-field-appearance-underline {\n .oui-select-trigger {\n margin-left: 9px;\n .oui-select-value {\n overflow: visible;\n .oui-select-placeholder,\n .oui-select-value-text {\n width: 100%;\n @include oui-truncate-line();\n margin-left: -9px;\n display: block;\n height: 19px;\n }\n }\n }\n .oui-select.oui-input {\n border: 1px solid transparent;\n padding-left: 0;\n padding-right: 0;\n border-bottom: 1px solid #c8c8c8;\n > div {\n padding-right: 9px;\n }\n &:after {\n content: '';\n height: 1px;\n width: 100%;\n background: #c8c8c8;\n position: absolute;\n left: 0;\n bottom: 0;\n }\n }\n}\n\n.oui-form-field.oui-form-field-appearance-underline .oui-select.oui-input {\n &.oui-select-disabled {\n border: 1px solid transparent;\n border-bottom: 1px solid #e4e4e4;\n background: transparent;\n &:after {\n background: #e4e4e4;\n }\n }\n}\n.oui-form-field .oui-select.oui-input {\n &.oui-select-disabled {\n border: 1px solid #e4e4e4;\n background-color: #f9f9f9;\n color: #9b9b9b;\n cursor: default;\n .oui-select-arrow svg path {\n fill: #c8c8c8;\n }\n }\n &.oui-form-field-appearance-underline {\n .oui-select.oui-input {\n transition: border 0.5s, padding 0.5s;\n }\n }\n}\n\n/*-- search dropdown underline --*/\n\n.oui-option:focus:not(.oui-option-disabled),\n.oui-option:hover:not(.oui-option-disabled),\n.oui-option.oui-selected:not(.oui-option-multiple):not(.oui-option-disabled) {\n background: $gray-color;\n}\n\n.select-overlay-top.oui-select-panel {\n margin-bottom: 7px;\n border-top: 1px solid #c8c8c8;\n}\n\n.noResults {\n color: #333333;\n font-size: 14px;\n line-height: 22px;\n padding: 0 10px;\n margin: 17px 0 21px;\n word-break: break-all;\n}\n\noui-select-search {\n height: 60px;\n display: block;\n}\n.oui-select-disabled .oui-select-placeholder {\n color: #9b9b9b;\n}\n.oui-select-action-wrapper {\n height: 60px;\n display: block;\n margin-top: 10px;\n}\n\n.oui-select-action-items {\n position: absolute;\n background: #fff;\n bottom: 1px;\n border-top: 1px solid #ddd;\n padding: 12px 10px;\n z-index: 100;\n box-sizing: border-box;\n width: 100%;\n display: flex;\n justify-content: space-between;\n}\n", "styleUrl": "select.scss" } ], @@ -37086,7 +46959,7 @@ "deprecationMessage": "" } ], - "line": 539, + "line": 541, "jsdoctags": [ { "name": "_changeDetectorRef", @@ -37293,7 +47166,7 @@ } ], "returnType": "void", - "line": 436, + "line": 438, "rawdescription": "\n\n", "description": "", "jsdoctags": [ @@ -37312,7 +47185,7 @@ "name": "focused", "type": "boolean", "returnType": "boolean", - "line": 430, + "line": 432, "rawdescription": "\nWhether the select is focused.", "description": "

Whether the select is focused.

\n" } @@ -37333,7 +47206,7 @@ } ], "returnType": "void", - "line": 450, + "line": 452, "jsdoctags": [ { "name": "value", @@ -37350,7 +47223,7 @@ "name": "placeholder", "type": "string", "returnType": "string", - "line": 447, + "line": 449, "rawdescription": "\nPlaceholder to be shown if no value has been selected.", "description": "

Placeholder to be shown if no value has been selected.

\n" } @@ -37371,7 +47244,7 @@ } ], "returnType": "void", - "line": 460, + "line": 462, "jsdoctags": [ { "name": "value", @@ -37388,7 +47261,7 @@ "name": "required", "type": "boolean", "returnType": "boolean", - "line": 457, + "line": 459, "rawdescription": "\nWhether the component is required.", "description": "

Whether the component is required.

\n" } @@ -37409,7 +47282,7 @@ } ], "returnType": "void", - "line": 470, + "line": 472, "jsdoctags": [ { "name": "value", @@ -37426,7 +47299,7 @@ "name": "multiple", "type": "boolean", "returnType": "boolean", - "line": 467, + "line": 469, "rawdescription": "\nWhether the user should be allowed to select multiple options.", "description": "

Whether the user should be allowed to select multiple options.

\n" } @@ -37447,7 +47320,7 @@ } ], "returnType": "void", - "line": 483, + "line": 485, "jsdoctags": [ { "name": "value", @@ -37464,7 +47337,7 @@ "name": "actionItems", "type": "boolean", "returnType": "boolean", - "line": 480, + "line": 482, "rawdescription": "\nWhether the action items are required and use saveSelectionChange instead of selectionChange.", "description": "

Whether the action items are required and use saveSelectionChange instead of selectionChange.

\n" } @@ -37485,7 +47358,7 @@ } ], "returnType": "void", - "line": 495, + "line": 497, "jsdoctags": [ { "name": "value", @@ -37502,7 +47375,7 @@ "name": "disableOptionCentering", "type": "boolean", "returnType": "boolean", - "line": 492, + "line": 494, "rawdescription": "\nWhether to center the active option over the trigger.", "description": "

Whether to center the active option over the trigger.

\n" } @@ -37537,7 +47410,7 @@ } ], "returnType": "void", - "line": 508, + "line": 510, "jsdoctags": [ { "name": "fn", @@ -37568,7 +47441,7 @@ "name": "compareWith", "type": "", "returnType": "", - "line": 505, + "line": 507, "rawdescription": "\n\nFunction to compare the option values with the selected values. The first argument\nis a value from an option. The second is a value from the selection. A boolean\nshould be returned.\n", "description": "

Function to compare the option values with the selected values. The first argument\nis a value from an option. The second is a value from the selection. A boolean\nshould be returned.

\n" } @@ -37589,7 +47462,7 @@ } ], "returnType": "void", - "line": 524, + "line": 526, "jsdoctags": [ { "name": "newValue", @@ -37606,7 +47479,7 @@ "name": "value", "type": "any", "returnType": "any", - "line": 521, + "line": 523, "rawdescription": "\nValue of the select control.", "description": "

Value of the select control.

\n" } @@ -37627,7 +47500,7 @@ } ], "returnType": "void", - "line": 536, + "line": 538, "jsdoctags": [ { "name": "value", @@ -37644,7 +47517,7 @@ "name": "id", "type": "string", "returnType": "string", - "line": 533, + "line": 535, "rawdescription": "\nUnique id of the element.", "description": "

Unique id of the element.

\n" } @@ -37655,7 +47528,7 @@ "name": "panelOpen", "type": "boolean", "returnType": "boolean", - "line": 737, + "line": 739, "rawdescription": "\nWhether or not the overlay panel is open.", "description": "

Whether or not the overlay panel is open.

\n" } @@ -37666,7 +47539,7 @@ "name": "selected", "type": "", "returnType": "OuiOption | []", - "line": 742, + "line": 744, "rawdescription": "\nThe currently selected option.", "description": "

The currently selected option.

\n" } @@ -37677,7 +47550,7 @@ "name": "triggerValue", "type": "string", "returnType": "string", - "line": 749, + "line": 751, "rawdescription": "\nThe value displayed in the trigger.", "description": "

The value displayed in the trigger.

\n" } @@ -37688,7 +47561,7 @@ "name": "empty", "type": "boolean", "returnType": "boolean", - "line": 962, + "line": 964, "rawdescription": "\nWhether the select has a value.", "description": "

Whether the select has a value.

\n" } @@ -37699,20 +47572,20 @@ "name": "shouldLabelFloat", "type": "boolean", "returnType": "boolean", - "line": 1240, + "line": 1261, "rawdescription": "\n\nImplemented as part of OuiFormFieldControl.\n\n", "description": "

Implemented as part of OuiFormFieldControl.

\n", "jsdoctags": [ { - "pos": 37781, - "end": 37799, + "pos": 37264, + "end": 37281, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, "kind": 325, "tagName": { - "pos": 37782, - "end": 37794, + "pos": 37265, + "end": 37277, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -37725,11 +47598,11 @@ } } }, - "templateData": "\r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n\r\n
\r\n \r\n
\r\n\r\n\r\n \r\n \r\n
\r\n
\r\n \r\n Cancel\r\n Done\r\n \r\n
\r\n
\r\n \r\n
\r\n" + "templateData": "\n
\n \n \n \n \n \n \n \n
\n\n
\n \n
\n\n\n \n \n
\n
\n \n Cancel\n Done\n \n
\n
\n \n
\n" }, { "name": "OuiSelectSearchComponent", - "id": "component-OuiSelectSearchComponent-afb8b4e0f1b4fe8706347bf96aba5905dbb578f037783a5be1c24e840bb5b890b562669e8c52a6334d317d873e3e7cd28423fa2f233ecb5b79e6e005e151f2d4", + "id": "component-OuiSelectSearchComponent-b5d4c942d8d465cd33b7e0ab034e1639b058da8de7ffbe5cfcd068b4fb84a6d15bef9387e48a64144b08ade938ddda170356d33b0931029d448ba35fc976c761", "file": "ui/src/components/select/search/index.ts", "encapsulation": [], "entryComponents": [], @@ -37751,6 +47624,15 @@ ], "viewProviders": [], "inputsClass": [ + { + "name": "aria-label", + "defaultValue": "'Type to filter'", + "deprecated": false, + "deprecationMessage": "", + "line": 41, + "type": "string", + "decorators": [] + }, { "name": "placeholderLabel", "defaultValue": "''", @@ -37758,7 +47640,7 @@ "deprecationMessage": "", "rawdescription": "\nLabel of the search placeholder", "description": "

Label of the search placeholder

\n", - "line": 42, + "line": 44, "type": "string", "decorators": [] } @@ -37786,7 +47668,7 @@ "type": "string", "optional": false, "description": "", - "line": 47, + "line": 49, "modifierKind": [ 121 ] @@ -37799,7 +47681,7 @@ "type": "function", "optional": false, "description": "", - "line": 48, + "line": 50, "modifierKind": [ 121 ] @@ -37812,7 +47694,7 @@ "type": "", "optional": false, "description": "", - "line": 49 + "line": 51 }, { "name": "ouiSelect", @@ -37821,7 +47703,7 @@ "type": "OuiSelect", "optional": false, "description": "", - "line": 52, + "line": 54, "decorators": [ { "name": "Inject", @@ -37852,7 +47734,7 @@ "type": "ElementRef", "optional": false, "description": "

Reference to the search input field

\n", - "line": 46, + "line": 48, "rawdescription": "\nReference to the search input field", "decorators": [ { @@ -37869,7 +47751,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 135, + "line": 137, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nFocuses the search input field\n", @@ -37892,7 +47774,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 148, + "line": 150, "deprecated": false, "deprecationMessage": "", "rawdescription": "\n\nResets the current search value\nfocus whether to focus after resetting\n", @@ -37919,7 +47801,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 158, + "line": 160, "deprecated": false, "deprecationMessage": "", "modifierKind": [ @@ -37932,7 +47814,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 107, + "line": 109, "deprecated": false, "deprecationMessage": "" }, @@ -37942,7 +47824,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 91, + "line": 93, "deprecated": false, "deprecationMessage": "" }, @@ -37952,7 +47834,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 63, + "line": 65, "deprecated": false, "deprecationMessage": "" }, @@ -37969,7 +47851,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 100, + "line": 102, "deprecated": false, "deprecationMessage": "", "jsdoctags": [ @@ -38005,7 +47887,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 55, + "line": 57, "deprecated": false, "deprecationMessage": "", "jsdoctags": [ @@ -38042,7 +47924,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 59, + "line": 61, "deprecated": false, "deprecationMessage": "", "jsdoctags": [ @@ -38071,7 +47953,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 117, + "line": 119, "deprecated": false, "deprecationMessage": "", "jsdoctags": [ @@ -38092,7 +47974,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 78, + "line": 80, "deprecated": false, "deprecationMessage": "", "modifierKind": [ @@ -38112,7 +47994,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 96, + "line": 98, "deprecated": false, "deprecationMessage": "", "jsdoctags": [ @@ -38135,11 +48017,11 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import {\r\n Component,\r\n ElementRef,\r\n Inject,\r\n Input,\r\n OnInit,\r\n ViewChild,\r\n Optional,\r\n AfterViewChecked,\r\n forwardRef,\r\n OnDestroy,\r\n} from '@angular/core';\r\nimport { DOCUMENT } from '@angular/common';\r\nimport { OuiSelect } from '../select.component';\r\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\r\nimport { takeUntil, filter } from 'rxjs/operators';\r\nimport { Subject } from 'rxjs';\r\nimport { OuiOption } from '../../core/option/option';\r\n\r\n@Component({\r\n selector: 'oui-select-search',\r\n templateUrl: './option-search.html',\r\n styleUrls: ['./option-search.scss'],\r\n providers: [\r\n {\r\n provide: NG_VALUE_ACCESSOR,\r\n useExisting: forwardRef(() => OuiSelectSearchComponent),\r\n multi: true,\r\n },\r\n ],\r\n})\r\nexport class OuiSelectSearchComponent\r\n implements OnInit, AfterViewChecked, ControlValueAccessor, OnDestroy\r\n{\r\n /** Previously selected values when using */\r\n private previousSelectedValues: any[];\r\n\r\n /** Subject that emits when the component has been destroyed. */\r\n private _onDestroy = new Subject();\r\n\r\n /** Label of the search placeholder */\r\n @Input() placeholderLabel = '';\r\n\r\n /** Reference to the search input field */\r\n @ViewChild('searchSelectInput', { read: ElementRef, static: true })\r\n searchSelectInput: ElementRef;\r\n private _value: string;\r\n private onChange: (value: any) => void = () => {};\r\n onTouched = () => {};\r\n\r\n constructor(\r\n @Inject(OuiSelect) public ouiSelect: OuiSelect,\r\n @Optional() @Inject(DOCUMENT) private _document: any\r\n ) {}\r\n registerOnChange(fn: (value: any) => void) {\r\n this.onChange = fn;\r\n }\r\n\r\n registerOnTouched(fn: () => void): void {\r\n this.onTouched = fn;\r\n }\r\n\r\n ngOnInit() {\r\n // when the select dropdown panel is opened or closed\r\n this.ouiSelect.openedChange.subscribe((opened) => {\r\n if (opened) {\r\n // focus the search field when opening\r\n this._focus();\r\n } else {\r\n // clear it when closing\r\n this._reset();\r\n }\r\n });\r\n this.initMultipleHandling();\r\n this.storeInitialValuesIntoPrevious();\r\n }\r\n\r\n private storeInitialValuesIntoPrevious() {\r\n this.ouiSelect._openedStream\r\n .pipe(\r\n takeUntil(this._onDestroy),\r\n filter(() => this.ouiSelect.multiple)\r\n )\r\n .subscribe(() => {\r\n this.previousSelectedValues = (\r\n this.ouiSelect.selected as OuiOption[]\r\n ).map((option) => option.value);\r\n });\r\n }\r\n\r\n ngOnDestroy() {\r\n this._onDestroy.next();\r\n this._onDestroy.complete();\r\n }\r\n\r\n writeValue(value: any): void {\r\n this.onChange(value);\r\n }\r\n\r\n onInputChange(value) {\r\n const valueChanged = value !== this._value;\r\n if (valueChanged) {\r\n this._value = value;\r\n this.onChange(value);\r\n }\r\n }\r\n ngAfterViewChecked() {\r\n const searchQueryString = '.oui-select-search-inner';\r\n if (this._document.querySelector(searchQueryString)) {\r\n this.scrollCalc(searchQueryString);\r\n }\r\n const actionItemsQueryString = '.oui-select-action-items';\r\n if (this._document.querySelector(actionItemsQueryString)) {\r\n this.scrollCalc(actionItemsQueryString);\r\n }\r\n }\r\n scrollCalc(selectQueryString: string) {\r\n const searchInput = this._document.querySelector(selectQueryString);\r\n const outter = this._document.querySelector('.oui-select-panel');\r\n let inner = this._document.querySelector('.oui-option');\r\n if (inner === null) {\r\n inner = 0;\r\n }\r\n const scrollbarWidth = outter.offsetWidth - inner.offsetWidth;\r\n if (scrollbarWidth > 5) {\r\n searchInput.style.width = `${inner.offsetWidth}px`;\r\n } else {\r\n searchInput.style.width = `calc(100% + 8px)`;\r\n }\r\n }\r\n\r\n /**\r\n * Focuses the search input field\r\n */\r\n public _focus() {\r\n if (!this.searchSelectInput) {\r\n return;\r\n }\r\n // focus\r\n setTimeout((_) => this.searchSelectInput.nativeElement.focus());\r\n this.ouiSelect.ouiSelectInputOuter();\r\n }\r\n\r\n /**\r\n * Resets the current search value\r\n * focus whether to focus after resetting\r\n */\r\n public _reset(focus?: boolean) {\r\n if (!this.searchSelectInput) {\r\n return;\r\n }\r\n this.searchSelectInput.nativeElement.value = '';\r\n this.onInputChange('');\r\n if (focus) {\r\n this._focus();\r\n }\r\n }\r\n private initMultipleHandling() {\r\n // In oui-search, if we filter something then the options which has disappeared, will be treated as deselected. To avoid this problem we can store the previously selected value and restore them if those values are not available in visible option.\r\n this.ouiSelect.valueChange\r\n .pipe(takeUntil(this._onDestroy))\r\n .subscribe((values) => {\r\n if (this.ouiSelect.multiple) {\r\n let restoreSelectedValues = false;\r\n if (\r\n this._value &&\r\n this._value.length &&\r\n this.previousSelectedValues &&\r\n Array.isArray(this.previousSelectedValues)\r\n ) {\r\n if (!values || !Array.isArray(values)) {\r\n values = [];\r\n }\r\n const optionValues = this.ouiSelect.options.map(\r\n (option) => option.value\r\n );\r\n this.previousSelectedValues.forEach((previousValue) => {\r\n if (\r\n values.indexOf(previousValue) === -1 &&\r\n optionValues.indexOf(previousValue) === -1\r\n ) {\r\n // if a value that was selected before is not found in the options due to filtering then it will be treated as deselected\r\n // to avoid this we can push them again.\r\n values.push(previousValue);\r\n restoreSelectedValues = true;\r\n }\r\n });\r\n }\r\n\r\n if (restoreSelectedValues) {\r\n this.ouiSelect._onChange(values);\r\n }\r\n\r\n this.previousSelectedValues = values;\r\n }\r\n });\r\n }\r\n}\r\n", + "sourceCode": "import {\n Component,\n ElementRef,\n Inject,\n Input,\n OnInit,\n ViewChild,\n Optional,\n AfterViewChecked,\n forwardRef,\n OnDestroy,\n} from '@angular/core';\nimport { DOCUMENT } from '@angular/common';\nimport { OuiSelect } from '../select.component';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { takeUntil, filter } from 'rxjs/operators';\nimport { Subject } from 'rxjs';\nimport { OuiOption } from '../../core/option/option';\n\n@Component({\n selector: 'oui-select-search',\n templateUrl: './option-search.html',\n styleUrls: ['./option-search.scss'],\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => OuiSelectSearchComponent),\n multi: true,\n },\n ],\n})\nexport class OuiSelectSearchComponent\n implements OnInit, AfterViewChecked, ControlValueAccessor, OnDestroy\n{\n /** Previously selected values when using */\n private previousSelectedValues: any[];\n\n /** Subject that emits when the component has been destroyed. */\n private _onDestroy = new Subject();\n\n @Input('aria-label') ariaLabel = 'Type to filter';\n\n /** Label of the search placeholder */\n @Input() placeholderLabel = '';\n\n /** Reference to the search input field */\n @ViewChild('searchSelectInput', { read: ElementRef, static: true })\n searchSelectInput: ElementRef;\n private _value: string;\n private onChange: (value: any) => void = () => {};\n onTouched = () => {};\n\n constructor(\n @Inject(OuiSelect) public ouiSelect: OuiSelect,\n @Optional() @Inject(DOCUMENT) private _document: any\n ) {}\n registerOnChange(fn: (value: any) => void) {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n ngOnInit() {\n // when the select dropdown panel is opened or closed\n this.ouiSelect.openedChange.subscribe((opened) => {\n if (opened) {\n // focus the search field when opening\n this._focus();\n } else {\n // clear it when closing\n this._reset();\n }\n });\n this.initMultipleHandling();\n this.storeInitialValuesIntoPrevious();\n }\n\n private storeInitialValuesIntoPrevious() {\n this.ouiSelect._openedStream\n .pipe(\n takeUntil(this._onDestroy),\n filter(() => this.ouiSelect.multiple)\n )\n .subscribe(() => {\n this.previousSelectedValues = (\n this.ouiSelect.selected as OuiOption[]\n ).map((option) => option.value);\n });\n }\n\n ngOnDestroy() {\n this._onDestroy.next();\n this._onDestroy.complete();\n }\n\n writeValue(value: any): void {\n this.onChange(value);\n }\n\n onInputChange(value) {\n const valueChanged = value !== this._value;\n if (valueChanged) {\n this._value = value;\n this.onChange(value);\n }\n }\n ngAfterViewChecked() {\n const searchQueryString = '.oui-select-search-inner';\n if (this._document.querySelector(searchQueryString)) {\n this.scrollCalc(searchQueryString);\n }\n const actionItemsQueryString = '.oui-select-action-items';\n if (this._document.querySelector(actionItemsQueryString)) {\n this.scrollCalc(actionItemsQueryString);\n }\n }\n scrollCalc(selectQueryString: string) {\n const searchInput = this._document.querySelector(selectQueryString);\n const outter = this._document.querySelector('.oui-select-panel');\n let inner = this._document.querySelector('.oui-option');\n if (inner === null) {\n inner = 0;\n }\n const scrollbarWidth = outter.offsetWidth - inner.offsetWidth;\n if (scrollbarWidth > 5) {\n searchInput.style.width = `${inner.offsetWidth}px`;\n } else {\n searchInput.style.width = `calc(100% + 8px)`;\n }\n }\n\n /**\n * Focuses the search input field\n */\n public _focus() {\n if (!this.searchSelectInput) {\n return;\n }\n // focus\n setTimeout((_) => this.searchSelectInput.nativeElement.focus());\n this.ouiSelect.ouiSelectInputOuter();\n }\n\n /**\n * Resets the current search value\n * focus whether to focus after resetting\n */\n public _reset(focus?: boolean) {\n if (!this.searchSelectInput) {\n return;\n }\n this.searchSelectInput.nativeElement.value = '';\n this.onInputChange('');\n if (focus) {\n this._focus();\n }\n }\n private initMultipleHandling() {\n // In oui-search, if we filter something then the options which has disappeared, will be treated as deselected. To avoid this problem we can store the previously selected value and restore them if those values are not available in visible option.\n this.ouiSelect.valueChange\n .pipe(takeUntil(this._onDestroy))\n .subscribe((values) => {\n if (this.ouiSelect.multiple) {\n let restoreSelectedValues = false;\n if (\n this._value &&\n this._value.length &&\n this.previousSelectedValues &&\n Array.isArray(this.previousSelectedValues)\n ) {\n if (!values || !Array.isArray(values)) {\n values = [];\n }\n const optionValues = this.ouiSelect.options.map(\n (option) => option.value\n );\n this.previousSelectedValues.forEach((previousValue) => {\n if (\n values.indexOf(previousValue) === -1 &&\n optionValues.indexOf(previousValue) === -1\n ) {\n // if a value that was selected before is not found in the options due to filtering then it will be treated as deselected\n // to avoid this we can push them again.\n values.push(previousValue);\n restoreSelectedValues = true;\n }\n });\n }\n\n if (restoreSelectedValues) {\n this.ouiSelect._onChange(values);\n }\n\n this.previousSelectedValues = values;\n }\n });\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": "$oui-menu-side-padding: 16px !default;\r\n$scrollbar-width: 17px;\r\n$clear-button-width: 20px;\r\n$multiple-check-width: 33px;\r\n$defaul-color: #c8c8c8;\r\n\r\n.oui-select-search-hidden {\r\n visibility: hidden;\r\n}\r\n.oui-select-search-inner {\r\n position: absolute;\r\n background: #fff;\r\n top: 6px;\r\n left: -9px;\r\n width: 100%;\r\n z-index: 100;\r\n padding: 10px;\r\n box-sizing: border-box;\r\n &.oui-select-search-inner-multiple {\r\n width: calc(\r\n 100% + #{2 * $oui-menu-side-padding - $scrollbar-width +\r\n $multiple-check-width}\r\n );\r\n }\r\n .oui-select-search-input {\r\n &:focus {\r\n border-color: $defaul-color;\r\n }\r\n }\r\n}\r\n.oui-select-search-panel {\r\n /* allow absolute positioning relative to outer options container */\r\n transform: none !important;\r\n max-height: 150px;\r\n}\r\n.oui-select-search-no-entries-found {\r\n padding: $oui-menu-side-padding;\r\n}\r\n.oui-select-search-clear {\r\n position: absolute;\r\n right: 0;\r\n top: 4px;\r\n}\r\n", + "data": "$oui-menu-side-padding: 16px !default;\n$scrollbar-width: 17px;\n$clear-button-width: 20px;\n$multiple-check-width: 33px;\n$defaul-color: #c8c8c8;\n\n.oui-select-search-hidden {\n visibility: hidden;\n}\n.oui-select-search-inner {\n position: absolute;\n background: #fff;\n top: 6px;\n left: -9px;\n width: 100%;\n z-index: 100;\n padding: 10px;\n box-sizing: border-box;\n &.oui-select-search-inner-multiple {\n width: calc(\n 100% + #{2 * $oui-menu-side-padding - $scrollbar-width +\n $multiple-check-width}\n );\n }\n .oui-select-search-input {\n &:focus {\n border-color: $defaul-color;\n }\n }\n}\n.oui-select-search-panel {\n /* allow absolute positioning relative to outer options container */\n transform: none !important;\n max-height: 150px;\n}\n.oui-select-search-no-entries-found {\n padding: $oui-menu-side-padding;\n}\n.oui-select-search-clear {\n position: absolute;\n right: 0;\n top: 4px;\n}\n", "styleUrl": "./option-search.scss" } ], @@ -38163,7 +48045,7 @@ "deprecationMessage": "" } ], - "line": 49, + "line": 51, "jsdoctags": [ { "name": "ouiSelect", @@ -38191,11 +48073,11 @@ "ControlValueAccessor", "OnDestroy" ], - "templateData": "
\r\n \r\n \r\n \r\n
\r\n" + "templateData": "
\n \n \n \n
\n" }, { "name": "OuiSlideToggle", - "id": "component-OuiSlideToggle-c7b89f5a3a0d52976355462f714b7ce948ae7eb1f57a85d2b6d18d88dde84939450e249636d7f20d981c8518092e12054288f1e51eb38a5d30e30898a5bc82e5", + "id": "component-OuiSlideToggle-640e4a9221c32a9afb8580a88323b3849e25ee94103d2b1e4eed5fa1f8263b546ca008953de9115f310eb23366a9715e3a6455d7e7553f1b1688c11b5e4c20e8", "file": "ui/src/components/slide-toggle/slide-toggle.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -38562,11 +48444,11 @@ "description": "

Container for form controls that applies Oncehub Design styling and behavior.

\n", "rawdescription": "\nContainer for form controls that applies Oncehub Design styling and behavior.", "type": "component", - "sourceCode": "import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n ElementRef,\r\n Input,\r\n Output,\r\n EventEmitter,\r\n ChangeDetectorRef,\r\n ViewEncapsulation,\r\n AfterContentInit,\r\n Attribute,\r\n OnDestroy,\r\n NgZone,\r\n forwardRef,\r\n} from '@angular/core';\r\nimport { FocusMonitor } from '@angular/cdk/a11y';\r\nimport { mixinColor } from '../core';\r\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\r\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\r\nimport { Subscription } from 'rxjs';\r\nlet nextUniqueId = 0;\r\n/**\r\n * Boilerplate for applying mixins to OuiSlideToggle.\r\n *\r\n * @docs-private\r\n */\r\n/** @docs-private */\r\nexport const OUI_SLIDE_TOGGLE_VALUE_ACCESSOR: any = {\r\n provide: NG_VALUE_ACCESSOR,\r\n useExisting: forwardRef(() => OuiSlideToggle),\r\n multi: true,\r\n};\r\n\r\nexport class OuiSlideToggleBase {\r\n constructor(public _elementRef: ElementRef) {}\r\n}\r\nexport const _OuiSlideToggleMixinBase: typeof OuiSlideToggleBase =\r\n mixinColor(OuiSlideToggleBase);\r\n\r\n/** Container for form controls that applies Oncehub Design styling and behavior. */\r\n@Component({\r\n selector: 'oui-slide-toggle',\r\n exportAs: 'ouiSlideToggle',\r\n templateUrl: 'slide-toggle.html',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-slide-toggle',\r\n '[class.oui-disabled]': 'disabled',\r\n '[attr.tabindex]': 'disabled ? null : -1',\r\n },\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['disabled', 'tabIndex'],\r\n styleUrls: ['./slide-toggle.scss'],\r\n providers: [OUI_SLIDE_TOGGLE_VALUE_ACCESSOR],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n})\r\nexport class OuiSlideToggle\r\n extends _OuiSlideToggleMixinBase\r\n implements AfterContentInit, ControlValueAccessor, OnDestroy\r\n{\r\n private _checked = false;\r\n tabIndex: any;\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n private _focusMonitorSubscription: Subscription = Subscription.EMPTY;\r\n /** Whether the slide-toggle element is checked or not. */\r\n @Input()\r\n get checked(): boolean {\r\n return this._checked;\r\n }\r\n set checked(value) {\r\n this._checked = coerceBooleanProperty(value);\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n @Input()\r\n disabled = false;\r\n @Input()\r\n color = 'primary';\r\n @Input()\r\n id = `oui-slide-toggletoggle-${++nextUniqueId}`;\r\n\r\n /** Used to set the aria-label attribute on the underlying input element. */\r\n @Input('aria-label')\r\n ariaLabel: string | null = null;\r\n\r\n /** Used to set the aria-labelledby attribute on the underlying input element. */\r\n @Input('aria-labelledby')\r\n ariaLabelledby: string | null = null;\r\n\r\n // eslint-disable-next-line @angular-eslint/no-output-rename\r\n @Output('state-change')\r\n change = new EventEmitter();\r\n\r\n wrapper: ElementRef;\r\n\r\n private onChange = (_: any) => {};\r\n private onTouched = () => {};\r\n constructor(\r\n protected elementRef: ElementRef,\r\n private _ngZone: NgZone,\r\n private _focusMonitor: FocusMonitor,\r\n private _changeDetectorRef: ChangeDetectorRef,\r\n @Attribute('tabindex') tabIndex: string\r\n ) {\r\n super(elementRef);\r\n this.tabIndex = parseInt(tabIndex, 10) || 0;\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this._elementRef, true)\r\n .subscribe(() =>\r\n this._ngZone.run(() => {\r\n this._changeDetectorRef.markForCheck();\r\n })\r\n );\r\n }\r\n ngAfterContentInit() {\r\n this._focusMonitorSubscription = this._focusMonitor\r\n .monitor(this._elementRef, true)\r\n .subscribe((focusOrigin) => {\r\n if (!focusOrigin) {\r\n Promise.resolve().then(() => this.onTouched());\r\n }\r\n });\r\n }\r\n\r\n emitChange() {\r\n if (!this.disabled) {\r\n this.toggle();\r\n this.onChange(this.checked);\r\n this.change.emit(this.checked);\r\n }\r\n }\r\n\r\n /** Toggles the checked state of the slide-toggle. */\r\n toggle() {\r\n this.checked = !this.checked;\r\n }\r\n\r\n /** Implemented as part of ControlValueAccessor. */\r\n writeValue(value: any): void {\r\n this.checked = !!value;\r\n }\r\n\r\n /** Implemented as part of ControlValueAccessor. */\r\n registerOnChange(fn: any): void {\r\n this.onChange = fn;\r\n }\r\n\r\n /** Implemented as part of ControlValueAccessor. */\r\n registerOnTouched(fn: any): void {\r\n this.onTouched = fn;\r\n }\r\n\r\n /** Implemented as a part of ControlValueAccessor. */\r\n setDisabledState(isDisabled: boolean): void {\r\n this.disabled = isDisabled;\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n /** Focuses the slide-toggle. */\r\n focus() {\r\n this._focusMonitor.focusVia(this.wrapper.nativeElement, 'keyboard');\r\n }\r\n ngOnDestroy() {\r\n this._focusMonitor.stopMonitoring(this._elementRef);\r\n this._monitorSubscription.unsubscribe();\r\n this._focusMonitorSubscription.unsubscribe();\r\n }\r\n}\r\n", + "sourceCode": "import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n Input,\n Output,\n EventEmitter,\n ChangeDetectorRef,\n ViewEncapsulation,\n AfterContentInit,\n Attribute,\n OnDestroy,\n NgZone,\n forwardRef,\n} from '@angular/core';\nimport { FocusMonitor } from '@angular/cdk/a11y';\nimport { mixinColor } from '../core';\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { Subscription } from 'rxjs';\nlet nextUniqueId = 0;\n/**\n * Boilerplate for applying mixins to OuiSlideToggle.\n *\n * @docs-private\n */\n/** @docs-private */\nexport const OUI_SLIDE_TOGGLE_VALUE_ACCESSOR: any = {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => OuiSlideToggle),\n multi: true,\n};\n\nexport class OuiSlideToggleBase {\n constructor(public _elementRef: ElementRef) {}\n}\nexport const _OuiSlideToggleMixinBase: typeof OuiSlideToggleBase =\n mixinColor(OuiSlideToggleBase);\n\n/** Container for form controls that applies Oncehub Design styling and behavior. */\n@Component({\n selector: 'oui-slide-toggle',\n exportAs: 'ouiSlideToggle',\n templateUrl: 'slide-toggle.html',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-slide-toggle',\n '[class.oui-disabled]': 'disabled',\n '[attr.tabindex]': 'disabled ? null : -1',\n },\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\n inputs: ['disabled', 'tabIndex'],\n styleUrls: ['./slide-toggle.scss'],\n providers: [OUI_SLIDE_TOGGLE_VALUE_ACCESSOR],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n})\nexport class OuiSlideToggle\n extends _OuiSlideToggleMixinBase\n implements AfterContentInit, ControlValueAccessor, OnDestroy\n{\n private _checked = false;\n tabIndex: any;\n private _monitorSubscription: Subscription = Subscription.EMPTY;\n private _focusMonitorSubscription: Subscription = Subscription.EMPTY;\n /** Whether the slide-toggle element is checked or not. */\n @Input()\n get checked(): boolean {\n return this._checked;\n }\n set checked(value) {\n this._checked = coerceBooleanProperty(value);\n this._changeDetectorRef.markForCheck();\n }\n @Input()\n disabled = false;\n @Input()\n color = 'primary';\n @Input()\n id = `oui-slide-toggletoggle-${++nextUniqueId}`;\n\n /** Used to set the aria-label attribute on the underlying input element. */\n @Input('aria-label')\n ariaLabel: string | null = null;\n\n /** Used to set the aria-labelledby attribute on the underlying input element. */\n @Input('aria-labelledby')\n ariaLabelledby: string | null = null;\n\n // eslint-disable-next-line @angular-eslint/no-output-rename\n @Output('state-change')\n change = new EventEmitter();\n\n wrapper: ElementRef;\n\n private onChange = (_: any) => {};\n private onTouched = () => {};\n constructor(\n protected elementRef: ElementRef,\n private _ngZone: NgZone,\n private _focusMonitor: FocusMonitor,\n private _changeDetectorRef: ChangeDetectorRef,\n @Attribute('tabindex') tabIndex: string\n ) {\n super(elementRef);\n this.tabIndex = parseInt(tabIndex, 10) || 0;\n this._monitorSubscription = this._focusMonitor\n .monitor(this._elementRef, true)\n .subscribe(() =>\n this._ngZone.run(() => {\n this._changeDetectorRef.markForCheck();\n })\n );\n }\n ngAfterContentInit() {\n this._focusMonitorSubscription = this._focusMonitor\n .monitor(this._elementRef, true)\n .subscribe((focusOrigin) => {\n if (!focusOrigin) {\n Promise.resolve().then(() => this.onTouched());\n }\n });\n }\n\n emitChange() {\n if (!this.disabled) {\n this.toggle();\n this.onChange(this.checked);\n this.change.emit(this.checked);\n }\n }\n\n /** Toggles the checked state of the slide-toggle. */\n toggle() {\n this.checked = !this.checked;\n }\n\n /** Implemented as part of ControlValueAccessor. */\n writeValue(value: any): void {\n this.checked = !!value;\n }\n\n /** Implemented as part of ControlValueAccessor. */\n registerOnChange(fn: any): void {\n this.onChange = fn;\n }\n\n /** Implemented as part of ControlValueAccessor. */\n registerOnTouched(fn: any): void {\n this.onTouched = fn;\n }\n\n /** Implemented as a part of ControlValueAccessor. */\n setDisabledState(isDisabled: boolean): void {\n this.disabled = isDisabled;\n this._changeDetectorRef.markForCheck();\n }\n /** Focuses the slide-toggle. */\n focus() {\n this._focusMonitor.focusVia(this.wrapper.nativeElement, 'keyboard');\n }\n ngOnDestroy() {\n this._focusMonitor.stopMonitoring(this._elementRef);\n this._monitorSubscription.unsubscribe();\n this._focusMonitorSubscription.unsubscribe();\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": "$width: 46px;\r\n$border-radius: 13px;\r\n$height: 25px;\r\n$font-size: 11px;\r\n$translate-px: 15px;\r\n$text-translate-px: 16px;\r\n$grey: #c8c8c8;\r\n\r\n.oui-slide-toogle-wrapper {\r\n position: relative;\r\n display: inline-block;\r\n width: $width;\r\n height: $height;\r\n &:focus {\r\n outline: none;\r\n }\r\n input {\r\n opacity: 0;\r\n width: 0;\r\n height: 0;\r\n }\r\n &.oui-checked {\r\n opacity: 1 !important;\r\n }\r\n .oui-slider {\r\n position: absolute;\r\n cursor: pointer;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n transition: 0.4s;\r\n &:focus {\r\n outline: none;\r\n }\r\n }\r\n input + .oui-slider:before {\r\n content: 'OFF';\r\n position: absolute;\r\n color: #333333;\r\n font-size: $font-size;\r\n top: -1px;\r\n left: -1px;\r\n background-color: $grey;\r\n transition: 0.4s;\r\n width: 30px;\r\n text-align: center;\r\n height: 25px;\r\n line-height: 25px;\r\n }\r\n input:checked + .oui-slider:before {\r\n content: 'ON';\r\n color: #ffffff;\r\n }\r\n input:checked + .oui-slider:before {\r\n -webkit-transform: translateX($text-translate-px);\r\n transform: translateX($text-translate-px);\r\n }\r\n /* Rounded sliders */\r\n .oui-slider.round {\r\n border-radius: 34px;\r\n border: 1px solid $grey;\r\n }\r\n .oui-slider.round:before {\r\n border-radius: 34px;\r\n }\r\n}\r\n.oui-slide-toggle {\r\n &.oui-disabled {\r\n opacity: 0.5;\r\n }\r\n &.oui-disabled .oui-slider {\r\n cursor: default;\r\n }\r\n &:focus {\r\n outline: none;\r\n }\r\n}\r\n", + "data": "$width: 46px;\n$border-radius: 13px;\n$height: 25px;\n$font-size: 11px;\n$translate-px: 15px;\n$text-translate-px: 16px;\n$grey: #c8c8c8;\n\n.oui-slide-toogle-wrapper {\n position: relative;\n display: inline-block;\n width: $width;\n height: $height;\n &:focus {\n outline: none;\n }\n input {\n opacity: 0;\n width: 0;\n height: 0;\n }\n &.oui-checked {\n opacity: 1 !important;\n }\n .oui-slider {\n position: absolute;\n cursor: pointer;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n transition: 0.4s;\n &:focus {\n outline: none;\n }\n }\n input + .oui-slider:before {\n content: 'OFF';\n position: absolute;\n color: #333333;\n font-size: $font-size;\n top: -1px;\n left: -1px;\n background-color: $grey;\n transition: 0.4s;\n width: 30px;\n text-align: center;\n height: 25px;\n line-height: 25px;\n }\n input:checked + .oui-slider:before {\n content: 'ON';\n color: #ffffff;\n }\n input:checked + .oui-slider:before {\n -webkit-transform: translateX($text-translate-px);\n transform: translateX($text-translate-px);\n }\n /* Rounded sliders */\n .oui-slider.round {\n border-radius: 34px;\n border: 1px solid $grey;\n }\n .oui-slider.round:before {\n border-radius: 34px;\n }\n}\n.oui-slide-toggle {\n &.oui-disabled {\n opacity: 0.5;\n }\n &.oui-disabled .oui-slider {\n cursor: default;\n }\n &:focus {\n outline: none;\n }\n}\n", "styleUrl": "./slide-toggle.scss" } ], @@ -38703,11 +48585,11 @@ } } }, - "templateData": "\r\n" + "templateData": "\n" }, { "name": "OuiSortHeader", - "id": "component-OuiSortHeader-4717641713d241cab001db987fc7a8caedcbcd3599b5591ef6fe987c139bb6fcd548281b22693638199ab3eaf8ef83533cc472e375db98aa3385053dd3292c6e", + "id": "component-OuiSortHeader-98f8434c04f2234c1bbe192cbd5e37fe09bd24e7c4c8d6d5d3b1681a4d80407c1152007cc5b70aa2c90fff616681dd1b677f0b53cfc0fa6194657b59d014969b", "file": "ui/src/components/sort/sort-header.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -39088,11 +48970,11 @@ "description": "

Applies sorting behavior (click to change sort) and styles to an element, including an\narrow to display the current sort direction.

\n

Must be provided with an id and contained within a parent OuiSort directive.

\n

If used on header cells in a CdkTable, it will automatically default its id from its containing\ncolumn definition.

\n", "rawdescription": "\n\nApplies sorting behavior (click to change sort) and styles to an element, including an\narrow to display the current sort direction.\n\nMust be provided with an id and contained within a parent OuiSort directive.\n\nIf used on header cells in a CdkTable, it will automatically default its id from its containing\ncolumn definition.\n", "type": "component", - "sourceCode": "import { coerceBooleanProperty } from '@angular/cdk/coercion';\r\nimport {\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n Input,\r\n OnDestroy,\r\n OnInit,\r\n Optional,\r\n ViewEncapsulation,\r\n Inject,\r\n ElementRef,\r\n IterableDiffers,\r\n NgZone,\r\n} from '@angular/core';\r\nimport { CanDisable, CanDisableCtor, mixinDisabled } from '../core';\r\nimport { merge, Subscription } from 'rxjs';\r\nimport { OuiSort, OuiSortable } from './sort';\r\nimport { ouiSortAnimations } from './sort-animations';\r\nimport { SortDirection } from './sort-direction';\r\nimport { getSortHeaderNotContainedWithinSortError } from './sort-errors';\r\nimport { OuiSortHeaderIntl } from './sort-header-intl';\r\nimport { FocusMonitor } from '@angular/cdk/a11y';\r\n\r\n// Boilerplate for applying mixins to the sort header.\r\n/** @docs-private */\r\nexport class OuiSortHeaderBase {}\r\nexport const _OuiSortHeaderMixinBase: CanDisableCtor &\r\n typeof OuiSortHeaderBase = mixinDisabled(OuiSortHeaderBase);\r\n\r\n/**\r\n * Valid positions for the arrow to be in for its opacity and translation. If the state is a\r\n * sort direction, the position of the arrow will be above/below and opacity 0. If the state is\r\n * hint, the arrow will be in the center with a slight opacity. Active state means the arrow will\r\n * be fully opaque in the center.\r\n *\r\n * @docs-private\r\n */\r\nexport type ArrowViewState = SortDirection | 'hint' | 'active';\r\n\r\n/**\r\n * States describing the arrow's animated position (animating fromState to toState).\r\n * If the fromState is not defined, there will be no animated transition to the toState.\r\n *\r\n * @docs-private\r\n */\r\nexport interface ArrowViewStateTransition {\r\n fromState?: ArrowViewState;\r\n toState: ArrowViewState;\r\n}\r\n\r\n/** Column definition associated with a `OuiSortHeader`. */\r\ninterface OuiSortHeaderColumnDef {\r\n name: string;\r\n}\r\n\r\n/**\r\n * Applies sorting behavior (click to change sort) and styles to an element, including an\r\n * arrow to display the current sort direction.\r\n *\r\n * Must be provided with an id and contained within a parent OuiSort directive.\r\n *\r\n * If used on header cells in a CdkTable, it will automatically default its id from its containing\r\n * column definition.\r\n */\r\n@Component({\r\n // eslint-disable-next-line\r\n selector: '[oui-sort-header]',\r\n exportAs: 'ouiSortHeader',\r\n templateUrl: 'sort-header.html',\r\n styleUrls: ['sort-header.scss'],\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n '(click)': '_handleClick()',\r\n '(mouseenter)': '_setIndicatorHintVisible(true)',\r\n '(longpress)': '_setIndicatorHintVisible(true)',\r\n '(mouseleave)': '_setIndicatorHintVisible(false)',\r\n '[attr.aria-sort]': '_getAriaSortAttribute()',\r\n '[class.oui-sort-header-disabled]': '_isDisabled()',\r\n },\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\r\n inputs: ['disabled'],\r\n animations: [\r\n ouiSortAnimations.indicator,\r\n ouiSortAnimations.leftPointer,\r\n ouiSortAnimations.rightPointer,\r\n ouiSortAnimations.arrowOpacity,\r\n ouiSortAnimations.arrowPosition,\r\n ouiSortAnimations.allowChildren,\r\n ],\r\n})\r\nexport class OuiSortHeader\r\n extends _OuiSortHeaderMixinBase\r\n implements CanDisable, OuiSortable, OnDestroy, OnInit\r\n{\r\n private _rerenderSubscription: Subscription;\r\n\r\n /**\r\n * Flag set to true when the indicator should be displayed while the sort is not active. Used to\r\n * provide an affordance that the header is sortable by showing on focus and hover.\r\n */\r\n _showIndicatorHint = false;\r\n\r\n /**\r\n * The view transition state of the arrow (translation/ opacity) - indicates its `from` and `to`\r\n * position through the animation. If animations are currently disabled, the fromState is removed\r\n * so that there is no animation displayed.\r\n */\r\n _viewState: ArrowViewStateTransition;\r\n\r\n /** The direction the arrow should be facing according to the current state. */\r\n _arrowDirection: SortDirection = '';\r\n\r\n /**\r\n * Whether the view state animation should show the transition between the `from` and `to` states.\r\n */\r\n _disableViewStateAnimation = false;\r\n\r\n /**\r\n * ID of this sort header. If used within the context of a CdkColumnDef, this will default to\r\n * the column's name.\r\n */\r\n // eslint-disable-next-line @angular-eslint/no-input-rename\r\n @Input('oui-sort-header') id: string;\r\n\r\n /** Sets the position of the arrow that displays when sorted. */\r\n @Input() arrowPosition: 'before' | 'after' = 'after';\r\n\r\n /** Overrides the sort start value of the containing OuiSort for this OuiSortable. */\r\n @Input() start: 'asc' | 'desc';\r\n\r\n // To set browser tooltip\r\n title: string;\r\n\r\n /** Overrides the disable clear value of the containing OuiSort for this OuiSortable. */\r\n @Input()\r\n get disableClear(): boolean {\r\n return this._disableClear;\r\n }\r\n set disableClear(v) {\r\n this._disableClear = coerceBooleanProperty(v);\r\n }\r\n private _disableClear: boolean;\r\n private _monitorSubscription: Subscription = Subscription.EMPTY;\r\n constructor(\r\n public _intl: OuiSortHeaderIntl,\r\n changeDetectorRef: ChangeDetectorRef,\r\n protected elementRef: ElementRef,\r\n protected _differs: IterableDiffers,\r\n private _focusMonitor: FocusMonitor,\r\n private _ngZone: NgZone,\r\n @Optional() public _sort: OuiSort,\r\n @Inject('OUI_SORT_HEADER_COLUMN_DEF')\r\n @Optional()\r\n public _columnDef: OuiSortHeaderColumnDef,\r\n private _elementRef: ElementRef\r\n ) {\r\n // Note that we use a string token for the `_columnDef`, because the value is provided both by\r\n // `once-ui/table` and `cdk/table` and we can't have the CDK depending on once-ui,\r\n // and we want to avoid having the sort header depending on the CDK table because\r\n // of this single reference.\r\n super();\r\n this._monitorSubscription = this._focusMonitor\r\n .monitor(this.elementRef, true)\r\n .subscribe(() => this._ngZone.run(() => {}));\r\n\r\n if (!_sort) {\r\n throw getSortHeaderNotContainedWithinSortError();\r\n }\r\n\r\n this._rerenderSubscription = merge(\r\n _sort.sortChange,\r\n _sort._stateChanges,\r\n _intl.changes\r\n ).subscribe(() => {\r\n if (this._isSorted()) {\r\n this._updateArrowDirection();\r\n }\r\n\r\n // If this header was recently active and now no longer sorted, animate away the arrow.\r\n if (\r\n !this._isSorted() &&\r\n this._viewState &&\r\n this._viewState.toState === 'active'\r\n ) {\r\n this._disableViewStateAnimation = false;\r\n this._setAnimationTransitionState({\r\n fromState: 'active',\r\n toState: this._arrowDirection,\r\n });\r\n }\r\n\r\n changeDetectorRef.markForCheck();\r\n });\r\n }\r\n\r\n ngOnInit() {\r\n const columnHeading: string = this._elementRef.nativeElement.innerText;\r\n this.title = 'Sort by ' + columnHeading;\r\n if (!this.id && this._columnDef) {\r\n this.id = this._columnDef.name;\r\n }\r\n\r\n // Initialize the direction of the arrow and set the view state to be immediately that state.\r\n this._updateArrowDirection();\r\n this._setAnimationTransitionState({\r\n toState: this._isSorted() ? 'active' : this._arrowDirection,\r\n });\r\n\r\n this._sort.register(this);\r\n }\r\n\r\n ngOnDestroy() {\r\n this._sort.deregister(this);\r\n this._rerenderSubscription.unsubscribe();\r\n this._focusMonitor.stopMonitoring(this.elementRef);\r\n this._monitorSubscription.unsubscribe();\r\n }\r\n\r\n /**\r\n * Sets the \"hint\" state such that the arrow will be semi-transparently displayed as a hint to the\r\n * user showing what the active sort will become. If set to false, the arrow will fade away.\r\n */\r\n _setIndicatorHintVisible(visible: boolean) {\r\n // No-op if the sort header is disabled - should not make the hint visible.\r\n if (this._isDisabled() && visible) {\r\n return;\r\n }\r\n\r\n this._showIndicatorHint = visible;\r\n\r\n if (!this._isSorted()) {\r\n this._updateArrowDirection();\r\n if (this._showIndicatorHint) {\r\n this._setAnimationTransitionState({\r\n fromState: this._arrowDirection,\r\n toState: 'hint',\r\n });\r\n } else {\r\n this._setAnimationTransitionState({\r\n fromState: 'hint',\r\n toState: this._arrowDirection,\r\n });\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Sets the animation transition view state for the arrow's position and opacity. If the\r\n * `disableViewStateAnimation` flag is set to true, the `fromState` will be ignored so that\r\n * no animation appears.\r\n */\r\n _setAnimationTransitionState(viewState: ArrowViewStateTransition) {\r\n this._viewState = viewState;\r\n\r\n // If the animation for arrow position state (opacity/translation) should be disabled,\r\n // remove the fromState so that it jumps right to the toState.\r\n if (this._disableViewStateAnimation) {\r\n this._viewState = { toState: viewState.toState };\r\n }\r\n }\r\n\r\n /** Triggers the sort on this sort header and removes the indicator hint. */\r\n _handleClick() {\r\n if (this._isDisabled()) {\r\n return;\r\n }\r\n\r\n this._sort.sort(this);\r\n\r\n // Do not show the animation if the header was already shown in the right position.\r\n if (\r\n this._viewState.toState === 'hint' ||\r\n this._viewState.toState === 'active'\r\n ) {\r\n this._disableViewStateAnimation = true;\r\n }\r\n\r\n // If the arrow is now sorted, animate the arrow into place. Otherwise, animate it away into\r\n // the direction it is facing.\r\n const viewState: ArrowViewStateTransition = this._isSorted()\r\n ? { fromState: this._arrowDirection, toState: 'active' }\r\n : { fromState: 'active', toState: this._arrowDirection };\r\n this._setAnimationTransitionState(viewState);\r\n\r\n this._showIndicatorHint = false;\r\n }\r\n\r\n /** Whether this OuiSortHeader is currently sorted in either ascending or descending order. */\r\n _isSorted() {\r\n return (\r\n this._sort.active === this.id &&\r\n (this._sort.direction === 'asc' || this._sort.direction === 'desc')\r\n );\r\n }\r\n\r\n /** Returns the animation state for the arrow direction (indicator and pointers). */\r\n _getArrowDirectionState() {\r\n return `${this._isSorted() ? 'active-' : ''}${this._arrowDirection}`;\r\n }\r\n\r\n /** Returns the arrow position state (opacity, translation). */\r\n _getArrowViewState() {\r\n const fromState = this._viewState.fromState;\r\n return (fromState ? `${fromState}-to-` : '') + this._viewState.toState;\r\n }\r\n\r\n /**\r\n * Updates the direction the arrow should be pointing. If it is not sorted, the arrow should be\r\n * facing the start direction. Otherwise if it is sorted, the arrow should point in the currently\r\n * active sorted direction. The reason this is updated through a function is because the direction\r\n * should only be changed at specific times - when deactivated but the hint is displayed and when\r\n * the sort is active and the direction changes. Otherwise the arrow's direction should linger\r\n * in cases such as the sort becoming deactivated but we want to animate the arrow away while\r\n * preserving its direction, even though the next sort direction is actually different and should\r\n * only be changed once the arrow displays again (hint or activation).\r\n */\r\n _updateArrowDirection() {\r\n this._arrowDirection = this._isSorted()\r\n ? this._sort.direction\r\n : this.start || this._sort.start;\r\n }\r\n\r\n _isDisabled() {\r\n return this._sort.disabled || this.disabled;\r\n }\r\n\r\n /**\r\n * Gets the aria-sort attribute that should be applied to this sort header. If this header\r\n * is not sorted, returns null so that the attribute is removed from the host element. Aria spec\r\n * says that the aria-sort property should only be present on one header at a time, so removing\r\n * ensures this is true.\r\n */\r\n _getAriaSortAttribute() {\r\n if (!this._isSorted()) {\r\n return null;\r\n }\r\n\r\n return this._sort.direction === 'asc' ? 'ascending' : 'descending';\r\n }\r\n}\r\n", + "sourceCode": "import { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n Input,\n OnDestroy,\n OnInit,\n Optional,\n ViewEncapsulation,\n Inject,\n ElementRef,\n IterableDiffers,\n NgZone,\n} from '@angular/core';\nimport { CanDisable, CanDisableCtor, mixinDisabled } from '../core';\nimport { merge, Subscription } from 'rxjs';\nimport { OuiSort, OuiSortable } from './sort';\nimport { ouiSortAnimations } from './sort-animations';\nimport { SortDirection } from './sort-direction';\nimport { getSortHeaderNotContainedWithinSortError } from './sort-errors';\nimport { OuiSortHeaderIntl } from './sort-header-intl';\nimport { FocusMonitor } from '@angular/cdk/a11y';\n\n// Boilerplate for applying mixins to the sort header.\n/** @docs-private */\nexport class OuiSortHeaderBase {}\nexport const _OuiSortHeaderMixinBase: CanDisableCtor &\n typeof OuiSortHeaderBase = mixinDisabled(OuiSortHeaderBase);\n\n/**\n * Valid positions for the arrow to be in for its opacity and translation. If the state is a\n * sort direction, the position of the arrow will be above/below and opacity 0. If the state is\n * hint, the arrow will be in the center with a slight opacity. Active state means the arrow will\n * be fully opaque in the center.\n *\n * @docs-private\n */\nexport type ArrowViewState = SortDirection | 'hint' | 'active';\n\n/**\n * States describing the arrow's animated position (animating fromState to toState).\n * If the fromState is not defined, there will be no animated transition to the toState.\n *\n * @docs-private\n */\nexport interface ArrowViewStateTransition {\n fromState?: ArrowViewState;\n toState: ArrowViewState;\n}\n\n/** Column definition associated with a `OuiSortHeader`. */\ninterface OuiSortHeaderColumnDef {\n name: string;\n}\n\n/**\n * Applies sorting behavior (click to change sort) and styles to an element, including an\n * arrow to display the current sort direction.\n *\n * Must be provided with an id and contained within a parent OuiSort directive.\n *\n * If used on header cells in a CdkTable, it will automatically default its id from its containing\n * column definition.\n */\n@Component({\n // eslint-disable-next-line\n selector: '[oui-sort-header]',\n exportAs: 'ouiSortHeader',\n templateUrl: 'sort-header.html',\n styleUrls: ['sort-header.scss'],\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n '(click)': '_handleClick()',\n '(mouseenter)': '_setIndicatorHintVisible(true)',\n '(longpress)': '_setIndicatorHintVisible(true)',\n '(mouseleave)': '_setIndicatorHintVisible(false)',\n '[attr.aria-sort]': '_getAriaSortAttribute()',\n '[class.oui-sort-header-disabled]': '_isDisabled()',\n },\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property\n inputs: ['disabled'],\n animations: [\n ouiSortAnimations.indicator,\n ouiSortAnimations.leftPointer,\n ouiSortAnimations.rightPointer,\n ouiSortAnimations.arrowOpacity,\n ouiSortAnimations.arrowPosition,\n ouiSortAnimations.allowChildren,\n ],\n})\nexport class OuiSortHeader\n extends _OuiSortHeaderMixinBase\n implements CanDisable, OuiSortable, OnDestroy, OnInit\n{\n private _rerenderSubscription: Subscription;\n\n /**\n * Flag set to true when the indicator should be displayed while the sort is not active. Used to\n * provide an affordance that the header is sortable by showing on focus and hover.\n */\n _showIndicatorHint = false;\n\n /**\n * The view transition state of the arrow (translation/ opacity) - indicates its `from` and `to`\n * position through the animation. If animations are currently disabled, the fromState is removed\n * so that there is no animation displayed.\n */\n _viewState: ArrowViewStateTransition;\n\n /** The direction the arrow should be facing according to the current state. */\n _arrowDirection: SortDirection = '';\n\n /**\n * Whether the view state animation should show the transition between the `from` and `to` states.\n */\n _disableViewStateAnimation = false;\n\n /**\n * ID of this sort header. If used within the context of a CdkColumnDef, this will default to\n * the column's name.\n */\n // eslint-disable-next-line @angular-eslint/no-input-rename\n @Input('oui-sort-header') id: string;\n\n /** Sets the position of the arrow that displays when sorted. */\n @Input() arrowPosition: 'before' | 'after' = 'after';\n\n /** Overrides the sort start value of the containing OuiSort for this OuiSortable. */\n @Input() start: 'asc' | 'desc';\n\n // To set browser tooltip\n title: string;\n\n /** Overrides the disable clear value of the containing OuiSort for this OuiSortable. */\n @Input()\n get disableClear(): boolean {\n return this._disableClear;\n }\n set disableClear(v) {\n this._disableClear = coerceBooleanProperty(v);\n }\n private _disableClear: boolean;\n private _monitorSubscription: Subscription = Subscription.EMPTY;\n constructor(\n public _intl: OuiSortHeaderIntl,\n changeDetectorRef: ChangeDetectorRef,\n protected elementRef: ElementRef,\n protected _differs: IterableDiffers,\n private _focusMonitor: FocusMonitor,\n private _ngZone: NgZone,\n @Optional() public _sort: OuiSort,\n @Inject('OUI_SORT_HEADER_COLUMN_DEF')\n @Optional()\n public _columnDef: OuiSortHeaderColumnDef,\n private _elementRef: ElementRef\n ) {\n // Note that we use a string token for the `_columnDef`, because the value is provided both by\n // `once-ui/table` and `cdk/table` and we can't have the CDK depending on once-ui,\n // and we want to avoid having the sort header depending on the CDK table because\n // of this single reference.\n super();\n this._monitorSubscription = this._focusMonitor\n .monitor(this.elementRef, true)\n .subscribe(() => this._ngZone.run(() => {}));\n\n if (!_sort) {\n throw getSortHeaderNotContainedWithinSortError();\n }\n\n this._rerenderSubscription = merge(\n _sort.sortChange,\n _sort._stateChanges,\n _intl.changes\n ).subscribe(() => {\n if (this._isSorted()) {\n this._updateArrowDirection();\n }\n\n // If this header was recently active and now no longer sorted, animate away the arrow.\n if (\n !this._isSorted() &&\n this._viewState &&\n this._viewState.toState === 'active'\n ) {\n this._disableViewStateAnimation = false;\n this._setAnimationTransitionState({\n fromState: 'active',\n toState: this._arrowDirection,\n });\n }\n\n changeDetectorRef.markForCheck();\n });\n }\n\n ngOnInit() {\n const columnHeading: string = this._elementRef.nativeElement.innerText;\n this.title = 'Sort by ' + columnHeading;\n if (!this.id && this._columnDef) {\n this.id = this._columnDef.name;\n }\n\n // Initialize the direction of the arrow and set the view state to be immediately that state.\n this._updateArrowDirection();\n this._setAnimationTransitionState({\n toState: this._isSorted() ? 'active' : this._arrowDirection,\n });\n\n this._sort.register(this);\n }\n\n ngOnDestroy() {\n this._sort.deregister(this);\n this._rerenderSubscription.unsubscribe();\n this._focusMonitor.stopMonitoring(this.elementRef);\n this._monitorSubscription.unsubscribe();\n }\n\n /**\n * Sets the \"hint\" state such that the arrow will be semi-transparently displayed as a hint to the\n * user showing what the active sort will become. If set to false, the arrow will fade away.\n */\n _setIndicatorHintVisible(visible: boolean) {\n // No-op if the sort header is disabled - should not make the hint visible.\n if (this._isDisabled() && visible) {\n return;\n }\n\n this._showIndicatorHint = visible;\n\n if (!this._isSorted()) {\n this._updateArrowDirection();\n if (this._showIndicatorHint) {\n this._setAnimationTransitionState({\n fromState: this._arrowDirection,\n toState: 'hint',\n });\n } else {\n this._setAnimationTransitionState({\n fromState: 'hint',\n toState: this._arrowDirection,\n });\n }\n }\n }\n\n /**\n * Sets the animation transition view state for the arrow's position and opacity. If the\n * `disableViewStateAnimation` flag is set to true, the `fromState` will be ignored so that\n * no animation appears.\n */\n _setAnimationTransitionState(viewState: ArrowViewStateTransition) {\n this._viewState = viewState;\n\n // If the animation for arrow position state (opacity/translation) should be disabled,\n // remove the fromState so that it jumps right to the toState.\n if (this._disableViewStateAnimation) {\n this._viewState = { toState: viewState.toState };\n }\n }\n\n /** Triggers the sort on this sort header and removes the indicator hint. */\n _handleClick() {\n if (this._isDisabled()) {\n return;\n }\n\n this._sort.sort(this);\n\n // Do not show the animation if the header was already shown in the right position.\n if (\n this._viewState.toState === 'hint' ||\n this._viewState.toState === 'active'\n ) {\n this._disableViewStateAnimation = true;\n }\n\n // If the arrow is now sorted, animate the arrow into place. Otherwise, animate it away into\n // the direction it is facing.\n const viewState: ArrowViewStateTransition = this._isSorted()\n ? { fromState: this._arrowDirection, toState: 'active' }\n : { fromState: 'active', toState: this._arrowDirection };\n this._setAnimationTransitionState(viewState);\n\n this._showIndicatorHint = false;\n }\n\n /** Whether this OuiSortHeader is currently sorted in either ascending or descending order. */\n _isSorted() {\n return (\n this._sort.active === this.id &&\n (this._sort.direction === 'asc' || this._sort.direction === 'desc')\n );\n }\n\n /** Returns the animation state for the arrow direction (indicator and pointers). */\n _getArrowDirectionState() {\n return `${this._isSorted() ? 'active-' : ''}${this._arrowDirection}`;\n }\n\n /** Returns the arrow position state (opacity, translation). */\n _getArrowViewState() {\n const fromState = this._viewState.fromState;\n return (fromState ? `${fromState}-to-` : '') + this._viewState.toState;\n }\n\n /**\n * Updates the direction the arrow should be pointing. If it is not sorted, the arrow should be\n * facing the start direction. Otherwise if it is sorted, the arrow should point in the currently\n * active sorted direction. The reason this is updated through a function is because the direction\n * should only be changed at specific times - when deactivated but the hint is displayed and when\n * the sort is active and the direction changes. Otherwise the arrow's direction should linger\n * in cases such as the sort becoming deactivated but we want to animate the arrow away while\n * preserving its direction, even though the next sort direction is actually different and should\n * only be changed once the arrow displays again (hint or activation).\n */\n _updateArrowDirection() {\n this._arrowDirection = this._isSorted()\n ? this._sort.direction\n : this.start || this._sort.start;\n }\n\n _isDisabled() {\n return this._sort.disabled || this.disabled;\n }\n\n /**\n * Gets the aria-sort attribute that should be applied to this sort header. If this header\n * is not sorted, returns null so that the attribute is removed from the host element. Aria spec\n * says that the aria-sort property should only be present on one header at a time, so removing\n * ensures this is true.\n */\n _getAriaSortAttribute() {\n if (!this._isSorted()) {\n return null;\n }\n\n return this._sort.direction === 'asc' ? 'ascending' : 'descending';\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": "$arrow-selected: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='12px' height='12px' viewBox='0 0 12 12' version='1.1'%3E%3C!-- Generator: Sketch 53.1 (72631) - https://sketchapp.com --%3E%3Ctitle%3EArtboard Copy 4%3C/title%3E%3Cdesc%3ECreated with Sketch.%3C/desc%3E%3Cg id='Artboard-Copy-4' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Cpath d='M3.68421053,6.31578947 L10,6.31578947 L10,8 L3.68421053,8 L2,8 L2,-1.42108547e-12 L3.68421053,-1.42108547e-12 L3.68421053,6.31578947 Z' id='Combined-Shape-Copy-8' fill='%234A4A4A' transform='translate(6.000000, 4.000000) scale(-1, 1) rotate(-45.000000) translate(-6.000000, -4.000000) '/%3E%3C/g%3E%3C/svg%3E\");\r\n$arrow-faded: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMTJweCIgaGVpZ2h0PSIxMnB4IiB2aWV3Qm94PSIwIDAgMTIgMTIiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUzLjEgKDcyNjMxKSAtIGh0dHBzOi8vc2tldGNoYXBwLmNvbSAtLT4KICAgIDx0aXRsZT5BcnRib2FyZCBDb3B5IDU8L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZyBpZD0iQXJ0Ym9hcmQtQ29weS01IiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4KICAgICAgICA8cGF0aCBkPSJNMy42ODQyMTA1Myw2LjMxNTc4OTQ3IEwxMCw2LjMxNTc4OTQ3IEwxMCw4IEwzLjY4NDIxMDUzLDggTDIsOCBMMiwtMS40MjEwODU0N2UtMTIgTDMuNjg0MjEwNTMsLTEuNDIxMDg1NDdlLTEyIEwzLjY4NDIxMDUzLDYuMzE1Nzg5NDcgWiIgaWQ9IkNvbWJpbmVkLVNoYXBlLUNvcHktOCIgZmlsbD0iIzhDOEM4QyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNi4wMDAwMDAsIDQuMDAwMDAwKSBzY2FsZSgtMSwgMSkgcm90YXRlKC00NS4wMDAwMDApIHRyYW5zbGF0ZSgtNi4wMDAwMDAsIC00LjAwMDAwMCkgIj48L3BhdGg+CiAgICA8L2c+Cjwvc3ZnPg==');\r\n$arrow-margin: 1px 0px 0px 7px;\r\n\r\n.oui-sort-header-button {\r\n border: none;\r\n background: 0 0;\r\n display: inline-block;\r\n align-items: center;\r\n padding: 0;\r\n cursor: inherit;\r\n outline: 0;\r\n font: inherit;\r\n color: currentColor;\r\n text-transform: capitalize;\r\n}\r\n.oui-sort-header-arrow {\r\n display: inline-block;\r\n vertical-align: middle;\r\n cursor: pointer;\r\n position: relative;\r\n width: 16px;\r\n height: 16px;\r\n .oui-sort-arrow-outer {\r\n position: absolute;\r\n width: 16px;\r\n height: 16px;\r\n background: #e8e8e8;\r\n border-radius: 50%;\r\n margin: 0px 0 0 5px;\r\n top: -1px;\r\n opacity: 0;\r\n }\r\n}\r\n.oui-sort-header-arrow-selected {\r\n height: 12px;\r\n width: 12px;\r\n background: $arrow-selected center center no-repeat;\r\n display: inline-block;\r\n margin: $arrow-margin;\r\n vertical-align: top;\r\n position: relative;\r\n}\r\n.oui-sort-header-arrow-faded {\r\n opacity: 0;\r\n height: 12px;\r\n width: 12px;\r\n background: $arrow-faded center center no-repeat;\r\n display: inline-block;\r\n margin: $arrow-margin;\r\n vertical-align: top;\r\n position: relative;\r\n}\r\n.oui-header-cell:hover .oui-sort-header-arrow-faded {\r\n opacity: 1;\r\n}\r\n.oui-header-cell {\r\n &[class^='cdk'],\r\n &[class$='focused'] {\r\n .oui-sort-header-arrow-faded {\r\n opacity: 1;\r\n }\r\n .oui-sort-arrow-outer {\r\n opacity: 1;\r\n }\r\n }\r\n}\r\n", + "data": "$arrow-selected: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='12px' height='12px' viewBox='0 0 12 12' version='1.1'%3E%3C!-- Generator: Sketch 53.1 (72631) - https://sketchapp.com --%3E%3Ctitle%3EArtboard Copy 4%3C/title%3E%3Cdesc%3ECreated with Sketch.%3C/desc%3E%3Cg id='Artboard-Copy-4' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Cpath d='M3.68421053,6.31578947 L10,6.31578947 L10,8 L3.68421053,8 L2,8 L2,-1.42108547e-12 L3.68421053,-1.42108547e-12 L3.68421053,6.31578947 Z' id='Combined-Shape-Copy-8' fill='%234A4A4A' transform='translate(6.000000, 4.000000) scale(-1, 1) rotate(-45.000000) translate(-6.000000, -4.000000) '/%3E%3C/g%3E%3C/svg%3E\");\n$arrow-faded: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMTJweCIgaGVpZ2h0PSIxMnB4IiB2aWV3Qm94PSIwIDAgMTIgMTIiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUzLjEgKDcyNjMxKSAtIGh0dHBzOi8vc2tldGNoYXBwLmNvbSAtLT4KICAgIDx0aXRsZT5BcnRib2FyZCBDb3B5IDU8L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZyBpZD0iQXJ0Ym9hcmQtQ29weS01IiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4KICAgICAgICA8cGF0aCBkPSJNMy42ODQyMTA1Myw2LjMxNTc4OTQ3IEwxMCw2LjMxNTc4OTQ3IEwxMCw4IEwzLjY4NDIxMDUzLDggTDIsOCBMMiwtMS40MjEwODU0N2UtMTIgTDMuNjg0MjEwNTMsLTEuNDIxMDg1NDdlLTEyIEwzLjY4NDIxMDUzLDYuMzE1Nzg5NDcgWiIgaWQ9IkNvbWJpbmVkLVNoYXBlLUNvcHktOCIgZmlsbD0iIzhDOEM4QyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNi4wMDAwMDAsIDQuMDAwMDAwKSBzY2FsZSgtMSwgMSkgcm90YXRlKC00NS4wMDAwMDApIHRyYW5zbGF0ZSgtNi4wMDAwMDAsIC00LjAwMDAwMCkgIj48L3BhdGg+CiAgICA8L2c+Cjwvc3ZnPg==');\n$arrow-margin: 1px 0px 0px 7px;\n\n.oui-sort-header-button {\n border: none;\n background: 0 0;\n display: inline-block;\n align-items: center;\n padding: 0;\n cursor: inherit;\n outline: 0;\n font: inherit;\n color: currentColor;\n text-transform: capitalize;\n}\n.oui-sort-header-arrow {\n display: inline-block;\n vertical-align: middle;\n cursor: pointer;\n position: relative;\n width: 16px;\n height: 16px;\n .oui-sort-arrow-outer {\n position: absolute;\n width: 16px;\n height: 16px;\n background: #e8e8e8;\n border-radius: 50%;\n margin: 0px 0 0 5px;\n top: -1px;\n opacity: 0;\n }\n}\n.oui-sort-header-arrow-selected {\n height: 12px;\n width: 12px;\n background: $arrow-selected center center no-repeat;\n display: inline-block;\n margin: $arrow-margin;\n vertical-align: top;\n position: relative;\n}\n.oui-sort-header-arrow-faded {\n opacity: 0;\n height: 12px;\n width: 12px;\n background: $arrow-faded center center no-repeat;\n display: inline-block;\n margin: $arrow-margin;\n vertical-align: top;\n position: relative;\n}\n.oui-header-cell:hover .oui-sort-header-arrow-faded {\n opacity: 1;\n}\n.oui-header-cell {\n &[class^='cdk'],\n &[class$='focused'] {\n .oui-sort-header-arrow-faded {\n opacity: 1;\n }\n .oui-sort-arrow-outer {\n opacity: 1;\n }\n }\n}\n", "styleUrl": "sort-header.scss" } ], @@ -39290,11 +49172,11 @@ } } }, - "templateData": "\r\n \r\n \r\n \r\n\r\n
\r\n
\r\n
\r\n \r\n\r\n" + "templateData": "\n \n \n \n\n
\n
\n
\n \n\n" }, { "name": "OuiTable", - "id": "component-OuiTable-8074bc1c959936c37161994b19faefcfa0beb768ba4bb48192a5ae5533efd5c43ce10c4595f3bf3524fe644b63bf286dc4fa81815fcce464dd3ce17a84671d9c", + "id": "component-OuiTable-b8fe2910e9f77472decbff257ff0019134d822e944ce42431198e4548f5364d765be75c4dd05e284b825b48df1c50caba0d1006ed144e3d54c9bb03d070b06bd", "file": "ui/src/components/table/table.ts", "changeDetection": "ChangeDetectionStrategy.Default", "encapsulation": [ @@ -39372,11 +49254,11 @@ "description": "

Wrapper for the CdkTable with Once-UI design styles.

\n", "rawdescription": "\n\nWrapper for the CdkTable with Once-UI design styles.\n", "type": "component", - "sourceCode": "import {\r\n CDK_TABLE_TEMPLATE,\r\n CdkTable,\r\n CDK_TABLE,\r\n _CoalescedStyleScheduler,\r\n _COALESCED_STYLE_SCHEDULER,\r\n STICKY_POSITIONING_LISTENER,\r\n} from '@angular/cdk/table';\r\nimport {\r\n ChangeDetectionStrategy,\r\n Component,\r\n Directive,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\nimport {\r\n _DisposeViewRepeaterStrategy,\r\n _RecycleViewRepeaterStrategy,\r\n _VIEW_REPEATER_STRATEGY,\r\n} from '@angular/cdk/collections';\r\n\r\n/**\r\n * Enables the recycle view repeater strategy, which reduces rendering latency. Not compatible with\r\n * tables that animate rows.\r\n */\r\n@Directive({\r\n selector: 'oui-table[recycleRows], table[oui-table][recycleRows]',\r\n providers: [\r\n {\r\n provide: _VIEW_REPEATER_STRATEGY,\r\n useClass: _RecycleViewRepeaterStrategy,\r\n },\r\n ],\r\n})\r\nexport class MatRecycleRows {}\r\n\r\n/**\r\n * Wrapper for the CdkTable with Once-UI design styles.\r\n */\r\n@Component({\r\n selector: 'oui-table, table[oui-table]',\r\n exportAs: 'ouiTable',\r\n template: CDK_TABLE_TEMPLATE,\r\n styleUrls: ['table.scss'],\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n class: 'oui-table',\r\n '[class.oui-table-fixed-layout]': 'fixedLayout',\r\n },\r\n providers: [\r\n // TODO(michaeljamesparsons) Abstract the view repeater strategy to a directive API so this code\r\n // is only included in the build if used.\r\n {\r\n provide: _VIEW_REPEATER_STRATEGY,\r\n useClass: _DisposeViewRepeaterStrategy,\r\n },\r\n { provide: CdkTable, useExisting: OuiTable },\r\n { provide: CDK_TABLE, useExisting: OuiTable },\r\n { provide: _COALESCED_STYLE_SCHEDULER, useClass: _CoalescedStyleScheduler },\r\n // Prevent nested tables from seeing this table's StickyPositioningListener.\r\n { provide: STICKY_POSITIONING_LISTENER, useValue: null },\r\n ],\r\n encapsulation: ViewEncapsulation.None,\r\n // See note on CdkTable for explanation on why this uses the default change detection strategy.\r\n // eslint:disable-next-line:validate-decorators\r\n changeDetection: ChangeDetectionStrategy.Default,\r\n})\r\nexport class OuiTable extends CdkTable {\r\n /** Overrides the sticky CSS class set by the `CdkTable`. */\r\n protected override stickyCssClass = 'oui-table-sticky';\r\n\r\n /** Overrides the need to add position: sticky on every sticky cell element in `CdkTable`. */\r\n protected override needsPositionStickyOnElement = false;\r\n}\r\n", + "sourceCode": "import {\n CDK_TABLE_TEMPLATE,\n CdkTable,\n CDK_TABLE,\n _CoalescedStyleScheduler,\n _COALESCED_STYLE_SCHEDULER,\n STICKY_POSITIONING_LISTENER,\n} from '@angular/cdk/table';\nimport {\n ChangeDetectionStrategy,\n Component,\n Directive,\n ViewEncapsulation,\n} from '@angular/core';\nimport {\n _DisposeViewRepeaterStrategy,\n _RecycleViewRepeaterStrategy,\n _VIEW_REPEATER_STRATEGY,\n} from '@angular/cdk/collections';\n\n/**\n * Enables the recycle view repeater strategy, which reduces rendering latency. Not compatible with\n * tables that animate rows.\n */\n@Directive({\n selector: 'oui-table[recycleRows], table[oui-table][recycleRows]',\n providers: [\n {\n provide: _VIEW_REPEATER_STRATEGY,\n useClass: _RecycleViewRepeaterStrategy,\n },\n ],\n})\nexport class MatRecycleRows {}\n\n/**\n * Wrapper for the CdkTable with Once-UI design styles.\n */\n@Component({\n selector: 'oui-table, table[oui-table]',\n exportAs: 'ouiTable',\n template: CDK_TABLE_TEMPLATE,\n styleUrls: ['table.scss'],\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n class: 'oui-table',\n '[class.oui-table-fixed-layout]': 'fixedLayout',\n },\n providers: [\n // TODO(michaeljamesparsons) Abstract the view repeater strategy to a directive API so this code\n // is only included in the build if used.\n {\n provide: _VIEW_REPEATER_STRATEGY,\n useClass: _DisposeViewRepeaterStrategy,\n },\n { provide: CdkTable, useExisting: OuiTable },\n { provide: CDK_TABLE, useExisting: OuiTable },\n { provide: _COALESCED_STYLE_SCHEDULER, useClass: _CoalescedStyleScheduler },\n // Prevent nested tables from seeing this table's StickyPositioningListener.\n { provide: STICKY_POSITIONING_LISTENER, useValue: null },\n ],\n encapsulation: ViewEncapsulation.None,\n // See note on CdkTable for explanation on why this uses the default change detection strategy.\n // eslint:disable-next-line:validate-decorators\n changeDetection: ChangeDetectionStrategy.Default,\n})\nexport class OuiTable extends CdkTable {\n /** Overrides the sticky CSS class set by the `CdkTable`. */\n protected override stickyCssClass = 'oui-table-sticky';\n\n /** Overrides the need to add position: sticky on every sticky cell element in `CdkTable`. */\n protected override needsPositionStickyOnElement = false;\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": "$oui-header-row-height: 56px;\r\n$oui-row-height: 48px;\r\n$oui-row-horizontal-padding: 24px;\r\n$oui-focus-color: #e8e8e8;\r\n$oui-row-background-color: #f6f6f6;\r\n\r\n/**\r\n * Flex-based table structure\r\n */\r\noui-table {\r\n display: block;\r\n border-spacing: 0;\r\n}\r\n\r\noui-header-row {\r\n background-color: $oui-row-background-color;\r\n border-bottom: 1px solid #dfdfdf;\r\n border-top: 1px solid #dfdfdf !important;\r\n}\r\n\r\noui-header-cell {\r\n padding: 16px 12.5px;\r\n font-size: 14px;\r\n font-weight: 700;\r\n line-height: 19px;\r\n color: #333;\r\n text-align: left;\r\n vertical-align: middle;\r\n}\r\n\r\noui-cell {\r\n padding: 13px 12.5px;\r\n vertical-align: middle;\r\n font-size: 14px;\r\n color: #333;\r\n line-height: 20px;\r\n}\r\n.oui-row .oui-cell:nth-child(1),\r\n.oui-header-row .oui-header-cell:nth-child(1) {\r\n padding-left: 16px;\r\n}\r\n.oui-row .oui-cell:nth-last-child(1),\r\n.oui-header-row .oui-header-cell:nth-last-child(1) {\r\n padding-right: 16px;\r\n}\r\noui-row,\r\noui-header-row,\r\noui-footer-row {\r\n display: flex;\r\n // Define a border style, but then widths default to 3px. Reset them to 0px except the bottom\r\n // which should be 1px;\r\n border-width: 0;\r\n border-bottom: 1px solid #dfdfdf;\r\n align-items: center;\r\n box-sizing: border-box;\r\n\r\n // Workaround for https://goo.gl/pFmjJD in IE 11. Adds a pseudo\r\n // element that will stretch the row the correct height. See:\r\n // https://connect.microsoft.com/IE/feedback/details/802625\r\n &::after {\r\n display: inline-block;\r\n min-height: inherit;\r\n content: '';\r\n }\r\n}\r\noui-row {\r\n &:hover {\r\n background: $oui-row-background-color;\r\n cursor: pointer;\r\n }\r\n &[class^='cdk'],\r\n &[class$='focused'] {\r\n background: $oui-row-background-color;\r\n outline: none;\r\n }\r\n}\r\n\r\noui-cell,\r\noui-header-cell,\r\noui-footer-cell {\r\n flex: 1;\r\n display: flex;\r\n align-items: center;\r\n overflow: hidden;\r\n word-wrap: break-word;\r\n min-height: inherit;\r\n}\r\n\r\n/**\r\n * Native HTML table structure\r\n */\r\n\r\ntable.oui-table {\r\n border-spacing: 0;\r\n border-collapse: collapse;\r\n border-spacing: 0;\r\n}\r\n\r\ntr.oui-header-row {\r\n background-color: $oui-row-background-color;\r\n border-bottom: 1px solid #dfdfdf;\r\n border-top: 1px solid #dfdfdf;\r\n}\r\n\r\nth.oui-header-cell {\r\n padding: 16px 12.5px;\r\n font-size: 14px;\r\n font-weight: 700;\r\n line-height: 19px;\r\n color: #333;\r\n text-align: left;\r\n vertical-align: middle;\r\n}\r\ntd.oui-cell {\r\n padding: 13px 12.5px;\r\n vertical-align: middle;\r\n font-size: 14px;\r\n color: #333;\r\n line-height: 20px;\r\n}\r\ntr.oui-row {\r\n border-bottom: 1px solid #dfdfdf;\r\n &:hover {\r\n background: $oui-row-background-color;\r\n cursor: pointer;\r\n }\r\n &[class^='cdk'],\r\n &[class$='focused'] {\r\n background: $oui-row-background-color;\r\n outline: none;\r\n }\r\n}\r\n", + "data": "$oui-header-row-height: 56px;\n$oui-row-height: 48px;\n$oui-row-horizontal-padding: 24px;\n$oui-focus-color: #e8e8e8;\n$oui-row-background-color: #f6f6f6;\n\n/**\n * Flex-based table structure\n */\noui-table {\n display: block;\n border-spacing: 0;\n}\n\noui-header-row {\n background-color: $oui-row-background-color;\n border-bottom: 1px solid #dfdfdf;\n border-top: 1px solid #dfdfdf !important;\n}\n\noui-header-cell {\n padding: 16px 12.5px;\n font-size: 14px;\n font-weight: 700;\n line-height: 19px;\n color: #333;\n text-align: left;\n vertical-align: middle;\n}\n\noui-cell {\n padding: 13px 12.5px;\n vertical-align: middle;\n font-size: 14px;\n color: #333;\n line-height: 20px;\n}\n.oui-row .oui-cell:nth-child(1),\n.oui-header-row .oui-header-cell:nth-child(1) {\n padding-left: 16px;\n}\n.oui-row .oui-cell:nth-last-child(1),\n.oui-header-row .oui-header-cell:nth-last-child(1) {\n padding-right: 16px;\n}\noui-row,\noui-header-row,\noui-footer-row {\n display: flex;\n // Define a border style, but then widths default to 3px. Reset them to 0px except the bottom\n // which should be 1px;\n border-width: 0;\n border-bottom: 1px solid #dfdfdf;\n align-items: center;\n box-sizing: border-box;\n\n // Workaround for https://goo.gl/pFmjJD in IE 11. Adds a pseudo\n // element that will stretch the row the correct height. See:\n // https://connect.microsoft.com/IE/feedback/details/802625\n &::after {\n display: inline-block;\n min-height: inherit;\n content: '';\n }\n}\noui-row {\n &:hover {\n background: $oui-row-background-color;\n cursor: pointer;\n }\n &[class^='cdk'],\n &[class$='focused'] {\n background: $oui-row-background-color;\n outline: none;\n }\n}\n\noui-cell,\noui-header-cell,\noui-footer-cell {\n flex: 1;\n display: flex;\n align-items: center;\n overflow: hidden;\n word-wrap: break-word;\n min-height: inherit;\n}\n\n/**\n * Native HTML table structure\n */\n\ntable.oui-table {\n border-spacing: 0;\n border-collapse: collapse;\n border-spacing: 0;\n}\n\ntr.oui-header-row {\n background-color: $oui-row-background-color;\n border-bottom: 1px solid #dfdfdf;\n border-top: 1px solid #dfdfdf;\n}\n\nth.oui-header-cell {\n padding: 16px 12.5px;\n font-size: 14px;\n font-weight: 700;\n line-height: 19px;\n color: #333;\n text-align: left;\n vertical-align: middle;\n}\ntd.oui-cell {\n padding: 13px 12.5px;\n vertical-align: middle;\n font-size: 14px;\n color: #333;\n line-height: 20px;\n}\ntr.oui-row {\n border-bottom: 1px solid #dfdfdf;\n &:hover {\n background: $oui-row-background-color;\n cursor: pointer;\n }\n &[class^='cdk'],\n &[class$='focused'] {\n background: $oui-row-background-color;\n outline: none;\n }\n}\n", "styleUrl": "table.scss" } ], @@ -39385,7 +49267,7 @@ }, { "name": "OuiTableCustomStorybook", - "id": "component-OuiTableCustomStorybook-c5c59f105028101b05ae93cc1e59f31a10792111c2c5afbf8541837387a2812c3ec084c84c0788a29287bfeae36015d14a30849723706b51fe1746a9ec36cad1", + "id": "component-OuiTableCustomStorybook-fedcbe70cd705819ae3edf240f9da33304b30db338f6a2a8bea5523f7cb38c61a7cdafe1fe6a26ea19b38bafb27dc275011f155c77666456905db1bff004032a", "file": "ui/src/stories/table/table.component.ts", "encapsulation": [], "entryComponents": [], @@ -39422,7 +49304,7 @@ "propertiesClass": [ { "name": "INTEGRATIONS", - "defaultValue": "{\r\n paypal: this.sanitizer.bypassSecurityTrustHtml(STORY_ICONS.PAYPAL),\r\n zapier: this.sanitizer.bypassSecurityTrustHtml(STORY_ICONS.ZAPIER),\r\n 'google-calender': this.sanitizer.bypassSecurityTrustHtml(\r\n STORY_ICONS.GOOGLE_CALENDAR\r\n ),\r\n salesforce: this.sanitizer.bypassSecurityTrustHtml(STORY_ICONS.SALESFORCE),\r\n }", + "defaultValue": "{\n paypal: this.sanitizer.bypassSecurityTrustHtml(STORY_ICONS.PAYPAL),\n zapier: this.sanitizer.bypassSecurityTrustHtml(STORY_ICONS.ZAPIER),\n 'google-calender': this.sanitizer.bypassSecurityTrustHtml(\n STORY_ICONS.GOOGLE_CALENDAR\n ),\n salesforce: this.sanitizer.bypassSecurityTrustHtml(STORY_ICONS.SALESFORCE),\n }", "deprecated": false, "deprecationMessage": "", "type": "object", @@ -39432,7 +49314,7 @@ }, { "name": "LICENCES", - "defaultValue": "{\r\n scheduleonce: this.sanitizer.bypassSecurityTrustHtml(\r\n STORY_ICONS.SCHEDULEONCE\r\n ),\r\n chatonce: this.sanitizer.bypassSecurityTrustHtml(STORY_ICONS.CHATONCE),\r\n }", + "defaultValue": "{\n scheduleonce: this.sanitizer.bypassSecurityTrustHtml(\n STORY_ICONS.SCHEDULEONCE\n ),\n chatonce: this.sanitizer.bypassSecurityTrustHtml(STORY_ICONS.CHATONCE),\n }", "deprecated": false, "deprecationMessage": "", "type": "object", @@ -39495,7 +49377,7 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import { STORY_ICONS, USERINFOCOLUMNS } from './const';\r\nimport { Component, Input, OnChanges, OnInit, ViewChild } from '@angular/core';\r\nimport { OuiTableDataSource, OuiSort, OuiPaginator } from '../../components';\r\nimport { DomSanitizer } from '@angular/platform-browser';\r\n@Component({\r\n selector: 'oui-table-storybook',\r\n template: `\r\n \r\n \r\n \r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n {{ column }}\r\n {{ element[column] }}
\r\n\r\n \r\n
\r\n `,\r\n})\r\nexport class OuiTableStorybook implements OnInit, OnChanges {\r\n @ViewChild(OuiSort, { static: true }) sort: OuiSort;\r\n @ViewChild(OuiPaginator, { static: true }) paginator: OuiPaginator;\r\n // displayedColumns: string[] = [];\r\n @Input() users: any[] = [\r\n { position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H' },\r\n { position: 2, name: 'Helium', weight: 4.0026, symbol: 'He' },\r\n { position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li' },\r\n { position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be' },\r\n { position: 5, name: 'Boron', weight: 10.811, symbol: 'B' },\r\n { position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C' },\r\n { position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N' },\r\n { position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O' },\r\n { position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F' },\r\n { position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne' },\r\n { position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H' },\r\n { position: 2, name: 'Helium', weight: 4.0026, symbol: 'He' },\r\n { position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li' },\r\n { position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be' },\r\n { position: 5, name: 'Boron', weight: 10.811, symbol: 'B' },\r\n { position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C' },\r\n { position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N' },\r\n { position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O' },\r\n { position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F' },\r\n { position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne' },\r\n ];\r\n @Input() pageSize: any[] = [];\r\n displayedColumns: string[] = [];\r\n dataSource = new OuiTableDataSource(this.users);\r\n constructor() {}\r\n\r\n ngOnInit() {\r\n // eslint-disable-next-line guard-for-in\r\n for (const key in this.users[0]) {\r\n this.displayedColumns.push(key);\r\n }\r\n }\r\n\r\n ngOnChanges() {\r\n this.dataSource = new OuiTableDataSource(this.users);\r\n this.dataSource.sort = this.sort;\r\n this.dataSource.paginator = this.paginator;\r\n }\r\n\r\n applyFilter(filterValue: string) {\r\n this.dataSource.filter = filterValue.trim().toLowerCase();\r\n }\r\n}\r\n\r\n@Component({\r\n selector: 'oui-table-custom-storybook',\r\n template: `\r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
Users\r\n
\r\n
\r\n
\r\n
\r\n {{ user.name }}\r\n
\r\n
\r\n {{ user.email }}\r\n
\r\n
\r\n
\r\n
Role{{ element.role }}\r\n Integrations\r\n \r\n
\r\n
    \r\n
  • \r\n \r\n
  • \r\n
\r\n
\r\n
\r\n Licenses\r\n \r\n
\r\n
    \r\n
  • \r\n \r\n
  • \r\n
\r\n
\r\n
\r\n Status\r\n {{ element.status }}
\r\n
\r\n `,\r\n})\r\nexport class OuiTableCustomStorybook implements OnChanges {\r\n INTEGRATIONS = {\r\n paypal: this.sanitizer.bypassSecurityTrustHtml(STORY_ICONS.PAYPAL),\r\n zapier: this.sanitizer.bypassSecurityTrustHtml(STORY_ICONS.ZAPIER),\r\n 'google-calender': this.sanitizer.bypassSecurityTrustHtml(\r\n STORY_ICONS.GOOGLE_CALENDAR\r\n ),\r\n salesforce: this.sanitizer.bypassSecurityTrustHtml(STORY_ICONS.SALESFORCE),\r\n };\r\n LICENCES = {\r\n scheduleonce: this.sanitizer.bypassSecurityTrustHtml(\r\n STORY_ICONS.SCHEDULEONCE\r\n ),\r\n chatonce: this.sanitizer.bypassSecurityTrustHtml(STORY_ICONS.CHATONCE),\r\n };\r\n @ViewChild(OuiSort, { static: true }) sort: OuiSort;\r\n userInfoColumns = USERINFOCOLUMNS;\r\n @Input() users: any[] = [];\r\n @Input() pageSize: any[] = [];\r\n userInfoDataSource = new OuiTableDataSource(this.users);\r\n constructor(private sanitizer: DomSanitizer) {}\r\n ngOnChanges() {\r\n this.userInfoDataSource = new OuiTableDataSource(this.users);\r\n this.userInfoDataSource.sort = this.sort;\r\n }\r\n}\r\n", + "sourceCode": "import { STORY_ICONS, USERINFOCOLUMNS } from './const';\nimport { Component, Input, OnChanges, OnInit, ViewChild } from '@angular/core';\nimport { OuiTableDataSource, OuiSort, OuiPaginator } from '../../components';\nimport { DomSanitizer } from '@angular/platform-browser';\n@Component({\n selector: 'oui-table-storybook',\n template: `\n \n \n \n
\n \n \n \n \n \n \n \n
\n {{ column }}\n {{ element[column] }}
\n\n \n
\n `,\n})\nexport class OuiTableStorybook implements OnInit, OnChanges {\n @ViewChild(OuiSort, { static: true }) sort: OuiSort;\n @ViewChild(OuiPaginator, { static: true }) paginator: OuiPaginator;\n // displayedColumns: string[] = [];\n @Input() users: any[] = [\n { position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H' },\n { position: 2, name: 'Helium', weight: 4.0026, symbol: 'He' },\n { position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li' },\n { position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be' },\n { position: 5, name: 'Boron', weight: 10.811, symbol: 'B' },\n { position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C' },\n { position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N' },\n { position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O' },\n { position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F' },\n { position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne' },\n { position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H' },\n { position: 2, name: 'Helium', weight: 4.0026, symbol: 'He' },\n { position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li' },\n { position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be' },\n { position: 5, name: 'Boron', weight: 10.811, symbol: 'B' },\n { position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C' },\n { position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N' },\n { position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O' },\n { position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F' },\n { position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne' },\n ];\n @Input() pageSize: any[] = [];\n displayedColumns: string[] = [];\n dataSource = new OuiTableDataSource(this.users);\n constructor() {}\n\n ngOnInit() {\n // eslint-disable-next-line guard-for-in\n for (const key in this.users[0]) {\n this.displayedColumns.push(key);\n }\n }\n\n ngOnChanges() {\n this.dataSource = new OuiTableDataSource(this.users);\n this.dataSource.sort = this.sort;\n this.dataSource.paginator = this.paginator;\n }\n\n applyFilter(filterValue: string) {\n this.dataSource.filter = filterValue.trim().toLowerCase();\n }\n}\n\n@Component({\n selector: 'oui-table-custom-storybook',\n template: `\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
Users\n
\n
\n
\n
\n {{ user.name }}\n
\n
\n {{ user.email }}\n
\n
\n
\n
Role{{ element.role }}\n Integrations\n \n
\n
    \n
  • \n \n
  • \n
\n
\n
\n Licenses\n \n
\n
    \n
  • \n \n
  • \n
\n
\n
\n Status\n {{ element.status }}
\n
\n `,\n})\nexport class OuiTableCustomStorybook implements OnChanges {\n INTEGRATIONS = {\n paypal: this.sanitizer.bypassSecurityTrustHtml(STORY_ICONS.PAYPAL),\n zapier: this.sanitizer.bypassSecurityTrustHtml(STORY_ICONS.ZAPIER),\n 'google-calender': this.sanitizer.bypassSecurityTrustHtml(\n STORY_ICONS.GOOGLE_CALENDAR\n ),\n salesforce: this.sanitizer.bypassSecurityTrustHtml(STORY_ICONS.SALESFORCE),\n };\n LICENCES = {\n scheduleonce: this.sanitizer.bypassSecurityTrustHtml(\n STORY_ICONS.SCHEDULEONCE\n ),\n chatonce: this.sanitizer.bypassSecurityTrustHtml(STORY_ICONS.CHATONCE),\n };\n @ViewChild(OuiSort, { static: true }) sort: OuiSort;\n userInfoColumns = USERINFOCOLUMNS;\n @Input() users: any[] = [];\n @Input() pageSize: any[] = [];\n userInfoDataSource = new OuiTableDataSource(this.users);\n constructor(private sanitizer: DomSanitizer) {}\n ngOnChanges() {\n this.userInfoDataSource = new OuiTableDataSource(this.users);\n this.userInfoDataSource.sort = this.sort;\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -39531,7 +49413,7 @@ }, { "name": "OuiTableStorybook", - "id": "component-OuiTableStorybook-c5c59f105028101b05ae93cc1e59f31a10792111c2c5afbf8541837387a2812c3ec084c84c0788a29287bfeae36015d14a30849723706b51fe1746a9ec36cad1", + "id": "component-OuiTableStorybook-fedcbe70cd705819ae3edf240f9da33304b30db338f6a2a8bea5523f7cb38c61a7cdafe1fe6a26ea19b38bafb27dc275011f155c77666456905db1bff004032a", "file": "ui/src/stories/table/table.component.ts", "encapsulation": [], "entryComponents": [], @@ -39556,7 +49438,7 @@ }, { "name": "users", - "defaultValue": "[\r\n { position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H' },\r\n { position: 2, name: 'Helium', weight: 4.0026, symbol: 'He' },\r\n { position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li' },\r\n { position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be' },\r\n { position: 5, name: 'Boron', weight: 10.811, symbol: 'B' },\r\n { position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C' },\r\n { position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N' },\r\n { position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O' },\r\n { position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F' },\r\n { position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne' },\r\n { position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H' },\r\n { position: 2, name: 'Helium', weight: 4.0026, symbol: 'He' },\r\n { position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li' },\r\n { position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be' },\r\n { position: 5, name: 'Boron', weight: 10.811, symbol: 'B' },\r\n { position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C' },\r\n { position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N' },\r\n { position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O' },\r\n { position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F' },\r\n { position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne' },\r\n ]", + "defaultValue": "[\n { position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H' },\n { position: 2, name: 'Helium', weight: 4.0026, symbol: 'He' },\n { position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li' },\n { position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be' },\n { position: 5, name: 'Boron', weight: 10.811, symbol: 'B' },\n { position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C' },\n { position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N' },\n { position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O' },\n { position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F' },\n { position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne' },\n { position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H' },\n { position: 2, name: 'Helium', weight: 4.0026, symbol: 'He' },\n { position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li' },\n { position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be' },\n { position: 5, name: 'Boron', weight: 10.811, symbol: 'B' },\n { position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C' },\n { position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N' },\n { position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O' },\n { position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F' },\n { position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne' },\n ]", "deprecated": false, "deprecationMessage": "", "line": 39, @@ -39674,7 +49556,7 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import { STORY_ICONS, USERINFOCOLUMNS } from './const';\r\nimport { Component, Input, OnChanges, OnInit, ViewChild } from '@angular/core';\r\nimport { OuiTableDataSource, OuiSort, OuiPaginator } from '../../components';\r\nimport { DomSanitizer } from '@angular/platform-browser';\r\n@Component({\r\n selector: 'oui-table-storybook',\r\n template: `\r\n \r\n \r\n \r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n {{ column }}\r\n {{ element[column] }}
\r\n\r\n \r\n
\r\n `,\r\n})\r\nexport class OuiTableStorybook implements OnInit, OnChanges {\r\n @ViewChild(OuiSort, { static: true }) sort: OuiSort;\r\n @ViewChild(OuiPaginator, { static: true }) paginator: OuiPaginator;\r\n // displayedColumns: string[] = [];\r\n @Input() users: any[] = [\r\n { position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H' },\r\n { position: 2, name: 'Helium', weight: 4.0026, symbol: 'He' },\r\n { position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li' },\r\n { position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be' },\r\n { position: 5, name: 'Boron', weight: 10.811, symbol: 'B' },\r\n { position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C' },\r\n { position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N' },\r\n { position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O' },\r\n { position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F' },\r\n { position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne' },\r\n { position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H' },\r\n { position: 2, name: 'Helium', weight: 4.0026, symbol: 'He' },\r\n { position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li' },\r\n { position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be' },\r\n { position: 5, name: 'Boron', weight: 10.811, symbol: 'B' },\r\n { position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C' },\r\n { position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N' },\r\n { position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O' },\r\n { position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F' },\r\n { position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne' },\r\n ];\r\n @Input() pageSize: any[] = [];\r\n displayedColumns: string[] = [];\r\n dataSource = new OuiTableDataSource(this.users);\r\n constructor() {}\r\n\r\n ngOnInit() {\r\n // eslint-disable-next-line guard-for-in\r\n for (const key in this.users[0]) {\r\n this.displayedColumns.push(key);\r\n }\r\n }\r\n\r\n ngOnChanges() {\r\n this.dataSource = new OuiTableDataSource(this.users);\r\n this.dataSource.sort = this.sort;\r\n this.dataSource.paginator = this.paginator;\r\n }\r\n\r\n applyFilter(filterValue: string) {\r\n this.dataSource.filter = filterValue.trim().toLowerCase();\r\n }\r\n}\r\n\r\n@Component({\r\n selector: 'oui-table-custom-storybook',\r\n template: `\r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
Users\r\n
\r\n
\r\n
\r\n
\r\n {{ user.name }}\r\n
\r\n
\r\n {{ user.email }}\r\n
\r\n
\r\n
\r\n
Role{{ element.role }}\r\n Integrations\r\n \r\n
\r\n
    \r\n
  • \r\n \r\n
  • \r\n
\r\n
\r\n
\r\n Licenses\r\n \r\n
\r\n
    \r\n
  • \r\n \r\n
  • \r\n
\r\n
\r\n
\r\n Status\r\n {{ element.status }}
\r\n
\r\n `,\r\n})\r\nexport class OuiTableCustomStorybook implements OnChanges {\r\n INTEGRATIONS = {\r\n paypal: this.sanitizer.bypassSecurityTrustHtml(STORY_ICONS.PAYPAL),\r\n zapier: this.sanitizer.bypassSecurityTrustHtml(STORY_ICONS.ZAPIER),\r\n 'google-calender': this.sanitizer.bypassSecurityTrustHtml(\r\n STORY_ICONS.GOOGLE_CALENDAR\r\n ),\r\n salesforce: this.sanitizer.bypassSecurityTrustHtml(STORY_ICONS.SALESFORCE),\r\n };\r\n LICENCES = {\r\n scheduleonce: this.sanitizer.bypassSecurityTrustHtml(\r\n STORY_ICONS.SCHEDULEONCE\r\n ),\r\n chatonce: this.sanitizer.bypassSecurityTrustHtml(STORY_ICONS.CHATONCE),\r\n };\r\n @ViewChild(OuiSort, { static: true }) sort: OuiSort;\r\n userInfoColumns = USERINFOCOLUMNS;\r\n @Input() users: any[] = [];\r\n @Input() pageSize: any[] = [];\r\n userInfoDataSource = new OuiTableDataSource(this.users);\r\n constructor(private sanitizer: DomSanitizer) {}\r\n ngOnChanges() {\r\n this.userInfoDataSource = new OuiTableDataSource(this.users);\r\n this.userInfoDataSource.sort = this.sort;\r\n }\r\n}\r\n", + "sourceCode": "import { STORY_ICONS, USERINFOCOLUMNS } from './const';\nimport { Component, Input, OnChanges, OnInit, ViewChild } from '@angular/core';\nimport { OuiTableDataSource, OuiSort, OuiPaginator } from '../../components';\nimport { DomSanitizer } from '@angular/platform-browser';\n@Component({\n selector: 'oui-table-storybook',\n template: `\n \n \n \n
\n \n \n \n \n \n \n \n
\n {{ column }}\n {{ element[column] }}
\n\n \n
\n `,\n})\nexport class OuiTableStorybook implements OnInit, OnChanges {\n @ViewChild(OuiSort, { static: true }) sort: OuiSort;\n @ViewChild(OuiPaginator, { static: true }) paginator: OuiPaginator;\n // displayedColumns: string[] = [];\n @Input() users: any[] = [\n { position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H' },\n { position: 2, name: 'Helium', weight: 4.0026, symbol: 'He' },\n { position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li' },\n { position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be' },\n { position: 5, name: 'Boron', weight: 10.811, symbol: 'B' },\n { position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C' },\n { position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N' },\n { position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O' },\n { position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F' },\n { position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne' },\n { position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H' },\n { position: 2, name: 'Helium', weight: 4.0026, symbol: 'He' },\n { position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li' },\n { position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be' },\n { position: 5, name: 'Boron', weight: 10.811, symbol: 'B' },\n { position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C' },\n { position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N' },\n { position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O' },\n { position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F' },\n { position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne' },\n ];\n @Input() pageSize: any[] = [];\n displayedColumns: string[] = [];\n dataSource = new OuiTableDataSource(this.users);\n constructor() {}\n\n ngOnInit() {\n // eslint-disable-next-line guard-for-in\n for (const key in this.users[0]) {\n this.displayedColumns.push(key);\n }\n }\n\n ngOnChanges() {\n this.dataSource = new OuiTableDataSource(this.users);\n this.dataSource.sort = this.sort;\n this.dataSource.paginator = this.paginator;\n }\n\n applyFilter(filterValue: string) {\n this.dataSource.filter = filterValue.trim().toLowerCase();\n }\n}\n\n@Component({\n selector: 'oui-table-custom-storybook',\n template: `\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
Users\n
\n
\n
\n
\n {{ user.name }}\n
\n
\n {{ user.email }}\n
\n
\n
\n
Role{{ element.role }}\n Integrations\n \n
\n
    \n
  • \n \n
  • \n
\n
\n
\n Licenses\n \n
\n
    \n
  • \n \n
  • \n
\n
\n
\n Status\n {{ element.status }}
\n
\n `,\n})\nexport class OuiTableCustomStorybook implements OnChanges {\n INTEGRATIONS = {\n paypal: this.sanitizer.bypassSecurityTrustHtml(STORY_ICONS.PAYPAL),\n zapier: this.sanitizer.bypassSecurityTrustHtml(STORY_ICONS.ZAPIER),\n 'google-calender': this.sanitizer.bypassSecurityTrustHtml(\n STORY_ICONS.GOOGLE_CALENDAR\n ),\n salesforce: this.sanitizer.bypassSecurityTrustHtml(STORY_ICONS.SALESFORCE),\n };\n LICENCES = {\n scheduleonce: this.sanitizer.bypassSecurityTrustHtml(\n STORY_ICONS.SCHEDULEONCE\n ),\n chatonce: this.sanitizer.bypassSecurityTrustHtml(STORY_ICONS.CHATONCE),\n };\n @ViewChild(OuiSort, { static: true }) sort: OuiSort;\n userInfoColumns = USERINFOCOLUMNS;\n @Input() users: any[] = [];\n @Input() pageSize: any[] = [];\n userInfoDataSource = new OuiTableDataSource(this.users);\n constructor(private sanitizer: DomSanitizer) {}\n ngOnChanges() {\n this.userInfoDataSource = new OuiTableDataSource(this.users);\n this.userInfoDataSource.sort = this.sort;\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -39693,7 +49575,7 @@ }, { "name": "OuiTooltipStorybook", - "id": "component-OuiTooltipStorybook-39feb7e007be1562703c030d86ae84b424ef0977ba288b39c24d702496e27be7a29927782a739af12abcaa4d772454b6b63de247b6470fc20ca56c3937d051fa", + "id": "component-OuiTooltipStorybook-fdfdd389ca4d09a87e29a1680147bfb98c3a1faad64988471d3c258b01013735a4c96ec423da901739be495b18d771ca8aa9fe967b18e9da63218adee6922306", "file": "ui/src/stories/tooltip/tooltip.component.ts", "encapsulation": [], "entryComponents": [], @@ -39745,7 +49627,7 @@ "description": "", "rawdescription": "\n", "type": "component", - "sourceCode": "import { OuiIconRegistry } from '../../components';\r\nimport { Input, Component } from '@angular/core';\r\nimport { DomSanitizer } from '@angular/platform-browser';\r\n\r\n@Component({\r\n selector: 'oui-tooltip-storybook',\r\n template: `\r\n \r\n `,\r\n})\r\nexport class OuiTooltipStorybook {\r\n @Input() disabled = false;\r\n @Input() _ouiTooltip = 'This is a tooltip';\r\n @Input() _ouiTooltipPosition = 'above';\r\n constructor(\r\n private ouiIconRegistry: OuiIconRegistry,\r\n private domSanitizer: DomSanitizer\r\n ) {\r\n this.ouiIconRegistry.addSvgIcon(\r\n `local`,\r\n this.domSanitizer.bypassSecurityTrustResourceUrl(\r\n `/assets/images/v-green.svg`\r\n )\r\n );\r\n\r\n this.ouiIconRegistry.addSvgIconSet(\r\n this.domSanitizer.bypassSecurityTrustResourceUrl(\r\n 'https://d1azc1qln24ryf.cloudfront.net/135790/oncehub-20/symbol-defs.svg?hn1bl5'\r\n )\r\n );\r\n }\r\n}\r\n", + "sourceCode": "import { OuiIconRegistry } from '../../components';\nimport { Input, Component } from '@angular/core';\nimport { DomSanitizer } from '@angular/platform-browser';\n\n@Component({\n selector: 'oui-tooltip-storybook',\n template: `\n \n `,\n})\nexport class OuiTooltipStorybook {\n @Input() disabled = false;\n @Input() _ouiTooltip = 'This is a tooltip';\n @Input() _ouiTooltipPosition = 'above';\n constructor(\n private ouiIconRegistry: OuiIconRegistry,\n private domSanitizer: DomSanitizer\n ) {\n this.ouiIconRegistry.addSvgIcon(\n `local`,\n this.domSanitizer.bypassSecurityTrustResourceUrl(\n `/assets/images/v-green.svg`\n )\n );\n\n this.ouiIconRegistry.addSvgIconSet(\n this.domSanitizer.bypassSecurityTrustResourceUrl(\n 'https://d1azc1qln24ryf.cloudfront.net/135790/oncehub-20/symbol-defs.svg?hn1bl5'\n )\n );\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -39793,7 +49675,7 @@ }, { "name": "OuiYearView", - "id": "component-OuiYearView-d9fc8ac3ff8383dea2a5a554cb204911b4af36f23a28276a140e185d0e2510fe3a3f153e47c796015101dcf65d18ba16aa2385d667a45c722c39cd3f2d7ad7fd", + "id": "component-OuiYearView-1f657582ef4c8754ca67bb7490eaa3ebf1396e53957ad98a99d333a3ff4703aaf9d65d4493778b86d7e8f2ee7e7a16455c8b98bf8b9e6b76f1c5c82300626a9e", "file": "ui/src/components/datepicker/year-view.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -40137,8 +50019,8 @@ "jsdoctags": [ { "name": { - "pos": 10940, - "end": 10943, + "pos": 10572, + "end": 10575, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -40149,8 +50031,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 10934, - "end": 10939, + "pos": 10566, + "end": 10571, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -40161,8 +50043,8 @@ }, { "tagName": { - "pos": 10972, - "end": 10979, + "pos": 10603, + "end": 10610, "flags": 4227072, "modifierFlagsCache": 0, "transformFlags": 0, @@ -40407,7 +50289,7 @@ "description": "

An internal component used to display a single year in the datepicker.

\n", "rawdescription": "\n\nAn internal component used to display a single year in the datepicker.\n", "type": "component", - "sourceCode": "import {\r\n DOWN_ARROW,\r\n END,\r\n ENTER,\r\n HOME,\r\n LEFT_ARROW,\r\n PAGE_DOWN,\r\n PAGE_UP,\r\n RIGHT_ARROW,\r\n UP_ARROW,\r\n SPACE,\r\n} from '@angular/cdk/keycodes';\r\nimport {\r\n AfterContentInit,\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n EventEmitter,\r\n Inject,\r\n Input,\r\n Optional,\r\n Output,\r\n ViewChild,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\nimport { Directionality } from '@angular/cdk/bidi';\r\nimport { OuiCalendarBody, OuiCalendarCell } from './calendar-body';\r\nimport { createMissingDateImplError } from './datepicker-errors';\r\nimport { OUI_DATE_FORMATS, OuiDateFormats } from './date-formats';\r\nimport { DateAdapter } from './date-adapter';\r\n\r\n/**\r\n * An internal component used to display a single year in the datepicker.\r\n */\r\n@Component({\r\n selector: 'oui-year-view',\r\n templateUrl: 'year-view.html',\r\n exportAs: 'ouiYearView',\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class OuiYearView implements AfterContentInit {\r\n /** The date to display in this year view (everything other than the year is ignored). */\r\n @Input()\r\n get activeDate(): D {\r\n return this._activeDate;\r\n }\r\n set activeDate(value: D) {\r\n const oldActiveDate = this._activeDate;\r\n const validDate =\r\n this._getValidDateOrNull(this._dateAdapter.deserialize(value)) ||\r\n this._dateAdapter.today();\r\n this._activeDate = this._dateAdapter.clampDate(\r\n validDate,\r\n this.minDate,\r\n this.maxDate\r\n );\r\n if (\r\n this._dateAdapter.getYear(oldActiveDate) !==\r\n this._dateAdapter.getYear(this._activeDate)\r\n ) {\r\n this._init();\r\n }\r\n }\r\n private _activeDate: D;\r\n\r\n /** The currently selected date. */\r\n @Input()\r\n get selected(): D | null {\r\n return this._selected;\r\n }\r\n set selected(value: D | null) {\r\n this._selected = this._getValidDateOrNull(\r\n this._dateAdapter.deserialize(value)\r\n );\r\n this._selectedMonth = this._getMonthInCurrentYear(this._selected);\r\n }\r\n private _selected: D | null;\r\n\r\n /** The minimum selectable date. */\r\n @Input()\r\n get minDate(): D | null {\r\n return this._minDate;\r\n }\r\n set minDate(value: D | null) {\r\n this._minDate = this._getValidDateOrNull(\r\n this._dateAdapter.deserialize(value)\r\n );\r\n }\r\n private _minDate: D | null;\r\n\r\n /** The maximum selectable date. */\r\n @Input()\r\n get maxDate(): D | null {\r\n return this._maxDate;\r\n }\r\n set maxDate(value: D | null) {\r\n this._maxDate = this._getValidDateOrNull(\r\n this._dateAdapter.deserialize(value)\r\n );\r\n }\r\n private _maxDate: D | null;\r\n\r\n /** A function used to filter which dates are selectable. */\r\n @Input() dateFilter: (date: D) => boolean;\r\n\r\n /** Emits when a new month is selected. */\r\n @Output() readonly selectedChange: EventEmitter = new EventEmitter();\r\n\r\n /** Emits the selected month. This doesn't imply a change on the selected date */\r\n @Output() readonly monthSelected: EventEmitter = new EventEmitter();\r\n\r\n /** Emits when any date is activated. */\r\n @Output() readonly activeDateChange: EventEmitter = new EventEmitter();\r\n\r\n /** The body of calendar table */\r\n @ViewChild(OuiCalendarBody)\r\n _ouiCalendarBody: OuiCalendarBody;\r\n\r\n /** Grid of calendar cells representing the months of the year. */\r\n _months: OuiCalendarCell[][];\r\n\r\n /** The label for this year (e.g. \"2017\"). */\r\n _yearLabel: string;\r\n\r\n /** The month in this year that today falls on. Null if today is in a different year. */\r\n _todayMonth: number | null;\r\n\r\n /**\r\n * The month in this year that the selected Date falls on.\r\n * Null if the selected Date is in a different year.\r\n */\r\n _selectedMonth: number | null;\r\n\r\n constructor(\r\n private _changeDetectorRef: ChangeDetectorRef,\r\n @Optional() @Inject(OUI_DATE_FORMATS) private _dateFormats: OuiDateFormats,\r\n @Optional() public _dateAdapter: DateAdapter,\r\n @Optional() private _dir?: Directionality\r\n ) {\r\n if (!this._dateAdapter) {\r\n throw createMissingDateImplError('DateAdapter');\r\n }\r\n if (!this._dateFormats) {\r\n throw createMissingDateImplError('OUI_DATE_FORMATS');\r\n }\r\n\r\n this._activeDate = this._dateAdapter.today();\r\n }\r\n\r\n ngAfterContentInit() {\r\n this._init();\r\n }\r\n\r\n /** Handles when a new month is selected. */\r\n _monthSelected(month: number) {\r\n const normalizedDate = this._dateAdapter.createDate(\r\n this._dateAdapter.getYear(this.activeDate),\r\n month,\r\n 1\r\n );\r\n\r\n this.monthSelected.emit(normalizedDate);\r\n\r\n const daysInMonth = this._dateAdapter.getNumDaysInMonth(normalizedDate);\r\n\r\n this.selectedChange.emit(\r\n this._dateAdapter.createDate(\r\n this._dateAdapter.getYear(this.activeDate),\r\n month,\r\n Math.min(this._dateAdapter.getDate(this.activeDate), daysInMonth)\r\n )\r\n );\r\n }\r\n\r\n /** Handles keydown events on the calendar body when calendar is in year view. */\r\n _handleCalendarBodyKeydown(event: KeyboardEvent): void {\r\n // TODO(mmalerba): We currently allow keyboard navigation to disabled dates, but just prevent\r\n // disabled ones from being selected. This may not be ideal, we should look into whether\r\n // navigation should skip over disabled dates, and if so, how to implement that efficiently.\r\n\r\n const oldActiveDate = this._activeDate;\r\n const isRtl = this._isRtl();\r\n\r\n switch (event.keyCode) {\r\n case LEFT_ARROW:\r\n this.activeDate = this._dateAdapter.addCalendarMonths(\r\n this._activeDate,\r\n isRtl ? 1 : -1\r\n );\r\n break;\r\n case RIGHT_ARROW:\r\n this.activeDate = this._dateAdapter.addCalendarMonths(\r\n this._activeDate,\r\n isRtl ? -1 : 1\r\n );\r\n break;\r\n case UP_ARROW:\r\n this.activeDate = this._dateAdapter.addCalendarMonths(\r\n this._activeDate,\r\n -4\r\n );\r\n break;\r\n case DOWN_ARROW:\r\n this.activeDate = this._dateAdapter.addCalendarMonths(\r\n this._activeDate,\r\n 4\r\n );\r\n break;\r\n case HOME:\r\n this.activeDate = this._dateAdapter.addCalendarMonths(\r\n this._activeDate,\r\n -this._dateAdapter.getMonth(this._activeDate)\r\n );\r\n break;\r\n case END:\r\n this.activeDate = this._dateAdapter.addCalendarMonths(\r\n this._activeDate,\r\n 11 - this._dateAdapter.getMonth(this._activeDate)\r\n );\r\n break;\r\n case PAGE_UP:\r\n this.activeDate = this._dateAdapter.addCalendarYears(\r\n this._activeDate,\r\n event.altKey ? -10 : -1\r\n );\r\n break;\r\n case PAGE_DOWN:\r\n this.activeDate = this._dateAdapter.addCalendarYears(\r\n this._activeDate,\r\n event.altKey ? 10 : 1\r\n );\r\n break;\r\n case ENTER:\r\n case SPACE:\r\n this._monthSelected(this._dateAdapter.getMonth(this._activeDate));\r\n break;\r\n default:\r\n // Don't prevent default or focus active cell on keys that we don't explicitly handle.\r\n return;\r\n }\r\n\r\n if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) {\r\n this.activeDateChange.emit(this.activeDate);\r\n }\r\n\r\n this._focusActiveCell();\r\n // Prevent unexpected default actions such as form submission.\r\n event.preventDefault();\r\n }\r\n\r\n /** Initializes this year view. */\r\n _init() {\r\n this._selectedMonth = this._getMonthInCurrentYear(this.selected);\r\n this._todayMonth = this._getMonthInCurrentYear(this._dateAdapter.today());\r\n this._yearLabel = this._dateAdapter.getYearName(this.activeDate);\r\n\r\n const monthNames = this._dateAdapter.getMonthNames('short');\r\n // First row of months only contains 5 elements so we can fit the year label on the same row.\r\n this._months = [\r\n [0, 1, 2, 3],\r\n [4, 5, 6, 7],\r\n [8, 9, 10, 11],\r\n ].map((row) =>\r\n row.map((month) => this._createCellForMonth(month, monthNames[month]))\r\n );\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n\r\n /** Focuses the active cell after the microtask queue is empty. */\r\n _focusActiveCell() {\r\n this._ouiCalendarBody._focusActiveCell();\r\n }\r\n\r\n /**\r\n * Gets the month in this year that the given Date falls on.\r\n * Returns null if the given Date is in another year.\r\n */\r\n private _getMonthInCurrentYear(date: D | null) {\r\n return date &&\r\n this._dateAdapter.getYear(date) ===\r\n this._dateAdapter.getYear(this.activeDate)\r\n ? this._dateAdapter.getMonth(date)\r\n : null;\r\n }\r\n\r\n /** Creates an OuiCalendarCell for the given month. */\r\n private _createCellForMonth(month: number, monthName: string) {\r\n const ariaLabel = this._dateAdapter.format(\r\n this._dateAdapter.createDate(\r\n this._dateAdapter.getYear(this.activeDate),\r\n month,\r\n 1\r\n ),\r\n this._dateFormats.display.monthYearA11yLabel\r\n );\r\n return new OuiCalendarCell(\r\n month,\r\n monthName.toLocaleUpperCase(),\r\n ariaLabel,\r\n this._shouldEnableMonth(month)\r\n );\r\n }\r\n\r\n /** Whether the given month is enabled. */\r\n private _shouldEnableMonth(month: number) {\r\n const activeYear = this._dateAdapter.getYear(this.activeDate);\r\n\r\n if (\r\n month === undefined ||\r\n month === null ||\r\n this._isYearAndMonthAfterMaxDate(activeYear, month) ||\r\n this._isYearAndMonthBeforeMinDate(activeYear, month)\r\n ) {\r\n return false;\r\n }\r\n\r\n if (!this.dateFilter) {\r\n return true;\r\n }\r\n\r\n const firstOfMonth = this._dateAdapter.createDate(activeYear, month, 1);\r\n\r\n // If any date in the month is enabled count the month as enabled.\r\n for (\r\n let date = firstOfMonth;\r\n this._dateAdapter.getMonth(date) === month;\r\n date = this._dateAdapter.addCalendarDays(date, 1)\r\n ) {\r\n if (this.dateFilter(date)) {\r\n return true;\r\n }\r\n }\r\n\r\n return false;\r\n }\r\n\r\n /**\r\n * Tests whether the combination month/year is after this.maxDate, considering\r\n * just the month and year of this.maxDate\r\n */\r\n private _isYearAndMonthAfterMaxDate(year: number, month: number) {\r\n if (this.maxDate) {\r\n const maxYear = this._dateAdapter.getYear(this.maxDate);\r\n const maxMonth = this._dateAdapter.getMonth(this.maxDate);\r\n\r\n return year > maxYear || (year === maxYear && month > maxMonth);\r\n }\r\n\r\n return false;\r\n }\r\n\r\n /**\r\n * Tests whether the combination month/year is before this.minDate, considering\r\n * just the month and year of this.minDate\r\n */\r\n private _isYearAndMonthBeforeMinDate(year: number, month: number) {\r\n if (this.minDate) {\r\n const minYear = this._dateAdapter.getYear(this.minDate);\r\n const minMonth = this._dateAdapter.getMonth(this.minDate);\r\n\r\n return year < minYear || (year === minYear && month < minMonth);\r\n }\r\n\r\n return false;\r\n }\r\n\r\n /**\r\n * @param obj The object to check.\r\n * @returns The given object if it is both a date instance and valid, otherwise null.\r\n */\r\n private _getValidDateOrNull(obj: any): D | null {\r\n return this._dateAdapter.isDateInstance(obj) &&\r\n this._dateAdapter.isValid(obj as any as D)\r\n ? obj\r\n : null;\r\n }\r\n\r\n /** Determines whether the user has the RTL layout direction. */\r\n private _isRtl() {\r\n return this._dir && this._dir.value === 'rtl';\r\n }\r\n}\r\n", + "sourceCode": "import {\n DOWN_ARROW,\n END,\n ENTER,\n HOME,\n LEFT_ARROW,\n PAGE_DOWN,\n PAGE_UP,\n RIGHT_ARROW,\n UP_ARROW,\n SPACE,\n} from '@angular/cdk/keycodes';\nimport {\n AfterContentInit,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n EventEmitter,\n Inject,\n Input,\n Optional,\n Output,\n ViewChild,\n ViewEncapsulation,\n} from '@angular/core';\nimport { Directionality } from '@angular/cdk/bidi';\nimport { OuiCalendarBody, OuiCalendarCell } from './calendar-body';\nimport { createMissingDateImplError } from './datepicker-errors';\nimport { OUI_DATE_FORMATS, OuiDateFormats } from './date-formats';\nimport { DateAdapter } from './date-adapter';\n\n/**\n * An internal component used to display a single year in the datepicker.\n */\n@Component({\n selector: 'oui-year-view',\n templateUrl: 'year-view.html',\n exportAs: 'ouiYearView',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class OuiYearView implements AfterContentInit {\n /** The date to display in this year view (everything other than the year is ignored). */\n @Input()\n get activeDate(): D {\n return this._activeDate;\n }\n set activeDate(value: D) {\n const oldActiveDate = this._activeDate;\n const validDate =\n this._getValidDateOrNull(this._dateAdapter.deserialize(value)) ||\n this._dateAdapter.today();\n this._activeDate = this._dateAdapter.clampDate(\n validDate,\n this.minDate,\n this.maxDate\n );\n if (\n this._dateAdapter.getYear(oldActiveDate) !==\n this._dateAdapter.getYear(this._activeDate)\n ) {\n this._init();\n }\n }\n private _activeDate: D;\n\n /** The currently selected date. */\n @Input()\n get selected(): D | null {\n return this._selected;\n }\n set selected(value: D | null) {\n this._selected = this._getValidDateOrNull(\n this._dateAdapter.deserialize(value)\n );\n this._selectedMonth = this._getMonthInCurrentYear(this._selected);\n }\n private _selected: D | null;\n\n /** The minimum selectable date. */\n @Input()\n get minDate(): D | null {\n return this._minDate;\n }\n set minDate(value: D | null) {\n this._minDate = this._getValidDateOrNull(\n this._dateAdapter.deserialize(value)\n );\n }\n private _minDate: D | null;\n\n /** The maximum selectable date. */\n @Input()\n get maxDate(): D | null {\n return this._maxDate;\n }\n set maxDate(value: D | null) {\n this._maxDate = this._getValidDateOrNull(\n this._dateAdapter.deserialize(value)\n );\n }\n private _maxDate: D | null;\n\n /** A function used to filter which dates are selectable. */\n @Input() dateFilter: (date: D) => boolean;\n\n /** Emits when a new month is selected. */\n @Output() readonly selectedChange: EventEmitter = new EventEmitter();\n\n /** Emits the selected month. This doesn't imply a change on the selected date */\n @Output() readonly monthSelected: EventEmitter = new EventEmitter();\n\n /** Emits when any date is activated. */\n @Output() readonly activeDateChange: EventEmitter = new EventEmitter();\n\n /** The body of calendar table */\n @ViewChild(OuiCalendarBody)\n _ouiCalendarBody: OuiCalendarBody;\n\n /** Grid of calendar cells representing the months of the year. */\n _months: OuiCalendarCell[][];\n\n /** The label for this year (e.g. \"2017\"). */\n _yearLabel: string;\n\n /** The month in this year that today falls on. Null if today is in a different year. */\n _todayMonth: number | null;\n\n /**\n * The month in this year that the selected Date falls on.\n * Null if the selected Date is in a different year.\n */\n _selectedMonth: number | null;\n\n constructor(\n private _changeDetectorRef: ChangeDetectorRef,\n @Optional() @Inject(OUI_DATE_FORMATS) private _dateFormats: OuiDateFormats,\n @Optional() public _dateAdapter: DateAdapter,\n @Optional() private _dir?: Directionality\n ) {\n if (!this._dateAdapter) {\n throw createMissingDateImplError('DateAdapter');\n }\n if (!this._dateFormats) {\n throw createMissingDateImplError('OUI_DATE_FORMATS');\n }\n\n this._activeDate = this._dateAdapter.today();\n }\n\n ngAfterContentInit() {\n this._init();\n }\n\n /** Handles when a new month is selected. */\n _monthSelected(month: number) {\n const normalizedDate = this._dateAdapter.createDate(\n this._dateAdapter.getYear(this.activeDate),\n month,\n 1\n );\n\n this.monthSelected.emit(normalizedDate);\n\n const daysInMonth = this._dateAdapter.getNumDaysInMonth(normalizedDate);\n\n this.selectedChange.emit(\n this._dateAdapter.createDate(\n this._dateAdapter.getYear(this.activeDate),\n month,\n Math.min(this._dateAdapter.getDate(this.activeDate), daysInMonth)\n )\n );\n }\n\n /** Handles keydown events on the calendar body when calendar is in year view. */\n _handleCalendarBodyKeydown(event: KeyboardEvent): void {\n // TODO(mmalerba): We currently allow keyboard navigation to disabled dates, but just prevent\n // disabled ones from being selected. This may not be ideal, we should look into whether\n // navigation should skip over disabled dates, and if so, how to implement that efficiently.\n\n const oldActiveDate = this._activeDate;\n const isRtl = this._isRtl();\n\n switch (event.keyCode) {\n case LEFT_ARROW:\n this.activeDate = this._dateAdapter.addCalendarMonths(\n this._activeDate,\n isRtl ? 1 : -1\n );\n break;\n case RIGHT_ARROW:\n this.activeDate = this._dateAdapter.addCalendarMonths(\n this._activeDate,\n isRtl ? -1 : 1\n );\n break;\n case UP_ARROW:\n this.activeDate = this._dateAdapter.addCalendarMonths(\n this._activeDate,\n -4\n );\n break;\n case DOWN_ARROW:\n this.activeDate = this._dateAdapter.addCalendarMonths(\n this._activeDate,\n 4\n );\n break;\n case HOME:\n this.activeDate = this._dateAdapter.addCalendarMonths(\n this._activeDate,\n -this._dateAdapter.getMonth(this._activeDate)\n );\n break;\n case END:\n this.activeDate = this._dateAdapter.addCalendarMonths(\n this._activeDate,\n 11 - this._dateAdapter.getMonth(this._activeDate)\n );\n break;\n case PAGE_UP:\n this.activeDate = this._dateAdapter.addCalendarYears(\n this._activeDate,\n event.altKey ? -10 : -1\n );\n break;\n case PAGE_DOWN:\n this.activeDate = this._dateAdapter.addCalendarYears(\n this._activeDate,\n event.altKey ? 10 : 1\n );\n break;\n case ENTER:\n case SPACE:\n this._monthSelected(this._dateAdapter.getMonth(this._activeDate));\n break;\n default:\n // Don't prevent default or focus active cell on keys that we don't explicitly handle.\n return;\n }\n\n if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) {\n this.activeDateChange.emit(this.activeDate);\n }\n\n this._focusActiveCell();\n // Prevent unexpected default actions such as form submission.\n event.preventDefault();\n }\n\n /** Initializes this year view. */\n _init() {\n this._selectedMonth = this._getMonthInCurrentYear(this.selected);\n this._todayMonth = this._getMonthInCurrentYear(this._dateAdapter.today());\n this._yearLabel = this._dateAdapter.getYearName(this.activeDate);\n\n const monthNames = this._dateAdapter.getMonthNames('short');\n // First row of months only contains 5 elements so we can fit the year label on the same row.\n this._months = [\n [0, 1, 2, 3],\n [4, 5, 6, 7],\n [8, 9, 10, 11],\n ].map((row) =>\n row.map((month) => this._createCellForMonth(month, monthNames[month]))\n );\n this._changeDetectorRef.markForCheck();\n }\n\n /** Focuses the active cell after the microtask queue is empty. */\n _focusActiveCell() {\n this._ouiCalendarBody._focusActiveCell();\n }\n\n /**\n * Gets the month in this year that the given Date falls on.\n * Returns null if the given Date is in another year.\n */\n private _getMonthInCurrentYear(date: D | null) {\n return date &&\n this._dateAdapter.getYear(date) ===\n this._dateAdapter.getYear(this.activeDate)\n ? this._dateAdapter.getMonth(date)\n : null;\n }\n\n /** Creates an OuiCalendarCell for the given month. */\n private _createCellForMonth(month: number, monthName: string) {\n const ariaLabel = this._dateAdapter.format(\n this._dateAdapter.createDate(\n this._dateAdapter.getYear(this.activeDate),\n month,\n 1\n ),\n this._dateFormats.display.monthYearA11yLabel\n );\n return new OuiCalendarCell(\n month,\n monthName.toLocaleUpperCase(),\n ariaLabel,\n this._shouldEnableMonth(month)\n );\n }\n\n /** Whether the given month is enabled. */\n private _shouldEnableMonth(month: number) {\n const activeYear = this._dateAdapter.getYear(this.activeDate);\n\n if (\n month === undefined ||\n month === null ||\n this._isYearAndMonthAfterMaxDate(activeYear, month) ||\n this._isYearAndMonthBeforeMinDate(activeYear, month)\n ) {\n return false;\n }\n\n if (!this.dateFilter) {\n return true;\n }\n\n const firstOfMonth = this._dateAdapter.createDate(activeYear, month, 1);\n\n // If any date in the month is enabled count the month as enabled.\n for (\n let date = firstOfMonth;\n this._dateAdapter.getMonth(date) === month;\n date = this._dateAdapter.addCalendarDays(date, 1)\n ) {\n if (this.dateFilter(date)) {\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * Tests whether the combination month/year is after this.maxDate, considering\n * just the month and year of this.maxDate\n */\n private _isYearAndMonthAfterMaxDate(year: number, month: number) {\n if (this.maxDate) {\n const maxYear = this._dateAdapter.getYear(this.maxDate);\n const maxMonth = this._dateAdapter.getMonth(this.maxDate);\n\n return year > maxYear || (year === maxYear && month > maxMonth);\n }\n\n return false;\n }\n\n /**\n * Tests whether the combination month/year is before this.minDate, considering\n * just the month and year of this.minDate\n */\n private _isYearAndMonthBeforeMinDate(year: number, month: number) {\n if (this.minDate) {\n const minYear = this._dateAdapter.getYear(this.minDate);\n const minMonth = this._dateAdapter.getMonth(this.minDate);\n\n return year < minYear || (year === minYear && month < minMonth);\n }\n\n return false;\n }\n\n /**\n * @param obj The object to check.\n * @returns The given object if it is both a date instance and valid, otherwise null.\n */\n private _getValidDateOrNull(obj: any): D | null {\n return this._dateAdapter.isDateInstance(obj) &&\n this._dateAdapter.isValid(obj as any as D)\n ? obj\n : null;\n }\n\n /** Determines whether the user has the RTL layout direction. */\n private _isRtl() {\n return this._dir && this._dir.value === 'rtl';\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -40641,11 +50523,11 @@ } } }, - "templateData": "\r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n" + "templateData": "\n \n \n \n \n \n \n
\n" }, { "name": "TooltipComponent", - "id": "component-TooltipComponent-2635a23757a2778a9b0834f26c6fb831b8ab7b27762e9933e089e3fc21ad4414a443d4aefabf1700c29b2acc24f80f96188097be469a76acba03896269552c1a", + "id": "component-TooltipComponent-ffb93a0366a6db45703111c4fe658119cf87b8d44e269ec2926a30e8ecd94bebfb4adcfd399aeca78af8a2424610392ab8cece9c0abb63c2a560ad78706daebd", "file": "ui/src/components/tooltip/tooltip.ts", "changeDetection": "ChangeDetectionStrategy.OnPush", "encapsulation": [ @@ -40694,7 +50576,7 @@ }, { "name": "_isHandset", - "defaultValue": "this._breakpointObserver.observe(\r\n Breakpoints.Handset\r\n )", + "defaultValue": "this._breakpointObserver.observe(\n Breakpoints.Handset\n )", "deprecated": false, "deprecationMessage": "", "type": "Observable", @@ -40881,11 +50763,11 @@ "description": "

Internal component that wraps the tooltip's content.

\n", "rawdescription": "\n\nInternal component that wraps the tooltip's content.\n\n", "type": "component", - "sourceCode": "import { AnimationEvent } from '@angular/animations';\r\nimport { AriaDescriber, FocusMonitor } from '@angular/cdk/a11y';\r\nimport { Directionality } from '@angular/cdk/bidi';\r\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\r\nimport {\r\n BreakpointObserver,\r\n Breakpoints,\r\n BreakpointState,\r\n} from '@angular/cdk/layout';\r\nimport {\r\n FlexibleConnectedPositionStrategy,\r\n HorizontalConnectionPos,\r\n OriginConnectionPosition,\r\n Overlay,\r\n OverlayConnectionPosition,\r\n OverlayRef,\r\n VerticalConnectionPos,\r\n ScrollStrategy,\r\n} from '@angular/cdk/overlay';\r\nimport { ScrollDispatcher } from '@angular/cdk/scrolling';\r\nimport { Platform } from '@angular/cdk/platform';\r\nimport { ComponentPortal } from '@angular/cdk/portal';\r\nimport { take, takeUntil } from 'rxjs/operators';\r\nimport {\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n Directive,\r\n ElementRef,\r\n Inject,\r\n InjectionToken,\r\n Input,\r\n NgZone,\r\n OnDestroy,\r\n Optional,\r\n ViewContainerRef,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\nimport { Subject, Observable } from 'rxjs';\r\nimport { ouiTooltipAnimations } from './tooltip-animations';\r\nimport { CanDisable } from '../core';\r\n\r\nexport type TooltipPosition = 'left' | 'right' | 'above' | 'below';\r\n\r\n/** Time in ms to throttle repositioning after scroll events. */\r\nexport const SCROLL_THROTTLE_MS = 20;\r\n\r\n/** CSS class that will be attached to the overlay panel. */\r\nexport const TOOLTIP_PANEL_CLASS = 'oui-tooltip-panel';\r\n\r\n/**\r\n * Creates an error to be thrown if the user supplied an invalid tooltip position.\r\n *\r\n * @docs-private\r\n */\r\nexport function getOuiTooltipInvalidPositionError(position: string) {\r\n return Error(`Tooltip position \"${position}\" is invalid.`);\r\n}\r\n\r\n/** Injection token that determines the scroll handling while a tooltip is visible. */\r\nexport const OUI_TOOLTIP_SCROLL_STRATEGY = new InjectionToken<\r\n () => ScrollStrategy\r\n>('oui-tooltip-scroll-strategy');\r\n\r\n/** @docs-private */\r\nexport function OUI_TOOLTIP_SCROLL_STRATEGY_FACTORY(\r\n overlay: Overlay\r\n): () => ScrollStrategy {\r\n return () =>\r\n overlay.scrollStrategies.reposition({ scrollThrottle: SCROLL_THROTTLE_MS });\r\n}\r\n\r\n/** @docs-private */\r\nexport const OUI_TOOLTIP_SCROLL_STRATEGY_FACTORY_PROVIDER = {\r\n provide: OUI_TOOLTIP_SCROLL_STRATEGY,\r\n deps: [Overlay],\r\n useFactory: OUI_TOOLTIP_SCROLL_STRATEGY_FACTORY,\r\n};\r\n\r\n/** Default `ouiTooltip` options that can be overridden. */\r\nexport interface OuiTooltipDefaultOptions {\r\n showDelay: number;\r\n hideDelay: number;\r\n touchendHideDelay: number;\r\n}\r\n\r\nexport interface NewCSSStyleDeclaration extends CSSStyleDeclaration {\r\n msUserSelect: string;\r\n}\r\n\r\n/** Injection token to be used to override the default options for `ouiTooltip`. */\r\nexport const OUI_TOOLTIP_DEFAULT_OPTIONS =\r\n new InjectionToken('oui-tooltip-default-options', {\r\n providedIn: 'root',\r\n factory: OUI_TOOLTIP_DEFAULT_OPTIONS_FACTORY,\r\n });\r\n\r\n/** @docs-private */\r\nexport function OUI_TOOLTIP_DEFAULT_OPTIONS_FACTORY(): OuiTooltipDefaultOptions {\r\n return {\r\n showDelay: 0,\r\n hideDelay: 0,\r\n touchendHideDelay: 1500,\r\n };\r\n}\r\n\r\nexport type TooltipVisibility = 'initial' | 'visible' | 'hidden';\r\n\r\n/**\r\n * Internal component that wraps the tooltip's content.\r\n *\r\n * @docs-private\r\n */\r\n@Component({\r\n selector: 'oui-tooltip-component',\r\n templateUrl: 'tooltip.html',\r\n styleUrls: ['tooltip.scss'],\r\n encapsulation: ViewEncapsulation.None,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n animations: [ouiTooltipAnimations.tooltipState],\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n // Forces the element to have a layout in IE and Edge. This fixes issues where the element\r\n // won't be rendered if the animations are disabled or there is no web animations polyfill.\r\n '[style.zoom]': '_visibility === \"visible\" ? 1 : null',\r\n '(body:click)': 'this._handleBodyInteraction()',\r\n 'aria-hidden': 'true',\r\n },\r\n})\r\nexport class TooltipComponent {\r\n /** Message to display in the tooltip */\r\n message: string;\r\n /** Classes to be added to the tooltip. Supports the same syntax as `ngClass`. */\r\n tooltipClass: string | string[] | Set | { [key: string]: any };\r\n\r\n /** The timeout ID of any current timer set to show the tooltip */\r\n _showTimeoutId: number | null;\r\n\r\n /** The timeout ID of any current timer set to hide the tooltip */\r\n _hideTimeoutId: number | null;\r\n\r\n /** Property watched by the animation framework to show or hide the tooltip */\r\n _visibility: TooltipVisibility = 'initial';\r\n\r\n /** Whether interactions on the page should close the tooltip */\r\n private _closeOnInteraction = false;\r\n\r\n /** Subject for notifying that the tooltip has been hidden from the view */\r\n private readonly _onHide: Subject = new Subject();\r\n\r\n /** Stream that emits whether the user has a handset-sized display. */\r\n _isHandset: Observable = this._breakpointObserver.observe(\r\n Breakpoints.Handset\r\n );\r\n\r\n constructor(\r\n private _changeDetectorRef: ChangeDetectorRef,\r\n private _breakpointObserver: BreakpointObserver\r\n ) {}\r\n\r\n /**\r\n * Shows the tooltip with an animation originating from the provided origin\r\n *\r\n * @param delay Amount of milliseconds to the delay showing the tooltip.\r\n */\r\n show(): void {\r\n // Cancel the delayed hide if it is scheduled\r\n if (this._hideTimeoutId) {\r\n clearTimeout(this._hideTimeoutId);\r\n this._hideTimeoutId = null;\r\n }\r\n\r\n // Body interactions should cancel the tooltip if there is a delay in showing.\r\n this._closeOnInteraction = true;\r\n setTimeout(() => {\r\n this._visibility = 'visible';\r\n this._showTimeoutId = null;\r\n\r\n // Mark for check so if any parent component has set the\r\n // ChangeDetectionStrategy to OnPush it will be checked anyways\r\n this._markForCheck();\r\n }, 0);\r\n }\r\n\r\n /**\r\n * Begins the animation to hide the tooltip after the provided delay in ms.\r\n *\r\n * @param delay Amount of milliseconds to delay showing the tooltip.\r\n */\r\n hide(): void {\r\n // Cancel the delayed show if it is scheduled\r\n if (this._showTimeoutId) {\r\n clearTimeout(this._showTimeoutId);\r\n this._showTimeoutId = null;\r\n }\r\n setTimeout(() => {\r\n this._visibility = 'hidden';\r\n this._hideTimeoutId = null;\r\n // Mark for check so if any parent component has set the\r\n // ChangeDetectionStrategy to OnPush it will be checked anyways\r\n this._markForCheck();\r\n }, 0);\r\n }\r\n\r\n /** Returns an observable that notifies when the tooltip has been hidden from view. */\r\n afterHidden(): Observable {\r\n return this._onHide.asObservable();\r\n }\r\n\r\n /** Whether the tooltip is being displayed. */\r\n isVisible(): boolean {\r\n return this._visibility === 'visible';\r\n }\r\n\r\n _animationStart() {\r\n this._closeOnInteraction = false;\r\n }\r\n\r\n _animationDone(event: AnimationEvent): void {\r\n const toState = event.toState as TooltipVisibility;\r\n\r\n if (toState === 'hidden' && !this.isVisible()) {\r\n this._onHide.next();\r\n }\r\n\r\n if (toState === 'visible' || toState === 'hidden') {\r\n this._closeOnInteraction = true;\r\n }\r\n }\r\n\r\n /**\r\n * Interactions on the HTML body should close the tooltip immediately\r\n */\r\n _handleBodyInteraction(): void {\r\n if (this._closeOnInteraction) {\r\n this.hide();\r\n }\r\n }\r\n\r\n /**\r\n * Marks that the tooltip needs to be checked in the next change detection run.\r\n * Mainly used for rendering the initial text before positioning a tooltip, which\r\n * can be problematic in components with OnPush change detection.\r\n */\r\n _markForCheck(): void {\r\n this._changeDetectorRef.markForCheck();\r\n }\r\n}\r\n\r\n/**\r\n * Directive that attaches a tooltip to the host element. Animates the showing and\r\n * hiding of a tooltip provided position (defaults to below the element).\r\n */\r\n@Directive({\r\n selector: '[ouiTooltip]',\r\n exportAs: 'ouiTooltip',\r\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\r\n host: {\r\n '(longpress)': 'show()',\r\n '(keydown)': '_handleKeydown($event)',\r\n '(touchend)': '_handleTouchend()',\r\n '[attr.tabindex]': 'disabled ? -1 : 0',\r\n '[attr.aria-hidden]': 'false',\r\n },\r\n})\r\nexport class OuiTooltip implements OnDestroy, CanDisable {\r\n _overlayRef: OverlayRef | null;\r\n _tooltipInstance: TooltipComponent | null;\r\n\r\n private _portal: ComponentPortal;\r\n private _position: TooltipPosition = 'below';\r\n private _disabled = false;\r\n private _tooltipClass:\r\n | string\r\n | string[]\r\n | Set\r\n | { [key: string]: any };\r\n private _scrollStrategy: () => ScrollStrategy;\r\n\r\n /** Allows the user to define the position of the tooltip relative to the parent element */\r\n @Input('ouiTooltipPosition')\r\n get position(): TooltipPosition {\r\n return this._position;\r\n }\r\n set position(value: TooltipPosition) {\r\n if (value !== this._position) {\r\n this._position = value;\r\n if (this._overlayRef) {\r\n this._updatePosition();\r\n\r\n if (this._tooltipInstance) {\r\n this._tooltipInstance!.show();\r\n }\r\n\r\n this._overlayRef.updatePosition();\r\n }\r\n }\r\n }\r\n\r\n /** Disables the display of the tooltip. */\r\n @Input('ouiTooltipDisabled')\r\n get disabled(): boolean {\r\n return this._disabled;\r\n }\r\n set disabled(value) {\r\n this._disabled = coerceBooleanProperty(value);\r\n\r\n // If tooltip is disabled, hide immediately.\r\n if (this._disabled) {\r\n this.hide();\r\n }\r\n }\r\n\r\n private _message = '';\r\n\r\n /** The message to be displayed in the tooltip */\r\n @Input('ouiTooltip')\r\n get message() {\r\n return this._message;\r\n }\r\n set message(value: string) {\r\n this._ariaDescriber.removeDescription(\r\n this._elementRef.nativeElement,\r\n this._message\r\n );\r\n\r\n // If the message is not a string (e.g. number), convert it to a string and trim it.\r\n this._message = value != null ? `${value}`.trim() : '';\r\n\r\n if (!this._message && this._isTooltipVisible()) {\r\n this.hide();\r\n } else {\r\n this._updateTooltipMessage();\r\n this._ariaDescriber.describe(\r\n this._elementRef.nativeElement,\r\n this.message\r\n );\r\n }\r\n }\r\n\r\n /** Classes to be passed to the tooltip. Supports the same syntax as `ngClass`. */\r\n @Input('ouiTooltipClass')\r\n get tooltipClass() {\r\n return this._tooltipClass;\r\n }\r\n set tooltipClass(\r\n value: string | string[] | Set | { [key: string]: any }\r\n ) {\r\n this._tooltipClass = value;\r\n if (this._tooltipInstance) {\r\n this._tooltipInstance._markForCheck();\r\n this._setTooltipClass(this._tooltipClass);\r\n }\r\n }\r\n\r\n private _manualListeners = new Map<\r\n string,\r\n EventListenerOrEventListenerObject\r\n >();\r\n\r\n /** Emits when the component is destroyed. */\r\n private readonly _destroyed = new Subject();\r\n\r\n constructor(\r\n private _overlay: Overlay,\r\n private _elementRef: ElementRef,\r\n private _scrollDispatcher: ScrollDispatcher,\r\n private _viewContainerRef: ViewContainerRef,\r\n private _ngZone: NgZone,\r\n platform: Platform,\r\n private _ariaDescriber: AriaDescriber,\r\n private _focusMonitor: FocusMonitor,\r\n @Inject(OUI_TOOLTIP_SCROLL_STRATEGY) scrollStrategy: any,\r\n @Optional() private _dir: Directionality\r\n ) {\r\n this._scrollStrategy = scrollStrategy;\r\n const element: HTMLElement = _elementRef.nativeElement;\r\n const elementStyle = element.style as NewCSSStyleDeclaration & {\r\n webkitUserDrag: string;\r\n };\r\n const hasGestures = typeof window === 'undefined' || (window as any).Hammer;\r\n\r\n // The mouse events shouldn't be bound on mobile devices, because they can prevent the\r\n // first tap from firing its click event or can cause the tooltip to open for clicks.\r\n if (!platform.IOS && !platform.ANDROID) {\r\n this._manualListeners\r\n .set('mouseenter', () => this.show())\r\n .set('mouseleave', () => this.hide());\r\n } else if (!hasGestures) {\r\n // there's no way for the user to trigger the tooltip on a touch device.\r\n this._manualListeners.set('touchstart', () => this.show());\r\n }\r\n\r\n this._manualListeners.forEach((listener, event) =>\r\n element.addEventListener(event, listener)\r\n );\r\n\r\n if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\r\n elementStyle.webkitUserSelect =\r\n elementStyle.userSelect =\r\n elementStyle.msUserSelect =\r\n '';\r\n }\r\n\r\n // Hammer applies `-webkit-user-drag: none` on all elements by default,\r\n // which breaks the native drag&drop. If the consumer explicitly made\r\n // the element draggable, clear the `-webkit-user-drag`.\r\n if (element.draggable && elementStyle.webkitUserDrag === 'none') {\r\n elementStyle.webkitUserDrag = '';\r\n }\r\n\r\n _focusMonitor\r\n .monitor(_elementRef)\r\n .pipe(takeUntil(this._destroyed))\r\n .subscribe((origin) => {\r\n // Note that the focus monitor runs outside the Angular zone.\r\n if (!origin) {\r\n _ngZone.run(() => this.hide());\r\n } else if (origin === 'keyboard') {\r\n _ngZone.run(() => this.show());\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * Dispose the tooltip when destroyed.\r\n */\r\n ngOnDestroy() {\r\n if (this._overlayRef) {\r\n this._overlayRef.dispose();\r\n this._tooltipInstance = null;\r\n }\r\n\r\n // Clean up the event listeners set in the constructor\r\n this._manualListeners.forEach((listener, event) => {\r\n this._elementRef.nativeElement.removeEventListener(event, listener);\r\n });\r\n this._manualListeners.clear();\r\n\r\n this._destroyed.next();\r\n this._destroyed.complete();\r\n\r\n this._ariaDescriber.removeDescription(\r\n this._elementRef.nativeElement,\r\n this.message\r\n );\r\n this._focusMonitor.stopMonitoring(this._elementRef);\r\n }\r\n\r\n /** Shows the tooltip after the delay in ms, defaults to tooltip-delay-show or 0ms if no input */\r\n show(): void {\r\n if (\r\n this.disabled ||\r\n !this.message ||\r\n (this._isTooltipVisible() &&\r\n !this._tooltipInstance!._showTimeoutId &&\r\n !this._tooltipInstance!._hideTimeoutId)\r\n ) {\r\n return;\r\n }\r\n\r\n const overlayRef = this._createOverlay();\r\n\r\n this._detach();\r\n this._portal =\r\n this._portal ||\r\n new ComponentPortal(TooltipComponent, this._viewContainerRef);\r\n this._tooltipInstance = overlayRef.attach(this._portal).instance;\r\n this._tooltipInstance\r\n .afterHidden()\r\n .pipe(takeUntil(this._destroyed))\r\n .subscribe(() => this._detach());\r\n this._setTooltipClass(this._tooltipClass);\r\n this._updateTooltipMessage();\r\n this._tooltipInstance!.show();\r\n }\r\n\r\n /** Hides the tooltip after the delay in ms, defaults to tooltip-delay-hide or 0ms if no input */\r\n hide(): void {\r\n if (this._tooltipInstance) {\r\n this._tooltipInstance.hide();\r\n }\r\n }\r\n\r\n /** Shows/hides the tooltip */\r\n toggle(): void {\r\n this._isTooltipVisible() ? this.hide() : this.show();\r\n }\r\n\r\n /** Returns true if the tooltip is currently visible to the user */\r\n _isTooltipVisible(): boolean {\r\n return !!this._tooltipInstance && this._tooltipInstance.isVisible();\r\n }\r\n\r\n /** Handles the keydown events on the host element. */\r\n _handleKeydown(e: KeyboardEvent) {\r\n if (this._isTooltipVisible() && e.key === 'Escape') {\r\n e.stopPropagation();\r\n this.hide();\r\n }\r\n }\r\n\r\n /** Handles the touchend events on the host element. */\r\n _handleTouchend() {\r\n this.hide();\r\n }\r\n\r\n /** Create the overlay config and position strategy */\r\n private _createOverlay(): OverlayRef {\r\n if (this._overlayRef) {\r\n return this._overlayRef;\r\n }\r\n\r\n // Create connected position strategy that listens for scroll events to reposition.\r\n const strategy = this._overlay\r\n .position()\r\n .flexibleConnectedTo(this._elementRef)\r\n .withTransformOriginOn('.oui-tooltip')\r\n .withFlexibleDimensions(false)\r\n .withViewportMargin(8);\r\n\r\n const scrollableAncestors =\r\n this._scrollDispatcher.getAncestorScrollContainers(this._elementRef);\r\n\r\n strategy.withScrollableContainers(scrollableAncestors);\r\n\r\n strategy.positionChanges\r\n .pipe(takeUntil(this._destroyed))\r\n .subscribe((change) => {\r\n if (this._tooltipInstance) {\r\n if (\r\n change.scrollableViewProperties.isOverlayClipped &&\r\n this._tooltipInstance.isVisible()\r\n ) {\r\n // After position changes occur and the overlay is clipped by\r\n // a parent scrollable then close the tooltip.\r\n this._ngZone.run(() => this.hide());\r\n }\r\n }\r\n });\r\n\r\n this._overlayRef = this._overlay.create({\r\n direction: this._dir,\r\n positionStrategy: strategy,\r\n panelClass: TOOLTIP_PANEL_CLASS,\r\n scrollStrategy: this._scrollStrategy(),\r\n });\r\n\r\n this._updatePosition();\r\n\r\n this._overlayRef\r\n .detachments()\r\n .pipe(takeUntil(this._destroyed))\r\n .subscribe(() => this._detach());\r\n\r\n return this._overlayRef;\r\n }\r\n\r\n /** Detaches the currently-attached tooltip. */\r\n private _detach() {\r\n if (this._overlayRef && this._overlayRef.hasAttached()) {\r\n this._overlayRef.detach();\r\n }\r\n\r\n this._tooltipInstance = null;\r\n }\r\n\r\n /** Updates the position of the current tooltip. */\r\n private _updatePosition() {\r\n const position = this._overlayRef!.getConfig()\r\n .positionStrategy as FlexibleConnectedPositionStrategy;\r\n const origin = this._getOrigin();\r\n const overlay = this._getOverlayPosition();\r\n\r\n position.withPositions([\r\n { ...origin.main, ...overlay.main },\r\n { ...origin.fallback, ...overlay.fallback },\r\n ]);\r\n }\r\n\r\n /**\r\n * Returns the origin position and a fallback position based on the user's position preference.\r\n * The fallback position is the inverse of the origin (e.g. `'below' -> 'above'`).\r\n */\r\n _getOrigin(): {\r\n main: OriginConnectionPosition;\r\n fallback: OriginConnectionPosition;\r\n } {\r\n const isLtr = !this._dir || this._dir.value === 'ltr';\r\n const position = this.position;\r\n let originPosition: OriginConnectionPosition;\r\n\r\n if (position === 'above' || position === 'below') {\r\n originPosition = {\r\n originX: 'center',\r\n originY: position === 'above' ? 'top' : 'bottom',\r\n };\r\n } else if (\r\n (position === 'left' && isLtr) ||\r\n (position === 'right' && !isLtr)\r\n ) {\r\n originPosition = { originX: 'start', originY: 'center' };\r\n } else if (\r\n (position === 'right' && isLtr) ||\r\n (position === 'left' && !isLtr)\r\n ) {\r\n originPosition = { originX: 'end', originY: 'center' };\r\n } else {\r\n throw getOuiTooltipInvalidPositionError(position);\r\n }\r\n\r\n const { x, y } = this._invertPosition(\r\n originPosition.originX,\r\n originPosition.originY\r\n );\r\n\r\n return {\r\n main: originPosition,\r\n fallback: { originX: x, originY: y },\r\n };\r\n }\r\n\r\n /** Returns the overlay position and a fallback position based on the user's preference */\r\n _getOverlayPosition(): {\r\n main: OverlayConnectionPosition;\r\n fallback: OverlayConnectionPosition;\r\n } {\r\n const isLtr = !this._dir || this._dir.value === 'ltr';\r\n const position = this.position;\r\n let overlayPosition: OverlayConnectionPosition;\r\n\r\n if (position === 'above') {\r\n overlayPosition = { overlayX: 'center', overlayY: 'bottom' };\r\n } else if (position === 'below') {\r\n overlayPosition = { overlayX: 'center', overlayY: 'top' };\r\n } else if (\r\n (position === 'left' && isLtr) ||\r\n (position === 'right' && !isLtr)\r\n ) {\r\n overlayPosition = { overlayX: 'end', overlayY: 'center' };\r\n } else if (\r\n (position === 'right' && isLtr) ||\r\n (position === 'left' && !isLtr)\r\n ) {\r\n overlayPosition = { overlayX: 'start', overlayY: 'center' };\r\n } else {\r\n throw getOuiTooltipInvalidPositionError(position);\r\n }\r\n\r\n const { x, y } = this._invertPosition(\r\n overlayPosition.overlayX,\r\n overlayPosition.overlayY\r\n );\r\n\r\n return {\r\n main: overlayPosition,\r\n fallback: { overlayX: x, overlayY: y },\r\n };\r\n }\r\n\r\n /** Updates the tooltip message and repositions the overlay according to the new message length */\r\n private _updateTooltipMessage() {\r\n // Must wait for the message to be painted to the tooltip so that the overlay can properly\r\n // calculate the correct positioning based on the size of the text.\r\n if (this._tooltipInstance) {\r\n this._tooltipInstance.message = this.message;\r\n this._tooltipInstance._markForCheck();\r\n\r\n this._ngZone.onMicrotaskEmpty\r\n .asObservable()\r\n .pipe(take(1), takeUntil(this._destroyed))\r\n .subscribe(() => {\r\n if (this._tooltipInstance) {\r\n this._overlayRef!.updatePosition();\r\n }\r\n });\r\n }\r\n }\r\n\r\n /** Updates the tooltip class */\r\n private _setTooltipClass(\r\n tooltipClass: string | string[] | Set | { [key: string]: any }\r\n ) {\r\n if (this._tooltipInstance) {\r\n this._tooltipInstance.tooltipClass = tooltipClass;\r\n this._tooltipInstance._markForCheck();\r\n }\r\n }\r\n\r\n /** Inverts an overlay position. */\r\n private _invertPosition(\r\n x: HorizontalConnectionPos,\r\n y: VerticalConnectionPos\r\n ) {\r\n if (this.position === 'above' || this.position === 'below') {\r\n if (y === 'top') {\r\n y = 'bottom';\r\n } else if (y === 'bottom') {\r\n y = 'top';\r\n }\r\n } else {\r\n if (x === 'end') {\r\n x = 'start';\r\n } else if (x === 'start') {\r\n x = 'end';\r\n }\r\n }\r\n\r\n return { x, y };\r\n }\r\n}\r\n", + "sourceCode": "import { AnimationEvent } from '@angular/animations';\nimport { AriaDescriber, FocusMonitor } from '@angular/cdk/a11y';\nimport { Directionality } from '@angular/cdk/bidi';\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport {\n BreakpointObserver,\n Breakpoints,\n BreakpointState,\n} from '@angular/cdk/layout';\nimport {\n FlexibleConnectedPositionStrategy,\n HorizontalConnectionPos,\n OriginConnectionPosition,\n Overlay,\n OverlayConnectionPosition,\n OverlayRef,\n VerticalConnectionPos,\n ScrollStrategy,\n} from '@angular/cdk/overlay';\nimport { ScrollDispatcher } from '@angular/cdk/scrolling';\nimport { Platform } from '@angular/cdk/platform';\nimport { ComponentPortal } from '@angular/cdk/portal';\nimport { take, takeUntil } from 'rxjs/operators';\nimport {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n Directive,\n ElementRef,\n Inject,\n InjectionToken,\n Input,\n NgZone,\n OnDestroy,\n Optional,\n ViewContainerRef,\n ViewEncapsulation,\n} from '@angular/core';\nimport { Subject, Observable } from 'rxjs';\nimport { ouiTooltipAnimations } from './tooltip-animations';\nimport { CanDisable } from '../core';\n\nexport type TooltipPosition = 'left' | 'right' | 'above' | 'below';\n\n/** Time in ms to throttle repositioning after scroll events. */\nexport const SCROLL_THROTTLE_MS = 20;\n\n/** CSS class that will be attached to the overlay panel. */\nexport const TOOLTIP_PANEL_CLASS = 'oui-tooltip-panel';\n\n/**\n * Creates an error to be thrown if the user supplied an invalid tooltip position.\n *\n * @docs-private\n */\nexport function getOuiTooltipInvalidPositionError(position: string) {\n return Error(`Tooltip position \"${position}\" is invalid.`);\n}\n\n/** Injection token that determines the scroll handling while a tooltip is visible. */\nexport const OUI_TOOLTIP_SCROLL_STRATEGY = new InjectionToken<\n () => ScrollStrategy\n>('oui-tooltip-scroll-strategy');\n\n/** @docs-private */\nexport function OUI_TOOLTIP_SCROLL_STRATEGY_FACTORY(\n overlay: Overlay\n): () => ScrollStrategy {\n return () =>\n overlay.scrollStrategies.reposition({ scrollThrottle: SCROLL_THROTTLE_MS });\n}\n\n/** @docs-private */\nexport const OUI_TOOLTIP_SCROLL_STRATEGY_FACTORY_PROVIDER = {\n provide: OUI_TOOLTIP_SCROLL_STRATEGY,\n deps: [Overlay],\n useFactory: OUI_TOOLTIP_SCROLL_STRATEGY_FACTORY,\n};\n\n/** Default `ouiTooltip` options that can be overridden. */\nexport interface OuiTooltipDefaultOptions {\n showDelay: number;\n hideDelay: number;\n touchendHideDelay: number;\n}\n\nexport interface NewCSSStyleDeclaration extends CSSStyleDeclaration {\n msUserSelect: string;\n}\n\n/** Injection token to be used to override the default options for `ouiTooltip`. */\nexport const OUI_TOOLTIP_DEFAULT_OPTIONS =\n new InjectionToken('oui-tooltip-default-options', {\n providedIn: 'root',\n factory: OUI_TOOLTIP_DEFAULT_OPTIONS_FACTORY,\n });\n\n/** @docs-private */\nexport function OUI_TOOLTIP_DEFAULT_OPTIONS_FACTORY(): OuiTooltipDefaultOptions {\n return {\n showDelay: 0,\n hideDelay: 0,\n touchendHideDelay: 1500,\n };\n}\n\nexport type TooltipVisibility = 'initial' | 'visible' | 'hidden';\n\n/**\n * Internal component that wraps the tooltip's content.\n *\n * @docs-private\n */\n@Component({\n selector: 'oui-tooltip-component',\n templateUrl: 'tooltip.html',\n styleUrls: ['tooltip.scss'],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n animations: [ouiTooltipAnimations.tooltipState],\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n // Forces the element to have a layout in IE and Edge. This fixes issues where the element\n // won't be rendered if the animations are disabled or there is no web animations polyfill.\n '[style.zoom]': '_visibility === \"visible\" ? 1 : null',\n '(body:click)': 'this._handleBodyInteraction()',\n 'aria-hidden': 'true',\n },\n})\nexport class TooltipComponent {\n /** Message to display in the tooltip */\n message: string;\n /** Classes to be added to the tooltip. Supports the same syntax as `ngClass`. */\n tooltipClass: string | string[] | Set | { [key: string]: any };\n\n /** The timeout ID of any current timer set to show the tooltip */\n _showTimeoutId: number | null;\n\n /** The timeout ID of any current timer set to hide the tooltip */\n _hideTimeoutId: number | null;\n\n /** Property watched by the animation framework to show or hide the tooltip */\n _visibility: TooltipVisibility = 'initial';\n\n /** Whether interactions on the page should close the tooltip */\n private _closeOnInteraction = false;\n\n /** Subject for notifying that the tooltip has been hidden from the view */\n private readonly _onHide: Subject = new Subject();\n\n /** Stream that emits whether the user has a handset-sized display. */\n _isHandset: Observable = this._breakpointObserver.observe(\n Breakpoints.Handset\n );\n\n constructor(\n private _changeDetectorRef: ChangeDetectorRef,\n private _breakpointObserver: BreakpointObserver\n ) {}\n\n /**\n * Shows the tooltip with an animation originating from the provided origin\n *\n * @param delay Amount of milliseconds to the delay showing the tooltip.\n */\n show(): void {\n // Cancel the delayed hide if it is scheduled\n if (this._hideTimeoutId) {\n clearTimeout(this._hideTimeoutId);\n this._hideTimeoutId = null;\n }\n\n // Body interactions should cancel the tooltip if there is a delay in showing.\n this._closeOnInteraction = true;\n setTimeout(() => {\n this._visibility = 'visible';\n this._showTimeoutId = null;\n\n // Mark for check so if any parent component has set the\n // ChangeDetectionStrategy to OnPush it will be checked anyways\n this._markForCheck();\n }, 0);\n }\n\n /**\n * Begins the animation to hide the tooltip after the provided delay in ms.\n *\n * @param delay Amount of milliseconds to delay showing the tooltip.\n */\n hide(): void {\n // Cancel the delayed show if it is scheduled\n if (this._showTimeoutId) {\n clearTimeout(this._showTimeoutId);\n this._showTimeoutId = null;\n }\n setTimeout(() => {\n this._visibility = 'hidden';\n this._hideTimeoutId = null;\n // Mark for check so if any parent component has set the\n // ChangeDetectionStrategy to OnPush it will be checked anyways\n this._markForCheck();\n }, 0);\n }\n\n /** Returns an observable that notifies when the tooltip has been hidden from view. */\n afterHidden(): Observable {\n return this._onHide.asObservable();\n }\n\n /** Whether the tooltip is being displayed. */\n isVisible(): boolean {\n return this._visibility === 'visible';\n }\n\n _animationStart() {\n this._closeOnInteraction = false;\n }\n\n _animationDone(event: AnimationEvent): void {\n const toState = event.toState as TooltipVisibility;\n\n if (toState === 'hidden' && !this.isVisible()) {\n this._onHide.next();\n }\n\n if (toState === 'visible' || toState === 'hidden') {\n this._closeOnInteraction = true;\n }\n }\n\n /**\n * Interactions on the HTML body should close the tooltip immediately\n */\n _handleBodyInteraction(): void {\n if (this._closeOnInteraction) {\n this.hide();\n }\n }\n\n /**\n * Marks that the tooltip needs to be checked in the next change detection run.\n * Mainly used for rendering the initial text before positioning a tooltip, which\n * can be problematic in components with OnPush change detection.\n */\n _markForCheck(): void {\n this._changeDetectorRef.markForCheck();\n }\n}\n\n/**\n * Directive that attaches a tooltip to the host element. Animates the showing and\n * hiding of a tooltip provided position (defaults to below the element).\n */\n@Directive({\n selector: '[ouiTooltip]',\n exportAs: 'ouiTooltip',\n // eslint-disable-next-line @angular-eslint/no-host-metadata-property\n host: {\n '(longpress)': 'show()',\n '(keydown)': '_handleKeydown($event)',\n '(touchend)': '_handleTouchend()',\n '[attr.tabindex]': 'disabled ? -1 : 0',\n '[attr.aria-hidden]': 'false',\n },\n})\nexport class OuiTooltip implements OnDestroy, CanDisable {\n _overlayRef: OverlayRef | null;\n _tooltipInstance: TooltipComponent | null;\n\n private _portal: ComponentPortal;\n private _position: TooltipPosition = 'below';\n private _disabled = false;\n private _tooltipClass:\n | string\n | string[]\n | Set\n | { [key: string]: any };\n private _scrollStrategy: () => ScrollStrategy;\n\n /** Allows the user to define the position of the tooltip relative to the parent element */\n @Input('ouiTooltipPosition')\n get position(): TooltipPosition {\n return this._position;\n }\n set position(value: TooltipPosition) {\n if (value !== this._position) {\n this._position = value;\n if (this._overlayRef) {\n this._updatePosition();\n\n if (this._tooltipInstance) {\n this._tooltipInstance!.show();\n }\n\n this._overlayRef.updatePosition();\n }\n }\n }\n\n /** Disables the display of the tooltip. */\n @Input('ouiTooltipDisabled')\n get disabled(): boolean {\n return this._disabled;\n }\n set disabled(value) {\n this._disabled = coerceBooleanProperty(value);\n\n // If tooltip is disabled, hide immediately.\n if (this._disabled) {\n this.hide();\n }\n }\n\n private _message = '';\n\n /** The message to be displayed in the tooltip */\n @Input('ouiTooltip')\n get message() {\n return this._message;\n }\n set message(value: string) {\n this._ariaDescriber.removeDescription(\n this._elementRef.nativeElement,\n this._message\n );\n\n // If the message is not a string (e.g. number), convert it to a string and trim it.\n this._message = value != null ? `${value}`.trim() : '';\n\n if (!this._message && this._isTooltipVisible()) {\n this.hide();\n } else {\n this._updateTooltipMessage();\n this._ariaDescriber.describe(\n this._elementRef.nativeElement,\n this.message\n );\n }\n }\n\n /** Classes to be passed to the tooltip. Supports the same syntax as `ngClass`. */\n @Input('ouiTooltipClass')\n get tooltipClass() {\n return this._tooltipClass;\n }\n set tooltipClass(\n value: string | string[] | Set | { [key: string]: any }\n ) {\n this._tooltipClass = value;\n if (this._tooltipInstance) {\n this._tooltipInstance._markForCheck();\n this._setTooltipClass(this._tooltipClass);\n }\n }\n\n private _manualListeners = new Map<\n string,\n EventListenerOrEventListenerObject\n >();\n\n /** Emits when the component is destroyed. */\n private readonly _destroyed = new Subject();\n\n constructor(\n private _overlay: Overlay,\n private _elementRef: ElementRef,\n private _scrollDispatcher: ScrollDispatcher,\n private _viewContainerRef: ViewContainerRef,\n private _ngZone: NgZone,\n platform: Platform,\n private _ariaDescriber: AriaDescriber,\n private _focusMonitor: FocusMonitor,\n @Inject(OUI_TOOLTIP_SCROLL_STRATEGY) scrollStrategy: any,\n @Optional() private _dir: Directionality\n ) {\n this._scrollStrategy = scrollStrategy;\n const element: HTMLElement = _elementRef.nativeElement;\n const elementStyle = element.style as NewCSSStyleDeclaration & {\n webkitUserDrag: string;\n };\n const hasGestures = typeof window === 'undefined' || (window as any).Hammer;\n\n // The mouse events shouldn't be bound on mobile devices, because they can prevent the\n // first tap from firing its click event or can cause the tooltip to open for clicks.\n if (!platform.IOS && !platform.ANDROID) {\n this._manualListeners\n .set('mouseenter', () => this.show())\n .set('mouseleave', () => this.hide());\n } else if (!hasGestures) {\n // there's no way for the user to trigger the tooltip on a touch device.\n this._manualListeners.set('touchstart', () => this.show());\n }\n\n this._manualListeners.forEach((listener, event) =>\n element.addEventListener(event, listener)\n );\n\n if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\n elementStyle.webkitUserSelect =\n elementStyle.userSelect =\n elementStyle.msUserSelect =\n '';\n }\n\n // Hammer applies `-webkit-user-drag: none` on all elements by default,\n // which breaks the native drag&drop. If the consumer explicitly made\n // the element draggable, clear the `-webkit-user-drag`.\n if (element.draggable && elementStyle.webkitUserDrag === 'none') {\n elementStyle.webkitUserDrag = '';\n }\n\n _focusMonitor\n .monitor(_elementRef)\n .pipe(takeUntil(this._destroyed))\n .subscribe((origin) => {\n // Note that the focus monitor runs outside the Angular zone.\n if (!origin) {\n _ngZone.run(() => this.hide());\n } else if (origin === 'keyboard') {\n _ngZone.run(() => this.show());\n }\n });\n }\n\n /**\n * Dispose the tooltip when destroyed.\n */\n ngOnDestroy() {\n if (this._overlayRef) {\n this._overlayRef.dispose();\n this._tooltipInstance = null;\n }\n\n // Clean up the event listeners set in the constructor\n this._manualListeners.forEach((listener, event) => {\n this._elementRef.nativeElement.removeEventListener(event, listener);\n });\n this._manualListeners.clear();\n\n this._destroyed.next();\n this._destroyed.complete();\n\n this._ariaDescriber.removeDescription(\n this._elementRef.nativeElement,\n this.message\n );\n this._focusMonitor.stopMonitoring(this._elementRef);\n }\n\n /** Shows the tooltip after the delay in ms, defaults to tooltip-delay-show or 0ms if no input */\n show(): void {\n if (\n this.disabled ||\n !this.message ||\n (this._isTooltipVisible() &&\n !this._tooltipInstance!._showTimeoutId &&\n !this._tooltipInstance!._hideTimeoutId)\n ) {\n return;\n }\n\n const overlayRef = this._createOverlay();\n\n this._detach();\n this._portal =\n this._portal ||\n new ComponentPortal(TooltipComponent, this._viewContainerRef);\n this._tooltipInstance = overlayRef.attach(this._portal).instance;\n this._tooltipInstance\n .afterHidden()\n .pipe(takeUntil(this._destroyed))\n .subscribe(() => this._detach());\n this._setTooltipClass(this._tooltipClass);\n this._updateTooltipMessage();\n this._tooltipInstance!.show();\n }\n\n /** Hides the tooltip after the delay in ms, defaults to tooltip-delay-hide or 0ms if no input */\n hide(): void {\n if (this._tooltipInstance) {\n this._tooltipInstance.hide();\n }\n }\n\n /** Shows/hides the tooltip */\n toggle(): void {\n this._isTooltipVisible() ? this.hide() : this.show();\n }\n\n /** Returns true if the tooltip is currently visible to the user */\n _isTooltipVisible(): boolean {\n return !!this._tooltipInstance && this._tooltipInstance.isVisible();\n }\n\n /** Handles the keydown events on the host element. */\n _handleKeydown(e: KeyboardEvent) {\n if (this._isTooltipVisible() && e.key === 'Escape') {\n e.stopPropagation();\n this.hide();\n }\n }\n\n /** Handles the touchend events on the host element. */\n _handleTouchend() {\n this.hide();\n }\n\n /** Create the overlay config and position strategy */\n private _createOverlay(): OverlayRef {\n if (this._overlayRef) {\n return this._overlayRef;\n }\n\n // Create connected position strategy that listens for scroll events to reposition.\n const strategy = this._overlay\n .position()\n .flexibleConnectedTo(this._elementRef)\n .withTransformOriginOn('.oui-tooltip')\n .withFlexibleDimensions(false)\n .withViewportMargin(8);\n\n const scrollableAncestors =\n this._scrollDispatcher.getAncestorScrollContainers(this._elementRef);\n\n strategy.withScrollableContainers(scrollableAncestors);\n\n strategy.positionChanges\n .pipe(takeUntil(this._destroyed))\n .subscribe((change) => {\n if (this._tooltipInstance) {\n if (\n change.scrollableViewProperties.isOverlayClipped &&\n this._tooltipInstance.isVisible()\n ) {\n // After position changes occur and the overlay is clipped by\n // a parent scrollable then close the tooltip.\n this._ngZone.run(() => this.hide());\n }\n }\n });\n\n this._overlayRef = this._overlay.create({\n direction: this._dir,\n positionStrategy: strategy,\n panelClass: TOOLTIP_PANEL_CLASS,\n scrollStrategy: this._scrollStrategy(),\n });\n\n this._updatePosition();\n\n this._overlayRef\n .detachments()\n .pipe(takeUntil(this._destroyed))\n .subscribe(() => this._detach());\n\n return this._overlayRef;\n }\n\n /** Detaches the currently-attached tooltip. */\n private _detach() {\n if (this._overlayRef && this._overlayRef.hasAttached()) {\n this._overlayRef.detach();\n }\n\n this._tooltipInstance = null;\n }\n\n /** Updates the position of the current tooltip. */\n private _updatePosition() {\n const position = this._overlayRef!.getConfig()\n .positionStrategy as FlexibleConnectedPositionStrategy;\n const origin = this._getOrigin();\n const overlay = this._getOverlayPosition();\n\n position.withPositions([\n { ...origin.main, ...overlay.main },\n { ...origin.fallback, ...overlay.fallback },\n ]);\n }\n\n /**\n * Returns the origin position and a fallback position based on the user's position preference.\n * The fallback position is the inverse of the origin (e.g. `'below' -> 'above'`).\n */\n _getOrigin(): {\n main: OriginConnectionPosition;\n fallback: OriginConnectionPosition;\n } {\n const isLtr = !this._dir || this._dir.value === 'ltr';\n const position = this.position;\n let originPosition: OriginConnectionPosition;\n\n if (position === 'above' || position === 'below') {\n originPosition = {\n originX: 'center',\n originY: position === 'above' ? 'top' : 'bottom',\n };\n } else if (\n (position === 'left' && isLtr) ||\n (position === 'right' && !isLtr)\n ) {\n originPosition = { originX: 'start', originY: 'center' };\n } else if (\n (position === 'right' && isLtr) ||\n (position === 'left' && !isLtr)\n ) {\n originPosition = { originX: 'end', originY: 'center' };\n } else {\n throw getOuiTooltipInvalidPositionError(position);\n }\n\n const { x, y } = this._invertPosition(\n originPosition.originX,\n originPosition.originY\n );\n\n return {\n main: originPosition,\n fallback: { originX: x, originY: y },\n };\n }\n\n /** Returns the overlay position and a fallback position based on the user's preference */\n _getOverlayPosition(): {\n main: OverlayConnectionPosition;\n fallback: OverlayConnectionPosition;\n } {\n const isLtr = !this._dir || this._dir.value === 'ltr';\n const position = this.position;\n let overlayPosition: OverlayConnectionPosition;\n\n if (position === 'above') {\n overlayPosition = { overlayX: 'center', overlayY: 'bottom' };\n } else if (position === 'below') {\n overlayPosition = { overlayX: 'center', overlayY: 'top' };\n } else if (\n (position === 'left' && isLtr) ||\n (position === 'right' && !isLtr)\n ) {\n overlayPosition = { overlayX: 'end', overlayY: 'center' };\n } else if (\n (position === 'right' && isLtr) ||\n (position === 'left' && !isLtr)\n ) {\n overlayPosition = { overlayX: 'start', overlayY: 'center' };\n } else {\n throw getOuiTooltipInvalidPositionError(position);\n }\n\n const { x, y } = this._invertPosition(\n overlayPosition.overlayX,\n overlayPosition.overlayY\n );\n\n return {\n main: overlayPosition,\n fallback: { overlayX: x, overlayY: y },\n };\n }\n\n /** Updates the tooltip message and repositions the overlay according to the new message length */\n private _updateTooltipMessage() {\n // Must wait for the message to be painted to the tooltip so that the overlay can properly\n // calculate the correct positioning based on the size of the text.\n if (this._tooltipInstance) {\n this._tooltipInstance.message = this.message;\n this._tooltipInstance._markForCheck();\n\n this._ngZone.onMicrotaskEmpty\n .asObservable()\n .pipe(take(1), takeUntil(this._destroyed))\n .subscribe(() => {\n if (this._tooltipInstance) {\n this._overlayRef!.updatePosition();\n }\n });\n }\n }\n\n /** Updates the tooltip class */\n private _setTooltipClass(\n tooltipClass: string | string[] | Set | { [key: string]: any }\n ) {\n if (this._tooltipInstance) {\n this._tooltipInstance.tooltipClass = tooltipClass;\n this._tooltipInstance._markForCheck();\n }\n }\n\n /** Inverts an overlay position. */\n private _invertPosition(\n x: HorizontalConnectionPos,\n y: VerticalConnectionPos\n ) {\n if (this.position === 'above' || this.position === 'below') {\n if (y === 'top') {\n y = 'bottom';\n } else if (y === 'bottom') {\n y = 'top';\n }\n } else {\n if (x === 'end') {\n x = 'start';\n } else if (x === 'start') {\n x = 'end';\n }\n }\n\n return { x, y };\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": ".oui-tooltip-wrapper {\r\n padding: 8px;\r\n}\r\n.oui-tooltip {\r\n display: block;\r\n font-style: normal;\r\n font-weight: normal;\r\n text-decoration: none;\r\n text-shadow: none;\r\n text-transform: none;\r\n letter-spacing: normal;\r\n word-break: normal;\r\n word-spacing: normal;\r\n word-wrap: normal;\r\n white-space: normal;\r\n line-break: auto;\r\n border: 1px solid #c8c8c8;\r\n background-color: #ffffff;\r\n -webkit-box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.3);\r\n -moz-box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.3);\r\n box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.3);\r\n z-index: 9999999;\r\n}\r\n.oui-tooltip .tooltip-inner {\r\n max-width: 280px;\r\n padding: 10px;\r\n color: #333333;\r\n font-size: 13px;\r\n line-height: 17px;\r\n text-align: center;\r\n}\r\n", + "data": ".oui-tooltip-wrapper {\n padding: 8px;\n}\n.oui-tooltip {\n display: block;\n font-style: normal;\n font-weight: normal;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n word-wrap: normal;\n white-space: normal;\n line-break: auto;\n border: 1px solid #c8c8c8;\n background-color: #ffffff;\n -webkit-box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.3);\n -moz-box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.3);\n box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.3);\n z-index: 9999999;\n}\n.oui-tooltip .tooltip-inner {\n max-width: 280px;\n padding: 10px;\n color: #333333;\n font-size: 13px;\n line-height: 17px;\n text-align: center;\n}\n", "styleUrl": "tooltip.scss" } ], @@ -40931,19 +50813,364 @@ } ] }, - "templateData": "
\r\n \r\n
{{message}}
\r\n
\r\n\r\n" + "templateData": "
\n \n
{{message}}
\n
\n\n" } ], "modules": [ + { + "name": "MatCardModule", + "id": "module-MatCardModule-c1d98a98eef1d0e9a32e7b3c41630b4768f0373dc706305de52368d7fe7957af7cffb813374986d01e04e8253a5ee182a233073b1631d5a3e0b9e73364ffa1ce", + "description": "", + "deprecationMessage": "", + "deprecated": false, + "file": "ui/src/components/card/module.ts", + "methods": [], + "sourceCode": "/**\r\n * @license\r\n * Copyright Google LLC All Rights Reserved.\r\n *\r\n * Use of this source code is governed by an MIT-style license that can be\r\n * found in the LICENSE file at https://angular.io/license\r\n */\r\n\r\nimport {CommonModule} from '@angular/common';\r\nimport {NgModule} from '@angular/core';\r\n// import {MatCommonModule} from '../core/common-behaviors/common-module';\r\nimport {\r\n MatCard,\r\n MatCardActions,\r\n MatCardAvatar,\r\n MatCardContent,\r\n MatCardFooter,\r\n MatCardHeader,\r\n MatCardImage,\r\n MatCardLgImage,\r\n MatCardMdImage,\r\n MatCardSmImage,\r\n MatCardSubtitle,\r\n MatCardTitle,\r\n MatCardTitleGroup,\r\n MatCardXlImage,\r\n} from './card';\r\n\r\nconst CARD_DIRECTIVES = [\r\n MatCard,\r\n MatCardActions,\r\n MatCardAvatar,\r\n MatCardContent,\r\n MatCardFooter,\r\n MatCardHeader,\r\n MatCardImage,\r\n MatCardLgImage,\r\n MatCardMdImage,\r\n MatCardSmImage,\r\n MatCardSubtitle,\r\n MatCardTitle,\r\n MatCardTitleGroup,\r\n MatCardXlImage,\r\n];\r\n\r\n@NgModule({\r\n imports: [CommonModule],\r\n exports: [CARD_DIRECTIVES],\r\n declarations: CARD_DIRECTIVES,\r\n})\r\nexport class MatCardModule {}\r\n", + "children": [ + { + "type": "providers", + "elements": [] + }, + { + "type": "declarations", + "elements": [ + { + "name": "MatCard" + }, + { + "name": "MatCardActions" + }, + { + "name": "MatCardAvatar" + }, + { + "name": "MatCardContent" + }, + { + "name": "MatCardFooter" + }, + { + "name": "MatCardHeader" + }, + { + "name": "MatCardImage" + }, + { + "name": "MatCardLgImage" + }, + { + "name": "MatCardMdImage" + }, + { + "name": "MatCardSmImage" + }, + { + "name": "MatCardSubtitle" + }, + { + "name": "MatCardTitle" + }, + { + "name": "MatCardTitleGroup" + }, + { + "name": "MatCardXlImage" + } + ] + }, + { + "type": "imports", + "elements": [] + }, + { + "type": "exports", + "elements": [ + { + "name": "MatCard" + }, + { + "name": "MatCardActions" + }, + { + "name": "MatCardAvatar" + }, + { + "name": "MatCardContent" + }, + { + "name": "MatCardFooter" + }, + { + "name": "MatCardHeader" + }, + { + "name": "MatCardImage" + }, + { + "name": "MatCardLgImage" + }, + { + "name": "MatCardMdImage" + }, + { + "name": "MatCardSmImage" + }, + { + "name": "MatCardSubtitle" + }, + { + "name": "MatCardTitle" + }, + { + "name": "MatCardTitleGroup" + }, + { + "name": "MatCardXlImage" + } + ] + }, + { + "type": "bootstrap", + "elements": [] + }, + { + "type": "classes", + "elements": [] + } + ] + }, + { + "name": "MatCommonModule", + "id": "module-MatCommonModule-97e7283dd0f34374aeeed060b56c1ffbc542dfe44cb86e4866db9603f26fc0416130da2ba8099fea9f977e88386fe5eab0d7df2a2b51233c2a8dffb6855a4c98", + "description": "

Module that captures anything that should be loaded and/or run for all Angular Material\ncomponents. This includes Bidi, etc.

\n

This module should be imported to each top-level component module (e.g., MatTabsModule).

\n", + "deprecationMessage": "", + "deprecated": false, + "file": "ui/src/components/core/common-behaviors/common-module.ts", + "methods": [ + { + "name": "_checkIsEnabled", + "args": [ + { + "name": "name", + "type": "", + "deprecated": false, + "deprecationMessage": "" + } + ], + "optional": false, + "returnType": "boolean", + "typeParameters": [], + "line": 89, + "deprecated": false, + "deprecationMessage": "", + "rawdescription": "\nGets whether a specific sanity check is enabled.", + "description": "

Gets whether a specific sanity check is enabled.

\n", + "modifierKind": [ + 121 + ], + "jsdoctags": [ + { + "name": "name", + "type": "", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + } + ], + "sourceCode": "/**\r\n * @license\r\n * Copyright Google LLC All Rights Reserved.\r\n *\r\n * Use of this source code is governed by an MIT-style license that can be\r\n * found in the LICENSE file at https://angular.io/license\r\n */\r\n\r\nimport {HighContrastModeDetector} from '@angular/cdk/a11y';\r\nimport {BidiModule} from '@angular/cdk/bidi';\r\nimport {inject, Inject, InjectionToken, NgModule, Optional} from '@angular/core';\r\nimport {VERSION as CDK_VERSION} from '@angular/cdk';\r\nimport {DOCUMENT} from '@angular/common';\r\nimport {Platform, _isTestEnvironment} from '@angular/cdk/platform';\r\nimport {VERSION} from '../version';\r\nimport { isDevMode } from '@angular/core';\r\n\r\n\r\n/** @docs-private */\r\nexport function MATERIAL_SANITY_CHECKS_FACTORY(): SanityChecks {\r\n return true;\r\n}\r\n\r\n/** Injection token that configures whether the Material sanity checks are enabled. */\r\nexport const MATERIAL_SANITY_CHECKS = new InjectionToken('mat-sanity-checks', {\r\n providedIn: 'root',\r\n factory: MATERIAL_SANITY_CHECKS_FACTORY,\r\n});\r\n\r\n/**\r\n * Possible sanity checks that can be enabled. If set to\r\n * true/false, all checks will be enabled/disabled.\r\n */\r\nexport type SanityChecks = boolean | GranularSanityChecks;\r\n\r\n/** Object that can be used to configure the sanity checks granularly. */\r\nexport interface GranularSanityChecks {\r\n doctype: boolean;\r\n theme: boolean;\r\n version: boolean;\r\n}\r\n\r\n/**\r\n * Module that captures anything that should be loaded and/or run for *all* Angular Material\r\n * components. This includes Bidi, etc.\r\n *\r\n * This module should be imported to each top-level component module (e.g., MatTabsModule).\r\n */\r\n@NgModule({\r\n imports: [BidiModule],\r\n exports: [BidiModule],\r\n})\r\nexport class MatCommonModule {\r\n /** Whether we've done the global sanity checks (e.g. a theme is loaded, there is a doctype). */\r\n private _hasDoneGlobalChecks = false;\r\n\r\n constructor(\r\n highContrastModeDetector: HighContrastModeDetector,\r\n @Optional() @Inject(MATERIAL_SANITY_CHECKS) private _sanityChecks: SanityChecks,\r\n @Inject(DOCUMENT) private _document: Document,\r\n ) {\r\n // While A11yModule also does this, we repeat it here to avoid importing A11yModule\r\n // in MatCommonModule.\r\n highContrastModeDetector._applyBodyHighContrastModeCssClasses();\r\n\r\n if (!this._hasDoneGlobalChecks) {\r\n this._hasDoneGlobalChecks = true;\r\n\r\n if (typeof isDevMode === 'undefined' || isDevMode) {\r\n // Inject in here so the reference to `Platform` can be removed in production mode.\r\n const platform = inject(Platform, {optional: true});\r\n\r\n if (this._checkIsEnabled('doctype')) {\r\n _checkDoctypeIsDefined(this._document);\r\n }\r\n\r\n if (this._checkIsEnabled('theme')) {\r\n _checkThemeIsPresent(this._document, !!platform?.isBrowser);\r\n }\r\n\r\n if (this._checkIsEnabled('version')) {\r\n _checkCdkVersionMatch();\r\n }\r\n }\r\n }\r\n }\r\n\r\n /** Gets whether a specific sanity check is enabled. */\r\n private _checkIsEnabled(name: keyof GranularSanityChecks): boolean {\r\n if (_isTestEnvironment()) {\r\n return false;\r\n }\r\n\r\n if (typeof this._sanityChecks === 'boolean') {\r\n return this._sanityChecks;\r\n }\r\n\r\n return !!this._sanityChecks[name];\r\n }\r\n}\r\n\r\n/** Checks that the page has a doctype. */\r\nfunction _checkDoctypeIsDefined(doc: Document): void {\r\n if (!doc.doctype) {\r\n console.warn(\r\n 'Current document does not have a doctype. This may cause ' +\r\n 'some Angular Material components not to behave as expected.',\r\n );\r\n }\r\n}\r\n\r\n/** Checks that a theme has been included. */\r\nfunction _checkThemeIsPresent(doc: Document, isBrowser: boolean): void {\r\n // We need to assert that the `body` is defined, because these checks run very early\r\n // and the `body` won't be defined if the consumer put their scripts in the `head`.\r\n if (!doc.body || !isBrowser) {\r\n return;\r\n }\r\n\r\n const testElement = doc.createElement('div');\r\n testElement.classList.add('mat-theme-loaded-marker');\r\n doc.body.appendChild(testElement);\r\n\r\n const computedStyle = getComputedStyle(testElement);\r\n\r\n // In some situations the computed style of the test element can be null. For example in\r\n // Firefox, the computed style is null if an application is running inside of a hidden iframe.\r\n // See: https://bugzilla.mozilla.org/show_bug.cgi?id=548397\r\n if (computedStyle && computedStyle.display !== 'none') {\r\n console.warn(\r\n 'Could not find Angular Material core theme. Most Material ' +\r\n 'components may not work as expected. For more info refer ' +\r\n 'to the theming guide: https://material.angular.io/guide/theming',\r\n );\r\n }\r\n\r\n testElement.remove();\r\n}\r\n\r\n/** Checks whether the Material version matches the CDK version. */\r\nfunction _checkCdkVersionMatch(): void {\r\n if (VERSION.full !== CDK_VERSION.full) {\r\n console.warn(\r\n 'The Angular Material version (' +\r\n VERSION.full +\r\n ') does not match ' +\r\n 'the Angular CDK version (' +\r\n CDK_VERSION.full +\r\n ').\\n' +\r\n 'Please ensure the versions of these two packages exactly match.',\r\n );\r\n }\r\n}\r\n", + "children": [ + { + "type": "providers", + "elements": [] + }, + { + "type": "declarations", + "elements": [] + }, + { + "type": "imports", + "elements": [] + }, + { + "type": "exports", + "elements": [] + }, + { + "type": "bootstrap", + "elements": [] + }, + { + "type": "classes", + "elements": [] + } + ] + }, + { + "name": "MatRippleModule", + "id": "module-MatRippleModule-f6b4f45d7ea482f7340dfc65f76c2f0783450f2dd3e5f4d77325480124dbae2f4d8aa91ce66b4bd038da5eb7c797b447934b51e4623ce5f567dcb51559362d8d", + "description": "", + "deprecationMessage": "", + "deprecated": false, + "file": "ui/src/components/core/ripple/index.ts", + "methods": [], + "sourceCode": "/**\r\n * @license\r\n * Copyright Google LLC All Rights Reserved.\r\n *\r\n * Use of this source code is governed by an MIT-style license that can be\r\n * found in the LICENSE file at https://angular.io/license\r\n */\r\n\r\nimport {NgModule} from '@angular/core';\r\nimport {MatCommonModule} from '../common-behaviors/common-module';\r\nimport {MatRipple} from './ripple';\r\n\r\nexport * from './ripple';\r\nexport * from './ripple-ref';\r\nexport * from './ripple-renderer';\r\n\r\n@NgModule({\r\n imports: [MatCommonModule],\r\n exports: [MatRipple, MatCommonModule],\r\n declarations: [MatRipple],\r\n})\r\nexport class MatRippleModule {}\r\n", + "children": [ + { + "type": "providers", + "elements": [] + }, + { + "type": "declarations", + "elements": [ + { + "name": "MatRipple" + } + ] + }, + { + "type": "imports", + "elements": [ + { + "name": "MatCommonModule" + } + ] + }, + { + "type": "exports", + "elements": [ + { + "name": "MatCommonModule" + }, + { + "name": "MatRipple" + } + ] + }, + { + "type": "bootstrap", + "elements": [] + }, + { + "type": "classes", + "elements": [] + } + ] + }, + { + "name": "MatTabsModule", + "id": "module-MatTabsModule-c0340aa2cb9ffb70f28d877b411fbcc0b0fa673ba207ae248db6996ee685488dda70850bc4a868c0ac60e6b5e26be38ad8fa7e3fe2755d0215330616c0b4edc0", + "description": "", + "deprecationMessage": "", + "deprecated": false, + "file": "ui/src/components/tabs/module.ts", + "methods": [], + "sourceCode": "/**\r\n * @license\r\n * Copyright Google LLC All Rights Reserved.\r\n *\r\n * Use of this source code is governed by an MIT-style license that can be\r\n * found in the LICENSE file at https://angular.io/license\r\n */\r\n\r\nimport {CommonModule} from '@angular/common';\r\nimport {NgModule} from '@angular/core';\r\nimport { MatRippleModule} from '../core';\r\nimport {PortalModule} from '@angular/cdk/portal';\r\nimport {ObserversModule} from '@angular/cdk/observers';\r\nimport {A11yModule} from '@angular/cdk/a11y';\r\nimport {MatTabBodyPortal} from './tab-body';\r\nimport {MatTabContent} from './tab-content';\r\nimport {MatTabLabel} from './tab-label';\r\nimport {MatTabLabelWrapper} from './tab-label-wrapper';\r\nimport {MatTab} from './tab';\r\nimport {MatTabHeader} from './tab-header';\r\nimport {MatTabGroup} from './tab-group';\r\nimport {MatTabNav, MatTabNavPanel, MatTabLink} from './tab-nav-bar/tab-nav-bar';\r\nimport { MatTabBody } from './tab-body';\r\n\r\n@NgModule({\r\n imports: [\r\n CommonModule,\r\n PortalModule,\r\n MatRippleModule,\r\n ObserversModule,\r\n A11yModule,\r\n ],\r\n exports: [\r\n MatTabContent,\r\n MatTabLabel,\r\n MatTab,\r\n MatTabGroup,\r\n MatTabNav,\r\n MatTabNavPanel,\r\n MatTabLink,\r\n MatTabBodyPortal,\r\n MatTabBody\r\n ],\r\n declarations: [\r\n MatTabContent,\r\n MatTabLabel,\r\n MatTab,\r\n MatTabGroup,\r\n MatTabNav,\r\n MatTabNavPanel,\r\n MatTabLink,\r\n\r\n // Private directives, should not be exported.\r\n MatTabLabelWrapper,\r\n MatTabHeader,\r\n MatTabBody,\r\n MatTabBodyPortal,\r\n ],\r\n})\r\nexport class MatTabsModule {}\r\n", + "children": [ + { + "type": "providers", + "elements": [] + }, + { + "type": "declarations", + "elements": [ + { + "name": "MatTab" + }, + { + "name": "MatTabBody" + }, + { + "name": "MatTabBodyPortal" + }, + { + "name": "MatTabContent" + }, + { + "name": "MatTabGroup" + }, + { + "name": "MatTabHeader" + }, + { + "name": "MatTabLabel" + }, + { + "name": "MatTabLabelWrapper" + }, + { + "name": "MatTabLink" + }, + { + "name": "MatTabNav" + }, + { + "name": "MatTabNavPanel" + } + ] + }, + { + "type": "imports", + "elements": [ + { + "name": "MatRippleModule" + } + ] + }, + { + "type": "exports", + "elements": [ + { + "name": "MatTab" + }, + { + "name": "MatTabBody" + }, + { + "name": "MatTabBodyPortal" + }, + { + "name": "MatTabContent" + }, + { + "name": "MatTabGroup" + }, + { + "name": "MatTabLabel" + }, + { + "name": "MatTabLink" + }, + { + "name": "MatTabNav" + }, + { + "name": "MatTabNavPanel" + } + ] + }, + { + "type": "bootstrap", + "elements": [] + }, + { + "type": "classes", + "elements": [] + } + ] + }, { "name": "NativeDateModule", - "id": "module-NativeDateModule-39a6fd5ccb13e08d239599fe216501ef61227face3af62c04dc24378a059d19a3e6e50162573135394b9976d07eccde672b60256bb02a821c9442639b0682fbc", + "id": "module-NativeDateModule-d92c50e04b983286755710adbb7e6786841a500520ce0956220f6ef03171f5ed3c4715323a4de5bfd98f3219050dc589f78b6964194edecbb166e809fbefbe4d", "description": "", "deprecationMessage": "", "deprecated": false, "file": "ui/src/components/datepicker/native-date.module.ts", "methods": [], - "sourceCode": "import { PlatformModule } from '@angular/cdk/platform';\r\nimport { NgModule } from '@angular/core';\r\nimport { DateAdapter } from './date-adapter';\r\nimport { OUI_DATE_FORMATS } from './date-formats';\r\nimport { NativeDateAdapter } from './native-date-adapter';\r\nimport { OUI_NATIVE_DATE_FORMATS } from './native-date-formats';\r\n\r\nexport * from './date-adapter';\r\nexport * from './date-formats';\r\nexport * from './native-date-adapter';\r\nexport * from './native-date-formats';\r\n\r\n@NgModule({\r\n imports: [PlatformModule],\r\n providers: [{ provide: DateAdapter, useClass: NativeDateAdapter }],\r\n})\r\nexport class NativeDateModule {}\r\n\r\n@NgModule({\r\n imports: [NativeDateModule],\r\n providers: [{ provide: OUI_DATE_FORMATS, useValue: OUI_NATIVE_DATE_FORMATS }],\r\n})\r\nexport class OuiNativeDateModule {}\r\n", + "sourceCode": "import { PlatformModule } from '@angular/cdk/platform';\nimport { NgModule } from '@angular/core';\nimport { DateAdapter } from './date-adapter';\nimport { OUI_DATE_FORMATS } from './date-formats';\nimport { NativeDateAdapter } from './native-date-adapter';\nimport { OUI_NATIVE_DATE_FORMATS } from './native-date-formats';\n\nexport * from './date-adapter';\nexport * from './date-formats';\nexport * from './native-date-adapter';\nexport * from './native-date-formats';\n\n@NgModule({\n imports: [PlatformModule],\n providers: [{ provide: DateAdapter, useClass: NativeDateAdapter }],\n})\nexport class NativeDateModule {}\n\n@NgModule({\n imports: [NativeDateModule],\n providers: [{ provide: OUI_DATE_FORMATS, useValue: OUI_NATIVE_DATE_FORMATS }],\n})\nexport class OuiNativeDateModule {}\n", "children": [ { "type": "providers", @@ -40973,13 +51200,13 @@ }, { "name": "OuiAutocompleteModule", - "id": "module-OuiAutocompleteModule-b1b0e84d09580e3272a552a7bf54bc8ff60aa2ac52b48f03f3876d475c058a6f3c2cfe140f75a29e39126429df08bd652024856bcda6131334f3bd01f9e1207f", + "id": "module-OuiAutocompleteModule-3c324f4bcf7892891d195c8f98074b71986a60dab47334e4f27bf88e5b8366d0a79a8e5e804a95583a870c429cc2a3aaff5b40e1bf4e3c32c35836a75de7077a", "description": "", "deprecationMessage": "", "deprecated": false, "file": "ui/src/components/autocomplete/autocomplete-module.ts", "methods": [], - "sourceCode": "import { NgModule } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { OverlayModule } from '@angular/cdk/overlay';\r\nimport { OuiOptionModule } from '../core/option/index';\r\nimport { OuiAutocomplete } from './autocomplete';\r\nimport {\r\n OuiAutocompleteTrigger,\r\n OUI_AUTOCOMPLETE_SCROLL_STRATEGY_FACTORY_PROVIDER,\r\n} from './autocomplete-trigger';\r\nimport { OuiAutocompleteOrigin } from './autocomplete-origin';\r\n\r\n@NgModule({\r\n imports: [OuiOptionModule, OverlayModule, CommonModule],\r\n exports: [\r\n OuiAutocomplete,\r\n OuiOptionModule,\r\n OuiAutocompleteTrigger,\r\n OuiAutocompleteOrigin,\r\n ],\r\n declarations: [\r\n OuiAutocomplete,\r\n OuiAutocompleteTrigger,\r\n OuiAutocompleteOrigin,\r\n ],\r\n providers: [OUI_AUTOCOMPLETE_SCROLL_STRATEGY_FACTORY_PROVIDER],\r\n})\r\nexport class OuiAutocompleteModule {}\r\n", + "sourceCode": "import { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { OverlayModule } from '@angular/cdk/overlay';\nimport { OuiOptionModule } from '../core/option/index';\nimport { OuiAutocomplete } from './autocomplete';\nimport {\n OuiAutocompleteTrigger,\n OUI_AUTOCOMPLETE_SCROLL_STRATEGY_FACTORY_PROVIDER,\n} from './autocomplete-trigger';\nimport { OuiAutocompleteOrigin } from './autocomplete-origin';\n\n@NgModule({\n imports: [OuiOptionModule, OverlayModule, CommonModule],\n exports: [\n OuiAutocomplete,\n OuiOptionModule,\n OuiAutocompleteTrigger,\n OuiAutocompleteOrigin,\n ],\n declarations: [\n OuiAutocomplete,\n OuiAutocompleteTrigger,\n OuiAutocompleteOrigin,\n ],\n providers: [OUI_AUTOCOMPLETE_SCROLL_STRATEGY_FACTORY_PROVIDER],\n})\nexport class OuiAutocompleteModule {}\n", "children": [ { "type": "providers", @@ -41036,13 +51263,13 @@ }, { "name": "OuiButtonModule", - "id": "module-OuiButtonModule-91212115315c68bfc5e7b11f2e9915244b154e21fadd7c96b1f9b0cd00662c0ef3c995cd3d097e0e0f793a36b214d95322191dd7eab586c81c30cbb2e305cb05", + "id": "module-OuiButtonModule-597768e314b3edc90f17df66c4a879ab203885545c7118da4c2383fb94311fbe43a3855c045bdf535c46ba37918f283618b91e70cbbcfd97bf602508eae4d30a", "description": "", "deprecationMessage": "", "deprecated": false, "file": "ui/src/components/button/button-module.ts", "methods": [], - "sourceCode": "import { CommonModule } from '@angular/common';\r\nimport { NgModule } from '@angular/core';\r\nimport { OuiAnchor, OuiButton } from './button';\r\n\r\n@NgModule({\r\n imports: [CommonModule],\r\n exports: [OuiButton, OuiAnchor],\r\n declarations: [OuiButton, OuiAnchor],\r\n})\r\nexport class OuiButtonModule {}\r\n", + "sourceCode": "import { CommonModule } from '@angular/common';\nimport { NgModule } from '@angular/core';\nimport { OuiAnchor, OuiButton } from './button';\n\n@NgModule({\n imports: [CommonModule],\n exports: [OuiButton, OuiAnchor],\n declarations: [OuiButton, OuiAnchor],\n})\nexport class OuiButtonModule {}\n", "children": [ { "type": "providers", @@ -41086,13 +51313,13 @@ }, { "name": "OuiCheckboxModule", - "id": "module-OuiCheckboxModule-8ebe1609ff894cba47f61c63989db47e91ec1b5b61456b6db18ca28889269425dd51d82b3c6fb030eba8ee360ac787f0889bc3e05dae4ffaae7306051d282e70", + "id": "module-OuiCheckboxModule-8241eb03974f67682d79b2248450e59a70bb7519caeb058da44b77158eba3224686b56265c2bcf27048991a1aa3af8e94f8b7029ba6cd0a2386403c6587eab25", "description": "", "deprecationMessage": "", "deprecated": false, "file": "ui/src/components/checkbox/checkbox.module.ts", "methods": [], - "sourceCode": "import { NgModule } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { Checkbox } from './checkbox';\r\n\r\n@NgModule({\r\n imports: [CommonModule],\r\n exports: [Checkbox],\r\n declarations: [Checkbox],\r\n})\r\nexport class OuiCheckboxModule {}\r\n", + "sourceCode": "import { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { Checkbox } from './checkbox';\n\n@NgModule({\n imports: [CommonModule],\n exports: [Checkbox],\n declarations: [Checkbox],\n})\nexport class OuiCheckboxModule {}\n", "children": [ { "type": "providers", @@ -41130,13 +51357,13 @@ }, { "name": "OuiDatepickerModule", - "id": "module-OuiDatepickerModule-1bca28c68b052901d34115b3b723636e41362496a0ba206aeda83e02ae99207205ee917c2dd12ab5240b9671c9031f4c28e5e4ebfeb6e593ce73b3c30ac930bd", + "id": "module-OuiDatepickerModule-115d874217f9b26def41d88e276ebd873d06af9cfb6e0cf2fd51419f137769638a01b9e143d1cd436a12df06613f9285d075f8feb65975e9062ca1c5614205e8", "description": "", "deprecationMessage": "", "deprecated": false, "file": "ui/src/components/datepicker/datepicker-module.ts", "methods": [], - "sourceCode": "import { A11yModule } from '@angular/cdk/a11y';\r\nimport { OverlayModule } from '@angular/cdk/overlay';\r\nimport { PortalModule } from '@angular/cdk/portal';\r\nimport { CommonModule } from '@angular/common';\r\nimport { NgModule } from '@angular/core';\r\nimport { OuiButtonModule } from '../button/button-module';\r\nimport { OuiDialogModule } from '../dialog/dialog-module';\r\nimport { OuiCalendar, OuiCalendarHeader } from './calendar';\r\nimport { OuiCalendarBody } from './calendar-body';\r\nimport {\r\n OuiDatepicker,\r\n OuiDatepickerContent,\r\n OUI_DATEPICKER_SCROLL_STRATEGY_FACTORY_PROVIDER,\r\n} from './datepicker';\r\nimport { OuiDatepickerInput } from './datepicker-input';\r\nimport { OuiDatepickerIntl } from './datepicker-intl';\r\nimport {\r\n OuiDatepickerToggle,\r\n OuiDatepickerToggleIcon,\r\n} from './datepicker-toggle';\r\nimport { OuiMonthView } from './month-view';\r\nimport { OuiMultiYearView } from './multi-year-view';\r\nimport { OuiYearView } from './year-view';\r\nimport { OuiNativeDateModule } from './native-date.module';\r\nimport { OuiIconModule } from '../icon/icon.module';\r\n\r\n@NgModule({\r\n imports: [\r\n CommonModule,\r\n OuiButtonModule,\r\n OuiDialogModule,\r\n OverlayModule,\r\n A11yModule,\r\n PortalModule,\r\n OuiNativeDateModule,\r\n OuiIconModule,\r\n ],\r\n exports: [\r\n OuiCalendar,\r\n OuiCalendarBody,\r\n OuiDatepicker,\r\n OuiDatepickerContent,\r\n OuiDatepickerInput,\r\n OuiDatepickerToggle,\r\n OuiDatepickerToggleIcon,\r\n OuiMonthView,\r\n OuiYearView,\r\n OuiMultiYearView,\r\n OuiCalendarHeader,\r\n ],\r\n declarations: [\r\n OuiCalendar,\r\n OuiCalendarBody,\r\n OuiDatepicker,\r\n OuiDatepickerContent,\r\n OuiDatepickerInput,\r\n OuiDatepickerToggle,\r\n OuiDatepickerToggleIcon,\r\n OuiMonthView,\r\n OuiYearView,\r\n OuiMultiYearView,\r\n OuiCalendarHeader,\r\n ],\r\n providers: [\r\n OuiDatepickerIntl,\r\n OUI_DATEPICKER_SCROLL_STRATEGY_FACTORY_PROVIDER,\r\n ]\r\n})\r\nexport class OuiDatepickerModule {}\r\n", + "sourceCode": "import { A11yModule } from '@angular/cdk/a11y';\nimport { OverlayModule } from '@angular/cdk/overlay';\nimport { PortalModule } from '@angular/cdk/portal';\nimport { CommonModule } from '@angular/common';\nimport { NgModule } from '@angular/core';\nimport { OuiButtonModule } from '../button/button-module';\nimport { OuiDialogModule } from '../dialog/dialog-module';\nimport { OuiCalendar, OuiCalendarHeader } from './calendar';\nimport { OuiCalendarBody } from './calendar-body';\nimport {\n OuiDatepicker,\n OuiDatepickerContent,\n OUI_DATEPICKER_SCROLL_STRATEGY_FACTORY_PROVIDER,\n} from './datepicker';\nimport { OuiDatepickerInput } from './datepicker-input';\nimport { OuiDatepickerIntl } from './datepicker-intl';\nimport {\n OuiDatepickerToggle,\n OuiDatepickerToggleIcon,\n} from './datepicker-toggle';\nimport { OuiMonthView } from './month-view';\nimport { OuiMultiYearView } from './multi-year-view';\nimport { OuiYearView } from './year-view';\nimport { OuiNativeDateModule } from './native-date.module';\nimport { OuiIconModule } from '../icon/icon.module';\n\n@NgModule({\n imports: [\n CommonModule,\n OuiButtonModule,\n OuiDialogModule,\n OverlayModule,\n A11yModule,\n PortalModule,\n OuiNativeDateModule,\n OuiIconModule,\n ],\n exports: [\n OuiCalendar,\n OuiCalendarBody,\n OuiDatepicker,\n OuiDatepickerContent,\n OuiDatepickerInput,\n OuiDatepickerToggle,\n OuiDatepickerToggleIcon,\n OuiMonthView,\n OuiYearView,\n OuiMultiYearView,\n OuiCalendarHeader,\n ],\n declarations: [\n OuiCalendar,\n OuiCalendarBody,\n OuiDatepicker,\n OuiDatepickerContent,\n OuiDatepickerInput,\n OuiDatepickerToggle,\n OuiDatepickerToggleIcon,\n OuiMonthView,\n OuiYearView,\n OuiMultiYearView,\n OuiCalendarHeader,\n ],\n providers: [\n OuiDatepickerIntl,\n OUI_DATEPICKER_SCROLL_STRATEGY_FACTORY_PROVIDER,\n ],\n})\nexport class OuiDatepickerModule {}\n", "children": [ { "type": "providers", @@ -41251,13 +51478,13 @@ }, { "name": "OuiDialogModule", - "id": "module-OuiDialogModule-2cef6880b1a46a29fc8d6cea2effcdfe6c3ad43481988e210877cb5dba78faa4fe6fada776240c8bbb9658a12c754739d079ed6be50d6c846a5764de0004256c", + "id": "module-OuiDialogModule-dbca18db33489c2b2e84235ff6f07c0de10eac6259531b022c0fb572ee086681d9d749a38c21eaf0fc1b844235ce739d76e39f150fa0cda6f8c3209b2bc2be19", "description": "", "deprecationMessage": "", "deprecated": false, "file": "ui/src/components/dialog/dialog-module.ts", "methods": [], - "sourceCode": "import { OverlayModule } from '@angular/cdk/overlay';\r\nimport { PortalModule } from '@angular/cdk/portal';\r\nimport { CommonModule } from '@angular/common';\r\nimport { NgModule } from '@angular/core';\r\nimport { OUI_DIALOG_SCROLL_STRATEGY_PROVIDER, OuiDialog } from './dialog';\r\nimport { OuiDialogContainer } from './dialog-container';\r\nimport { DialogScrollStrategy } from './dialog-scroll-strategy';\r\nimport {\r\n OuiDialogHeader,\r\n OuiDialogContent,\r\n OuiDialogFooter,\r\n OuiDialogHeaderAction,\r\n OuiDialogHeaderArticle,\r\n OuiDialogHeaderClose,\r\n OuiDialogHeaderTitle,\r\n OuiDialogHeaderVideo,\r\n OuiDialogClose,\r\n OuiDialogHeaderSeparator,\r\n OuiDialogFooterActionLeft,\r\n OuiDialogFooterActionRight,\r\n OuiDialogHeaderImage,\r\n} from './dialog-content';\r\nimport { OuiIconModule } from '../icon/icon.module';\r\n\r\n@NgModule({\r\n imports: [CommonModule, OverlayModule, PortalModule, OuiIconModule],\r\n exports: [\r\n OuiDialogContainer,\r\n OuiDialogHeader,\r\n OuiDialogHeaderImage,\r\n OuiDialogContent,\r\n OuiDialogFooter,\r\n OuiDialogHeaderAction,\r\n OuiDialogHeaderArticle,\r\n OuiDialogHeaderClose,\r\n OuiDialogHeaderTitle,\r\n OuiDialogHeaderVideo,\r\n OuiDialogClose,\r\n OuiDialogHeaderSeparator,\r\n OuiDialogFooterActionLeft,\r\n OuiDialogFooterActionRight,\r\n ],\r\n declarations: [\r\n OuiDialogContainer,\r\n OuiDialogHeader,\r\n OuiDialogHeaderImage,\r\n OuiDialogContent,\r\n OuiDialogFooter,\r\n OuiDialogHeaderAction,\r\n OuiDialogHeaderArticle,\r\n OuiDialogHeaderClose,\r\n OuiDialogHeaderTitle,\r\n OuiDialogHeaderVideo,\r\n OuiDialogClose,\r\n OuiDialogHeaderSeparator,\r\n OuiDialogFooterActionLeft,\r\n OuiDialogFooterActionRight,\r\n ],\r\n providers: [\r\n OuiDialog,\r\n DialogScrollStrategy,\r\n OUI_DIALOG_SCROLL_STRATEGY_PROVIDER,\r\n ]\r\n})\r\nexport class OuiDialogModule {}\r\n", + "sourceCode": "import { OverlayModule } from '@angular/cdk/overlay';\nimport { PortalModule } from '@angular/cdk/portal';\nimport { CommonModule } from '@angular/common';\nimport { NgModule } from '@angular/core';\nimport { OUI_DIALOG_SCROLL_STRATEGY_PROVIDER, OuiDialog } from './dialog';\nimport { OuiDialogContainer } from './dialog-container';\nimport { DialogScrollStrategy } from './dialog-scroll-strategy';\nimport {\n OuiDialogHeader,\n OuiDialogContent,\n OuiDialogFooter,\n OuiDialogHeaderAction,\n OuiDialogHeaderArticle,\n OuiDialogHeaderClose,\n OuiDialogHeaderTitle,\n OuiDialogHeaderVideo,\n OuiDialogClose,\n OuiDialogHeaderSeparator,\n OuiDialogFooterActionLeft,\n OuiDialogFooterActionRight,\n OuiDialogHeaderImage,\n} from './dialog-content';\nimport { OuiIconModule } from '../icon/icon.module';\n\n@NgModule({\n imports: [CommonModule, OverlayModule, PortalModule, OuiIconModule],\n exports: [\n OuiDialogContainer,\n OuiDialogHeader,\n OuiDialogHeaderImage,\n OuiDialogContent,\n OuiDialogFooter,\n OuiDialogHeaderAction,\n OuiDialogHeaderArticle,\n OuiDialogHeaderClose,\n OuiDialogHeaderTitle,\n OuiDialogHeaderVideo,\n OuiDialogClose,\n OuiDialogHeaderSeparator,\n OuiDialogFooterActionLeft,\n OuiDialogFooterActionRight,\n ],\n declarations: [\n OuiDialogContainer,\n OuiDialogHeader,\n OuiDialogHeaderImage,\n OuiDialogContent,\n OuiDialogFooter,\n OuiDialogHeaderAction,\n OuiDialogHeaderArticle,\n OuiDialogHeaderClose,\n OuiDialogHeaderTitle,\n OuiDialogHeaderVideo,\n OuiDialogClose,\n OuiDialogHeaderSeparator,\n OuiDialogFooterActionLeft,\n OuiDialogFooterActionRight,\n ],\n providers: [\n OuiDialog,\n DialogScrollStrategy,\n OUI_DIALOG_SCROLL_STRATEGY_PROVIDER,\n ],\n})\nexport class OuiDialogModule {}\n", "children": [ { "type": "providers", @@ -41384,13 +51611,13 @@ }, { "name": "OuiFormFieldModule", - "id": "module-OuiFormFieldModule-f0f6d28cf32cd040e7d0d3d8028e25f520264b9a3649abdf92f955aad26cdb66c62e3cac2cb52e19738d357e9f7aa0478abd8015aa126b8121ace227c364dc29", + "id": "module-OuiFormFieldModule-254161b6736b7bcbdc70161c32b8bf936e0bdd43e178622643398bdd12cc4917d48d8ea69b28f5f378e804cdbf5a8f60c87ea61c9af9f266f5be3271133b6a76", "description": "", "deprecationMessage": "", "deprecated": false, "file": "ui/src/components/form-field/form-field-module.ts", "methods": [], - "sourceCode": "import { CommonModule } from '@angular/common';\r\nimport { NgModule } from '@angular/core';\r\nimport { ObserversModule } from '@angular/cdk/observers';\r\nimport { OuiFormField } from './form-field';\r\nimport { OuiError } from './error';\r\n\r\n@NgModule({\r\n declarations: [OuiFormField, OuiError],\r\n imports: [CommonModule, ObserversModule],\r\n exports: [OuiFormField, OuiError],\r\n})\r\nexport class OuiFormFieldModule {}\r\n", + "sourceCode": "import { CommonModule } from '@angular/common';\nimport { NgModule } from '@angular/core';\nimport { ObserversModule } from '@angular/cdk/observers';\nimport { OuiFormField } from './form-field';\nimport { OuiError } from './error';\n\n@NgModule({\n declarations: [OuiFormField, OuiError],\n imports: [CommonModule, ObserversModule],\n exports: [OuiFormField, OuiError],\n})\nexport class OuiFormFieldModule {}\n", "children": [ { "type": "providers", @@ -41434,13 +51661,13 @@ }, { "name": "OuiIconModule", - "id": "module-OuiIconModule-57df8417e3bf5040a4c27107c49743c104f972e7367cb6d7ceb53317870bb2b32cc2cecf512ad8cea474617fcd3849835edca2bc6385246816a6cc275b605e05", + "id": "module-OuiIconModule-5e0010b93c7213b288500e4a059f323c6e1af33eac3272cd62809107bebe6799cbd2d0d978271ded5b18b2e1c1d6686fffbbea5083eacc6bf790d167d4e37e5a", "description": "", "deprecationMessage": "", "deprecated": false, "file": "ui/src/components/icon/icon.module.ts", "methods": [], - "sourceCode": "import { NgModule } from '@angular/core';\r\nimport { CommonModule, TitleCasePipe } from '@angular/common';\r\nimport { HttpClientModule } from '@angular/common/http';\r\nimport { Icon } from './icon';\r\nimport { OuiIconRegistry } from './icon-registery';\r\n\r\n@NgModule({\r\n imports: [CommonModule, HttpClientModule],\r\n exports: [Icon],\r\n declarations: [Icon],\r\n providers: [OuiIconRegistry, TitleCasePipe],\r\n})\r\nexport class OuiIconModule {}\r\n", + "sourceCode": "import { NgModule } from '@angular/core';\nimport { CommonModule, TitleCasePipe } from '@angular/common';\nimport { HttpClientModule } from '@angular/common/http';\nimport { Icon } from './icon';\nimport { OuiIconRegistry } from './icon-registery';\n\n@NgModule({\n imports: [CommonModule, HttpClientModule],\n exports: [Icon],\n declarations: [Icon],\n providers: [OuiIconRegistry, TitleCasePipe],\n})\nexport class OuiIconModule {}\n", "children": [ { "type": "providers", @@ -41482,13 +51709,13 @@ }, { "name": "OuiIconTestingModule", - "id": "module-OuiIconTestingModule-1a60087934a689531baba66a482d4168541cc03c2a26fed0244d73c826c9b13d6fc99ee1df6252692060e442ec017437481eec6cced4f79cb82c433e4161c5c3", + "id": "module-OuiIconTestingModule-37da20d4a94e682eea010d4a13225186e9bc158b3ceb90929f851198bffb205a5a0ab82bf128b6c07a815f9c4626b615c635e668b73c491b3bf0c2331ebfed9a", "description": "

Use this module to install the null icon registry.

\n", "deprecationMessage": "", "deprecated": false, "file": "ui/src/components/icon/testing/fake-icon-registry.ts", "methods": [], - "sourceCode": "import { Injectable, NgModule } from '@angular/core';\r\nimport { OuiIconRegistry } from '../icon-registery';\r\nimport { Observable, of as observableOf } from 'rxjs';\r\n\r\n/* eslint-disable @typescript-eslint/no-explicit-any */\r\ntype PublicApi = {\r\n [K in keyof T]: T[K] extends (...x: any[]) => T\r\n ? (...x: any[]) => PublicApi\r\n : T[K];\r\n};\r\n/* eslint-enable @typescript-eslint/no-explicit-any */\r\n\r\n/**\r\n * A null icon registry that must be imported to allow disabling of custom icons\r\n */\r\n@Injectable()\r\nexport class FakeOuiIconRegistry implements PublicApi {\r\n addSvgIcon(): this {\r\n return this;\r\n }\r\n\r\n addSvgIconLiteral(): this {\r\n return this;\r\n }\r\n\r\n addSvgIconSet(): this {\r\n return this;\r\n }\r\n\r\n getNamedSvgIcon(): Observable {\r\n return observableOf(this._generateEmptySvg());\r\n }\r\n\r\n private _generateEmptySvg(): SVGElement {\r\n const emptySvg = document.createElementNS(\r\n 'http://www.w3.org/2000/svg',\r\n 'svg'\r\n );\r\n emptySvg.classList.add('fake-testing-svg');\r\n // Emulate real icon characteristics from `OuiIconRegistry` so size remains consistent in tests.\r\n emptySvg.setAttribute('fit', '');\r\n emptySvg.setAttribute('height', '100%');\r\n emptySvg.setAttribute('width', '100%');\r\n emptySvg.setAttribute('preserveAspectRatio', 'xMidYMid meet');\r\n emptySvg.setAttribute('focusable', 'false');\r\n return emptySvg;\r\n }\r\n}\r\n\r\n/** Use this module to install the null icon registry. */\r\n@NgModule({\r\n providers: [{ provide: OuiIconRegistry, useClass: FakeOuiIconRegistry }],\r\n})\r\nexport class OuiIconTestingModule {}\r\n", + "sourceCode": "import { Injectable, NgModule } from '@angular/core';\nimport { OuiIconRegistry } from '../icon-registery';\nimport { Observable, of as observableOf } from 'rxjs';\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\ntype PublicApi = {\n [K in keyof T]: T[K] extends (...x: any[]) => T\n ? (...x: any[]) => PublicApi\n : T[K];\n};\n/* eslint-enable @typescript-eslint/no-explicit-any */\n\n/**\n * A null icon registry that must be imported to allow disabling of custom icons\n */\n@Injectable()\nexport class FakeOuiIconRegistry implements PublicApi {\n addSvgIcon(): this {\n return this;\n }\n\n addSvgIconLiteral(): this {\n return this;\n }\n\n addSvgIconSet(): this {\n return this;\n }\n\n getNamedSvgIcon(): Observable {\n return observableOf(this._generateEmptySvg());\n }\n\n private _generateEmptySvg(): SVGElement {\n const emptySvg = document.createElementNS(\n 'http://www.w3.org/2000/svg',\n 'svg'\n );\n emptySvg.classList.add('fake-testing-svg');\n // Emulate real icon characteristics from `OuiIconRegistry` so size remains consistent in tests.\n emptySvg.setAttribute('fit', '');\n emptySvg.setAttribute('height', '100%');\n emptySvg.setAttribute('width', '100%');\n emptySvg.setAttribute('preserveAspectRatio', 'xMidYMid meet');\n emptySvg.setAttribute('focusable', 'false');\n return emptySvg;\n }\n}\n\n/** Use this module to install the null icon registry. */\n@NgModule({\n providers: [{ provide: OuiIconRegistry, useClass: FakeOuiIconRegistry }],\n})\nexport class OuiIconTestingModule {}\n", "children": [ { "type": "providers", @@ -41518,13 +51745,13 @@ }, { "name": "OuiInputModule", - "id": "module-OuiInputModule-1cba0fcf4279b2d18c2e8378eea7a97f50a4b4c389a65eae6d70d2ba35c0b4a52943ac4b97919f2d11f81c4dee964bb9e85e6d47f793daf89359d5c808961fb8", + "id": "module-OuiInputModule-bcb5814f6b4410d6fe5f730f7686abdadd29b55da8f533164a911c4eed750d847c00b07757cef17f08962fc6e5491093f5de82bcc434d87e5876d17e1ee8d5ab", "description": "", "deprecationMessage": "", "deprecated": false, "file": "ui/src/components/input/input-module.ts", "methods": [], - "sourceCode": "import { TextFieldModule } from '@angular/cdk/text-field';\r\nimport { CommonModule } from '@angular/common';\r\nimport { NgModule } from '@angular/core';\r\nimport { ErrorStateMatcher } from '../core/common-behaviors/error-options';\r\nimport { OuiFormFieldModule } from '../form-field/form-field-module';\r\nimport { OuiInput } from './input';\r\nimport { ReactiveFormsModule } from '@angular/forms';\r\n\r\n@NgModule({\r\n declarations: [OuiInput],\r\n imports: [\r\n CommonModule,\r\n TextFieldModule,\r\n OuiFormFieldModule,\r\n ReactiveFormsModule,\r\n ],\r\n exports: [\r\n TextFieldModule,\r\n // We re-export the `OuiFormFieldModule` since `OuiInput` will almost always\r\n // be used together with `OuiFormField`.\r\n OuiFormFieldModule,\r\n OuiInput,\r\n ],\r\n providers: [ErrorStateMatcher],\r\n})\r\nexport class OuiInputModule {}\r\n", + "sourceCode": "import { TextFieldModule } from '@angular/cdk/text-field';\nimport { CommonModule } from '@angular/common';\nimport { NgModule } from '@angular/core';\nimport { ErrorStateMatcher } from '../core/common-behaviors/error-options';\nimport { OuiFormFieldModule } from '../form-field/form-field-module';\nimport { OuiInput } from './input';\nimport { ReactiveFormsModule } from '@angular/forms';\n\n@NgModule({\n declarations: [OuiInput],\n imports: [\n CommonModule,\n TextFieldModule,\n OuiFormFieldModule,\n ReactiveFormsModule,\n ],\n exports: [\n TextFieldModule,\n // We re-export the `OuiFormFieldModule` since `OuiInput` will almost always\n // be used together with `OuiFormField`.\n OuiFormFieldModule,\n OuiInput,\n ],\n providers: [ErrorStateMatcher],\n})\nexport class OuiInputModule {}\n", "children": [ { "type": "providers", @@ -41573,13 +51800,13 @@ }, { "name": "OuiMenuModule", - "id": "module-OuiMenuModule-7a0b28feeb28c3f70672014f4935f72e5b93079a9fe4498e15c68f9a061722f55aaa05e8a1f144011906275dbf653eed2d0daefa28208c90f603ac36a739590d", + "id": "module-OuiMenuModule-b4fdf64091e24c371f386c4cb3b837383d9e5a5dcdd23b58e564b40537b40018aad92760d115ec877eff3a5edb1ed48a2af7f16e7ba1214a3c0674e54f8eaa5f", "description": "", "deprecationMessage": "", "deprecated": false, "file": "ui/src/components/menu/menu-module.ts", "methods": [], - "sourceCode": "import { OverlayModule } from '@angular/cdk/overlay';\r\nimport { CommonModule } from '@angular/common';\r\nimport { NgModule } from '@angular/core';\r\nimport { OuiMenuContent } from './menu-content';\r\nimport { OuiMenu } from './menu-directive';\r\nimport { OuiMenuItem } from './menu-item';\r\nimport {\r\n OuiMenuTrigger,\r\n OUI_MENU_SCROLL_STRATEGY_FACTORY_PROVIDER,\r\n} from './menu-trigger';\r\nimport { OuiMenuIcon } from './menu-icon';\r\nimport { OuiIconModule } from '../icon/icon.module';\r\n\r\n@NgModule({\r\n imports: [CommonModule, OverlayModule, OuiIconModule],\r\n exports: [OuiMenu, OuiMenuItem, OuiMenuTrigger, OuiMenuContent, OuiMenuIcon],\r\n declarations: [\r\n OuiMenu,\r\n OuiMenuItem,\r\n OuiMenuTrigger,\r\n OuiMenuContent,\r\n OuiMenuIcon,\r\n ],\r\n providers: [OUI_MENU_SCROLL_STRATEGY_FACTORY_PROVIDER],\r\n})\r\nexport class OuiMenuModule {}\r\n", + "sourceCode": "import { OverlayModule } from '@angular/cdk/overlay';\nimport { CommonModule } from '@angular/common';\nimport { NgModule } from '@angular/core';\nimport { OuiMenuContent } from './menu-content';\nimport { OuiMenu } from './menu-directive';\nimport { OuiMenuItem } from './menu-item';\nimport {\n OuiMenuTrigger,\n OUI_MENU_SCROLL_STRATEGY_FACTORY_PROVIDER,\n} from './menu-trigger';\nimport { OuiMenuIcon } from './menu-icon';\nimport { OuiIconModule } from '../icon/icon.module';\n\n@NgModule({\n imports: [CommonModule, OverlayModule, OuiIconModule],\n exports: [OuiMenu, OuiMenuItem, OuiMenuTrigger, OuiMenuContent, OuiMenuIcon],\n declarations: [\n OuiMenu,\n OuiMenuItem,\n OuiMenuTrigger,\n OuiMenuContent,\n OuiMenuIcon,\n ],\n providers: [OUI_MENU_SCROLL_STRATEGY_FACTORY_PROVIDER],\n})\nexport class OuiMenuModule {}\n", "children": [ { "type": "providers", @@ -41645,13 +51872,13 @@ }, { "name": "OuiNativeDateModule", - "id": "module-OuiNativeDateModule-39a6fd5ccb13e08d239599fe216501ef61227face3af62c04dc24378a059d19a3e6e50162573135394b9976d07eccde672b60256bb02a821c9442639b0682fbc", + "id": "module-OuiNativeDateModule-d92c50e04b983286755710adbb7e6786841a500520ce0956220f6ef03171f5ed3c4715323a4de5bfd98f3219050dc589f78b6964194edecbb166e809fbefbe4d", "description": "", "deprecationMessage": "", "deprecated": false, "file": "ui/src/components/datepicker/native-date.module.ts", "methods": [], - "sourceCode": "import { PlatformModule } from '@angular/cdk/platform';\r\nimport { NgModule } from '@angular/core';\r\nimport { DateAdapter } from './date-adapter';\r\nimport { OUI_DATE_FORMATS } from './date-formats';\r\nimport { NativeDateAdapter } from './native-date-adapter';\r\nimport { OUI_NATIVE_DATE_FORMATS } from './native-date-formats';\r\n\r\nexport * from './date-adapter';\r\nexport * from './date-formats';\r\nexport * from './native-date-adapter';\r\nexport * from './native-date-formats';\r\n\r\n@NgModule({\r\n imports: [PlatformModule],\r\n providers: [{ provide: DateAdapter, useClass: NativeDateAdapter }],\r\n})\r\nexport class NativeDateModule {}\r\n\r\n@NgModule({\r\n imports: [NativeDateModule],\r\n providers: [{ provide: OUI_DATE_FORMATS, useValue: OUI_NATIVE_DATE_FORMATS }],\r\n})\r\nexport class OuiNativeDateModule {}\r\n", + "sourceCode": "import { PlatformModule } from '@angular/cdk/platform';\nimport { NgModule } from '@angular/core';\nimport { DateAdapter } from './date-adapter';\nimport { OUI_DATE_FORMATS } from './date-formats';\nimport { NativeDateAdapter } from './native-date-adapter';\nimport { OUI_NATIVE_DATE_FORMATS } from './native-date-formats';\n\nexport * from './date-adapter';\nexport * from './date-formats';\nexport * from './native-date-adapter';\nexport * from './native-date-formats';\n\n@NgModule({\n imports: [PlatformModule],\n providers: [{ provide: DateAdapter, useClass: NativeDateAdapter }],\n})\nexport class NativeDateModule {}\n\n@NgModule({\n imports: [NativeDateModule],\n providers: [{ provide: OUI_DATE_FORMATS, useValue: OUI_NATIVE_DATE_FORMATS }],\n})\nexport class OuiNativeDateModule {}\n", "children": [ { "type": "providers", @@ -41685,13 +51912,13 @@ }, { "name": "OuiOptionModule", - "id": "module-OuiOptionModule-36906435383f60f8952cae020a4f493ad64479808be798b7bfed35a467f39350f87b8df74da690bd3aab886392b47cc730062d7ea3c5147edd361b69e2dd2a4b", + "id": "module-OuiOptionModule-c2f9aa4a01442c8ce70456880dca929d4ba05f9bd4f1cf1dee6041bf33da2ca1b4e43f68b80c83da6b9d14ad921343d1ab8e8bccd8675f08608cca77ae0fd264", "description": "", "deprecationMessage": "", "deprecated": false, "file": "ui/src/components/core/option/index.ts", "methods": [], - "sourceCode": "import { NgModule } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { OuiOption } from './option';\r\nimport { OuiOptgroup } from './optgroup';\r\nimport { OuiPseudoCheckboxModule } from '../selection/index';\r\n\r\n@NgModule({\r\n imports: [CommonModule, OuiPseudoCheckboxModule],\r\n exports: [OuiOption, OuiOptgroup],\r\n declarations: [OuiOption, OuiOptgroup],\r\n})\r\nexport class OuiOptionModule {}\r\n\r\nexport * from './option';\r\nexport * from './optgroup';\r\n", + "sourceCode": "import { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { OuiOption } from './option';\nimport { OuiOptgroup } from './optgroup';\nimport { OuiPseudoCheckboxModule } from '../selection/index';\n\n@NgModule({\n imports: [CommonModule, OuiPseudoCheckboxModule],\n exports: [OuiOption, OuiOptgroup],\n declarations: [OuiOption, OuiOptgroup],\n})\nexport class OuiOptionModule {}\n\nexport * from './option';\nexport * from './optgroup';\n", "children": [ { "type": "providers", @@ -41739,13 +51966,13 @@ }, { "name": "OuiPaginatorModule", - "id": "module-OuiPaginatorModule-27d09ba89c60c0036a2984e87890cdffb4fb872842495d5252aee0c62004923e8dd753ed8097b244cd27d722a792af921f5f539635cbcfac897ab4e7daea3661", + "id": "module-OuiPaginatorModule-2326a784a1a493324bf7a47557cf65dd1ddf46f2dc37cd52560bd92012e87782d70b9e82ac8328dd57b4d73a32a4c44ea2b3b5eebf63fbb0d748c9611a8162d9", "description": "", "deprecationMessage": "", "deprecated": false, "file": "ui/src/components/paginator/paginator-module.ts", "methods": [], - "sourceCode": "import { CommonModule } from '@angular/common';\r\nimport { NgModule } from '@angular/core';\r\nimport { OuiButtonModule } from '../button/public-api';\r\nimport { OuiTooltipModule } from '../tooltip/public-api';\r\nimport { OuiPaginator } from './paginator';\r\nimport { OUI_PAGINATOR_INTL_PROVIDER } from './paginator-intl';\r\n\r\n@NgModule({\r\n imports: [CommonModule, OuiButtonModule, OuiTooltipModule],\r\n exports: [OuiPaginator],\r\n declarations: [OuiPaginator],\r\n providers: [OUI_PAGINATOR_INTL_PROVIDER],\r\n})\r\nexport class OuiPaginatorModule {}\r\n", + "sourceCode": "import { CommonModule } from '@angular/common';\nimport { NgModule } from '@angular/core';\nimport { OuiButtonModule } from '../button/public-api';\nimport { OuiTooltipModule } from '../tooltip/public-api';\nimport { OuiPaginator } from './paginator';\nimport { OUI_PAGINATOR_INTL_PROVIDER } from './paginator-intl';\n\n@NgModule({\n imports: [CommonModule, OuiButtonModule, OuiTooltipModule],\n exports: [OuiPaginator],\n declarations: [OuiPaginator],\n providers: [OUI_PAGINATOR_INTL_PROVIDER],\n})\nexport class OuiPaginatorModule {}\n", "children": [ { "type": "providers", @@ -41790,13 +52017,13 @@ }, { "name": "OuiPanelModule", - "id": "module-OuiPanelModule-ba2f77edf68e97a1ffe6036ef539d9979ecf4da9c791e61ea9e919cce8b8f51c31c68085ce73ffb07b734f55d82cb6e0139a375c6d47b99045aa31a8bc1da83b", + "id": "module-OuiPanelModule-30e4383fceef924f7b1f8c150863a1cea87f3b1f2f4c44fd3703f332a0875f299a3443b80d3c9582b84c86897c85da019bfbeb639ddfb358252bb75bf3cc5237", "description": "", "deprecationMessage": "", "deprecated": false, "file": "ui/src/components/panel/panel-module.ts", "methods": [], - "sourceCode": "import { OverlayModule } from '@angular/cdk/overlay';\r\nimport { CommonModule } from '@angular/common';\r\nimport { NgModule } from '@angular/core';\r\nimport { OuiPanelContent } from './panel-content';\r\nimport { OuiPanel, OuiPanelIcon } from './panel';\r\nimport {\r\n OuiPanelTrigger,\r\n OUI_PANEL_SCROLL_STRATEGY_FACTORY_PROVIDER,\r\n} from './panel-trigger';\r\nimport { OuiIconModule } from '../icon/icon.module';\r\n\r\n@NgModule({\r\n imports: [CommonModule, OverlayModule, OuiIconModule],\r\n exports: [OuiPanel, OuiPanelTrigger, OuiPanelContent, OuiPanelIcon],\r\n declarations: [OuiPanel, OuiPanelTrigger, OuiPanelContent, OuiPanelIcon],\r\n providers: [OUI_PANEL_SCROLL_STRATEGY_FACTORY_PROVIDER],\r\n})\r\nexport class OuiPanelModule {}\r\n", + "sourceCode": "import { OverlayModule } from '@angular/cdk/overlay';\nimport { CommonModule } from '@angular/common';\nimport { NgModule } from '@angular/core';\nimport { OuiPanelContent } from './panel-content';\nimport { OuiPanel, OuiPanelIcon } from './panel';\nimport {\n OuiPanelTrigger,\n OUI_PANEL_SCROLL_STRATEGY_FACTORY_PROVIDER,\n} from './panel-trigger';\nimport { OuiIconModule } from '../icon/icon.module';\n\n@NgModule({\n imports: [CommonModule, OverlayModule, OuiIconModule],\n exports: [OuiPanel, OuiPanelTrigger, OuiPanelContent, OuiPanelIcon],\n declarations: [OuiPanel, OuiPanelTrigger, OuiPanelContent, OuiPanelIcon],\n providers: [OUI_PANEL_SCROLL_STRATEGY_FACTORY_PROVIDER],\n})\nexport class OuiPanelModule {}\n", "children": [ { "type": "providers", @@ -41856,13 +52083,13 @@ }, { "name": "OuiProgressBarModule", - "id": "module-OuiProgressBarModule-3008f4fc74be48ec4c754a2026310b1dcbd2e2583ead036a7cd93685551ee118e0428207eb82785be5f5f36fab56004eb7c23a87e4dc4aa1d3fccfe7760ac3ea", + "id": "module-OuiProgressBarModule-aa7a95a2d53899da00f2bd4441591e83e06e4840c62fee76e86d16bab7ae654d76b9c495050275cc0ee2453b01792cbc62f73a1e96894c8f72fd51d0ba6194c3", "description": "", "deprecationMessage": "", "deprecated": false, "file": "ui/src/components/progress-bar/progress-bar.module.ts", "methods": [], - "sourceCode": "import { NgModule } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { OuiProgressBar } from './progress-bar';\r\n\r\n@NgModule({\r\n imports: [CommonModule],\r\n exports: [OuiProgressBar],\r\n declarations: [OuiProgressBar],\r\n})\r\nexport class OuiProgressBarModule {}\r\n", + "sourceCode": "import { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { OuiProgressBar } from './progress-bar';\n\n@NgModule({\n imports: [CommonModule],\n exports: [OuiProgressBar],\n declarations: [OuiProgressBar],\n})\nexport class OuiProgressBarModule {}\n", "children": [ { "type": "providers", @@ -41900,13 +52127,13 @@ }, { "name": "OuiProgressSpinnerModule", - "id": "module-OuiProgressSpinnerModule-1edb83da76e5d42d1a1087f19c34326147de12c8df97b732d81bfd980b6a950223022752fdfc17dd2eb5439b29720469bb9b515de44044be9859ef39c1f8eecf", + "id": "module-OuiProgressSpinnerModule-a8871e524ad59391bda7b563b07d6cf9a0f94cb002bd8a8478365a413752123c5bbc665ddad6a0a6ef80b0794dd2eb5a5a40a8834694e734970452adc308e38f", "description": "", "deprecationMessage": "", "deprecated": false, "file": "ui/src/components/progress-spinner/progress-spinner.module.ts", "methods": [], - "sourceCode": "import { NgModule } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { OuiProgressSpinner } from './progress-spinner';\r\n\r\n@NgModule({\r\n imports: [CommonModule],\r\n declarations: [OuiProgressSpinner],\r\n exports: [OuiProgressSpinner],\r\n})\r\nexport class OuiProgressSpinnerModule {}\r\n", + "sourceCode": "import { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { OuiProgressSpinner } from './progress-spinner';\n\n@NgModule({\n imports: [CommonModule],\n declarations: [OuiProgressSpinner],\n exports: [OuiProgressSpinner],\n})\nexport class OuiProgressSpinnerModule {}\n", "children": [ { "type": "providers", @@ -41944,13 +52171,13 @@ }, { "name": "OuiPseudoCheckboxModule", - "id": "module-OuiPseudoCheckboxModule-dacc92abc0621adbe7d9f556042974cd4b051781981946a8e6131d91649e37d3ac06298ad3f447d1755e4f3b0b281984c0da1873eb56b35ac53a6367f6f3899a", + "id": "module-OuiPseudoCheckboxModule-6e2b3119c639175e1dc3816aa37041ff455787782427d08c5f8adbe39531dce6dea5ac1e55390d70d363fc82d5d29318d79f9d797aade2689d97f1c22bc51eea", "description": "", "deprecationMessage": "", "deprecated": false, "file": "ui/src/components/core/selection/index.ts", "methods": [], - "sourceCode": "import { NgModule } from '@angular/core';\r\nimport { OuiPseudoCheckbox } from './pseudo-checkbox/pseudo-checkbox';\r\n\r\n@NgModule({\r\n exports: [OuiPseudoCheckbox],\r\n declarations: [OuiPseudoCheckbox],\r\n})\r\nexport class OuiPseudoCheckboxModule {}\r\n\r\nexport * from './pseudo-checkbox/pseudo-checkbox';\r\n", + "sourceCode": "import { NgModule } from '@angular/core';\nimport { OuiPseudoCheckbox } from './pseudo-checkbox/pseudo-checkbox';\n\n@NgModule({\n exports: [OuiPseudoCheckbox],\n declarations: [OuiPseudoCheckbox],\n})\nexport class OuiPseudoCheckboxModule {}\n\nexport * from './pseudo-checkbox/pseudo-checkbox';\n", "children": [ { "type": "providers", @@ -41988,13 +52215,13 @@ }, { "name": "OuiRadioModule", - "id": "module-OuiRadioModule-4166d646acfcad2fa777ed74006f6374581e63ff3fb72574cc4a230351293efc9bbc675f540e471729432474d68e351aa228b9a8998a1eb41d31dca0287bd757", + "id": "module-OuiRadioModule-904fc0e888bbbf6a053864a1c6e801b269611ee35aedfd7278fa1c655cbf5626dd288a095b91298767cb57338fcc365a3973c42c2bbe9c556c302329bc99a991", "description": "", "deprecationMessage": "", "deprecated": false, "file": "ui/src/components/radio/radio-module.ts", "methods": [], - "sourceCode": "import { CommonModule } from '@angular/common';\r\nimport { NgModule } from '@angular/core';\r\nimport { OverlayModule } from '@angular/cdk/overlay';\r\n\r\nimport { ObserversModule } from '@angular/cdk/observers';\r\nimport { OuiRadioGroup, OuiRadioButton } from './radio';\r\n\r\n@NgModule({\r\n declarations: [OuiRadioGroup, OuiRadioButton],\r\n imports: [CommonModule, ObserversModule, OverlayModule],\r\n exports: [OuiRadioGroup, OuiRadioButton],\r\n})\r\nexport class OuiRadioModule {}\r\n", + "sourceCode": "import { CommonModule } from '@angular/common';\nimport { NgModule } from '@angular/core';\nimport { OverlayModule } from '@angular/cdk/overlay';\n\nimport { ObserversModule } from '@angular/cdk/observers';\nimport { OuiRadioGroup, OuiRadioButton } from './radio';\n\n@NgModule({\n declarations: [OuiRadioGroup, OuiRadioButton],\n imports: [CommonModule, ObserversModule, OverlayModule],\n exports: [OuiRadioGroup, OuiRadioButton],\n})\nexport class OuiRadioModule {}\n", "children": [ { "type": "providers", @@ -42038,13 +52265,13 @@ }, { "name": "OuiScrollbarModule", - "id": "module-OuiScrollbarModule-6ee2f83c0816b0420abd9b5ec558e84f692660dcc8bec3352c234d5b5a56592ccc0fdaab55fefdd9f75a07ea8dcb87d74f3e93ed1e346f13ebcb1ef97d38ac83", + "id": "module-OuiScrollbarModule-f1bb48e3f907d6745cc882ffb7bc3061ae6a379370e105e403579a56621a3d174cc3afbe8c26fc74584df7fe84a1bd5d488b673cab0f783b43ea8538968c3e72", "description": "", "deprecationMessage": "", "deprecated": false, "file": "ui/src/components/scrollbar/scrollbar-module.ts", "methods": [], - "sourceCode": "import { NgModule } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { OuiScrollbar } from './scrollbar';\r\n\r\n@NgModule({\r\n imports: [CommonModule],\r\n declarations: [OuiScrollbar],\r\n exports: [OuiScrollbar],\r\n})\r\nexport class OuiScrollbarModule {}\r\n", + "sourceCode": "import { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { OuiScrollbar } from './scrollbar';\n\n@NgModule({\n imports: [CommonModule],\n declarations: [OuiScrollbar],\n exports: [OuiScrollbar],\n})\nexport class OuiScrollbarModule {}\n", "children": [ { "type": "providers", @@ -42082,13 +52309,13 @@ }, { "name": "OuiSelectModule", - "id": "module-OuiSelectModule-b92e58c6d324760e97ff456d9e0c63fdf618f585d642fc92ee8332383d298a3286db2550548eefdf95cd6978c41ad3a2b5a74669d5d9e82fc9ed4e513b0cc168", + "id": "module-OuiSelectModule-51b36084d18fdcf89fb6dd22e3d8364467910d5a2fd96b02799d91f4a2ed4fa52fffcfa91ed67842f64007546f5bde9addc360f674a52416d05031b434605d62", "description": "", "deprecationMessage": "", "deprecated": false, "file": "ui/src/components/select/select-module.ts", "methods": [], - "sourceCode": "import { NgModule } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { OverlayModule } from '@angular/cdk/overlay';\r\nimport { OuiOptionModule } from '../core/option/index';\r\nimport { OuiInputModule } from '../input/input-module';\r\nimport { FilterPipe } from './filter.pipe';\r\nimport { OuiSelectTrigger, OuiSelect } from './select.component';\r\nimport { OuiSelectSearchComponent } from './search/index';\r\nimport { OuiIconModule } from '../icon/icon.module';\r\nimport { OuiButtonModule } from '../button/button-module';\r\n\r\n@NgModule({\r\n imports: [\r\n OuiOptionModule,\r\n OverlayModule,\r\n OuiInputModule,\r\n CommonModule,\r\n OuiIconModule,\r\n OuiButtonModule,\r\n ],\r\n exports: [\r\n OuiOptionModule,\r\n OuiSelectTrigger,\r\n OuiSelectSearchComponent,\r\n FilterPipe,\r\n OuiSelect,\r\n ],\r\n declarations: [\r\n OuiSelectTrigger,\r\n OuiSelectSearchComponent,\r\n FilterPipe,\r\n OuiSelect,\r\n ],\r\n})\r\nexport class OuiSelectModule {}\r\n", + "sourceCode": "import { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { OverlayModule } from '@angular/cdk/overlay';\nimport { OuiOptionModule } from '../core/option/index';\nimport { OuiInputModule } from '../input/input-module';\nimport { FilterPipe } from './filter.pipe';\nimport { OuiSelectTrigger, OuiSelect } from './select.component';\nimport { OuiSelectSearchComponent } from './search/index';\nimport { OuiIconModule } from '../icon/icon.module';\nimport { OuiButtonModule } from '../button/button-module';\n\n@NgModule({\n imports: [\n OuiOptionModule,\n OverlayModule,\n OuiInputModule,\n CommonModule,\n OuiIconModule,\n OuiButtonModule,\n ],\n exports: [\n OuiOptionModule,\n OuiSelectTrigger,\n OuiSelectSearchComponent,\n FilterPipe,\n OuiSelect,\n ],\n declarations: [\n OuiSelectTrigger,\n OuiSelectSearchComponent,\n FilterPipe,\n OuiSelect,\n ],\n})\nexport class OuiSelectModule {}\n", "children": [ { "type": "providers", @@ -42160,13 +52387,13 @@ }, { "name": "OuiSlideToggleModule", - "id": "module-OuiSlideToggleModule-307c07d4553e29253260fd2e5cc38ea325406b477599f80624281130ef537d8e61f17c0b6b7b5fbca2741f4eaaf3447850307d3fb019378b63bff18c3b7f7eca", + "id": "module-OuiSlideToggleModule-986043d7dd22c04f77e902380c6b1904bd59bf6a012b39ddc5fff60749bfa405d8a8e0c2d913242142857cb149d02101471f5690ba849f8a5cb0901420ef30f5", "description": "", "deprecationMessage": "", "deprecated": false, "file": "ui/src/components/slide-toggle/slide-toggle-module.ts", "methods": [], - "sourceCode": "import { CommonModule } from '@angular/common';\r\nimport { NgModule } from '@angular/core';\r\nimport { ObserversModule } from '@angular/cdk/observers';\r\nimport { OuiSlideToggle } from './slide-toggle';\r\n\r\n@NgModule({\r\n declarations: [OuiSlideToggle],\r\n imports: [CommonModule, ObserversModule],\r\n exports: [OuiSlideToggle],\r\n})\r\nexport class OuiSlideToggleModule {}\r\n", + "sourceCode": "import { CommonModule } from '@angular/common';\nimport { NgModule } from '@angular/core';\nimport { ObserversModule } from '@angular/cdk/observers';\nimport { OuiSlideToggle } from './slide-toggle';\n\n@NgModule({\n declarations: [OuiSlideToggle],\n imports: [CommonModule, ObserversModule],\n exports: [OuiSlideToggle],\n})\nexport class OuiSlideToggleModule {}\n", "children": [ { "type": "providers", @@ -42204,13 +52431,13 @@ }, { "name": "OuiSortModule", - "id": "module-OuiSortModule-6160a9f40a2413384e53e553abf2b127d59215f97c074ee4b3e6154cb7920c9cd97fdf7c2d514943d9063f6f928aa047757e15e8e5ef351def87aea09a48b13c", + "id": "module-OuiSortModule-e00622f0ff8045340df75804e3a53939e17a7b49f44e43cfa9754516d2d93b7010f7fbae16817c8a4b5f095d7c6a52026705b282d977a6a4df38a5e6ebd375fb", "description": "", "deprecationMessage": "", "deprecated": false, "file": "ui/src/components/sort/sort-module.ts", "methods": [], - "sourceCode": "import { NgModule } from '@angular/core';\r\nimport { OuiSortHeader } from './sort-header';\r\nimport { OuiSort } from './sort';\r\nimport { OUI_SORT_HEADER_INTL_PROVIDER } from './sort-header-intl';\r\nimport { CommonModule } from '@angular/common';\r\n\r\n@NgModule({\r\n imports: [CommonModule],\r\n exports: [OuiSort, OuiSortHeader],\r\n declarations: [OuiSort, OuiSortHeader],\r\n providers: [OUI_SORT_HEADER_INTL_PROVIDER],\r\n})\r\nexport class OuiSortModule {}\r\n", + "sourceCode": "import { NgModule } from '@angular/core';\nimport { OuiSortHeader } from './sort-header';\nimport { OuiSort } from './sort';\nimport { OUI_SORT_HEADER_INTL_PROVIDER } from './sort-header-intl';\nimport { CommonModule } from '@angular/common';\n\n@NgModule({\n imports: [CommonModule],\n exports: [OuiSort, OuiSortHeader],\n declarations: [OuiSort, OuiSortHeader],\n providers: [OUI_SORT_HEADER_INTL_PROVIDER],\n})\nexport class OuiSortModule {}\n", "children": [ { "type": "providers", @@ -42254,13 +52481,13 @@ }, { "name": "OuiTableModule", - "id": "module-OuiTableModule-26bb580a3ff115b9df151c01330b18cbb6e91fae42c2df8040ed601f13502d388c05be5a307923f0a66fb78ecde7c68f3a44203483ddb1f0e27afddb18a8bdc0", + "id": "module-OuiTableModule-12e4e679b62534f93e1e4ffd62b9751a70558b76516b4de334997962e970847794bde0f9dd0e6cc613e468d35b81252302537d2d064e76888cbf6f7fd303bbf7", "description": "", "deprecationMessage": "", "deprecated": false, "file": "ui/src/components/table/table-module.ts", "methods": [], - "sourceCode": "import { NgModule } from '@angular/core';\r\nimport { OuiTable } from './table';\r\nimport { CdkTableModule } from '@angular/cdk/table';\r\nimport {\r\n OuiCell,\r\n OuiCellDef,\r\n OuiColumnDef,\r\n OuiFooterCell,\r\n OuiFooterCellDef,\r\n OuiHeaderCell,\r\n OuiHeaderCellDef,\r\n} from './cell';\r\nimport {\r\n OuiFooterRow,\r\n OuiFooterRowDef,\r\n OuiHeaderRow,\r\n OuiHeaderRowDef,\r\n OuiRow,\r\n OuiRowDef,\r\n} from './row';\r\nimport { CommonModule } from '@angular/common';\r\n\r\nconst EXPORTED_DECLARATIONS = [\r\n // Table\r\n OuiTable,\r\n\r\n // Template defs\r\n OuiHeaderCellDef,\r\n OuiHeaderRowDef,\r\n OuiColumnDef,\r\n OuiCellDef,\r\n OuiRowDef,\r\n OuiFooterCellDef,\r\n OuiFooterRowDef,\r\n\r\n // Cell directives\r\n OuiHeaderCell,\r\n OuiCell,\r\n OuiFooterCell,\r\n\r\n // Row directions\r\n OuiHeaderRow,\r\n OuiRow,\r\n OuiFooterRow,\r\n];\r\n\r\n@NgModule({\r\n imports: [CdkTableModule, CommonModule],\r\n exports: EXPORTED_DECLARATIONS,\r\n declarations: EXPORTED_DECLARATIONS,\r\n})\r\nexport class OuiTableModule {}\r\n", + "sourceCode": "import { NgModule } from '@angular/core';\nimport { OuiTable } from './table';\nimport { CdkTableModule } from '@angular/cdk/table';\nimport {\n OuiCell,\n OuiCellDef,\n OuiColumnDef,\n OuiFooterCell,\n OuiFooterCellDef,\n OuiHeaderCell,\n OuiHeaderCellDef,\n} from './cell';\nimport {\n OuiFooterRow,\n OuiFooterRowDef,\n OuiHeaderRow,\n OuiHeaderRowDef,\n OuiRow,\n OuiRowDef,\n} from './row';\nimport { CommonModule } from '@angular/common';\n\nconst EXPORTED_DECLARATIONS = [\n // Table\n OuiTable,\n\n // Template defs\n OuiHeaderCellDef,\n OuiHeaderRowDef,\n OuiColumnDef,\n OuiCellDef,\n OuiRowDef,\n OuiFooterCellDef,\n OuiFooterRowDef,\n\n // Cell directives\n OuiHeaderCell,\n OuiCell,\n OuiFooterCell,\n\n // Row directions\n OuiHeaderRow,\n OuiRow,\n OuiFooterRow,\n];\n\n@NgModule({\n imports: [CdkTableModule, CommonModule],\n exports: EXPORTED_DECLARATIONS,\n declarations: EXPORTED_DECLARATIONS,\n})\nexport class OuiTableModule {}\n", "children": [ { "type": "providers", @@ -42376,13 +52603,13 @@ }, { "name": "OuiTooltipModule", - "id": "module-OuiTooltipModule-9ddfdddbde7651873423be7a3dd1d6ffbe26f6665b99226458068c9bb0a1c30f94fafa7ce39cb3fcfdd8637b521029e418875dd96eece80d45285ba67e4fd0a0", + "id": "module-OuiTooltipModule-afd61104f96da74eb6b711b4dbe50409b726b6b67c0abc1397e3bdc0c0b35580c17ca8e23d64954f04721b316fb4bfdfbc9c804d3d4531d90406caba223b10af", "description": "", "deprecationMessage": "", "deprecated": false, "file": "ui/src/components/tooltip/tooltip-module.ts", "methods": [], - "sourceCode": "import { CommonModule } from '@angular/common';\r\nimport { NgModule } from '@angular/core';\r\nimport { OverlayModule } from '@angular/cdk/overlay';\r\n\r\nimport { ObserversModule } from '@angular/cdk/observers';\r\nimport {\r\n OuiTooltip,\r\n TooltipComponent,\r\n OUI_TOOLTIP_SCROLL_STRATEGY_FACTORY_PROVIDER,\r\n} from './tooltip';\r\n\r\n@NgModule({\r\n declarations: [OuiTooltip, TooltipComponent],\r\n imports: [CommonModule, ObserversModule, OverlayModule],\r\n exports: [TooltipComponent, OuiTooltip],\r\n providers: [OUI_TOOLTIP_SCROLL_STRATEGY_FACTORY_PROVIDER]\r\n})\r\nexport class OuiTooltipModule {}\r\n", + "sourceCode": "import { CommonModule } from '@angular/common';\nimport { NgModule } from '@angular/core';\nimport { OverlayModule } from '@angular/cdk/overlay';\n\nimport { ObserversModule } from '@angular/cdk/observers';\nimport {\n OuiTooltip,\n TooltipComponent,\n OUI_TOOLTIP_SCROLL_STRATEGY_FACTORY_PROVIDER,\n} from './tooltip';\n\n@NgModule({\n declarations: [OuiTooltip, TooltipComponent],\n imports: [CommonModule, ObserversModule, OverlayModule],\n exports: [TooltipComponent, OuiTooltip],\n providers: [OUI_TOOLTIP_SCROLL_STRATEGY_FACTORY_PROVIDER],\n})\nexport class OuiTooltipModule {}\n", "children": [ { "type": "providers", @@ -42435,7 +52662,59 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "(opt: string[], value: string): string[] => {\r\n const filterValue = value.toLowerCase();\r\n\r\n return opt.filter((item) => item.toLowerCase().indexOf(filterValue) === 0);\r\n}" + "defaultValue": "(opt: string[], value: string): string[] => {\n const filterValue = value.toLowerCase();\n\n return opt.filter((item) => item.toLowerCase().indexOf(filterValue) === 0);\n}" + }, + { + "name": "_MAT_INK_BAR_POSITIONER", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/ink-bar.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "new InjectionToken<_MatInkBarPositioner>(\r\n 'MatInkBarPositioner',\r\n {\r\n providedIn: 'root',\r\n factory: _MAT_INK_BAR_POSITIONER_FACTORY,\r\n },\r\n)", + "rawdescription": "Injection token for the MatInkBar's Positioner.", + "description": "

Injection token for the MatInkBar's Positioner.

\n" + }, + { + "name": "_MatTabGroupMixinBase", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/tab-group.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "mixinColor(\r\n mixinDisableRipple(\r\n class {\r\n constructor(public _elementRef: ElementRef) {}\r\n },\r\n ),\r\n 'primary',\r\n)" + }, + { + "name": "_MatTabLabelWrapperMixinBase", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/tab-label-wrapper.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "mixinInkBarItem(\r\n mixinDisabled(\r\n class {\r\n elementRef: ElementRef;\r\n },\r\n ),\r\n)" + }, + { + "name": "_MatTabLinkMixinBase", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/tab-nav-bar/tab-nav-bar.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "mixinInkBarItem(\r\n mixinTabIndex(\r\n mixinDisableRipple(\r\n mixinDisabled(\r\n class {\r\n elementRef: ElementRef;\r\n },\r\n ),\r\n ),\r\n ),\r\n)" + }, + { + "name": "_MatTabMixinBase", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/tab.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "mixinColor(OuiTabsBase)" }, { "name": "_OuiDatepickerContentMixinBase", @@ -42537,7 +52816,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "mixinTabIndex(\r\n mixinDisabled(mixinErrorState(OuiSelectBase))\r\n)" + "defaultValue": "mixinTabIndex(\n mixinDisabled(mixinErrorState(OuiSelectBase))\n)" }, { "name": "_OuiSlideToggleMixinBase", @@ -42578,7 +52857,7 @@ "deprecationMessage": "", "type": "number", "defaultValue": "0", - "rawdescription": "Autocomplete IDs need to be unique across components, so this counter exists outside of\r\nthe component definition.", + "rawdescription": "Autocomplete IDs need to be unique across components, so this counter exists outside of\nthe component definition.", "description": "

Autocomplete IDs need to be unique across components, so this counter exists outside of\nthe component definition.

\n" }, { @@ -42590,7 +52869,7 @@ "deprecationMessage": "", "type": "number", "defaultValue": "0", - "rawdescription": "Option IDs need to be unique across components, so this counter exists outside of\r\nthe component definition.", + "rawdescription": "Option IDs need to be unique across components, so this counter exists outside of\nthe component definition.", "description": "

Option IDs need to be unique across components, so this counter exists outside of\nthe component definition.

\n" }, { @@ -42603,6 +52882,18 @@ "type": "number", "defaultValue": "0" }, + { + "name": "ACTIVE_CLASS", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/ink-bar.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "string", + "defaultValue": "'mdc-tab-indicator--active'", + "rawdescription": "Class that is applied when a tab indicator is active.", + "description": "

Class that is applied when a tab indicator is active.

\n" + }, { "name": "APPEARANCE", "ctype": "miscellaneous", @@ -42622,7 +52913,7 @@ "deprecationMessage": "", "type": "number", "defaultValue": "48", - "rawdescription": "The following style constants are necessary to save here in order\r\nto properly calculate the scrollTop of the panel. Because we are not\r\nactually focusing the active item, scroll must be handled manually.", + "rawdescription": "The following style constants are necessary to save here in order\nto properly calculate the scrollTop of the panel. Because we are not\nactually focusing the active item, scroll must be handled manually.", "description": "

The following style constants are necessary to save here in order\nto properly calculate the scrollTop of the panel. Because we are not\nactually focusing the active item, scroll must be handled manually.

\n" }, { @@ -42669,10 +52960,20 @@ "deprecated": false, "deprecationMessage": "", "type": "[]", - "defaultValue": "[\r\n 'oui-button',\r\n 'oui-ghost-button',\r\n 'oui-link-button',\r\n 'oui-icon-button',\r\n 'oui-icon-text-button',\r\n]", - "rawdescription": "List of classes to add to Button instances based on host attributes to\r\nstyle as different variants.", + "defaultValue": "[\n 'oui-button',\n 'oui-ghost-button',\n 'oui-link-button',\n 'oui-icon-button',\n 'oui-icon-text-button',\n]", + "rawdescription": "List of classes to add to Button instances based on host attributes to\nstyle as different variants.", "description": "

List of classes to add to Button instances based on host attributes to\nstyle as different variants.

\n" }, + { + "name": "CARD_DIRECTIVES", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/card/module.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "[]", + "defaultValue": "[\r\n MatCard,\r\n MatCardActions,\r\n MatCardAvatar,\r\n MatCardContent,\r\n MatCardFooter,\r\n MatCardHeader,\r\n MatCardImage,\r\n MatCardLgImage,\r\n MatCardMdImage,\r\n MatCardSmImage,\r\n MatCardSubtitle,\r\n MatCardTitle,\r\n MatCardTitleGroup,\r\n MatCardXlImage,\r\n]" + }, { "name": "COLORS", "ctype": "miscellaneous", @@ -42691,7 +52992,7 @@ "deprecated": false, "deprecationMessage": "", "type": "[]", - "defaultValue": "[\r\n 'maroon',\r\n 'red',\r\n 'orange',\r\n 'yellow',\r\n 'olive',\r\n 'green',\r\n 'purple',\r\n 'fuchsia',\r\n 'lime',\r\n 'teal',\r\n 'aqua',\r\n 'blue',\r\n 'navy',\r\n 'black',\r\n 'gray',\r\n]" + "defaultValue": "[\n 'maroon',\n 'red',\n 'orange',\n 'yellow',\n 'olive',\n 'green',\n 'purple',\n 'fuchsia',\n 'lime',\n 'teal',\n 'aqua',\n 'blue',\n 'navy',\n 'black',\n 'gray',\n]" }, { "name": "context", @@ -42711,7 +53012,7 @@ "deprecated": false, "deprecationMessage": "", "type": "[]", - "defaultValue": "[\r\n 'Aruba',\r\n 'Benin',\r\n 'Chad',\r\n 'Chile',\r\n 'China',\r\n 'Congo',\r\n 'Cuba',\r\n 'Egypt',\r\n 'Fiji',\r\n 'Gabon',\r\n 'Ghana',\r\n 'Guam',\r\n 'Haiti',\r\n 'India',\r\n 'Iraq',\r\n 'Italy',\r\n 'Japan',\r\n 'Kenya',\r\n 'Libya',\r\n 'Macao',\r\n 'Mali',\r\n 'Malta',\r\n 'Nauru',\r\n 'Nepal',\r\n 'Niger',\r\n 'Niue',\r\n 'Oman',\r\n 'Palau',\r\n 'Peru',\r\n 'Qatar',\r\n 'Samoa',\r\n 'Spain',\r\n 'Sudan',\r\n 'Togo',\r\n 'Tonga',\r\n 'Yemen',\r\n]" + "defaultValue": "[\n 'Aruba',\n 'Benin',\n 'Chad',\n 'Chile',\n 'China',\n 'Congo',\n 'Cuba',\n 'Egypt',\n 'Fiji',\n 'Gabon',\n 'Ghana',\n 'Guam',\n 'Haiti',\n 'India',\n 'Iraq',\n 'Italy',\n 'Japan',\n 'Kenya',\n 'Libya',\n 'Macao',\n 'Mali',\n 'Malta',\n 'Nauru',\n 'Nepal',\n 'Niger',\n 'Niue',\n 'Oman',\n 'Palau',\n 'Peru',\n 'Qatar',\n 'Samoa',\n 'Spain',\n 'Sudan',\n 'Togo',\n 'Tonga',\n 'Yemen',\n]" }, { "name": "CURRENT_DATE", @@ -42731,7 +53032,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "(props) => ({\r\n moduleMetadata: {\r\n imports: [\r\n OuiDatepickerModule,\r\n OuiFormFieldModule,\r\n OuiInputModule,\r\n BrowserAnimationsModule,\r\n ],\r\n schemas: [],\r\n\r\n declarations: [OuiDatepickerCustomStorybook],\r\n },\r\n template: ``,\r\n props: {\r\n ...props,\r\n closed: action('closed'),\r\n monthSelected: action('monthSelected'),\r\n datepickeropened: action('opened'),\r\n yearSelected: action('yearSelected'),\r\n dateChange: action('dateChange'),\r\n },\r\n})" + "defaultValue": "(props) => ({\n moduleMetadata: {\n imports: [\n OuiDatepickerModule,\n OuiFormFieldModule,\n OuiInputModule,\n BrowserAnimationsModule,\n ],\n schemas: [],\n\n declarations: [OuiDatepickerCustomStorybook],\n },\n template: ``,\n props: {\n ...props,\n closed: action('closed'),\n monthSelected: action('monthSelected'),\n datepickeropened: action('opened'),\n yearSelected: action('yearSelected'),\n dateChange: action('dateChange'),\n },\n})" }, { "name": "DATEPICKER_FOCUS_CLASS", @@ -42763,7 +53064,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "(props) => ({\r\n moduleMetadata: {\r\n imports: [\r\n OuiDatepickerModule,\r\n OuiFormFieldModule,\r\n OuiInputModule,\r\n BrowserAnimationsModule,\r\n ],\r\n schemas: [],\r\n declarations: [OuiDaterangepickerStorybook],\r\n },\r\n template: ``,\r\n props: {\r\n ...props,\r\n closed: action('closed'),\r\n monthSelected: action('monthSelected'),\r\n datepickeropened: action('opened'),\r\n yearSelected: action('yearSelected'),\r\n dateChange: action('dateChange'),\r\n },\r\n})" + "defaultValue": "(props) => ({\n moduleMetadata: {\n imports: [\n OuiDatepickerModule,\n OuiFormFieldModule,\n OuiInputModule,\n BrowserAnimationsModule,\n ],\n schemas: [],\n declarations: [OuiDaterangepickerStorybook],\n },\n template: ``,\n props: {\n ...props,\n closed: action('closed'),\n monthSelected: action('monthSelected'),\n datepickeropened: action('opened'),\n yearSelected: action('yearSelected'),\n dateChange: action('dateChange'),\n },\n})" }, { "name": "DAYS_PER_WEEK", @@ -42799,6 +53100,18 @@ "rawdescription": "Default color palette for input", "description": "

Default color palette for input

\n" }, + { + "name": "DEFAULT_COLOR", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/tab.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "string", + "defaultValue": "'primary'", + "rawdescription": "Default color palette for the tab", + "description": "

Default color palette for the tab

\n" + }, { "name": "DEFAULT_DATE_NAMES", "ctype": "miscellaneous", @@ -42819,7 +53132,7 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n long: [\r\n 'Sunday',\r\n 'Monday',\r\n 'Tuesday',\r\n 'Wednesday',\r\n 'Thursday',\r\n 'Friday',\r\n 'Saturday',\r\n ],\r\n short: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],\r\n narrow: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sat'],\r\n}", + "defaultValue": "{\n long: [\n 'Sunday',\n 'Monday',\n 'Tuesday',\n 'Wednesday',\n 'Thursday',\n 'Friday',\n 'Saturday',\n ],\n short: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],\n narrow: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sat'],\n}", "rawdescription": "The default day of the week names to use if Intl API is not available.", "description": "

The default day of the week names to use if Intl API is not available.

\n" }, @@ -42831,7 +53144,7 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n long: [\r\n 'January',\r\n 'February',\r\n 'March',\r\n 'April',\r\n 'May',\r\n 'June',\r\n 'July',\r\n 'August',\r\n 'September',\r\n 'October',\r\n 'November',\r\n 'December',\r\n ],\r\n short: [\r\n 'Jan',\r\n 'Feb',\r\n 'Mar',\r\n 'Apr',\r\n 'May',\r\n 'Jun',\r\n 'Jul',\r\n 'Aug',\r\n 'Sep',\r\n 'Oct',\r\n 'Nov',\r\n 'Dec',\r\n ],\r\n narrow: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],\r\n}", + "defaultValue": "{\n long: [\n 'January',\n 'February',\n 'March',\n 'April',\n 'May',\n 'June',\n 'July',\n 'August',\n 'September',\n 'October',\n 'November',\n 'December',\n ],\n short: [\n 'Jan',\n 'Feb',\n 'Mar',\n 'Apr',\n 'May',\n 'Jun',\n 'Jul',\n 'Aug',\n 'Sep',\n 'Oct',\n 'Nov',\n 'Dec',\n ],\n narrow: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],\n}", "rawdescription": "The default month names to use if Intl API is not available.", "description": "

The default month names to use if Intl API is not available.

\n" }, @@ -42847,6 +53160,18 @@ "rawdescription": "The default page size if there is no page size and there are no provided page size options.", "description": "

The default page size if there is no page size and there are no provided page size options.

\n" }, + { + "name": "defaultRippleAnimationConfig", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/core/ripple/ripple-renderer.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "object", + "defaultValue": "{\r\n enterDuration: 225,\r\n exitDuration: 150,\r\n}", + "rawdescription": "Default ripple animation configuration for ripples without an explicit\r\nanimation config specified.", + "description": "

Default ripple animation configuration for ripples without an explicit\nanimation config specified.

\n" + }, { "name": "dialogElementUid", "ctype": "miscellaneous", @@ -42877,7 +53202,7 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n production: true,\r\n}" + "defaultValue": "{\n production: true,\n}" }, { "name": "environment", @@ -42887,7 +53212,7 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n production: false,\r\n}" + "defaultValue": "{\n production: false,\n}" }, { "name": "EXPORTED_DECLARATIONS", @@ -42897,7 +53222,31 @@ "deprecated": false, "deprecationMessage": "", "type": "[]", - "defaultValue": "[\r\n // Table\r\n OuiTable,\r\n\r\n // Template defs\r\n OuiHeaderCellDef,\r\n OuiHeaderRowDef,\r\n OuiColumnDef,\r\n OuiCellDef,\r\n OuiRowDef,\r\n OuiFooterCellDef,\r\n OuiFooterRowDef,\r\n\r\n // Cell directives\r\n OuiHeaderCell,\r\n OuiCell,\r\n OuiFooterCell,\r\n\r\n // Row directions\r\n OuiHeaderRow,\r\n OuiRow,\r\n OuiFooterRow,\r\n]" + "defaultValue": "[\n // Table\n OuiTable,\n\n // Template defs\n OuiHeaderCellDef,\n OuiHeaderRowDef,\n OuiColumnDef,\n OuiCellDef,\n OuiRowDef,\n OuiFooterCellDef,\n OuiFooterRowDef,\n\n // Cell directives\n OuiHeaderCell,\n OuiCell,\n OuiFooterCell,\n\n // Row directions\n OuiHeaderRow,\n OuiRow,\n OuiFooterRow,\n]" + }, + { + "name": "HEADER_SCROLL_DELAY", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/paginated-tab-header.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "number", + "defaultValue": "650", + "rawdescription": "Amount of milliseconds to wait before starting to scroll the header automatically.\r\nSet a little conservatively in order to handle fake events dispatched on touch devices.", + "description": "

Amount of milliseconds to wait before starting to scroll the header automatically.\nSet a little conservatively in order to handle fake events dispatched on touch devices.

\n" + }, + { + "name": "HEADER_SCROLL_INTERVAL", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/paginated-tab-header.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "number", + "defaultValue": "100", + "rawdescription": "Interval in milliseconds at which to scroll the header\r\nwhile the user is holding their pointer.", + "description": "

Interval in milliseconds at which to scroll the header\nwhile the user is holding their pointer.

\n" }, { "name": "ICON_COLORS", @@ -42917,7 +53266,19 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n DOWN_ARROW: `\r\n \r\n\r\n \r\n \r\n \r\n\r\n`,\r\n PANEL_ICON: ``,\r\n VIDEO_ICON: ``,\r\n ARTICLE_ICON: ``,\r\n CLOSE_ICON: ``,\r\n THREE_DOT_MENU_ICON: ``,\r\n ARROW_ICON: ``,\r\n SELECT_ARROW_ICON: ``,\r\n CLOSE_ICON_8X8: ``,\r\n}" + "defaultValue": "{\n DOWN_ARROW: `\n \n\n \n \n \n\n`,\n PANEL_ICON: ``,\n VIDEO_ICON: ``,\n ARTICLE_ICON: ``,\n CLOSE_ICON: ``,\n THREE_DOT_MENU_ICON: ``,\n ARROW_ICON: ``,\n SELECT_ARROW_ICON: ``,\n CLOSE_ICON_8X8: ``,\n}" + }, + { + "name": "ignoreMouseEventsTimeout", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/core/ripple/ripple-renderer.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "number", + "defaultValue": "800", + "rawdescription": "Timeout for ignoring mouse events. Mouse events will be temporary ignored after touch\r\nevents to avoid synthetic mouse events.", + "description": "

Timeout for ignoring mouse events. Mouse events will be temporary ignored after touch\nevents to avoid synthetic mouse events.

\n" }, { "name": "INDETERMINATE_ANIMATION_TEMPLATE", @@ -42927,7 +53288,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "`\r\n @keyframes oui-progress-spinner-stroke-rotate-DIAMETER {\r\n 0% { stroke-dashoffset: START_VALUE; transform: rotate(0); }\r\n 12.5% { stroke-dashoffset: END_VALUE; transform: rotate(0); }\r\n 12.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(72.5deg); }\r\n 25% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(72.5deg); }\r\n\r\n 25.0001% { stroke-dashoffset: START_VALUE; transform: rotate(270deg); }\r\n 37.5% { stroke-dashoffset: END_VALUE; transform: rotate(270deg); }\r\n 37.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(161.5deg); }\r\n 50% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(161.5deg); }\r\n\r\n 50.0001% { stroke-dashoffset: START_VALUE; transform: rotate(180deg); }\r\n 62.5% { stroke-dashoffset: END_VALUE; transform: rotate(180deg); }\r\n 62.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(251.5deg); }\r\n 75% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(251.5deg); }\r\n\r\n 75.0001% { stroke-dashoffset: START_VALUE; transform: rotate(90deg); }\r\n 87.5% { stroke-dashoffset: END_VALUE; transform: rotate(90deg); }\r\n 87.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(341.5deg); }\r\n 100% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(341.5deg); }\r\n }\r\n`" + "defaultValue": "`\n @keyframes oui-progress-spinner-stroke-rotate-DIAMETER {\n 0% { stroke-dashoffset: START_VALUE; transform: rotate(0); }\n 12.5% { stroke-dashoffset: END_VALUE; transform: rotate(0); }\n 12.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(72.5deg); }\n 25% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(72.5deg); }\n\n 25.0001% { stroke-dashoffset: START_VALUE; transform: rotate(270deg); }\n 37.5% { stroke-dashoffset: END_VALUE; transform: rotate(270deg); }\n 37.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(161.5deg); }\n 50% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(161.5deg); }\n\n 50.0001% { stroke-dashoffset: START_VALUE; transform: rotate(180deg); }\n 62.5% { stroke-dashoffset: END_VALUE; transform: rotate(180deg); }\n 62.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(251.5deg); }\n 75% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(251.5deg); }\n\n 75.0001% { stroke-dashoffset: START_VALUE; transform: rotate(90deg); }\n 87.5% { stroke-dashoffset: END_VALUE; transform: rotate(90deg); }\n 87.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(341.5deg); }\n 100% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(341.5deg); }\n }\n`" }, { "name": "INPUT_HOST_ATTRIBUTES", @@ -42938,7 +53299,7 @@ "deprecationMessage": "", "type": "[]", "defaultValue": "['oui-input']", - "rawdescription": "List of classes to add to Button instances based on host attributes to\r\nstyle as different variants.", + "rawdescription": "List of classes to add to Button instances based on host attributes to\nstyle as different variants.", "description": "

List of classes to add to Button instances based on host attributes to\nstyle as different variants.

\n" }, { @@ -42950,7 +53311,7 @@ "deprecationMessage": "", "type": "", "defaultValue": "/^\\d{4}-\\d{2}-\\d{2}(?:T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|(?:(?:\\+|-)\\d{2}:\\d{2}))?)?$/", - "rawdescription": "Matches strings that have the form of a valid RFC 3339 string\r\n(https://tools.ietf.org/html/rfc3339). Note that the string may not actually be a valid date\r\nbecause the regex will match strings an with out of bounds month, date, etc.", + "rawdescription": "Matches strings that have the form of a valid RFC 3339 string\n(https://tools.ietf.org/html/rfc3339). Note that the string may not actually be a valid date\nbecause the regex will match strings an with out of bounds month, date, etc.", "description": "

Matches strings that have the form of a valid RFC 3339 string\n(https://tools.ietf.org/html/rfc3339). Note that the string may not actually be a valid date\nbecause the regex will match strings an with out of bounds month, date, etc.

\n" }, { @@ -42963,6 +53324,114 @@ "type": "[]", "defaultValue": "['before', 'after']" }, + { + "name": "MAT_CARD_CONFIG", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/card/card.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "new InjectionToken('MAT_CARD_CONFIG')", + "rawdescription": "Injection token that can be used to provide the default options the card module.", + "description": "

Injection token that can be used to provide the default options the card module.

\n" + }, + { + "name": "MAT_RIPPLE_GLOBAL_OPTIONS", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/core/ripple/ripple.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "new InjectionToken(\r\n 'mat-ripple-global-options',\r\n)", + "rawdescription": "Injection token that can be used to specify the global ripple options.", + "description": "

Injection token that can be used to specify the global ripple options.

\n" + }, + { + "name": "MAT_TAB", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/tab-label.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "new InjectionToken('MAT_TAB')", + "rawdescription": "Used to provide a tab label to a tab without causing a circular dependency.", + "description": "

Used to provide a tab label to a tab without causing a circular dependency.

\n" + }, + { + "name": "MAT_TAB_CONTENT", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/tab-content.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "new InjectionToken('MatTabContent')", + "rawdescription": "Injection token that can be used to reference instances of `MatTabContent`. It serves as\r\nalternative token to the actual `MatTabContent` class which could cause unnecessary\r\nretention of the class and its directive metadata.", + "description": "

Injection token that can be used to reference instances of MatTabContent. It serves as\nalternative token to the actual MatTabContent class which could cause unnecessary\nretention of the class and its directive metadata.

\n" + }, + { + "name": "MAT_TAB_GROUP", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/tab.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "new InjectionToken('MAT_TAB_GROUP')", + "rawdescription": "Used to provide a tab group to a tab without causing a circular dependency.", + "description": "

Used to provide a tab group to a tab without causing a circular dependency.

\n" + }, + { + "name": "MAT_TAB_LABEL", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/tab-label.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "new InjectionToken('MatTabLabel')", + "rawdescription": "Injection token that can be used to reference instances of `MatTabLabel`. It serves as\r\nalternative token to the actual `MatTabLabel` class which could cause unnecessary\r\nretention of the class and its directive metadata.", + "description": "

Injection token that can be used to reference instances of MatTabLabel. It serves as\nalternative token to the actual MatTabLabel class which could cause unnecessary\nretention of the class and its directive metadata.

\n" + }, + { + "name": "MAT_TABS_CONFIG", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/tab-config.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "new InjectionToken('MAT_TABS_CONFIG')", + "rawdescription": "Injection token that can be used to provide the default options the tabs module.", + "description": "

Injection token that can be used to provide the default options the tabs module.

\n" + }, + { + "name": "MATERIAL_SANITY_CHECKS", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/core/common-behaviors/common-module.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "new InjectionToken('mat-sanity-checks', {\r\n providedIn: 'root',\r\n factory: MATERIAL_SANITY_CHECKS_FACTORY,\r\n})", + "rawdescription": "Injection token that configures whether the Material sanity checks are enabled.", + "description": "

Injection token that configures whether the Material sanity checks are enabled.

\n" + }, + { + "name": "matTabsAnimations", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/tabs-animations.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "literal type", + "defaultValue": "{\r\n /** Animation translates a tab along the X axis. */\r\n translateTab: trigger('translateTab', [\r\n // Transitions to `none` instead of 0, because some browsers might blur the content.\r\n state('center, void, left-origin-center, right-origin-center', style({transform: 'none'})),\r\n\r\n // If the tab is either on the left or right, we additionally add a `min-height` of 1px\r\n // in order to ensure that the element has a height before its state changes. This is\r\n // necessary because Chrome does seem to skip the transition in RTL mode if the element does\r\n // not have a static height and is not rendered. See related issue: #9465\r\n state(\r\n 'left',\r\n style({\r\n transform: 'translate3d(-100%, 0, 0)',\r\n minHeight: '1px',\r\n\r\n // Normally this is redundant since we detach the content from the DOM, but if the user\r\n // opted into keeping the content in the DOM, we have to hide it so it isn't focusable.\r\n visibility: 'hidden',\r\n }),\r\n ),\r\n state(\r\n 'right',\r\n style({\r\n transform: 'translate3d(100%, 0, 0)',\r\n minHeight: '1px',\r\n visibility: 'hidden',\r\n }),\r\n ),\r\n\r\n transition(\r\n '* => left, * => right, left => center, right => center',\r\n animate('{{animationDuration}} cubic-bezier(0.35, 0, 0.25, 1)'),\r\n ),\r\n transition('void => left-origin-center', [\r\n style({transform: 'translate3d(-100%, 0, 0)', visibility: 'hidden'}),\r\n animate('{{animationDuration}} cubic-bezier(0.35, 0, 0.25, 1)'),\r\n ]),\r\n transition('void => right-origin-center', [\r\n style({transform: 'translate3d(100%, 0, 0)', visibility: 'hidden'}),\r\n animate('{{animationDuration}} cubic-bezier(0.35, 0, 0.25, 1)'),\r\n ]),\r\n ]),\r\n}", + "rawdescription": "Animations used by the Material tabs.", + "description": "

Animations used by the Material tabs.

\n" + }, { "name": "MAX_DATE", "ctype": "miscellaneous", @@ -42982,7 +53451,7 @@ "deprecationMessage": "", "type": "number", "defaultValue": "9007199254740991", - "rawdescription": "Corresponds to `Number.MAX_SAFE_INTEGER`. Moved out into a variable here due to\r\nflaky browser support and the value not being defined in Closure's typings.", + "rawdescription": "Corresponds to `Number.MAX_SAFE_INTEGER`. Moved out into a variable here due to\nflaky browser support and the value not being defined in Closure's typings.", "description": "

Corresponds to Number.MAX_SAFE_INTEGER. Moved out into a variable here due to\nflaky browser support and the value not being defined in Closure's typings.

\n" }, { @@ -43017,7 +53486,19 @@ "deprecated": false, "deprecationMessage": "", "type": "[]", - "defaultValue": "[\r\n 'Maia',\r\n 'Asher',\r\n 'Olivia',\r\n 'Atticus',\r\n 'Amelia',\r\n 'Jack',\r\n 'Charlotte',\r\n 'Theodore',\r\n 'Isla',\r\n 'Oliver',\r\n 'Isabella',\r\n 'Jasper',\r\n 'Cora',\r\n 'Levi',\r\n 'Violet',\r\n 'Arthur',\r\n 'Mia',\r\n 'Thomas',\r\n 'Elizabeth',\r\n]" + "defaultValue": "[\n 'Maia',\n 'Asher',\n 'Olivia',\n 'Atticus',\n 'Amelia',\n 'Jack',\n 'Charlotte',\n 'Theodore',\n 'Isla',\n 'Oliver',\n 'Isabella',\n 'Jasper',\n 'Cora',\n 'Levi',\n 'Violet',\n 'Arthur',\n 'Mia',\n 'Thomas',\n 'Elizabeth',\n]" + }, + { + "name": "nextId", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/tab-group.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "number", + "defaultValue": "0", + "rawdescription": "Used to generate unique ID's for each tab component", + "description": "

Used to generate unique ID's for each tab component

\n" }, { "name": "nextUniqueId", @@ -43053,7 +53534,7 @@ "name": "nextUniqueId", "ctype": "miscellaneous", "subtype": "variable", - "file": "ui/src/components/radio/radio.ts", + "file": "ui/src/components/select/select.component.ts", "deprecated": false, "deprecationMessage": "", "type": "number", @@ -43063,7 +53544,7 @@ "name": "nextUniqueId", "ctype": "miscellaneous", "subtype": "variable", - "file": "ui/src/components/select/select.component.ts", + "file": "ui/src/components/slide-toggle/slide-toggle.ts", "deprecated": false, "deprecationMessage": "", "type": "number", @@ -43073,12 +53554,34 @@ "name": "nextUniqueId", "ctype": "miscellaneous", "subtype": "variable", - "file": "ui/src/components/slide-toggle/slide-toggle.ts", + "file": "ui/src/components/radio/radio.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "number", + "defaultValue": "0" + }, + { + "name": "nextUniqueId", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/tab-nav-bar/tab-nav-bar.ts", "deprecated": false, "deprecationMessage": "", "type": "number", "defaultValue": "0" }, + { + "name": "NO_TRANSITION_CLASS", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/ink-bar.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "string", + "defaultValue": "'mdc-tab-indicator--no-transition'", + "rawdescription": "Class that is applied when the tab indicator should not transition.", + "description": "

Class that is applied when the tab indicator should not transition.

\n" + }, { "name": "OPTIONS", "ctype": "miscellaneous", @@ -43097,7 +53600,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken(\r\n 'oui-autocomplete-default-options',\r\n {\r\n providedIn: 'root',\r\n factory: OUI_AUTOCOMPLETE_DEFAULT_OPTIONS_FACTORY,\r\n }\r\n )", + "defaultValue": "new InjectionToken(\n 'oui-autocomplete-default-options',\n {\n providedIn: 'root',\n factory: OUI_AUTOCOMPLETE_DEFAULT_OPTIONS_FACTORY,\n }\n )", "rawdescription": "Injection token to be used to override the default options for `oui-autocomplete`.", "description": "

Injection token to be used to override the default options for oui-autocomplete.

\n" }, @@ -43109,7 +53612,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken<\r\n () => ScrollStrategy\r\n>('oui-autocomplete-scroll-strategy')", + "defaultValue": "new InjectionToken<\n () => ScrollStrategy\n>('oui-autocomplete-scroll-strategy')", "rawdescription": "Injection token that determines the scroll handling while the autocomplete panel is open.", "description": "

Injection token that determines the scroll handling while the autocomplete panel is open.

\n" }, @@ -43121,7 +53624,7 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n provide: OUI_AUTOCOMPLETE_SCROLL_STRATEGY,\r\n deps: [Overlay],\r\n useFactory: OUI_AUTOCOMPLETE_SCROLL_STRATEGY_FACTORY,\r\n}" + "defaultValue": "{\n provide: OUI_AUTOCOMPLETE_SCROLL_STRATEGY,\n deps: [Overlay],\n useFactory: OUI_AUTOCOMPLETE_SCROLL_STRATEGY_FACTORY,\n}" }, { "name": "OUI_AUTOCOMPLETE_VALUE_ACCESSOR", @@ -43131,7 +53634,7 @@ "deprecated": false, "deprecationMessage": "", "type": "any", - "defaultValue": "{\r\n provide: NG_VALUE_ACCESSOR,\r\n useExisting: forwardRef(() => OuiAutocompleteTrigger),\r\n multi: true,\r\n}", + "defaultValue": "{\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => OuiAutocompleteTrigger),\n multi: true,\n}", "rawdescription": "Provider that allows the autocomplete to register as a ControlValueAccessor.", "description": "

Provider that allows the autocomplete to register as a ControlValueAccessor.

\n" }, @@ -43143,7 +53646,7 @@ "deprecated": false, "deprecationMessage": "", "type": "OuiDateFormats", - "defaultValue": "{\r\n parse: {\r\n dateInput: null,\r\n },\r\n display: {\r\n dateInput: {\r\n year: 'numeric',\r\n day: '2-digit',\r\n month: '2-digit',\r\n },\r\n monthYearLabel: { year: 'numeric', month: 'short' },\r\n dateA11yLabel: { year: 'numeric', month: 'long', day: 'numeric' },\r\n monthYearA11yLabel: { year: 'numeric', month: 'long' },\r\n },\r\n}" + "defaultValue": "{\n parse: {\n dateInput: null,\n },\n display: {\n dateInput: {\n year: 'numeric',\n day: '2-digit',\n month: '2-digit',\n },\n monthYearLabel: { year: 'numeric', month: 'short' },\n dateA11yLabel: { year: 'numeric', month: 'long', day: 'numeric' },\n monthYearA11yLabel: { year: 'numeric', month: 'long' },\n },\n}" }, { "name": "OUI_DATE_FORMATS", @@ -43153,7 +53656,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken(\r\n 'oui-date-formats'\r\n)" + "defaultValue": "new InjectionToken(\n 'oui-date-formats'\n)" }, { "name": "OUI_DATE_LOCALE", @@ -43187,7 +53690,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken<\r\n () => ScrollStrategy\r\n>('oui-datepicker-scroll-strategy')", + "defaultValue": "new InjectionToken<\n () => ScrollStrategy\n>('oui-datepicker-scroll-strategy')", "rawdescription": "Injection token that determines the scroll handling while the calendar is open.", "description": "

Injection token that determines the scroll handling while the calendar is open.

\n" }, @@ -43199,7 +53702,7 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n provide: OUI_DATEPICKER_SCROLL_STRATEGY,\r\n deps: [Overlay],\r\n useFactory: OUI_DATEPICKER_SCROLL_STRATEGY_FACTORY,\r\n}" + "defaultValue": "{\n provide: OUI_DATEPICKER_SCROLL_STRATEGY,\n deps: [Overlay],\n useFactory: OUI_DATEPICKER_SCROLL_STRATEGY_FACTORY,\n}" }, { "name": "OUI_DATEPICKER_VALIDATORS", @@ -43209,7 +53712,7 @@ "deprecated": false, "deprecationMessage": "", "type": "any", - "defaultValue": "{\r\n provide: NG_VALIDATORS,\r\n useExisting: forwardRef(() => OuiDatepickerInput),\r\n multi: true,\r\n}" + "defaultValue": "{\n provide: NG_VALIDATORS,\n useExisting: forwardRef(() => OuiDatepickerInput),\n multi: true,\n}" }, { "name": "OUI_DATEPICKER_VALUE_ACCESSOR", @@ -43219,7 +53722,7 @@ "deprecated": false, "deprecationMessage": "", "type": "any", - "defaultValue": "{\r\n provide: NG_VALUE_ACCESSOR,\r\n useExisting: forwardRef(() => OuiDatepickerInput),\r\n multi: true,\r\n}" + "defaultValue": "{\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => OuiDatepickerInput),\n multi: true,\n}" }, { "name": "OUI_DIALOG_DATA", @@ -43241,7 +53744,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken(\r\n 'oui-dialog-default-options'\r\n)", + "defaultValue": "new InjectionToken(\n 'oui-dialog-default-options'\n)", "rawdescription": "Injection token that can be used to specify default dialog options.", "description": "

Injection token that can be used to specify default dialog options.

\n" }, @@ -43253,7 +53756,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken<\r\n () => ScrollStrategy\r\n>('oui-dialog-scroll-strategy')", + "defaultValue": "new InjectionToken<\n () => ScrollStrategy\n>('oui-dialog-scroll-strategy')", "rawdescription": "Injection token that determines the scroll handling while the dialog is open.", "description": "

Injection token that determines the scroll handling while the dialog is open.

\n" }, @@ -43265,7 +53768,7 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n provide: OUI_DIALOG_SCROLL_STRATEGY,\r\n deps: [Overlay],\r\n useFactory: OUI_DIALOG_SCROLL_STRATEGY_PROVIDER_FACTORY,\r\n}" + "defaultValue": "{\n provide: OUI_DIALOG_SCROLL_STRATEGY,\n deps: [Overlay],\n useFactory: OUI_DIALOG_SCROLL_STRATEGY_PROVIDER_FACTORY,\n}" }, { "name": "OUI_FORM_FIELD_DEFAULT_OPTIONS", @@ -43275,8 +53778,8 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken(\r\n 'OUI_FORM_FIELD_DEFAULT_OPTIONS'\r\n )", - "rawdescription": "Injection token that can be used to configure the\r\ndefault options for all form field within an app.", + "defaultValue": "new InjectionToken(\n 'OUI_FORM_FIELD_DEFAULT_OPTIONS'\n )", + "rawdescription": "Injection token that can be used to configure the\ndefault options for all form field within an app.", "description": "

Injection token that can be used to configure the\ndefault options for all form field within an app.

\n" }, { @@ -43287,8 +53790,8 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken(\r\n 'oui-icon-location',\r\n {\r\n providedIn: 'root',\r\n factory: OUI_ICON_LOCATION_FACTORY,\r\n }\r\n)", - "rawdescription": "Injection token used to provide the current location to `OuiIcon`.\r\nUsed to handle server-side rendering and to stub out during unit tests.", + "defaultValue": "new InjectionToken(\n 'oui-icon-location',\n {\n providedIn: 'root',\n factory: OUI_ICON_LOCATION_FACTORY,\n }\n)", + "rawdescription": "Injection token used to provide the current location to `OuiIcon`.\nUsed to handle server-side rendering and to stub out during unit tests.", "description": "

Injection token used to provide the current location to OuiIcon.\nUsed to handle server-side rendering and to stub out during unit tests.

\n" }, { @@ -43299,7 +53802,7 @@ "deprecated": false, "deprecationMessage": "", "type": "[]", - "defaultValue": "[\r\n 'button',\r\n 'checkbox',\r\n 'file',\r\n 'hidden',\r\n 'image',\r\n 'radio',\r\n 'range',\r\n 'reset',\r\n 'submit',\r\n]" + "defaultValue": "[\n 'button',\n 'checkbox',\n 'file',\n 'hidden',\n 'image',\n 'radio',\n 'range',\n 'reset',\n 'submit',\n]" }, { "name": "OUI_INPUT_VALUE_ACCESSOR", @@ -43309,8 +53812,8 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken<{ value: any }>(\r\n 'OUI_INPUT_VALUE_ACCESSOR'\r\n)", - "rawdescription": "This token is used to inject the object whose value should be set into `OuiInput`. If none is\r\nprovided, the native `HTMLInputElement` is used.", + "defaultValue": "new InjectionToken<{ value: any }>(\n 'OUI_INPUT_VALUE_ACCESSOR'\n)", + "rawdescription": "This token is used to inject the object whose value should be set into `OuiInput`. If none is\nprovided, the native `HTMLInputElement` is used.", "description": "

This token is used to inject the object whose value should be set into OuiInput. If none is\nprovided, the native HTMLInputElement is used.

\n" }, { @@ -43321,7 +53824,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken('oui-menu-default-options', {\r\n providedIn: 'root',\r\n factory: OUI_MENU_DEFAULT_OPTIONS_FACTORY,\r\n })", + "defaultValue": "new InjectionToken('oui-menu-default-options', {\n providedIn: 'root',\n factory: OUI_MENU_DEFAULT_OPTIONS_FACTORY,\n })", "rawdescription": "Injection token to be used to override the default options for `oui-menu`.", "description": "

Injection token to be used to override the default options for oui-menu.

\n" }, @@ -43333,7 +53836,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken(\r\n 'OUI_MENU_PANEL'\r\n)", + "defaultValue": "new InjectionToken(\n 'OUI_MENU_PANEL'\n)", "rawdescription": "Injection token used to provide the parent menu to menu-specific components.", "description": "

Injection token used to provide the parent menu to menu-specific components.

\n" }, @@ -43345,7 +53848,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken<\r\n () => ScrollStrategy\r\n>('oui-menu-scroll-strategy')", + "defaultValue": "new InjectionToken<\n () => ScrollStrategy\n>('oui-menu-scroll-strategy')", "rawdescription": "Injection token that determines the scroll handling while the menu is open.", "description": "

Injection token that determines the scroll handling while the menu is open.

\n" }, @@ -43357,7 +53860,7 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n provide: OUI_MENU_SCROLL_STRATEGY,\r\n deps: [Overlay],\r\n useFactory: OUI_MENU_SCROLL_STRATEGY_FACTORY,\r\n}" + "defaultValue": "{\n provide: OUI_MENU_SCROLL_STRATEGY,\n deps: [Overlay],\n useFactory: OUI_MENU_SCROLL_STRATEGY_FACTORY,\n}" }, { "name": "OUI_NATIVE_DATE_FORMATS", @@ -43367,7 +53870,7 @@ "deprecated": false, "deprecationMessage": "", "type": "OuiDateFormats", - "defaultValue": "{\r\n parse: {\r\n dateInput: null,\r\n },\r\n display: {\r\n dateInput: { year: 'numeric', month: 'short', day: 'numeric' },\r\n monthYearLabel: { year: 'numeric', month: 'short' },\r\n dateA11yLabel: { year: 'numeric', month: 'long', day: 'numeric' },\r\n monthYearA11yLabel: { year: 'numeric', month: 'long' },\r\n },\r\n}" + "defaultValue": "{\n parse: {\n dateInput: null,\n },\n display: {\n dateInput: { year: 'numeric', month: 'short', day: 'numeric' },\n monthYearLabel: { year: 'numeric', month: 'short' },\n dateA11yLabel: { year: 'numeric', month: 'long', day: 'numeric' },\n monthYearA11yLabel: { year: 'numeric', month: 'long' },\n },\n}" }, { "name": "OUI_OPTION_PARENT_COMPONENT", @@ -43389,7 +53892,7 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n // If there is already an OuiPaginatorIntl available, use that. Otherwise, provide a new one.\r\n provide: OuiPaginatorIntl,\r\n deps: [[new Optional(), new SkipSelf(), OuiPaginatorIntl]],\r\n useFactory: OUI_PAGINATOR_INTL_PROVIDER_FACTORY,\r\n}" + "defaultValue": "{\n // If there is already an OuiPaginatorIntl available, use that. Otherwise, provide a new one.\n provide: OuiPaginatorIntl,\n deps: [[new Optional(), new SkipSelf(), OuiPaginatorIntl]],\n useFactory: OUI_PAGINATOR_INTL_PROVIDER_FACTORY,\n}" }, { "name": "OUI_PANEL_DEFAULT_OPTIONS", @@ -43399,7 +53902,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken('oui-panel-default-options', {\r\n providedIn: 'root',\r\n factory: OUI_PANEL_DEFAULT_OPTIONS_FACTORY,\r\n })", + "defaultValue": "new InjectionToken('oui-panel-default-options', {\n providedIn: 'root',\n factory: OUI_PANEL_DEFAULT_OPTIONS_FACTORY,\n })", "rawdescription": "Injection token to be used to override the default options for `oui-menu`.", "description": "

Injection token to be used to override the default options for oui-menu.

\n" }, @@ -43411,7 +53914,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken(\r\n 'OUI_PANEL_OVERLAY'\r\n)", + "defaultValue": "new InjectionToken(\n 'OUI_PANEL_OVERLAY'\n)", "rawdescription": "Injection token used to provide the parent menu to menu-specific components.", "description": "

Injection token used to provide the parent menu to menu-specific components.

\n" }, @@ -43423,7 +53926,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken<\r\n () => ScrollStrategy\r\n>('oui-panel-scroll-strategy')", + "defaultValue": "new InjectionToken<\n () => ScrollStrategy\n>('oui-panel-scroll-strategy')", "rawdescription": "Injection token that determines the scroll handling while the panel-overlay is open.", "description": "

Injection token that determines the scroll handling while the panel-overlay is open.

\n" }, @@ -43435,7 +53938,7 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n provide: OUI_PANEL_SCROLL_STRATEGY,\r\n deps: [Overlay],\r\n useFactory: OUI_PANEL_SCROLL_STRATEGY_FACTORY,\r\n}" + "defaultValue": "{\n provide: OUI_PANEL_SCROLL_STRATEGY,\n deps: [Overlay],\n useFactory: OUI_PANEL_SCROLL_STRATEGY_FACTORY,\n}" }, { "name": "OUI_RADIO_GROUP_CONTROL_VALUE_ACCESSOR", @@ -43445,8 +53948,8 @@ "deprecated": false, "deprecationMessage": "", "type": "any", - "defaultValue": "{\r\n provide: NG_VALUE_ACCESSOR,\r\n useExisting: forwardRef(() => OuiRadioGroup),\r\n multi: true,\r\n}", - "rawdescription": "Provider Expression that allows oui-radio-group to register as a ControlValueAccessor. This\r\nallows it to support [(ngModel)] and ngControl.", + "defaultValue": "{\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => OuiRadioGroup),\n multi: true,\n}", + "rawdescription": "Provider Expression that allows oui-radio-group to register as a ControlValueAccessor. This\nallows it to support [(ngModel)] and ngControl.", "description": "

Provider Expression that allows oui-radio-group to register as a ControlValueAccessor. This\nallows it to support [(ngModel)] and ngControl.

\n" }, { @@ -43457,7 +53960,7 @@ "deprecated": false, "deprecationMessage": "", "type": "any", - "defaultValue": "{\r\n provide: NG_VALUE_ACCESSOR,\r\n useExisting: forwardRef(() => OuiSlideToggle),\r\n multi: true,\r\n}", + "defaultValue": "{\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => OuiSlideToggle),\n multi: true,\n}", "rawdescription": "Boilerplate for applying mixins to OuiSlideToggle.", "description": "

Boilerplate for applying mixins to OuiSlideToggle.

\n" }, @@ -43469,7 +53972,7 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n // If there is already an OuiSortHeaderIntl available, use that. Otherwise, provide a new one.\r\n provide: OuiSortHeaderIntl,\r\n deps: [[new Optional(), new SkipSelf(), OuiSortHeaderIntl]],\r\n useFactory: OUI_SORT_HEADER_INTL_PROVIDER_FACTORY,\r\n}" + "defaultValue": "{\n // If there is already an OuiSortHeaderIntl available, use that. Otherwise, provide a new one.\n provide: OuiSortHeaderIntl,\n deps: [[new Optional(), new SkipSelf(), OuiSortHeaderIntl]],\n useFactory: OUI_SORT_HEADER_INTL_PROVIDER_FACTORY,\n}" }, { "name": "OUI_TOOLTIP_DEFAULT_OPTIONS", @@ -43479,7 +53982,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken('oui-tooltip-default-options', {\r\n providedIn: 'root',\r\n factory: OUI_TOOLTIP_DEFAULT_OPTIONS_FACTORY,\r\n })", + "defaultValue": "new InjectionToken('oui-tooltip-default-options', {\n providedIn: 'root',\n factory: OUI_TOOLTIP_DEFAULT_OPTIONS_FACTORY,\n })", "rawdescription": "Injection token to be used to override the default options for `ouiTooltip`.", "description": "

Injection token to be used to override the default options for ouiTooltip.

\n" }, @@ -43491,7 +53994,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken<\r\n () => ScrollStrategy\r\n>('oui-tooltip-scroll-strategy')", + "defaultValue": "new InjectionToken<\n () => ScrollStrategy\n>('oui-tooltip-scroll-strategy')", "rawdescription": "Injection token that determines the scroll handling while a tooltip is visible.", "description": "

Injection token that determines the scroll handling while a tooltip is visible.

\n" }, @@ -43503,7 +54006,7 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n provide: OUI_TOOLTIP_SCROLL_STRATEGY,\r\n deps: [Overlay],\r\n useFactory: OUI_TOOLTIP_SCROLL_STRATEGY_FACTORY,\r\n}" + "defaultValue": "{\n provide: OUI_TOOLTIP_SCROLL_STRATEGY,\n deps: [Overlay],\n useFactory: OUI_TOOLTIP_SCROLL_STRATEGY_FACTORY,\n}" }, { "name": "OuiButtonMixinBase", @@ -43513,7 +54016,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "mixinProgress(\r\n mixinColor(mixinDisabled(OuiButtonBase))\r\n)" + "defaultValue": "mixinProgress(\n mixinColor(mixinDisabled(OuiButtonBase))\n)" }, { "name": "OuiCheckboxMixinBase", @@ -43533,7 +54036,7 @@ "deprecated": false, "deprecationMessage": "", "type": "literal type", - "defaultValue": "{\r\n /** Transforms the height of the datepicker's calendar. */\r\n transformPanel: trigger('transformPanel', [\r\n state(\r\n 'void',\r\n style({\r\n opacity: 0,\r\n transform: 'scale(1, 0.8)',\r\n })\r\n ),\r\n transition(\r\n 'void => enter',\r\n animate(\r\n '120ms cubic-bezier(0, 0, 0.2, 1)',\r\n style({\r\n opacity: 1,\r\n transform: 'scale(1, 1)',\r\n })\r\n )\r\n ),\r\n transition('* => void', animate('100ms linear', style({ opacity: 0 }))),\r\n ]),\r\n\r\n /** Fades in the content of the calendar. */\r\n fadeInCalendar: trigger('fadeInCalendar', [\r\n state('void', style({ opacity: 0 })),\r\n state('enter', style({ opacity: 1 })),\r\n\r\n // TODO(crisbeto): this animation should be removed since it isn't quite on spec, but we\r\n // need to keep it until #12440 gets in, otherwise the exit animation will look glitchy.\r\n transition(\r\n 'void => *',\r\n animate('120ms 100ms cubic-bezier(0.55, 0, 0.55, 0.2)')\r\n ),\r\n ]),\r\n}", + "defaultValue": "{\n /** Transforms the height of the datepicker's calendar. */\n transformPanel: trigger('transformPanel', [\n state(\n 'void',\n style({\n opacity: 0,\n transform: 'scale(1, 0.8)',\n })\n ),\n transition(\n 'void => enter',\n animate(\n '120ms cubic-bezier(0, 0, 0.2, 1)',\n style({\n opacity: 1,\n transform: 'scale(1, 1)',\n })\n )\n ),\n transition('* => void', animate('100ms linear', style({ opacity: 0 }))),\n ]),\n\n /** Fades in the content of the calendar. */\n fadeInCalendar: trigger('fadeInCalendar', [\n state('void', style({ opacity: 0 })),\n state('enter', style({ opacity: 1 })),\n\n // TODO(crisbeto): this animation should be removed since it isn't quite on spec, but we\n // need to keep it until #12440 gets in, otherwise the exit animation will look glitchy.\n transition(\n 'void => *',\n animate('120ms 100ms cubic-bezier(0.55, 0, 0.55, 0.2)')\n ),\n ]),\n}", "rawdescription": "Animations used by the datepicker.", "description": "

Animations used by the datepicker.

\n" }, @@ -43565,7 +54068,7 @@ "deprecated": false, "deprecationMessage": "", "type": "literal type", - "defaultValue": "{\r\n /** Animation that moves the sort indicator. */\r\n indicator: trigger('indicator', [\r\n state('active-asc', style({ transform: 'rotateX(0deg)' })),\r\n state('active-desc', style({ transform: 'rotateX(180deg)' })),\r\n transition(\r\n 'active-asc <=> active-desc',\r\n animate(SORT_ANIMATION_TRANSITION)\r\n ),\r\n ]),\r\n\r\n /** Animation that rotates the left pointer of the indicator based on the sorting direction. */\r\n leftPointer: trigger('leftPointer', [\r\n state('active-asc, asc', style({ transform: 'rotate(-45deg)' })),\r\n state('active-desc, desc', style({ transform: 'rotate(45deg)' })),\r\n transition(\r\n 'active-asc <=> active-desc',\r\n animate(SORT_ANIMATION_TRANSITION)\r\n ),\r\n ]),\r\n\r\n /** Animation that rotates the right pointer of the indicator based on the sorting direction. */\r\n rightPointer: trigger('rightPointer', [\r\n state('active-asc, asc', style({ transform: 'rotate(45deg)' })),\r\n state('active-desc, desc', style({ transform: 'rotate(-45deg)' })),\r\n transition(\r\n 'active-asc <=> active-desc',\r\n animate(SORT_ANIMATION_TRANSITION)\r\n ),\r\n ]),\r\n\r\n /** Animation that controls the arrow opacity. */\r\n arrowOpacity: trigger('arrowOpacity', [\r\n state('desc-to-active, asc-to-active, active', style({ opacity: 1 })),\r\n state('desc-to-hint, asc-to-hint, hint', style({ opacity: 0.54 })),\r\n state(\r\n 'hint-to-desc, active-to-desc, desc, hint-to-asc, active-to-asc, asc, void',\r\n style({ opacity: 0 })\r\n ),\r\n // Transition between all states except for immediate transitions\r\n transition(\r\n '* => asc, * => desc, * => active, * => hint, * => void',\r\n animate('0ms')\r\n ),\r\n transition('* <=> *', animate(SORT_ANIMATION_TRANSITION)),\r\n ]),\r\n\r\n /**\r\n * Animation for the translation of the arrow as a whole. States are separated into two\r\n * groups: ones with animations and others that are immediate. Immediate states are asc, desc,\r\n * peek, and active. The other states define a specific animation (source-to-destination)\r\n * and are determined as a function of their prev user-perceived state and what the next state\r\n * should be.\r\n */\r\n arrowPosition: trigger('arrowPosition', [\r\n // Hidden Above => Hint Center\r\n transition(\r\n '* => desc-to-hint, * => desc-to-active',\r\n animate(\r\n SORT_ANIMATION_TRANSITION,\r\n keyframes([\r\n style({ transform: 'translateY(-25%)' }),\r\n style({ transform: 'translateY(0)' }),\r\n ])\r\n )\r\n ),\r\n // Hint Center => Hidden Below\r\n transition(\r\n '* => hint-to-desc, * => active-to-desc',\r\n animate(\r\n SORT_ANIMATION_TRANSITION,\r\n keyframes([\r\n style({ transform: 'translateY(0)' }),\r\n style({ transform: 'translateY(25%)' }),\r\n ])\r\n )\r\n ),\r\n // Hidden Below => Hint Center\r\n transition(\r\n '* => asc-to-hint, * => asc-to-active',\r\n animate(\r\n SORT_ANIMATION_TRANSITION,\r\n keyframes([\r\n style({ transform: 'translateY(25%)' }),\r\n style({ transform: 'translateY(0)' }),\r\n ])\r\n )\r\n ),\r\n // Hint Center => Hidden Above\r\n transition(\r\n '* => hint-to-asc, * => active-to-asc',\r\n animate(\r\n SORT_ANIMATION_TRANSITION,\r\n keyframes([\r\n style({ transform: 'translateY(0)' }),\r\n style({ transform: 'translateY(-25%)' }),\r\n ])\r\n )\r\n ),\r\n state(\r\n 'desc-to-hint, asc-to-hint, hint, desc-to-active, asc-to-active, active',\r\n style({ transform: 'translateY(0)' })\r\n ),\r\n state(\r\n 'hint-to-desc, active-to-desc, desc',\r\n style({ transform: 'translateY(-25%)' })\r\n ),\r\n state(\r\n 'hint-to-asc, active-to-asc, asc',\r\n style({ transform: 'translateY(25%)' })\r\n ),\r\n ]),\r\n\r\n /** Necessary trigger that calls animate on children animations. */\r\n allowChildren: trigger('allowChildren', [\r\n transition('* <=> *', [query('@*', animateChild(), { optional: true })]),\r\n ]),\r\n}", + "defaultValue": "{\n /** Animation that moves the sort indicator. */\n indicator: trigger('indicator', [\n state('active-asc', style({ transform: 'rotateX(0deg)' })),\n state('active-desc', style({ transform: 'rotateX(180deg)' })),\n transition(\n 'active-asc <=> active-desc',\n animate(SORT_ANIMATION_TRANSITION)\n ),\n ]),\n\n /** Animation that rotates the left pointer of the indicator based on the sorting direction. */\n leftPointer: trigger('leftPointer', [\n state('active-asc, asc', style({ transform: 'rotate(-45deg)' })),\n state('active-desc, desc', style({ transform: 'rotate(45deg)' })),\n transition(\n 'active-asc <=> active-desc',\n animate(SORT_ANIMATION_TRANSITION)\n ),\n ]),\n\n /** Animation that rotates the right pointer of the indicator based on the sorting direction. */\n rightPointer: trigger('rightPointer', [\n state('active-asc, asc', style({ transform: 'rotate(45deg)' })),\n state('active-desc, desc', style({ transform: 'rotate(-45deg)' })),\n transition(\n 'active-asc <=> active-desc',\n animate(SORT_ANIMATION_TRANSITION)\n ),\n ]),\n\n /** Animation that controls the arrow opacity. */\n arrowOpacity: trigger('arrowOpacity', [\n state('desc-to-active, asc-to-active, active', style({ opacity: 1 })),\n state('desc-to-hint, asc-to-hint, hint', style({ opacity: 0.54 })),\n state(\n 'hint-to-desc, active-to-desc, desc, hint-to-asc, active-to-asc, asc, void',\n style({ opacity: 0 })\n ),\n // Transition between all states except for immediate transitions\n transition(\n '* => asc, * => desc, * => active, * => hint, * => void',\n animate('0ms')\n ),\n transition('* <=> *', animate(SORT_ANIMATION_TRANSITION)),\n ]),\n\n /**\n * Animation for the translation of the arrow as a whole. States are separated into two\n * groups: ones with animations and others that are immediate. Immediate states are asc, desc,\n * peek, and active. The other states define a specific animation (source-to-destination)\n * and are determined as a function of their prev user-perceived state and what the next state\n * should be.\n */\n arrowPosition: trigger('arrowPosition', [\n // Hidden Above => Hint Center\n transition(\n '* => desc-to-hint, * => desc-to-active',\n animate(\n SORT_ANIMATION_TRANSITION,\n keyframes([\n style({ transform: 'translateY(-25%)' }),\n style({ transform: 'translateY(0)' }),\n ])\n )\n ),\n // Hint Center => Hidden Below\n transition(\n '* => hint-to-desc, * => active-to-desc',\n animate(\n SORT_ANIMATION_TRANSITION,\n keyframes([\n style({ transform: 'translateY(0)' }),\n style({ transform: 'translateY(25%)' }),\n ])\n )\n ),\n // Hidden Below => Hint Center\n transition(\n '* => asc-to-hint, * => asc-to-active',\n animate(\n SORT_ANIMATION_TRANSITION,\n keyframes([\n style({ transform: 'translateY(25%)' }),\n style({ transform: 'translateY(0)' }),\n ])\n )\n ),\n // Hint Center => Hidden Above\n transition(\n '* => hint-to-asc, * => active-to-asc',\n animate(\n SORT_ANIMATION_TRANSITION,\n keyframes([\n style({ transform: 'translateY(0)' }),\n style({ transform: 'translateY(-25%)' }),\n ])\n )\n ),\n state(\n 'desc-to-hint, asc-to-hint, hint, desc-to-active, asc-to-active, active',\n style({ transform: 'translateY(0)' })\n ),\n state(\n 'hint-to-desc, active-to-desc, desc',\n style({ transform: 'translateY(-25%)' })\n ),\n state(\n 'hint-to-asc, active-to-asc, asc',\n style({ transform: 'translateY(25%)' })\n ),\n ]),\n\n /** Necessary trigger that calls animate on children animations. */\n allowChildren: trigger('allowChildren', [\n transition('* <=> *', [query('@*', animateChild(), { optional: true })]),\n ]),\n}", "rawdescription": "Animations used by OuiSort.", "description": "

Animations used by OuiSort.

\n" }, @@ -43577,7 +54080,7 @@ "deprecated": false, "deprecationMessage": "", "type": "literal type", - "defaultValue": "{\r\n /** Animation that transitions a tooltip in and out. */\r\n tooltipState: trigger('state', [\r\n state(\r\n 'initial, void, hidden',\r\n style({ opacity: 0, transform: 'scale(0)' })\r\n ),\r\n state('visible', style({ transform: 'scale(1)' })),\r\n transition(\r\n '* => visible',\r\n animate(\r\n '200ms cubic-bezier(0, 0, 0.2, 1)',\r\n keyframes([\r\n style({ opacity: 0, transform: 'scale(0)', offset: 0 }),\r\n style({ opacity: 0.5, transform: 'scale(0.99)', offset: 0.5 }),\r\n style({ opacity: 1, transform: 'scale(1)', offset: 1 }),\r\n ])\r\n )\r\n ),\r\n transition(\r\n '* => hidden',\r\n animate('0ms cubic-bezier(0, 0, 0.2, 1)', style({ opacity: 0 }))\r\n ),\r\n ]),\r\n}" + "defaultValue": "{\n /** Animation that transitions a tooltip in and out. */\n tooltipState: trigger('state', [\n state(\n 'initial, void, hidden',\n style({ opacity: 0, transform: 'scale(0)' })\n ),\n state('visible', style({ transform: 'scale(1)' })),\n transition(\n '* => visible',\n animate(\n '200ms cubic-bezier(0, 0, 0.2, 1)',\n keyframes([\n style({ opacity: 0, transform: 'scale(0)', offset: 0 }),\n style({ opacity: 0.5, transform: 'scale(0.99)', offset: 0.5 }),\n style({ opacity: 1, transform: 'scale(1)', offset: 1 }),\n ])\n )\n ),\n transition(\n '* => hidden',\n animate('0ms cubic-bezier(0, 0, 0.2, 1)', style({ opacity: 0 }))\n ),\n ]),\n}" }, { "name": "PANEL_OFFSET_X", @@ -43599,6 +54102,30 @@ "type": "number", "defaultValue": "34" }, + { + "name": "passiveCapturingEventOptions", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/core/ripple/ripple-event-manager.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "normalizePassiveListenerOptions({\r\n passive: true,\r\n capture: true,\r\n})", + "rawdescription": "Options used to bind a passive capturing event.", + "description": "

Options used to bind a passive capturing event.

\n" + }, + { + "name": "passiveCapturingEventOptions", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/core/ripple/ripple-renderer.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "normalizePassiveListenerOptions({\r\n passive: true,\r\n capture: true,\r\n})", + "rawdescription": "Options used to bind a passive capturing event.", + "description": "

Options used to bind a passive capturing event.

\n" + }, { "name": "passiveEventListenerOptions", "ctype": "miscellaneous", @@ -43607,10 +54134,46 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "normalizePassiveListenerOptions({\r\n passive: true,\r\n})", + "defaultValue": "normalizePassiveListenerOptions({\n passive: true,\n})", "rawdescription": "Options for binding a passive event listener.", "description": "

Options for binding a passive event listener.

\n" }, + { + "name": "passiveEventListenerOptions", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/paginated-tab-header.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "normalizePassiveListenerOptions({\r\n passive: true,\r\n}) as EventListenerOptions", + "rawdescription": "Config used to bind passive event listeners", + "description": "

Config used to bind passive event listeners

\n" + }, + { + "name": "pointerDownEvents", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/core/ripple/ripple-renderer.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "[]", + "defaultValue": "['mousedown', 'touchstart']", + "rawdescription": "Events that signal that the pointer is down.", + "description": "

Events that signal that the pointer is down.

\n" + }, + { + "name": "pointerUpEvents", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/core/ripple/ripple-renderer.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "[]", + "defaultValue": "['mouseup', 'mouseleave', 'touchend', 'touchcancel']", + "rawdescription": "Events that signal that the pointer is up.", + "description": "

Events that signal that the pointer is up.

\n" + }, { "name": "Regular", "ctype": "miscellaneous", @@ -43619,7 +54182,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "(props) => ({\r\n moduleMetadata: {\r\n imports: [\r\n OuiDatepickerModule,\r\n OuiFormFieldModule,\r\n OuiInputModule,\r\n BrowserAnimationsModule,\r\n ],\r\n schemas: [],\r\n declarations: [OuiDatepickerStorybook],\r\n },\r\n template: ``,\r\n props: {\r\n ...props,\r\n closed: action('closed'),\r\n monthSelected: action('monthSelected'),\r\n datepickeropened: action('opened'),\r\n yearSelected: action('yearSelected'),\r\n dateChange: action('dateChange'),\r\n },\r\n})" + "defaultValue": "(props) => ({\n moduleMetadata: {\n imports: [\n OuiDatepickerModule,\n OuiFormFieldModule,\n OuiInputModule,\n BrowserAnimationsModule,\n ],\n schemas: [],\n declarations: [OuiDatepickerStorybook],\n },\n template: ``,\n props: {\n ...props,\n closed: action('closed'),\n monthSelected: action('monthSelected'),\n datepickeropened: action('opened'),\n yearSelected: action('yearSelected'),\n dateChange: action('dateChange'),\n },\n})" }, { "name": "require", @@ -43663,7 +54226,7 @@ "deprecationMessage": "", "type": "number", "defaultValue": "0", - "rawdescription": "Distance between the panel edge and the option text in\r\nmulti-selection mode.\r\n\r\nCalculated as:\r\n(SELECT_PANEL_PADDING_X * 1.5) + 20 = 44\r\nThe padding is multiplied by 1.5 because the checkbox's margin is half the padding.\r\nThe checkbox width is 16px.", + "rawdescription": "Distance between the panel edge and the option text in\nmulti-selection mode.\n\nCalculated as:\n(SELECT_PANEL_PADDING_X * 1.5) + 20 = 44\nThe padding is multiplied by 1.5 because the checkbox's margin is half the padding.\nThe checkbox width is 16px.", "description": "

Distance between the panel edge and the option text in\nmulti-selection mode.

\n

Calculated as:\n(SELECT_PANEL_PADDING_X * 1.5) + 20 = 44\nThe padding is multiplied by 1.5 because the checkbox's margin is half the padding.\nThe checkbox width is 16px.

\n" }, { @@ -43675,7 +54238,7 @@ "deprecationMessage": "", "type": "number", "defaultValue": "40", - "rawdescription": "The following style constants are necessary to save here in order\r\nto properly calculate the alignment of the selected option over\r\nthe trigger element.", + "rawdescription": "The following style constants are necessary to save here in order\nto properly calculate the alignment of the selected option over\nthe trigger element.", "description": "

The following style constants are necessary to save here in order\nto properly calculate the alignment of the selected option over\nthe trigger element.

\n" }, { @@ -43723,7 +54286,7 @@ "deprecationMessage": "", "type": "number", "defaultValue": "8", - "rawdescription": "The select panel will only \"fit\" inside the viewport if it is positioned at\r\nthis value or more away from the viewport boundary.", + "rawdescription": "The select panel will only \"fit\" inside the viewport if it is positioned at\nthis value or more away from the viewport boundary.", "description": "

The select panel will only "fit" inside the viewport if it is positioned at\nthis value or more away from the viewport boundary.

\n" }, { @@ -43754,7 +54317,7 @@ "deprecated": false, "deprecationMessage": "", "type": "[]", - "defaultValue": "[\r\n {\r\n letter: 'A',\r\n names: ['Alabama', 'Alaska', 'Arizona', 'Arkansas'],\r\n },\r\n {\r\n letter: 'C',\r\n names: ['California', 'Colorado', 'Connecticut'],\r\n },\r\n {\r\n letter: 'D',\r\n names: ['Delaware'],\r\n },\r\n {\r\n letter: 'F',\r\n names: ['Florida'],\r\n },\r\n]" + "defaultValue": "[\n {\n letter: 'A',\n names: ['Alabama', 'Alaska', 'Arizona', 'Arkansas'],\n },\n {\n letter: 'C',\n names: ['California', 'Colorado', 'Connecticut'],\n },\n {\n letter: 'D',\n names: ['Delaware'],\n },\n {\n letter: 'F',\n names: ['Florida'],\n },\n]" }, { "name": "STORY_ICONS", @@ -43764,7 +54327,7 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n PAYPAL: `\r\n `,\r\n ZAPIER: ``,\r\n GOOGLE_CALENDAR: ``,\r\n SALESFORCE: ``,\r\n SCHEDULEONCE: ``,\r\n CHATONCE: ``,\r\n}" + "defaultValue": "{\n PAYPAL: `\n `,\n ZAPIER: ``,\n GOOGLE_CALENDAR: ``,\n SALESFORCE: ``,\n SCHEDULEONCE: ``,\n CHATONCE: ``,\n}" }, { "name": "SUPPORTS_INTL_API", @@ -43828,7 +54391,7 @@ "deprecated": false, "deprecationMessage": "", "type": "[]", - "defaultValue": "[\r\n 'Users',\r\n 'Role',\r\n 'Integrations',\r\n 'Licenses',\r\n 'Status',\r\n]" + "defaultValue": "[\n 'Users',\n 'Role',\n 'Integrations',\n 'Licenses',\n 'Status',\n]" }, { "name": "USERINFODATASOURCE", @@ -43838,7 +54401,19 @@ "deprecated": false, "deprecationMessage": "", "type": "[]", - "defaultValue": "[\r\n {\r\n name: 'anurag',\r\n email: 'anurag+122333223423523523.scheduleonce@gmail.com',\r\n role: 'member',\r\n image: '',\r\n integration: ['zapier', 'paypal', 'google-calender', 'salesforce'],\r\n licence: ['scheduleonce', 'chatonce'],\r\n status: 'invited',\r\n },\r\n {\r\n name: 'anurag',\r\n email: 'anurag.scheduleonce@gmail.com',\r\n role: 'member',\r\n image: '',\r\n integration: ['zapier', 'paypal', 'google-calender', 'salesforce'],\r\n licence: ['scheduleonce', 'chatonce'],\r\n status: 'invited',\r\n },\r\n {\r\n name: 'anurag',\r\n email: 'anurag.scheduleonce@gmail.com',\r\n role: 'member',\r\n image: '',\r\n integration: ['zapier', 'paypal', 'google-calender', 'salesforce'],\r\n licence: ['scheduleonce', 'chatonce'],\r\n status: 'invited',\r\n },\r\n {\r\n name: 'anurag',\r\n email: 'anurag.scheduleonce@gmail.com',\r\n role: 'member',\r\n image: '',\r\n integration: ['zapier', 'paypal', 'google-calender', 'salesforce'],\r\n licence: ['scheduleonce', 'chatonce'],\r\n status: 'invited',\r\n },\r\n]" + "defaultValue": "[\n {\n name: 'anurag',\n email: 'anurag+122333223423523523.scheduleonce@gmail.com',\n role: 'member',\n image: '',\n integration: ['zapier', 'paypal', 'google-calender', 'salesforce'],\n licence: ['scheduleonce', 'chatonce'],\n status: 'invited',\n },\n {\n name: 'anurag',\n email: 'anurag.scheduleonce@gmail.com',\n role: 'member',\n image: '',\n integration: ['zapier', 'paypal', 'google-calender', 'salesforce'],\n licence: ['scheduleonce', 'chatonce'],\n status: 'invited',\n },\n {\n name: 'anurag',\n email: 'anurag.scheduleonce@gmail.com',\n role: 'member',\n image: '',\n integration: ['zapier', 'paypal', 'google-calender', 'salesforce'],\n licence: ['scheduleonce', 'chatonce'],\n status: 'invited',\n },\n {\n name: 'anurag',\n email: 'anurag.scheduleonce@gmail.com',\n role: 'member',\n image: '',\n integration: ['zapier', 'paypal', 'google-calender', 'salesforce'],\n licence: ['scheduleonce', 'chatonce'],\n status: 'invited',\n },\n]" + }, + { + "name": "VERSION", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/core/version.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "new Version('0.0.0-PLACEHOLDER')", + "rawdescription": "Current version of Angular Material.", + "description": "

Current version of Angular Material.

\n" }, { "name": "yearsPerPage", @@ -43890,8 +54465,8 @@ "jsdoctags": [ { "name": { - "pos": 14264, - "end": 14270, + "pos": 13828, + "end": 13834, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -43903,8 +54478,8 @@ "deprecationMessage": "", "optional": true, "tagName": { - "pos": 14258, - "end": 14263, + "pos": 13822, + "end": 13827, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -43915,8 +54490,8 @@ }, { "name": { - "pos": 14305, - "end": 14319, + "pos": 13868, + "end": 13882, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -43928,8 +54503,8 @@ "deprecationMessage": "", "optional": true, "tagName": { - "pos": 14299, - "end": 14304, + "pos": 13862, + "end": 13867, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -43940,8 +54515,8 @@ }, { "tagName": { - "pos": 14351, - "end": 14358, + "pos": 13913, + "end": 13920, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -43952,6 +54527,90 @@ } ] }, + { + "name": "_checkCdkVersionMatch", + "file": "ui/src/components/core/common-behaviors/common-module.ts", + "ctype": "miscellaneous", + "subtype": "function", + "deprecated": false, + "deprecationMessage": "", + "description": "

Checks whether the Material version matches the CDK version.

\n", + "args": [], + "returnType": "void" + }, + { + "name": "_checkDoctypeIsDefined", + "file": "ui/src/components/core/common-behaviors/common-module.ts", + "ctype": "miscellaneous", + "subtype": "function", + "deprecated": false, + "deprecationMessage": "", + "description": "

Checks that the page has a doctype.

\n", + "args": [ + { + "name": "doc", + "type": "Document", + "deprecated": false, + "deprecationMessage": "" + } + ], + "returnType": "void", + "jsdoctags": [ + { + "name": "doc", + "type": "Document", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + }, + { + "name": "_checkThemeIsPresent", + "file": "ui/src/components/core/common-behaviors/common-module.ts", + "ctype": "miscellaneous", + "subtype": "function", + "deprecated": false, + "deprecationMessage": "", + "description": "

Checks that a theme has been included.

\n", + "args": [ + { + "name": "doc", + "type": "Document", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "isBrowser", + "type": "boolean", + "deprecated": false, + "deprecationMessage": "" + } + ], + "returnType": "void", + "jsdoctags": [ + { + "name": "doc", + "type": "Document", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "isBrowser", + "type": "boolean", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + }, { "name": "_countGroupLabelsBeforeOption", "file": "ui/src/components/core/option/option.ts", @@ -43984,8 +54643,8 @@ "jsdoctags": [ { "name": { - "pos": 8865, - "end": 8876, + "pos": 8577, + "end": 8588, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -43996,8 +54655,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 8859, - "end": 8864, + "pos": 8571, + "end": 8576, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -44008,8 +54667,8 @@ }, { "name": { - "pos": 8936, - "end": 8943, + "pos": 8647, + "end": 8654, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -44020,8 +54679,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 8930, - "end": 8935, + "pos": 8641, + "end": 8646, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -44032,8 +54691,8 @@ }, { "name": { - "pos": 8988, - "end": 9000, + "pos": 8698, + "end": 8710, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -44044,8 +54703,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 8982, - "end": 8987, + "pos": 8692, + "end": 8697, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -44094,8 +54753,8 @@ "jsdoctags": [ { "name": { - "pos": 9738, - "end": 9749, + "pos": 9417, + "end": 9428, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -44106,8 +54765,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 9732, - "end": 9737, + "pos": 9411, + "end": 9416, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -44118,8 +54777,8 @@ }, { "name": { - "pos": 9811, - "end": 9823, + "pos": 9489, + "end": 9501, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -44130,8 +54789,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 9805, - "end": 9810, + "pos": 9483, + "end": 9488, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -44142,8 +54801,8 @@ }, { "name": { - "pos": 9858, - "end": 9879, + "pos": 9535, + "end": 9556, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -44154,8 +54813,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 9852, - "end": 9857, + "pos": 9529, + "end": 9534, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -44166,8 +54825,8 @@ }, { "name": { - "pos": 9929, - "end": 9940, + "pos": 9605, + "end": 9616, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -44178,8 +54837,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 9923, - "end": 9928, + "pos": 9599, + "end": 9604, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -44190,6 +54849,17 @@ } ] }, + { + "name": "_MAT_INK_BAR_POSITIONER_FACTORY", + "file": "ui/src/components/tabs/ink-bar.ts", + "ctype": "miscellaneous", + "subtype": "function", + "deprecated": false, + "deprecationMessage": "", + "description": "

The default positioner function for the MatInkBar.

\n", + "args": [], + "returnType": "_MatInkBarPositioner" + }, { "name": "cloneSvg", "file": "ui/src/components/icon/icon-registery.ts", @@ -44737,6 +55407,64 @@ } ] }, + { + "name": "distanceToFurthestCorner", + "file": "ui/src/components/core/ripple/ripple-renderer.ts", + "ctype": "miscellaneous", + "subtype": "function", + "deprecated": false, + "deprecationMessage": "", + "description": "

Returns the distance from the point (x, y) to the furthest corner of a rectangle.

\n", + "args": [ + { + "name": "x", + "type": "number", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "y", + "type": "number", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "rect", + "type": "ClientRect", + "deprecated": false, + "deprecationMessage": "" + } + ], + "jsdoctags": [ + { + "name": "x", + "type": "number", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "y", + "type": "number", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "rect", + "type": "ClientRect", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + }, { "name": "getClosestDialog", "file": "ui/src/components/dialog/dialog-content.ts", @@ -44761,8 +55489,8 @@ "jsdoctags": [ { "name": { - "pos": 10463, - "end": 10470, + "pos": 10099, + "end": 10106, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -44773,8 +55501,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 10457, - "end": 10462, + "pos": 10093, + "end": 10098, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -44785,8 +55513,8 @@ }, { "name": { - "pos": 10530, - "end": 10541, + "pos": 10165, + "end": 10176, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -44796,8 +55524,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 10524, - "end": 10529, + "pos": 10159, + "end": 10164, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -44908,8 +55636,8 @@ "jsdoctags": [ { "name": { - "pos": 1707, - "end": 1714, + "pos": 1649, + "end": 1656, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -44920,8 +55648,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 1701, - "end": 1706, + "pos": 1643, + "end": 1648, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -44952,8 +55680,8 @@ "jsdoctags": [ { "name": { - "pos": 1278, - "end": 1281, + "pos": 1235, + "end": 1238, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -44964,8 +55692,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 1272, - "end": 1277, + "pos": 1229, + "end": 1234, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -45256,6 +55984,17 @@ } ] }, + { + "name": "MATERIAL_SANITY_CHECKS_FACTORY", + "file": "ui/src/components/core/common-behaviors/common-module.ts", + "ctype": "miscellaneous", + "subtype": "function", + "deprecated": false, + "deprecationMessage": "", + "description": "", + "args": [], + "returnType": "SanityChecks" + }, { "name": "mixinColor", "file": "ui/src/components/core/common-behaviors/color.ts", @@ -45329,6 +56068,62 @@ } ] }, + { + "name": "mixinDisableRipple", + "file": "ui/src/components/core/common-behaviors/disable-ripple.ts", + "ctype": "miscellaneous", + "subtype": "function", + "deprecated": false, + "deprecationMessage": "", + "description": "

Mixin to augment a directive with a disableRipple property.

\n", + "args": [ + { + "name": "base", + "type": "T", + "deprecated": false, + "deprecationMessage": "" + } + ], + "jsdoctags": [ + { + "name": "base", + "type": "T", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + }, + { + "name": "mixinDisableRipple", + "file": "ui/src/components/core/common-behaviors/disable-ripple.ts", + "ctype": "miscellaneous", + "subtype": "function", + "deprecated": false, + "deprecationMessage": "", + "description": "", + "args": [ + { + "name": "base", + "type": "T", + "deprecated": false, + "deprecationMessage": "" + } + ], + "jsdoctags": [ + { + "name": "base", + "type": "T", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + }, { "name": "mixinErrorState", "file": "ui/src/components/core/common-behaviors/error-state.ts", @@ -45385,6 +56180,34 @@ } ] }, + { + "name": "mixinInkBarItem", + "file": "ui/src/components/tabs/ink-bar.ts", + "ctype": "miscellaneous", + "subtype": "function", + "deprecated": false, + "deprecationMessage": "", + "description": "

Mixin that can be used to apply the MatInkBarItem behavior to a class.\nBase on MDC's MDCSlidingTabIndicatorFoundation:\nhttps://github.com/material-components/material-components-web/blob/c0a11ef0d000a098fd0c372be8f12d6a99302855/packages/mdc-tab-indicator/sliding-foundation.ts

\n", + "args": [ + { + "name": "base", + "type": "T", + "deprecated": false, + "deprecationMessage": "" + } + ], + "jsdoctags": [ + { + "name": "base", + "type": "T", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + }, { "name": "mixinProgress", "file": "ui/src/components/button/progress.ts", @@ -45943,6 +56766,17 @@ } ], "typealiases": [ + { + "name": "AbstractConstructor", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "", + "file": "ui/src/components/core/common-behaviors/constructor.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "", + "kind": 179 + }, { "name": "ArrowViewState", "ctype": "miscellaneous", @@ -45976,6 +56810,17 @@ "description": "", "kind": 177 }, + { + "name": "CanDisableRippleCtor", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "", + "file": "ui/src/components/core/common-behaviors/disable-ripple.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "", + "kind": 187 + }, { "name": "CanProgressCtor", "ctype": "miscellaneous", @@ -46031,6 +56876,61 @@ "description": "", "kind": 177 }, + { + "name": "MatCardAppearance", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "\"outlined\" | \"raised\"", + "file": "ui/src/components/card/card.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "", + "kind": 186 + }, + { + "name": "MatPaginatedTabHeaderItem", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "", + "file": "ui/src/components/tabs/paginated-tab-header.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "

Item inside a paginated tab header.

\n", + "kind": 187 + }, + { + "name": "MatTabBodyOriginState", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "\"left\" | \"right\"", + "file": "ui/src/components/tabs/tab-body.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "

The origin state is an internally used state that is set on a new tab body indicating if it\nbegan to the left or right of the prior selected index. For example, if the selected index was\nset to 1, and a new tab is created and selected at index 2, then the tab body would have an\norigin of right because its index was greater than the prior selected index.

\n", + "kind": 186 + }, + { + "name": "MatTabBodyPositionState", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "\"left\" | \"center\" | \"right\" | \"left-origin-center\" | \"right-origin-center\"", + "file": "ui/src/components/tabs/tab-body.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "

These position states are used internally as animation states for the tab body. Setting the\nposition state to left, right, or center will transition the tab body from its current\nposition to its respective state. If there is not current position (void, in the case of a new\ntab body), then there will be no transition animation to its state.

\n

In the case of a new tab body that should immediately be centered with an animating transition,\nthen left-origin-center or right-origin-center can be used, which will use left or right as its\npseudo-prior state.

\n", + "kind": 186 + }, + { + "name": "MatTabHeaderPosition", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "\"above\" | \"below\"", + "file": "ui/src/components/tabs/tab-group.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "

Possible positions for the tab header.

\n", + "kind": 186 + }, { "name": "MenuPositionX", "ctype": "miscellaneous", @@ -46163,6 +57063,39 @@ "description": "", "kind": 194 }, + { + "name": "RippleConfig", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "literal type", + "file": "ui/src/components/core/ripple/ripple-ref.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "", + "kind": 181 + }, + { + "name": "SanityChecks", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "boolean | GranularSanityChecks", + "file": "ui/src/components/core/common-behaviors/common-module.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "

Possible sanity checks that can be enabled. If set to\ntrue/false, all checks will be enabled/disabled.

\n", + "kind": 186 + }, + { + "name": "ScrollDirection", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "\"after\" | \"before\"", + "file": "ui/src/components/tabs/paginated-tab-header.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "

The directions that scrolling can go in when the header's tabs exceed the header width. 'After'\nwill scroll the header towards the end of the tabs list and 'before' will scroll towards the\nbeginning of the list.

\n", + "kind": 186 + }, { "name": "SortDirection", "ctype": "miscellaneous", @@ -46250,6 +57183,37 @@ "description": "", "file": "ui/src/components/core/keycodes/keycodes.ts" }, + { + "name": "RippleState", + "childs": [ + { + "name": "FADING_IN", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "VISIBLE", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "FADING_OUT", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "HIDDEN", + "deprecated": false, + "deprecationMessage": "" + } + ], + "ctype": "miscellaneous", + "subtype": "enum", + "deprecated": false, + "deprecationMessage": "", + "description": "", + "file": "ui/src/components/core/ripple/ripple-ref.ts" + }, { "name": "TransitionCheckState", "childs": [ @@ -46287,7 +57251,139 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "(opt: string[], value: string): string[] => {\r\n const filterValue = value.toLowerCase();\r\n\r\n return opt.filter((item) => item.toLowerCase().indexOf(filterValue) === 0);\r\n}" + "defaultValue": "(opt: string[], value: string): string[] => {\n const filterValue = value.toLowerCase();\n\n return opt.filter((item) => item.toLowerCase().indexOf(filterValue) === 0);\n}" + } + ], + "ui/src/components/tabs/ink-bar.ts": [ + { + "name": "_MAT_INK_BAR_POSITIONER", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/ink-bar.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "new InjectionToken<_MatInkBarPositioner>(\r\n 'MatInkBarPositioner',\r\n {\r\n providedIn: 'root',\r\n factory: _MAT_INK_BAR_POSITIONER_FACTORY,\r\n },\r\n)", + "rawdescription": "Injection token for the MatInkBar's Positioner.", + "description": "

Injection token for the MatInkBar's Positioner.

\n" + }, + { + "name": "ACTIVE_CLASS", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/ink-bar.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "string", + "defaultValue": "'mdc-tab-indicator--active'", + "rawdescription": "Class that is applied when a tab indicator is active.", + "description": "

Class that is applied when a tab indicator is active.

\n" + }, + { + "name": "NO_TRANSITION_CLASS", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/ink-bar.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "string", + "defaultValue": "'mdc-tab-indicator--no-transition'", + "rawdescription": "Class that is applied when the tab indicator should not transition.", + "description": "

Class that is applied when the tab indicator should not transition.

\n" + } + ], + "ui/src/components/tabs/tab-group.ts": [ + { + "name": "_MatTabGroupMixinBase", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/tab-group.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "mixinColor(\r\n mixinDisableRipple(\r\n class {\r\n constructor(public _elementRef: ElementRef) {}\r\n },\r\n ),\r\n 'primary',\r\n)" + }, + { + "name": "nextId", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/tab-group.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "number", + "defaultValue": "0", + "rawdescription": "Used to generate unique ID's for each tab component", + "description": "

Used to generate unique ID's for each tab component

\n" + } + ], + "ui/src/components/tabs/tab-label-wrapper.ts": [ + { + "name": "_MatTabLabelWrapperMixinBase", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/tab-label-wrapper.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "mixinInkBarItem(\r\n mixinDisabled(\r\n class {\r\n elementRef: ElementRef;\r\n },\r\n ),\r\n)" + } + ], + "ui/src/components/tabs/tab-nav-bar/tab-nav-bar.ts": [ + { + "name": "_MatTabLinkMixinBase", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/tab-nav-bar/tab-nav-bar.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "mixinInkBarItem(\r\n mixinTabIndex(\r\n mixinDisableRipple(\r\n mixinDisabled(\r\n class {\r\n elementRef: ElementRef;\r\n },\r\n ),\r\n ),\r\n ),\r\n)" + }, + { + "name": "nextUniqueId", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/tab-nav-bar/tab-nav-bar.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "number", + "defaultValue": "0" + } + ], + "ui/src/components/tabs/tab.ts": [ + { + "name": "_MatTabMixinBase", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/tab.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "mixinColor(OuiTabsBase)" + }, + { + "name": "DEFAULT_COLOR", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/tab.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "string", + "defaultValue": "'primary'", + "rawdescription": "Default color palette for the tab", + "description": "

Default color palette for the tab

\n" + }, + { + "name": "MAT_TAB_GROUP", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/tab.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "new InjectionToken('MAT_TAB_GROUP')", + "rawdescription": "Used to provide a tab group to a tab without causing a circular dependency.", + "description": "

Used to provide a tab group to a tab without causing a circular dependency.

\n" } ], "ui/src/components/datepicker/datepicker.ts": [ @@ -46321,7 +57417,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken<\r\n () => ScrollStrategy\r\n>('oui-datepicker-scroll-strategy')", + "defaultValue": "new InjectionToken<\n () => ScrollStrategy\n>('oui-datepicker-scroll-strategy')", "rawdescription": "Injection token that determines the scroll handling while the calendar is open.", "description": "

Injection token that determines the scroll handling while the calendar is open.

\n" }, @@ -46333,7 +57429,7 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n provide: OUI_DATEPICKER_SCROLL_STRATEGY,\r\n deps: [Overlay],\r\n useFactory: OUI_DATEPICKER_SCROLL_STRATEGY_FACTORY,\r\n}" + "defaultValue": "{\n provide: OUI_DATEPICKER_SCROLL_STRATEGY,\n deps: [Overlay],\n useFactory: OUI_DATEPICKER_SCROLL_STRATEGY_FACTORY,\n}" } ], "ui/src/components/form-field/form-field.ts": [ @@ -46357,8 +57453,8 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken(\r\n 'OUI_FORM_FIELD_DEFAULT_OPTIONS'\r\n )", - "rawdescription": "Injection token that can be used to configure the\r\ndefault options for all form field within an app.", + "defaultValue": "new InjectionToken(\n 'OUI_FORM_FIELD_DEFAULT_OPTIONS'\n )", + "rawdescription": "Injection token that can be used to configure the\ndefault options for all form field within an app.", "description": "

Injection token that can be used to configure the\ndefault options for all form field within an app.

\n" } ], @@ -46404,7 +57500,7 @@ "deprecationMessage": "", "type": "[]", "defaultValue": "['oui-input']", - "rawdescription": "List of classes to add to Button instances based on host attributes to\r\nstyle as different variants.", + "rawdescription": "List of classes to add to Button instances based on host attributes to\nstyle as different variants.", "description": "

List of classes to add to Button instances based on host attributes to\nstyle as different variants.

\n" }, { @@ -46425,7 +57521,7 @@ "deprecated": false, "deprecationMessage": "", "type": "[]", - "defaultValue": "[\r\n 'button',\r\n 'checkbox',\r\n 'file',\r\n 'hidden',\r\n 'image',\r\n 'radio',\r\n 'range',\r\n 'reset',\r\n 'submit',\r\n]" + "defaultValue": "[\n 'button',\n 'checkbox',\n 'file',\n 'hidden',\n 'image',\n 'radio',\n 'range',\n 'reset',\n 'submit',\n]" } ], "ui/src/components/menu/menu-item.ts": [ @@ -46541,7 +57637,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "`\r\n @keyframes oui-progress-spinner-stroke-rotate-DIAMETER {\r\n 0% { stroke-dashoffset: START_VALUE; transform: rotate(0); }\r\n 12.5% { stroke-dashoffset: END_VALUE; transform: rotate(0); }\r\n 12.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(72.5deg); }\r\n 25% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(72.5deg); }\r\n\r\n 25.0001% { stroke-dashoffset: START_VALUE; transform: rotate(270deg); }\r\n 37.5% { stroke-dashoffset: END_VALUE; transform: rotate(270deg); }\r\n 37.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(161.5deg); }\r\n 50% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(161.5deg); }\r\n\r\n 50.0001% { stroke-dashoffset: START_VALUE; transform: rotate(180deg); }\r\n 62.5% { stroke-dashoffset: END_VALUE; transform: rotate(180deg); }\r\n 62.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(251.5deg); }\r\n 75% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(251.5deg); }\r\n\r\n 75.0001% { stroke-dashoffset: START_VALUE; transform: rotate(90deg); }\r\n 87.5% { stroke-dashoffset: END_VALUE; transform: rotate(90deg); }\r\n 87.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(341.5deg); }\r\n 100% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(341.5deg); }\r\n }\r\n`" + "defaultValue": "`\n @keyframes oui-progress-spinner-stroke-rotate-DIAMETER {\n 0% { stroke-dashoffset: START_VALUE; transform: rotate(0); }\n 12.5% { stroke-dashoffset: END_VALUE; transform: rotate(0); }\n 12.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(72.5deg); }\n 25% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(72.5deg); }\n\n 25.0001% { stroke-dashoffset: START_VALUE; transform: rotate(270deg); }\n 37.5% { stroke-dashoffset: END_VALUE; transform: rotate(270deg); }\n 37.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(161.5deg); }\n 50% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(161.5deg); }\n\n 50.0001% { stroke-dashoffset: START_VALUE; transform: rotate(180deg); }\n 62.5% { stroke-dashoffset: END_VALUE; transform: rotate(180deg); }\n 62.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(251.5deg); }\n 75% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(251.5deg); }\n\n 75.0001% { stroke-dashoffset: START_VALUE; transform: rotate(90deg); }\n 87.5% { stroke-dashoffset: END_VALUE; transform: rotate(90deg); }\n 87.5001% { stroke-dashoffset: END_VALUE; transform: rotateX(180deg) rotate(341.5deg); }\n 100% { stroke-dashoffset: START_VALUE; transform: rotateX(180deg) rotate(341.5deg); }\n }\n`" } ], "ui/src/components/select/select.component.ts": [ @@ -46553,7 +57649,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "mixinTabIndex(\r\n mixinDisabled(mixinErrorState(OuiSelectBase))\r\n)" + "defaultValue": "mixinTabIndex(\n mixinDisabled(mixinErrorState(OuiSelectBase))\n)" }, { "name": "nextUniqueId", @@ -46586,7 +57682,7 @@ "deprecationMessage": "", "type": "number", "defaultValue": "0", - "rawdescription": "Distance between the panel edge and the option text in\r\nmulti-selection mode.\r\n\r\nCalculated as:\r\n(SELECT_PANEL_PADDING_X * 1.5) + 20 = 44\r\nThe padding is multiplied by 1.5 because the checkbox's margin is half the padding.\r\nThe checkbox width is 16px.", + "rawdescription": "Distance between the panel edge and the option text in\nmulti-selection mode.\n\nCalculated as:\n(SELECT_PANEL_PADDING_X * 1.5) + 20 = 44\nThe padding is multiplied by 1.5 because the checkbox's margin is half the padding.\nThe checkbox width is 16px.", "description": "

Distance between the panel edge and the option text in\nmulti-selection mode.

\n

Calculated as:\n(SELECT_PANEL_PADDING_X * 1.5) + 20 = 44\nThe padding is multiplied by 1.5 because the checkbox's margin is half the padding.\nThe checkbox width is 16px.

\n" }, { @@ -46598,7 +57694,7 @@ "deprecationMessage": "", "type": "number", "defaultValue": "40", - "rawdescription": "The following style constants are necessary to save here in order\r\nto properly calculate the alignment of the selected option over\r\nthe trigger element.", + "rawdescription": "The following style constants are necessary to save here in order\nto properly calculate the alignment of the selected option over\nthe trigger element.", "description": "

The following style constants are necessary to save here in order\nto properly calculate the alignment of the selected option over\nthe trigger element.

\n" }, { @@ -46646,7 +57742,7 @@ "deprecationMessage": "", "type": "number", "defaultValue": "8", - "rawdescription": "The select panel will only \"fit\" inside the viewport if it is positioned at\r\nthis value or more away from the viewport boundary.", + "rawdescription": "The select panel will only \"fit\" inside the viewport if it is positioned at\nthis value or more away from the viewport boundary.", "description": "

The select panel will only "fit" inside the viewport if it is positioned at\nthis value or more away from the viewport boundary.

\n" } ], @@ -46679,7 +57775,7 @@ "deprecated": false, "deprecationMessage": "", "type": "any", - "defaultValue": "{\r\n provide: NG_VALUE_ACCESSOR,\r\n useExisting: forwardRef(() => OuiSlideToggle),\r\n multi: true,\r\n}", + "defaultValue": "{\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => OuiSlideToggle),\n multi: true,\n}", "rawdescription": "Boilerplate for applying mixins to OuiSlideToggle.", "description": "

Boilerplate for applying mixins to OuiSlideToggle.

\n" } @@ -46718,7 +57814,7 @@ "deprecationMessage": "", "type": "number", "defaultValue": "0", - "rawdescription": "Autocomplete IDs need to be unique across components, so this counter exists outside of\r\nthe component definition.", + "rawdescription": "Autocomplete IDs need to be unique across components, so this counter exists outside of\nthe component definition.", "description": "

Autocomplete IDs need to be unique across components, so this counter exists outside of\nthe component definition.

\n" }, { @@ -46729,7 +57825,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken(\r\n 'oui-autocomplete-default-options',\r\n {\r\n providedIn: 'root',\r\n factory: OUI_AUTOCOMPLETE_DEFAULT_OPTIONS_FACTORY,\r\n }\r\n )", + "defaultValue": "new InjectionToken(\n 'oui-autocomplete-default-options',\n {\n providedIn: 'root',\n factory: OUI_AUTOCOMPLETE_DEFAULT_OPTIONS_FACTORY,\n }\n )", "rawdescription": "Injection token to be used to override the default options for `oui-autocomplete`.", "description": "

Injection token to be used to override the default options for oui-autocomplete.

\n" } @@ -46744,7 +57840,7 @@ "deprecationMessage": "", "type": "number", "defaultValue": "0", - "rawdescription": "Option IDs need to be unique across components, so this counter exists outside of\r\nthe component definition.", + "rawdescription": "Option IDs need to be unique across components, so this counter exists outside of\nthe component definition.", "description": "

Option IDs need to be unique across components, so this counter exists outside of\nthe component definition.

\n" }, { @@ -46789,7 +57885,7 @@ "deprecated": false, "deprecationMessage": "", "type": "[]", - "defaultValue": "[\r\n 'Aruba',\r\n 'Benin',\r\n 'Chad',\r\n 'Chile',\r\n 'China',\r\n 'Congo',\r\n 'Cuba',\r\n 'Egypt',\r\n 'Fiji',\r\n 'Gabon',\r\n 'Ghana',\r\n 'Guam',\r\n 'Haiti',\r\n 'India',\r\n 'Iraq',\r\n 'Italy',\r\n 'Japan',\r\n 'Kenya',\r\n 'Libya',\r\n 'Macao',\r\n 'Mali',\r\n 'Malta',\r\n 'Nauru',\r\n 'Nepal',\r\n 'Niger',\r\n 'Niue',\r\n 'Oman',\r\n 'Palau',\r\n 'Peru',\r\n 'Qatar',\r\n 'Samoa',\r\n 'Spain',\r\n 'Sudan',\r\n 'Togo',\r\n 'Tonga',\r\n 'Yemen',\r\n]" + "defaultValue": "[\n 'Aruba',\n 'Benin',\n 'Chad',\n 'Chile',\n 'China',\n 'Congo',\n 'Cuba',\n 'Egypt',\n 'Fiji',\n 'Gabon',\n 'Ghana',\n 'Guam',\n 'Haiti',\n 'India',\n 'Iraq',\n 'Italy',\n 'Japan',\n 'Kenya',\n 'Libya',\n 'Macao',\n 'Mali',\n 'Malta',\n 'Nauru',\n 'Nepal',\n 'Niger',\n 'Niue',\n 'Oman',\n 'Palau',\n 'Peru',\n 'Qatar',\n 'Samoa',\n 'Spain',\n 'Sudan',\n 'Togo',\n 'Tonga',\n 'Yemen',\n]" }, { "name": "DUMMY_TEXT", @@ -46852,7 +57948,7 @@ "deprecationMessage": "", "type": "number", "defaultValue": "48", - "rawdescription": "The following style constants are necessary to save here in order\r\nto properly calculate the scrollTop of the panel. Because we are not\r\nactually focusing the active item, scroll must be handled manually.", + "rawdescription": "The following style constants are necessary to save here in order\nto properly calculate the scrollTop of the panel. Because we are not\nactually focusing the active item, scroll must be handled manually.", "description": "

The following style constants are necessary to save here in order\nto properly calculate the scrollTop of the panel. Because we are not\nactually focusing the active item, scroll must be handled manually.

\n" }, { @@ -46875,7 +57971,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken<\r\n () => ScrollStrategy\r\n>('oui-autocomplete-scroll-strategy')", + "defaultValue": "new InjectionToken<\n () => ScrollStrategy\n>('oui-autocomplete-scroll-strategy')", "rawdescription": "Injection token that determines the scroll handling while the autocomplete panel is open.", "description": "

Injection token that determines the scroll handling while the autocomplete panel is open.

\n" }, @@ -46887,7 +57983,7 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n provide: OUI_AUTOCOMPLETE_SCROLL_STRATEGY,\r\n deps: [Overlay],\r\n useFactory: OUI_AUTOCOMPLETE_SCROLL_STRATEGY_FACTORY,\r\n}" + "defaultValue": "{\n provide: OUI_AUTOCOMPLETE_SCROLL_STRATEGY,\n deps: [Overlay],\n useFactory: OUI_AUTOCOMPLETE_SCROLL_STRATEGY_FACTORY,\n}" }, { "name": "OUI_AUTOCOMPLETE_VALUE_ACCESSOR", @@ -46897,7 +57993,7 @@ "deprecated": false, "deprecationMessage": "", "type": "any", - "defaultValue": "{\r\n provide: NG_VALUE_ACCESSOR,\r\n useExisting: forwardRef(() => OuiAutocompleteTrigger),\r\n multi: true,\r\n}", + "defaultValue": "{\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => OuiAutocompleteTrigger),\n multi: true,\n}", "rawdescription": "Provider that allows the autocomplete to register as a ControlValueAccessor.", "description": "

Provider that allows the autocomplete to register as a ControlValueAccessor.

\n" } @@ -46911,8 +58007,8 @@ "deprecated": false, "deprecationMessage": "", "type": "[]", - "defaultValue": "[\r\n 'oui-button',\r\n 'oui-ghost-button',\r\n 'oui-link-button',\r\n 'oui-icon-button',\r\n 'oui-icon-text-button',\r\n]", - "rawdescription": "List of classes to add to Button instances based on host attributes to\r\nstyle as different variants.", + "defaultValue": "[\n 'oui-button',\n 'oui-ghost-button',\n 'oui-link-button',\n 'oui-icon-button',\n 'oui-icon-text-button',\n]", + "rawdescription": "List of classes to add to Button instances based on host attributes to\nstyle as different variants.", "description": "

List of classes to add to Button instances based on host attributes to\nstyle as different variants.

\n" }, { @@ -46935,7 +58031,19 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "mixinProgress(\r\n mixinColor(mixinDisabled(OuiButtonBase))\r\n)" + "defaultValue": "mixinProgress(\n mixinColor(mixinDisabled(OuiButtonBase))\n)" + } + ], + "ui/src/components/card/module.ts": [ + { + "name": "CARD_DIRECTIVES", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/card/module.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "[]", + "defaultValue": "[\r\n MatCard,\r\n MatCardActions,\r\n MatCardAvatar,\r\n MatCardContent,\r\n MatCardFooter,\r\n MatCardHeader,\r\n MatCardImage,\r\n MatCardLgImage,\r\n MatCardMdImage,\r\n MatCardSmImage,\r\n MatCardSubtitle,\r\n MatCardTitle,\r\n MatCardTitleGroup,\r\n MatCardXlImage,\r\n]" } ], "ui/src/stories/table/const.ts": [ @@ -46947,7 +58055,7 @@ "deprecated": false, "deprecationMessage": "", "type": "[]", - "defaultValue": "[\r\n 'maroon',\r\n 'red',\r\n 'orange',\r\n 'yellow',\r\n 'olive',\r\n 'green',\r\n 'purple',\r\n 'fuchsia',\r\n 'lime',\r\n 'teal',\r\n 'aqua',\r\n 'blue',\r\n 'navy',\r\n 'black',\r\n 'gray',\r\n]" + "defaultValue": "[\n 'maroon',\n 'red',\n 'orange',\n 'yellow',\n 'olive',\n 'green',\n 'purple',\n 'fuchsia',\n 'lime',\n 'teal',\n 'aqua',\n 'blue',\n 'navy',\n 'black',\n 'gray',\n]" }, { "name": "NAMES", @@ -46957,7 +58065,7 @@ "deprecated": false, "deprecationMessage": "", "type": "[]", - "defaultValue": "[\r\n 'Maia',\r\n 'Asher',\r\n 'Olivia',\r\n 'Atticus',\r\n 'Amelia',\r\n 'Jack',\r\n 'Charlotte',\r\n 'Theodore',\r\n 'Isla',\r\n 'Oliver',\r\n 'Isabella',\r\n 'Jasper',\r\n 'Cora',\r\n 'Levi',\r\n 'Violet',\r\n 'Arthur',\r\n 'Mia',\r\n 'Thomas',\r\n 'Elizabeth',\r\n]" + "defaultValue": "[\n 'Maia',\n 'Asher',\n 'Olivia',\n 'Atticus',\n 'Amelia',\n 'Jack',\n 'Charlotte',\n 'Theodore',\n 'Isla',\n 'Oliver',\n 'Isabella',\n 'Jasper',\n 'Cora',\n 'Levi',\n 'Violet',\n 'Arthur',\n 'Mia',\n 'Thomas',\n 'Elizabeth',\n]" }, { "name": "STORY_ICONS", @@ -46967,7 +58075,7 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n PAYPAL: `\r\n `,\r\n ZAPIER: ``,\r\n GOOGLE_CALENDAR: ``,\r\n SALESFORCE: ``,\r\n SCHEDULEONCE: ``,\r\n CHATONCE: ``,\r\n}" + "defaultValue": "{\n PAYPAL: `\n `,\n ZAPIER: ``,\n GOOGLE_CALENDAR: ``,\n SALESFORCE: ``,\n SCHEDULEONCE: ``,\n CHATONCE: ``,\n}" }, { "name": "USERINFOCOLUMNS", @@ -46977,7 +58085,7 @@ "deprecated": false, "deprecationMessage": "", "type": "[]", - "defaultValue": "[\r\n 'Users',\r\n 'Role',\r\n 'Integrations',\r\n 'Licenses',\r\n 'Status',\r\n]" + "defaultValue": "[\n 'Users',\n 'Role',\n 'Integrations',\n 'Licenses',\n 'Status',\n]" }, { "name": "USERINFODATASOURCE", @@ -46987,7 +58095,7 @@ "deprecated": false, "deprecationMessage": "", "type": "[]", - "defaultValue": "[\r\n {\r\n name: 'anurag',\r\n email: 'anurag+122333223423523523.scheduleonce@gmail.com',\r\n role: 'member',\r\n image: '',\r\n integration: ['zapier', 'paypal', 'google-calender', 'salesforce'],\r\n licence: ['scheduleonce', 'chatonce'],\r\n status: 'invited',\r\n },\r\n {\r\n name: 'anurag',\r\n email: 'anurag.scheduleonce@gmail.com',\r\n role: 'member',\r\n image: '',\r\n integration: ['zapier', 'paypal', 'google-calender', 'salesforce'],\r\n licence: ['scheduleonce', 'chatonce'],\r\n status: 'invited',\r\n },\r\n {\r\n name: 'anurag',\r\n email: 'anurag.scheduleonce@gmail.com',\r\n role: 'member',\r\n image: '',\r\n integration: ['zapier', 'paypal', 'google-calender', 'salesforce'],\r\n licence: ['scheduleonce', 'chatonce'],\r\n status: 'invited',\r\n },\r\n {\r\n name: 'anurag',\r\n email: 'anurag.scheduleonce@gmail.com',\r\n role: 'member',\r\n image: '',\r\n integration: ['zapier', 'paypal', 'google-calender', 'salesforce'],\r\n licence: ['scheduleonce', 'chatonce'],\r\n status: 'invited',\r\n },\r\n]" + "defaultValue": "[\n {\n name: 'anurag',\n email: 'anurag+122333223423523523.scheduleonce@gmail.com',\n role: 'member',\n image: '',\n integration: ['zapier', 'paypal', 'google-calender', 'salesforce'],\n licence: ['scheduleonce', 'chatonce'],\n status: 'invited',\n },\n {\n name: 'anurag',\n email: 'anurag.scheduleonce@gmail.com',\n role: 'member',\n image: '',\n integration: ['zapier', 'paypal', 'google-calender', 'salesforce'],\n licence: ['scheduleonce', 'chatonce'],\n status: 'invited',\n },\n {\n name: 'anurag',\n email: 'anurag.scheduleonce@gmail.com',\n role: 'member',\n image: '',\n integration: ['zapier', 'paypal', 'google-calender', 'salesforce'],\n licence: ['scheduleonce', 'chatonce'],\n status: 'invited',\n },\n {\n name: 'anurag',\n email: 'anurag.scheduleonce@gmail.com',\n role: 'member',\n image: '',\n integration: ['zapier', 'paypal', 'google-calender', 'salesforce'],\n licence: ['scheduleonce', 'chatonce'],\n status: 'invited',\n },\n]" } ], "ui/src/test.ts": [ @@ -47030,7 +58138,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "(props) => ({\r\n moduleMetadata: {\r\n imports: [\r\n OuiDatepickerModule,\r\n OuiFormFieldModule,\r\n OuiInputModule,\r\n BrowserAnimationsModule,\r\n ],\r\n schemas: [],\r\n\r\n declarations: [OuiDatepickerCustomStorybook],\r\n },\r\n template: ``,\r\n props: {\r\n ...props,\r\n closed: action('closed'),\r\n monthSelected: action('monthSelected'),\r\n datepickeropened: action('opened'),\r\n yearSelected: action('yearSelected'),\r\n dateChange: action('dateChange'),\r\n },\r\n})" + "defaultValue": "(props) => ({\n moduleMetadata: {\n imports: [\n OuiDatepickerModule,\n OuiFormFieldModule,\n OuiInputModule,\n BrowserAnimationsModule,\n ],\n schemas: [],\n\n declarations: [OuiDatepickerCustomStorybook],\n },\n template: ``,\n props: {\n ...props,\n closed: action('closed'),\n monthSelected: action('monthSelected'),\n datepickeropened: action('opened'),\n yearSelected: action('yearSelected'),\n dateChange: action('dateChange'),\n },\n})" }, { "name": "Daterange_Picker", @@ -47040,7 +58148,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "(props) => ({\r\n moduleMetadata: {\r\n imports: [\r\n OuiDatepickerModule,\r\n OuiFormFieldModule,\r\n OuiInputModule,\r\n BrowserAnimationsModule,\r\n ],\r\n schemas: [],\r\n declarations: [OuiDaterangepickerStorybook],\r\n },\r\n template: ``,\r\n props: {\r\n ...props,\r\n closed: action('closed'),\r\n monthSelected: action('monthSelected'),\r\n datepickeropened: action('opened'),\r\n yearSelected: action('yearSelected'),\r\n dateChange: action('dateChange'),\r\n },\r\n})" + "defaultValue": "(props) => ({\n moduleMetadata: {\n imports: [\n OuiDatepickerModule,\n OuiFormFieldModule,\n OuiInputModule,\n BrowserAnimationsModule,\n ],\n schemas: [],\n declarations: [OuiDaterangepickerStorybook],\n },\n template: ``,\n props: {\n ...props,\n closed: action('closed'),\n monthSelected: action('monthSelected'),\n datepickeropened: action('opened'),\n yearSelected: action('yearSelected'),\n dateChange: action('dateChange'),\n },\n})" }, { "name": "MAX_DATE", @@ -47060,7 +58168,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "(props) => ({\r\n moduleMetadata: {\r\n imports: [\r\n OuiDatepickerModule,\r\n OuiFormFieldModule,\r\n OuiInputModule,\r\n BrowserAnimationsModule,\r\n ],\r\n schemas: [],\r\n declarations: [OuiDatepickerStorybook],\r\n },\r\n template: ``,\r\n props: {\r\n ...props,\r\n closed: action('closed'),\r\n monthSelected: action('monthSelected'),\r\n datepickeropened: action('opened'),\r\n yearSelected: action('yearSelected'),\r\n dateChange: action('dateChange'),\r\n },\r\n})" + "defaultValue": "(props) => ({\n moduleMetadata: {\n imports: [\n OuiDatepickerModule,\n OuiFormFieldModule,\n OuiInputModule,\n BrowserAnimationsModule,\n ],\n schemas: [],\n declarations: [OuiDatepickerStorybook],\n },\n template: ``,\n props: {\n ...props,\n closed: action('closed'),\n monthSelected: action('monthSelected'),\n datepickeropened: action('opened'),\n yearSelected: action('yearSelected'),\n dateChange: action('dateChange'),\n },\n})" }, { "name": "START_VIEWS", @@ -47092,7 +58200,7 @@ "deprecated": false, "deprecationMessage": "", "type": "any", - "defaultValue": "{\r\n provide: NG_VALIDATORS,\r\n useExisting: forwardRef(() => OuiDatepickerInput),\r\n multi: true,\r\n}" + "defaultValue": "{\n provide: NG_VALIDATORS,\n useExisting: forwardRef(() => OuiDatepickerInput),\n multi: true,\n}" }, { "name": "OUI_DATEPICKER_VALUE_ACCESSOR", @@ -47102,7 +58210,7 @@ "deprecated": false, "deprecationMessage": "", "type": "any", - "defaultValue": "{\r\n provide: NG_VALUE_ACCESSOR,\r\n useExisting: forwardRef(() => OuiDatepickerInput),\r\n multi: true,\r\n}" + "defaultValue": "{\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => OuiDatepickerInput),\n multi: true,\n}" } ], "ui/src/components/datepicker/month-view.ts": [ @@ -47138,7 +58246,7 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n long: [\r\n 'Sunday',\r\n 'Monday',\r\n 'Tuesday',\r\n 'Wednesday',\r\n 'Thursday',\r\n 'Friday',\r\n 'Saturday',\r\n ],\r\n short: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],\r\n narrow: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sat'],\r\n}", + "defaultValue": "{\n long: [\n 'Sunday',\n 'Monday',\n 'Tuesday',\n 'Wednesday',\n 'Thursday',\n 'Friday',\n 'Saturday',\n ],\n short: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],\n narrow: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sat'],\n}", "rawdescription": "The default day of the week names to use if Intl API is not available.", "description": "

The default day of the week names to use if Intl API is not available.

\n" }, @@ -47150,7 +58258,7 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n long: [\r\n 'January',\r\n 'February',\r\n 'March',\r\n 'April',\r\n 'May',\r\n 'June',\r\n 'July',\r\n 'August',\r\n 'September',\r\n 'October',\r\n 'November',\r\n 'December',\r\n ],\r\n short: [\r\n 'Jan',\r\n 'Feb',\r\n 'Mar',\r\n 'Apr',\r\n 'May',\r\n 'Jun',\r\n 'Jul',\r\n 'Aug',\r\n 'Sep',\r\n 'Oct',\r\n 'Nov',\r\n 'Dec',\r\n ],\r\n narrow: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],\r\n}", + "defaultValue": "{\n long: [\n 'January',\n 'February',\n 'March',\n 'April',\n 'May',\n 'June',\n 'July',\n 'August',\n 'September',\n 'October',\n 'November',\n 'December',\n ],\n short: [\n 'Jan',\n 'Feb',\n 'Mar',\n 'Apr',\n 'May',\n 'Jun',\n 'Jul',\n 'Aug',\n 'Sep',\n 'Oct',\n 'Nov',\n 'Dec',\n ],\n narrow: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],\n}", "rawdescription": "The default month names to use if Intl API is not available.", "description": "

The default month names to use if Intl API is not available.

\n" }, @@ -47163,7 +58271,7 @@ "deprecationMessage": "", "type": "", "defaultValue": "/^\\d{4}-\\d{2}-\\d{2}(?:T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|(?:(?:\\+|-)\\d{2}:\\d{2}))?)?$/", - "rawdescription": "Matches strings that have the form of a valid RFC 3339 string\r\n(https://tools.ietf.org/html/rfc3339). Note that the string may not actually be a valid date\r\nbecause the regex will match strings an with out of bounds month, date, etc.", + "rawdescription": "Matches strings that have the form of a valid RFC 3339 string\n(https://tools.ietf.org/html/rfc3339). Note that the string may not actually be a valid date\nbecause the regex will match strings an with out of bounds month, date, etc.", "description": "

Matches strings that have the form of a valid RFC 3339 string\n(https://tools.ietf.org/html/rfc3339). Note that the string may not actually be a valid date\nbecause the regex will match strings an with out of bounds month, date, etc.

\n" }, { @@ -47179,6 +58287,68 @@ "description": "

Whether the browser supports the Intl API.

\n" } ], + "ui/src/components/core/ripple/ripple-renderer.ts": [ + { + "name": "defaultRippleAnimationConfig", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/core/ripple/ripple-renderer.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "object", + "defaultValue": "{\r\n enterDuration: 225,\r\n exitDuration: 150,\r\n}", + "rawdescription": "Default ripple animation configuration for ripples without an explicit\r\nanimation config specified.", + "description": "

Default ripple animation configuration for ripples without an explicit\nanimation config specified.

\n" + }, + { + "name": "ignoreMouseEventsTimeout", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/core/ripple/ripple-renderer.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "number", + "defaultValue": "800", + "rawdescription": "Timeout for ignoring mouse events. Mouse events will be temporary ignored after touch\r\nevents to avoid synthetic mouse events.", + "description": "

Timeout for ignoring mouse events. Mouse events will be temporary ignored after touch\nevents to avoid synthetic mouse events.

\n" + }, + { + "name": "passiveCapturingEventOptions", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/core/ripple/ripple-renderer.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "normalizePassiveListenerOptions({\r\n passive: true,\r\n capture: true,\r\n})", + "rawdescription": "Options used to bind a passive capturing event.", + "description": "

Options used to bind a passive capturing event.

\n" + }, + { + "name": "pointerDownEvents", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/core/ripple/ripple-renderer.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "[]", + "defaultValue": "['mousedown', 'touchstart']", + "rawdescription": "Events that signal that the pointer is down.", + "description": "

Events that signal that the pointer is down.

\n" + }, + { + "name": "pointerUpEvents", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/core/ripple/ripple-renderer.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "[]", + "defaultValue": "['mouseup', 'mouseleave', 'touchend', 'touchcancel']", + "rawdescription": "Events that signal that the pointer is up.", + "description": "

Events that signal that the pointer is up.

\n" + } + ], "ui/src/components/dialog/dialog-content.ts": [ { "name": "dialogElementUid", @@ -47202,7 +58372,7 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n production: true,\r\n}" + "defaultValue": "{\n production: true,\n}" } ], "environments/environment.ts": [ @@ -47214,7 +58384,7 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n production: false,\r\n}" + "defaultValue": "{\n production: false,\n}" } ], "ui/src/components/table/table-module.ts": [ @@ -47226,7 +58396,45 @@ "deprecated": false, "deprecationMessage": "", "type": "[]", - "defaultValue": "[\r\n // Table\r\n OuiTable,\r\n\r\n // Template defs\r\n OuiHeaderCellDef,\r\n OuiHeaderRowDef,\r\n OuiColumnDef,\r\n OuiCellDef,\r\n OuiRowDef,\r\n OuiFooterCellDef,\r\n OuiFooterRowDef,\r\n\r\n // Cell directives\r\n OuiHeaderCell,\r\n OuiCell,\r\n OuiFooterCell,\r\n\r\n // Row directions\r\n OuiHeaderRow,\r\n OuiRow,\r\n OuiFooterRow,\r\n]" + "defaultValue": "[\n // Table\n OuiTable,\n\n // Template defs\n OuiHeaderCellDef,\n OuiHeaderRowDef,\n OuiColumnDef,\n OuiCellDef,\n OuiRowDef,\n OuiFooterCellDef,\n OuiFooterRowDef,\n\n // Cell directives\n OuiHeaderCell,\n OuiCell,\n OuiFooterCell,\n\n // Row directions\n OuiHeaderRow,\n OuiRow,\n OuiFooterRow,\n]" + } + ], + "ui/src/components/tabs/paginated-tab-header.ts": [ + { + "name": "HEADER_SCROLL_DELAY", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/paginated-tab-header.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "number", + "defaultValue": "650", + "rawdescription": "Amount of milliseconds to wait before starting to scroll the header automatically.\r\nSet a little conservatively in order to handle fake events dispatched on touch devices.", + "description": "

Amount of milliseconds to wait before starting to scroll the header automatically.\nSet a little conservatively in order to handle fake events dispatched on touch devices.

\n" + }, + { + "name": "HEADER_SCROLL_INTERVAL", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/paginated-tab-header.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "number", + "defaultValue": "100", + "rawdescription": "Interval in milliseconds at which to scroll the header\r\nwhile the user is holding their pointer.", + "description": "

Interval in milliseconds at which to scroll the header\nwhile the user is holding their pointer.

\n" + }, + { + "name": "passiveEventListenerOptions", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/paginated-tab-header.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "normalizePassiveListenerOptions({\r\n passive: true,\r\n}) as EventListenerOptions", + "rawdescription": "Config used to bind passive event listeners", + "description": "

Config used to bind passive event listeners

\n" } ], "ui/src/components/core/shared/icons.ts": [ @@ -47238,7 +58446,117 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n DOWN_ARROW: `\r\n \r\n\r\n \r\n \r\n \r\n\r\n`,\r\n PANEL_ICON: ``,\r\n VIDEO_ICON: ``,\r\n ARTICLE_ICON: ``,\r\n CLOSE_ICON: ``,\r\n THREE_DOT_MENU_ICON: ``,\r\n ARROW_ICON: ``,\r\n SELECT_ARROW_ICON: ``,\r\n CLOSE_ICON_8X8: ``,\r\n}" + "defaultValue": "{\n DOWN_ARROW: `\n \n\n \n \n \n\n`,\n PANEL_ICON: ``,\n VIDEO_ICON: ``,\n ARTICLE_ICON: ``,\n CLOSE_ICON: ``,\n THREE_DOT_MENU_ICON: ``,\n ARROW_ICON: ``,\n SELECT_ARROW_ICON: ``,\n CLOSE_ICON_8X8: ``,\n}" + } + ], + "ui/src/components/card/card.ts": [ + { + "name": "MAT_CARD_CONFIG", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/card/card.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "new InjectionToken('MAT_CARD_CONFIG')", + "rawdescription": "Injection token that can be used to provide the default options the card module.", + "description": "

Injection token that can be used to provide the default options the card module.

\n" + } + ], + "ui/src/components/core/ripple/ripple.ts": [ + { + "name": "MAT_RIPPLE_GLOBAL_OPTIONS", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/core/ripple/ripple.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "new InjectionToken(\r\n 'mat-ripple-global-options',\r\n)", + "rawdescription": "Injection token that can be used to specify the global ripple options.", + "description": "

Injection token that can be used to specify the global ripple options.

\n" + } + ], + "ui/src/components/tabs/tab-label.ts": [ + { + "name": "MAT_TAB", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/tab-label.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "new InjectionToken('MAT_TAB')", + "rawdescription": "Used to provide a tab label to a tab without causing a circular dependency.", + "description": "

Used to provide a tab label to a tab without causing a circular dependency.

\n" + }, + { + "name": "MAT_TAB_LABEL", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/tab-label.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "new InjectionToken('MatTabLabel')", + "rawdescription": "Injection token that can be used to reference instances of `MatTabLabel`. It serves as\r\nalternative token to the actual `MatTabLabel` class which could cause unnecessary\r\nretention of the class and its directive metadata.", + "description": "

Injection token that can be used to reference instances of MatTabLabel. It serves as\nalternative token to the actual MatTabLabel class which could cause unnecessary\nretention of the class and its directive metadata.

\n" + } + ], + "ui/src/components/tabs/tab-content.ts": [ + { + "name": "MAT_TAB_CONTENT", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/tab-content.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "new InjectionToken('MatTabContent')", + "rawdescription": "Injection token that can be used to reference instances of `MatTabContent`. It serves as\r\nalternative token to the actual `MatTabContent` class which could cause unnecessary\r\nretention of the class and its directive metadata.", + "description": "

Injection token that can be used to reference instances of MatTabContent. It serves as\nalternative token to the actual MatTabContent class which could cause unnecessary\nretention of the class and its directive metadata.

\n" + } + ], + "ui/src/components/tabs/tab-config.ts": [ + { + "name": "MAT_TABS_CONFIG", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/tab-config.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "new InjectionToken('MAT_TABS_CONFIG')", + "rawdescription": "Injection token that can be used to provide the default options the tabs module.", + "description": "

Injection token that can be used to provide the default options the tabs module.

\n" + } + ], + "ui/src/components/core/common-behaviors/common-module.ts": [ + { + "name": "MATERIAL_SANITY_CHECKS", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/core/common-behaviors/common-module.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "new InjectionToken('mat-sanity-checks', {\r\n providedIn: 'root',\r\n factory: MATERIAL_SANITY_CHECKS_FACTORY,\r\n})", + "rawdescription": "Injection token that configures whether the Material sanity checks are enabled.", + "description": "

Injection token that configures whether the Material sanity checks are enabled.

\n" + } + ], + "ui/src/components/tabs/tabs-animations.ts": [ + { + "name": "matTabsAnimations", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/tabs/tabs-animations.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "literal type", + "defaultValue": "{\r\n /** Animation translates a tab along the X axis. */\r\n translateTab: trigger('translateTab', [\r\n // Transitions to `none` instead of 0, because some browsers might blur the content.\r\n state('center, void, left-origin-center, right-origin-center', style({transform: 'none'})),\r\n\r\n // If the tab is either on the left or right, we additionally add a `min-height` of 1px\r\n // in order to ensure that the element has a height before its state changes. This is\r\n // necessary because Chrome does seem to skip the transition in RTL mode if the element does\r\n // not have a static height and is not rendered. See related issue: #9465\r\n state(\r\n 'left',\r\n style({\r\n transform: 'translate3d(-100%, 0, 0)',\r\n minHeight: '1px',\r\n\r\n // Normally this is redundant since we detach the content from the DOM, but if the user\r\n // opted into keeping the content in the DOM, we have to hide it so it isn't focusable.\r\n visibility: 'hidden',\r\n }),\r\n ),\r\n state(\r\n 'right',\r\n style({\r\n transform: 'translate3d(100%, 0, 0)',\r\n minHeight: '1px',\r\n visibility: 'hidden',\r\n }),\r\n ),\r\n\r\n transition(\r\n '* => left, * => right, left => center, right => center',\r\n animate('{{animationDuration}} cubic-bezier(0.35, 0, 0.25, 1)'),\r\n ),\r\n transition('void => left-origin-center', [\r\n style({transform: 'translate3d(-100%, 0, 0)', visibility: 'hidden'}),\r\n animate('{{animationDuration}} cubic-bezier(0.35, 0, 0.25, 1)'),\r\n ]),\r\n transition('void => right-origin-center', [\r\n style({transform: 'translate3d(100%, 0, 0)', visibility: 'hidden'}),\r\n animate('{{animationDuration}} cubic-bezier(0.35, 0, 0.25, 1)'),\r\n ]),\r\n ]),\r\n}", + "rawdescription": "Animations used by the Material tabs.", + "description": "

Animations used by the Material tabs.

\n" } ], "ui/src/components/table/table-data-source.ts": [ @@ -47251,7 +58569,7 @@ "deprecationMessage": "", "type": "number", "defaultValue": "9007199254740991", - "rawdescription": "Corresponds to `Number.MAX_SAFE_INTEGER`. Moved out into a variable here due to\r\nflaky browser support and the value not being defined in Closure's typings.", + "rawdescription": "Corresponds to `Number.MAX_SAFE_INTEGER`. Moved out into a variable here due to\nflaky browser support and the value not being defined in Closure's typings.", "description": "

Corresponds to Number.MAX_SAFE_INTEGER. Moved out into a variable here due to\nflaky browser support and the value not being defined in Closure's typings.

\n" } ], @@ -47288,7 +58606,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken<\r\n () => ScrollStrategy\r\n>('oui-menu-scroll-strategy')", + "defaultValue": "new InjectionToken<\n () => ScrollStrategy\n>('oui-menu-scroll-strategy')", "rawdescription": "Injection token that determines the scroll handling while the menu is open.", "description": "

Injection token that determines the scroll handling while the menu is open.

\n" }, @@ -47300,7 +58618,7 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n provide: OUI_MENU_SCROLL_STRATEGY,\r\n deps: [Overlay],\r\n useFactory: OUI_MENU_SCROLL_STRATEGY_FACTORY,\r\n}" + "defaultValue": "{\n provide: OUI_MENU_SCROLL_STRATEGY,\n deps: [Overlay],\n useFactory: OUI_MENU_SCROLL_STRATEGY_FACTORY,\n}" }, { "name": "passiveEventListenerOptions", @@ -47310,7 +58628,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "normalizePassiveListenerOptions({\r\n passive: true,\r\n})", + "defaultValue": "normalizePassiveListenerOptions({\n passive: true,\n})", "rawdescription": "Options for binding a passive event listener.", "description": "

Options for binding a passive event listener.

\n" } @@ -47368,8 +58686,8 @@ "deprecated": false, "deprecationMessage": "", "type": "any", - "defaultValue": "{\r\n provide: NG_VALUE_ACCESSOR,\r\n useExisting: forwardRef(() => OuiRadioGroup),\r\n multi: true,\r\n}", - "rawdescription": "Provider Expression that allows oui-radio-group to register as a ControlValueAccessor. This\r\nallows it to support [(ngModel)] and ngControl.", + "defaultValue": "{\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => OuiRadioGroup),\n multi: true,\n}", + "rawdescription": "Provider Expression that allows oui-radio-group to register as a ControlValueAccessor. This\nallows it to support [(ngModel)] and ngControl.", "description": "

Provider Expression that allows oui-radio-group to register as a ControlValueAccessor. This\nallows it to support [(ngModel)] and ngControl.

\n" }, { @@ -47402,7 +58720,7 @@ "deprecated": false, "deprecationMessage": "", "type": "[]", - "defaultValue": "[\r\n {\r\n letter: 'A',\r\n names: ['Alabama', 'Alaska', 'Arizona', 'Arkansas'],\r\n },\r\n {\r\n letter: 'C',\r\n names: ['California', 'Colorado', 'Connecticut'],\r\n },\r\n {\r\n letter: 'D',\r\n names: ['Delaware'],\r\n },\r\n {\r\n letter: 'F',\r\n names: ['Florida'],\r\n },\r\n]" + "defaultValue": "[\n {\n letter: 'A',\n names: ['Alabama', 'Alaska', 'Arizona', 'Arkansas'],\n },\n {\n letter: 'C',\n names: ['California', 'Colorado', 'Connecticut'],\n },\n {\n letter: 'D',\n names: ['Delaware'],\n },\n {\n letter: 'F',\n names: ['Florida'],\n },\n]" } ], "ui/src/stories/datepicker/datepicker.component.ts": [ @@ -47414,7 +58732,7 @@ "deprecated": false, "deprecationMessage": "", "type": "OuiDateFormats", - "defaultValue": "{\r\n parse: {\r\n dateInput: null,\r\n },\r\n display: {\r\n dateInput: {\r\n year: 'numeric',\r\n day: '2-digit',\r\n month: '2-digit',\r\n },\r\n monthYearLabel: { year: 'numeric', month: 'short' },\r\n dateA11yLabel: { year: 'numeric', month: 'long', day: 'numeric' },\r\n monthYearA11yLabel: { year: 'numeric', month: 'long' },\r\n },\r\n}" + "defaultValue": "{\n parse: {\n dateInput: null,\n },\n display: {\n dateInput: {\n year: 'numeric',\n day: '2-digit',\n month: '2-digit',\n },\n monthYearLabel: { year: 'numeric', month: 'short' },\n dateA11yLabel: { year: 'numeric', month: 'long', day: 'numeric' },\n monthYearA11yLabel: { year: 'numeric', month: 'long' },\n },\n}" } ], "ui/src/components/datepicker/date-formats.ts": [ @@ -47426,7 +58744,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken(\r\n 'oui-date-formats'\r\n)" + "defaultValue": "new InjectionToken(\n 'oui-date-formats'\n)" } ], "ui/src/components/datepicker/date-adapter.ts": [ @@ -47476,7 +58794,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken(\r\n 'oui-dialog-default-options'\r\n)", + "defaultValue": "new InjectionToken(\n 'oui-dialog-default-options'\n)", "rawdescription": "Injection token that can be used to specify default dialog options.", "description": "

Injection token that can be used to specify default dialog options.

\n" }, @@ -47488,7 +58806,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken<\r\n () => ScrollStrategy\r\n>('oui-dialog-scroll-strategy')", + "defaultValue": "new InjectionToken<\n () => ScrollStrategy\n>('oui-dialog-scroll-strategy')", "rawdescription": "Injection token that determines the scroll handling while the dialog is open.", "description": "

Injection token that determines the scroll handling while the dialog is open.

\n" }, @@ -47500,7 +58818,7 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n provide: OUI_DIALOG_SCROLL_STRATEGY,\r\n deps: [Overlay],\r\n useFactory: OUI_DIALOG_SCROLL_STRATEGY_PROVIDER_FACTORY,\r\n}" + "defaultValue": "{\n provide: OUI_DIALOG_SCROLL_STRATEGY,\n deps: [Overlay],\n useFactory: OUI_DIALOG_SCROLL_STRATEGY_PROVIDER_FACTORY,\n}" } ], "ui/src/components/icon/icon.ts": [ @@ -47512,8 +58830,8 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken(\r\n 'oui-icon-location',\r\n {\r\n providedIn: 'root',\r\n factory: OUI_ICON_LOCATION_FACTORY,\r\n }\r\n)", - "rawdescription": "Injection token used to provide the current location to `OuiIcon`.\r\nUsed to handle server-side rendering and to stub out during unit tests.", + "defaultValue": "new InjectionToken(\n 'oui-icon-location',\n {\n providedIn: 'root',\n factory: OUI_ICON_LOCATION_FACTORY,\n }\n)", + "rawdescription": "Injection token used to provide the current location to `OuiIcon`.\nUsed to handle server-side rendering and to stub out during unit tests.", "description": "

Injection token used to provide the current location to OuiIcon.\nUsed to handle server-side rendering and to stub out during unit tests.

\n" }, { @@ -47536,8 +58854,8 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken<{ value: any }>(\r\n 'OUI_INPUT_VALUE_ACCESSOR'\r\n)", - "rawdescription": "This token is used to inject the object whose value should be set into `OuiInput`. If none is\r\nprovided, the native `HTMLInputElement` is used.", + "defaultValue": "new InjectionToken<{ value: any }>(\n 'OUI_INPUT_VALUE_ACCESSOR'\n)", + "rawdescription": "This token is used to inject the object whose value should be set into `OuiInput`. If none is\nprovided, the native `HTMLInputElement` is used.", "description": "

This token is used to inject the object whose value should be set into OuiInput. If none is\nprovided, the native HTMLInputElement is used.

\n" } ], @@ -47550,7 +58868,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken('oui-menu-default-options', {\r\n providedIn: 'root',\r\n factory: OUI_MENU_DEFAULT_OPTIONS_FACTORY,\r\n })", + "defaultValue": "new InjectionToken('oui-menu-default-options', {\n providedIn: 'root',\n factory: OUI_MENU_DEFAULT_OPTIONS_FACTORY,\n })", "rawdescription": "Injection token to be used to override the default options for `oui-menu`.", "description": "

Injection token to be used to override the default options for oui-menu.

\n" } @@ -47564,7 +58882,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken(\r\n 'OUI_MENU_PANEL'\r\n)", + "defaultValue": "new InjectionToken(\n 'OUI_MENU_PANEL'\n)", "rawdescription": "Injection token used to provide the parent menu to menu-specific components.", "description": "

Injection token used to provide the parent menu to menu-specific components.

\n" } @@ -47578,7 +58896,7 @@ "deprecated": false, "deprecationMessage": "", "type": "OuiDateFormats", - "defaultValue": "{\r\n parse: {\r\n dateInput: null,\r\n },\r\n display: {\r\n dateInput: { year: 'numeric', month: 'short', day: 'numeric' },\r\n monthYearLabel: { year: 'numeric', month: 'short' },\r\n dateA11yLabel: { year: 'numeric', month: 'long', day: 'numeric' },\r\n monthYearA11yLabel: { year: 'numeric', month: 'long' },\r\n },\r\n}" + "defaultValue": "{\n parse: {\n dateInput: null,\n },\n display: {\n dateInput: { year: 'numeric', month: 'short', day: 'numeric' },\n monthYearLabel: { year: 'numeric', month: 'short' },\n dateA11yLabel: { year: 'numeric', month: 'long', day: 'numeric' },\n monthYearA11yLabel: { year: 'numeric', month: 'long' },\n },\n}" } ], "ui/src/components/paginator/paginator-intl.ts": [ @@ -47590,7 +58908,7 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n // If there is already an OuiPaginatorIntl available, use that. Otherwise, provide a new one.\r\n provide: OuiPaginatorIntl,\r\n deps: [[new Optional(), new SkipSelf(), OuiPaginatorIntl]],\r\n useFactory: OUI_PAGINATOR_INTL_PROVIDER_FACTORY,\r\n}" + "defaultValue": "{\n // If there is already an OuiPaginatorIntl available, use that. Otherwise, provide a new one.\n provide: OuiPaginatorIntl,\n deps: [[new Optional(), new SkipSelf(), OuiPaginatorIntl]],\n useFactory: OUI_PAGINATOR_INTL_PROVIDER_FACTORY,\n}" } ], "ui/src/components/panel/panel.ts": [ @@ -47602,7 +58920,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken('oui-panel-default-options', {\r\n providedIn: 'root',\r\n factory: OUI_PANEL_DEFAULT_OPTIONS_FACTORY,\r\n })", + "defaultValue": "new InjectionToken('oui-panel-default-options', {\n providedIn: 'root',\n factory: OUI_PANEL_DEFAULT_OPTIONS_FACTORY,\n })", "rawdescription": "Injection token to be used to override the default options for `oui-menu`.", "description": "

Injection token to be used to override the default options for oui-menu.

\n" } @@ -47616,7 +58934,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken(\r\n 'OUI_PANEL_OVERLAY'\r\n)", + "defaultValue": "new InjectionToken(\n 'OUI_PANEL_OVERLAY'\n)", "rawdescription": "Injection token used to provide the parent menu to menu-specific components.", "description": "

Injection token used to provide the parent menu to menu-specific components.

\n" } @@ -47630,7 +58948,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken<\r\n () => ScrollStrategy\r\n>('oui-panel-scroll-strategy')", + "defaultValue": "new InjectionToken<\n () => ScrollStrategy\n>('oui-panel-scroll-strategy')", "rawdescription": "Injection token that determines the scroll handling while the panel-overlay is open.", "description": "

Injection token that determines the scroll handling while the panel-overlay is open.

\n" }, @@ -47642,7 +58960,7 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n provide: OUI_PANEL_SCROLL_STRATEGY,\r\n deps: [Overlay],\r\n useFactory: OUI_PANEL_SCROLL_STRATEGY_FACTORY,\r\n}" + "defaultValue": "{\n provide: OUI_PANEL_SCROLL_STRATEGY,\n deps: [Overlay],\n useFactory: OUI_PANEL_SCROLL_STRATEGY_FACTORY,\n}" } ], "ui/src/components/sort/sort-header-intl.ts": [ @@ -47654,7 +58972,7 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n // If there is already an OuiSortHeaderIntl available, use that. Otherwise, provide a new one.\r\n provide: OuiSortHeaderIntl,\r\n deps: [[new Optional(), new SkipSelf(), OuiSortHeaderIntl]],\r\n useFactory: OUI_SORT_HEADER_INTL_PROVIDER_FACTORY,\r\n}" + "defaultValue": "{\n // If there is already an OuiSortHeaderIntl available, use that. Otherwise, provide a new one.\n provide: OuiSortHeaderIntl,\n deps: [[new Optional(), new SkipSelf(), OuiSortHeaderIntl]],\n useFactory: OUI_SORT_HEADER_INTL_PROVIDER_FACTORY,\n}" } ], "ui/src/components/tooltip/tooltip.ts": [ @@ -47666,7 +58984,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken('oui-tooltip-default-options', {\r\n providedIn: 'root',\r\n factory: OUI_TOOLTIP_DEFAULT_OPTIONS_FACTORY,\r\n })", + "defaultValue": "new InjectionToken('oui-tooltip-default-options', {\n providedIn: 'root',\n factory: OUI_TOOLTIP_DEFAULT_OPTIONS_FACTORY,\n })", "rawdescription": "Injection token to be used to override the default options for `ouiTooltip`.", "description": "

Injection token to be used to override the default options for ouiTooltip.

\n" }, @@ -47678,7 +58996,7 @@ "deprecated": false, "deprecationMessage": "", "type": "", - "defaultValue": "new InjectionToken<\r\n () => ScrollStrategy\r\n>('oui-tooltip-scroll-strategy')", + "defaultValue": "new InjectionToken<\n () => ScrollStrategy\n>('oui-tooltip-scroll-strategy')", "rawdescription": "Injection token that determines the scroll handling while a tooltip is visible.", "description": "

Injection token that determines the scroll handling while a tooltip is visible.

\n" }, @@ -47690,7 +59008,7 @@ "deprecated": false, "deprecationMessage": "", "type": "object", - "defaultValue": "{\r\n provide: OUI_TOOLTIP_SCROLL_STRATEGY,\r\n deps: [Overlay],\r\n useFactory: OUI_TOOLTIP_SCROLL_STRATEGY_FACTORY,\r\n}" + "defaultValue": "{\n provide: OUI_TOOLTIP_SCROLL_STRATEGY,\n deps: [Overlay],\n useFactory: OUI_TOOLTIP_SCROLL_STRATEGY_FACTORY,\n}" }, { "name": "SCROLL_THROTTLE_MS", @@ -47726,7 +59044,7 @@ "deprecated": false, "deprecationMessage": "", "type": "literal type", - "defaultValue": "{\r\n /** Transforms the height of the datepicker's calendar. */\r\n transformPanel: trigger('transformPanel', [\r\n state(\r\n 'void',\r\n style({\r\n opacity: 0,\r\n transform: 'scale(1, 0.8)',\r\n })\r\n ),\r\n transition(\r\n 'void => enter',\r\n animate(\r\n '120ms cubic-bezier(0, 0, 0.2, 1)',\r\n style({\r\n opacity: 1,\r\n transform: 'scale(1, 1)',\r\n })\r\n )\r\n ),\r\n transition('* => void', animate('100ms linear', style({ opacity: 0 }))),\r\n ]),\r\n\r\n /** Fades in the content of the calendar. */\r\n fadeInCalendar: trigger('fadeInCalendar', [\r\n state('void', style({ opacity: 0 })),\r\n state('enter', style({ opacity: 1 })),\r\n\r\n // TODO(crisbeto): this animation should be removed since it isn't quite on spec, but we\r\n // need to keep it until #12440 gets in, otherwise the exit animation will look glitchy.\r\n transition(\r\n 'void => *',\r\n animate('120ms 100ms cubic-bezier(0.55, 0, 0.55, 0.2)')\r\n ),\r\n ]),\r\n}", + "defaultValue": "{\n /** Transforms the height of the datepicker's calendar. */\n transformPanel: trigger('transformPanel', [\n state(\n 'void',\n style({\n opacity: 0,\n transform: 'scale(1, 0.8)',\n })\n ),\n transition(\n 'void => enter',\n animate(\n '120ms cubic-bezier(0, 0, 0.2, 1)',\n style({\n opacity: 1,\n transform: 'scale(1, 1)',\n })\n )\n ),\n transition('* => void', animate('100ms linear', style({ opacity: 0 }))),\n ]),\n\n /** Fades in the content of the calendar. */\n fadeInCalendar: trigger('fadeInCalendar', [\n state('void', style({ opacity: 0 })),\n state('enter', style({ opacity: 1 })),\n\n // TODO(crisbeto): this animation should be removed since it isn't quite on spec, but we\n // need to keep it until #12440 gets in, otherwise the exit animation will look glitchy.\n transition(\n 'void => *',\n animate('120ms 100ms cubic-bezier(0.55, 0, 0.55, 0.2)')\n ),\n ]),\n}", "rawdescription": "Animations used by the datepicker.", "description": "

Animations used by the datepicker.

\n" } @@ -47740,7 +59058,7 @@ "deprecated": false, "deprecationMessage": "", "type": "literal type", - "defaultValue": "{\r\n /** Animation that moves the sort indicator. */\r\n indicator: trigger('indicator', [\r\n state('active-asc', style({ transform: 'rotateX(0deg)' })),\r\n state('active-desc', style({ transform: 'rotateX(180deg)' })),\r\n transition(\r\n 'active-asc <=> active-desc',\r\n animate(SORT_ANIMATION_TRANSITION)\r\n ),\r\n ]),\r\n\r\n /** Animation that rotates the left pointer of the indicator based on the sorting direction. */\r\n leftPointer: trigger('leftPointer', [\r\n state('active-asc, asc', style({ transform: 'rotate(-45deg)' })),\r\n state('active-desc, desc', style({ transform: 'rotate(45deg)' })),\r\n transition(\r\n 'active-asc <=> active-desc',\r\n animate(SORT_ANIMATION_TRANSITION)\r\n ),\r\n ]),\r\n\r\n /** Animation that rotates the right pointer of the indicator based on the sorting direction. */\r\n rightPointer: trigger('rightPointer', [\r\n state('active-asc, asc', style({ transform: 'rotate(45deg)' })),\r\n state('active-desc, desc', style({ transform: 'rotate(-45deg)' })),\r\n transition(\r\n 'active-asc <=> active-desc',\r\n animate(SORT_ANIMATION_TRANSITION)\r\n ),\r\n ]),\r\n\r\n /** Animation that controls the arrow opacity. */\r\n arrowOpacity: trigger('arrowOpacity', [\r\n state('desc-to-active, asc-to-active, active', style({ opacity: 1 })),\r\n state('desc-to-hint, asc-to-hint, hint', style({ opacity: 0.54 })),\r\n state(\r\n 'hint-to-desc, active-to-desc, desc, hint-to-asc, active-to-asc, asc, void',\r\n style({ opacity: 0 })\r\n ),\r\n // Transition between all states except for immediate transitions\r\n transition(\r\n '* => asc, * => desc, * => active, * => hint, * => void',\r\n animate('0ms')\r\n ),\r\n transition('* <=> *', animate(SORT_ANIMATION_TRANSITION)),\r\n ]),\r\n\r\n /**\r\n * Animation for the translation of the arrow as a whole. States are separated into two\r\n * groups: ones with animations and others that are immediate. Immediate states are asc, desc,\r\n * peek, and active. The other states define a specific animation (source-to-destination)\r\n * and are determined as a function of their prev user-perceived state and what the next state\r\n * should be.\r\n */\r\n arrowPosition: trigger('arrowPosition', [\r\n // Hidden Above => Hint Center\r\n transition(\r\n '* => desc-to-hint, * => desc-to-active',\r\n animate(\r\n SORT_ANIMATION_TRANSITION,\r\n keyframes([\r\n style({ transform: 'translateY(-25%)' }),\r\n style({ transform: 'translateY(0)' }),\r\n ])\r\n )\r\n ),\r\n // Hint Center => Hidden Below\r\n transition(\r\n '* => hint-to-desc, * => active-to-desc',\r\n animate(\r\n SORT_ANIMATION_TRANSITION,\r\n keyframes([\r\n style({ transform: 'translateY(0)' }),\r\n style({ transform: 'translateY(25%)' }),\r\n ])\r\n )\r\n ),\r\n // Hidden Below => Hint Center\r\n transition(\r\n '* => asc-to-hint, * => asc-to-active',\r\n animate(\r\n SORT_ANIMATION_TRANSITION,\r\n keyframes([\r\n style({ transform: 'translateY(25%)' }),\r\n style({ transform: 'translateY(0)' }),\r\n ])\r\n )\r\n ),\r\n // Hint Center => Hidden Above\r\n transition(\r\n '* => hint-to-asc, * => active-to-asc',\r\n animate(\r\n SORT_ANIMATION_TRANSITION,\r\n keyframes([\r\n style({ transform: 'translateY(0)' }),\r\n style({ transform: 'translateY(-25%)' }),\r\n ])\r\n )\r\n ),\r\n state(\r\n 'desc-to-hint, asc-to-hint, hint, desc-to-active, asc-to-active, active',\r\n style({ transform: 'translateY(0)' })\r\n ),\r\n state(\r\n 'hint-to-desc, active-to-desc, desc',\r\n style({ transform: 'translateY(-25%)' })\r\n ),\r\n state(\r\n 'hint-to-asc, active-to-asc, asc',\r\n style({ transform: 'translateY(25%)' })\r\n ),\r\n ]),\r\n\r\n /** Necessary trigger that calls animate on children animations. */\r\n allowChildren: trigger('allowChildren', [\r\n transition('* <=> *', [query('@*', animateChild(), { optional: true })]),\r\n ]),\r\n}", + "defaultValue": "{\n /** Animation that moves the sort indicator. */\n indicator: trigger('indicator', [\n state('active-asc', style({ transform: 'rotateX(0deg)' })),\n state('active-desc', style({ transform: 'rotateX(180deg)' })),\n transition(\n 'active-asc <=> active-desc',\n animate(SORT_ANIMATION_TRANSITION)\n ),\n ]),\n\n /** Animation that rotates the left pointer of the indicator based on the sorting direction. */\n leftPointer: trigger('leftPointer', [\n state('active-asc, asc', style({ transform: 'rotate(-45deg)' })),\n state('active-desc, desc', style({ transform: 'rotate(45deg)' })),\n transition(\n 'active-asc <=> active-desc',\n animate(SORT_ANIMATION_TRANSITION)\n ),\n ]),\n\n /** Animation that rotates the right pointer of the indicator based on the sorting direction. */\n rightPointer: trigger('rightPointer', [\n state('active-asc, asc', style({ transform: 'rotate(45deg)' })),\n state('active-desc, desc', style({ transform: 'rotate(-45deg)' })),\n transition(\n 'active-asc <=> active-desc',\n animate(SORT_ANIMATION_TRANSITION)\n ),\n ]),\n\n /** Animation that controls the arrow opacity. */\n arrowOpacity: trigger('arrowOpacity', [\n state('desc-to-active, asc-to-active, active', style({ opacity: 1 })),\n state('desc-to-hint, asc-to-hint, hint', style({ opacity: 0.54 })),\n state(\n 'hint-to-desc, active-to-desc, desc, hint-to-asc, active-to-asc, asc, void',\n style({ opacity: 0 })\n ),\n // Transition between all states except for immediate transitions\n transition(\n '* => asc, * => desc, * => active, * => hint, * => void',\n animate('0ms')\n ),\n transition('* <=> *', animate(SORT_ANIMATION_TRANSITION)),\n ]),\n\n /**\n * Animation for the translation of the arrow as a whole. States are separated into two\n * groups: ones with animations and others that are immediate. Immediate states are asc, desc,\n * peek, and active. The other states define a specific animation (source-to-destination)\n * and are determined as a function of their prev user-perceived state and what the next state\n * should be.\n */\n arrowPosition: trigger('arrowPosition', [\n // Hidden Above => Hint Center\n transition(\n '* => desc-to-hint, * => desc-to-active',\n animate(\n SORT_ANIMATION_TRANSITION,\n keyframes([\n style({ transform: 'translateY(-25%)' }),\n style({ transform: 'translateY(0)' }),\n ])\n )\n ),\n // Hint Center => Hidden Below\n transition(\n '* => hint-to-desc, * => active-to-desc',\n animate(\n SORT_ANIMATION_TRANSITION,\n keyframes([\n style({ transform: 'translateY(0)' }),\n style({ transform: 'translateY(25%)' }),\n ])\n )\n ),\n // Hidden Below => Hint Center\n transition(\n '* => asc-to-hint, * => asc-to-active',\n animate(\n SORT_ANIMATION_TRANSITION,\n keyframes([\n style({ transform: 'translateY(25%)' }),\n style({ transform: 'translateY(0)' }),\n ])\n )\n ),\n // Hint Center => Hidden Above\n transition(\n '* => hint-to-asc, * => active-to-asc',\n animate(\n SORT_ANIMATION_TRANSITION,\n keyframes([\n style({ transform: 'translateY(0)' }),\n style({ transform: 'translateY(-25%)' }),\n ])\n )\n ),\n state(\n 'desc-to-hint, asc-to-hint, hint, desc-to-active, asc-to-active, active',\n style({ transform: 'translateY(0)' })\n ),\n state(\n 'hint-to-desc, active-to-desc, desc',\n style({ transform: 'translateY(-25%)' })\n ),\n state(\n 'hint-to-asc, active-to-asc, asc',\n style({ transform: 'translateY(25%)' })\n ),\n ]),\n\n /** Necessary trigger that calls animate on children animations. */\n allowChildren: trigger('allowChildren', [\n transition('* <=> *', [query('@*', animateChild(), { optional: true })]),\n ]),\n}", "rawdescription": "Animations used by OuiSort.", "description": "

Animations used by OuiSort.

\n" }, @@ -47764,7 +59082,7 @@ "deprecated": false, "deprecationMessage": "", "type": "literal type", - "defaultValue": "{\r\n /** Animation that transitions a tooltip in and out. */\r\n tooltipState: trigger('state', [\r\n state(\r\n 'initial, void, hidden',\r\n style({ opacity: 0, transform: 'scale(0)' })\r\n ),\r\n state('visible', style({ transform: 'scale(1)' })),\r\n transition(\r\n '* => visible',\r\n animate(\r\n '200ms cubic-bezier(0, 0, 0.2, 1)',\r\n keyframes([\r\n style({ opacity: 0, transform: 'scale(0)', offset: 0 }),\r\n style({ opacity: 0.5, transform: 'scale(0.99)', offset: 0.5 }),\r\n style({ opacity: 1, transform: 'scale(1)', offset: 1 }),\r\n ])\r\n )\r\n ),\r\n transition(\r\n '* => hidden',\r\n animate('0ms cubic-bezier(0, 0, 0.2, 1)', style({ opacity: 0 }))\r\n ),\r\n ]),\r\n}" + "defaultValue": "{\n /** Animation that transitions a tooltip in and out. */\n tooltipState: trigger('state', [\n state(\n 'initial, void, hidden',\n style({ opacity: 0, transform: 'scale(0)' })\n ),\n state('visible', style({ transform: 'scale(1)' })),\n transition(\n '* => visible',\n animate(\n '200ms cubic-bezier(0, 0, 0.2, 1)',\n keyframes([\n style({ opacity: 0, transform: 'scale(0)', offset: 0 }),\n style({ opacity: 0.5, transform: 'scale(0.99)', offset: 0.5 }),\n style({ opacity: 1, transform: 'scale(1)', offset: 1 }),\n ])\n )\n ),\n transition(\n '* => hidden',\n animate('0ms cubic-bezier(0, 0, 0.2, 1)', style({ opacity: 0 }))\n ),\n ]),\n}" } ], "ui/src/components/panel/panel-positions.ts": [ @@ -47789,6 +59107,20 @@ "defaultValue": "34" } ], + "ui/src/components/core/ripple/ripple-event-manager.ts": [ + { + "name": "passiveCapturingEventOptions", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/core/ripple/ripple-event-manager.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "normalizePassiveListenerOptions({\r\n passive: true,\r\n capture: true,\r\n})", + "rawdescription": "Options used to bind a passive capturing event.", + "description": "

Options used to bind a passive capturing event.

\n" + } + ], "ui/src/components/dialog/dialog-ref.ts": [ { "name": "uniqueId", @@ -47801,6 +59133,20 @@ "defaultValue": "0" } ], + "ui/src/components/core/version.ts": [ + { + "name": "VERSION", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "ui/src/components/core/version.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "", + "defaultValue": "new Version('0.0.0-PLACEHOLDER')", + "rawdescription": "Current version of Angular Material.", + "description": "

Current version of Angular Material.

\n" + } + ], "ui/src/components/datepicker/multi-year-view.ts": [ { "name": "yearsPerPage", @@ -47854,8 +59200,8 @@ "jsdoctags": [ { "name": { - "pos": 14264, - "end": 14270, + "pos": 13828, + "end": 13834, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -47867,8 +59213,8 @@ "deprecationMessage": "", "optional": true, "tagName": { - "pos": 14258, - "end": 14263, + "pos": 13822, + "end": 13827, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -47879,8 +59225,8 @@ }, { "name": { - "pos": 14305, - "end": 14319, + "pos": 13868, + "end": 13882, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -47892,8 +59238,8 @@ "deprecationMessage": "", "optional": true, "tagName": { - "pos": 14299, - "end": 14304, + "pos": 13862, + "end": 13867, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -47904,8 +59250,8 @@ }, { "tagName": { - "pos": 14351, - "end": 14358, + "pos": 13913, + "end": 13920, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -47975,6 +59321,103 @@ ] } ], + "ui/src/components/core/common-behaviors/common-module.ts": [ + { + "name": "_checkCdkVersionMatch", + "file": "ui/src/components/core/common-behaviors/common-module.ts", + "ctype": "miscellaneous", + "subtype": "function", + "deprecated": false, + "deprecationMessage": "", + "description": "

Checks whether the Material version matches the CDK version.

\n", + "args": [], + "returnType": "void" + }, + { + "name": "_checkDoctypeIsDefined", + "file": "ui/src/components/core/common-behaviors/common-module.ts", + "ctype": "miscellaneous", + "subtype": "function", + "deprecated": false, + "deprecationMessage": "", + "description": "

Checks that the page has a doctype.

\n", + "args": [ + { + "name": "doc", + "type": "Document", + "deprecated": false, + "deprecationMessage": "" + } + ], + "returnType": "void", + "jsdoctags": [ + { + "name": "doc", + "type": "Document", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + }, + { + "name": "_checkThemeIsPresent", + "file": "ui/src/components/core/common-behaviors/common-module.ts", + "ctype": "miscellaneous", + "subtype": "function", + "deprecated": false, + "deprecationMessage": "", + "description": "

Checks that a theme has been included.

\n", + "args": [ + { + "name": "doc", + "type": "Document", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "isBrowser", + "type": "boolean", + "deprecated": false, + "deprecationMessage": "" + } + ], + "returnType": "void", + "jsdoctags": [ + { + "name": "doc", + "type": "Document", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "isBrowser", + "type": "boolean", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + }, + { + "name": "MATERIAL_SANITY_CHECKS_FACTORY", + "file": "ui/src/components/core/common-behaviors/common-module.ts", + "ctype": "miscellaneous", + "subtype": "function", + "deprecated": false, + "deprecationMessage": "", + "description": "", + "args": [], + "returnType": "SanityChecks" + } + ], "ui/src/components/core/option/option.ts": [ { "name": "_countGroupLabelsBeforeOption", @@ -48008,8 +59451,8 @@ "jsdoctags": [ { "name": { - "pos": 8865, - "end": 8876, + "pos": 8577, + "end": 8588, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -48020,8 +59463,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 8859, - "end": 8864, + "pos": 8571, + "end": 8576, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -48032,8 +59475,8 @@ }, { "name": { - "pos": 8936, - "end": 8943, + "pos": 8647, + "end": 8654, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -48044,8 +59487,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 8930, - "end": 8935, + "pos": 8641, + "end": 8646, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -48056,8 +59499,8 @@ }, { "name": { - "pos": 8988, - "end": 9000, + "pos": 8698, + "end": 8710, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -48068,8 +59511,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 8982, - "end": 8987, + "pos": 8692, + "end": 8697, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -48118,8 +59561,8 @@ "jsdoctags": [ { "name": { - "pos": 9738, - "end": 9749, + "pos": 9417, + "end": 9428, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -48130,8 +59573,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 9732, - "end": 9737, + "pos": 9411, + "end": 9416, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -48142,8 +59585,8 @@ }, { "name": { - "pos": 9811, - "end": 9823, + "pos": 9489, + "end": 9501, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -48154,8 +59597,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 9805, - "end": 9810, + "pos": 9483, + "end": 9488, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -48166,8 +59609,8 @@ }, { "name": { - "pos": 9858, - "end": 9879, + "pos": 9535, + "end": 9556, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -48178,8 +59621,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 9852, - "end": 9857, + "pos": 9529, + "end": 9534, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -48190,8 +59633,8 @@ }, { "name": { - "pos": 9929, - "end": 9940, + "pos": 9605, + "end": 9616, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -48202,8 +59645,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 9923, - "end": 9928, + "pos": 9599, + "end": 9604, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -48215,6 +59658,47 @@ ] } ], + "ui/src/components/tabs/ink-bar.ts": [ + { + "name": "_MAT_INK_BAR_POSITIONER_FACTORY", + "file": "ui/src/components/tabs/ink-bar.ts", + "ctype": "miscellaneous", + "subtype": "function", + "deprecated": false, + "deprecationMessage": "", + "description": "

The default positioner function for the MatInkBar.

\n", + "args": [], + "returnType": "_MatInkBarPositioner" + }, + { + "name": "mixinInkBarItem", + "file": "ui/src/components/tabs/ink-bar.ts", + "ctype": "miscellaneous", + "subtype": "function", + "deprecated": false, + "deprecationMessage": "", + "description": "

Mixin that can be used to apply the MatInkBarItem behavior to a class.\nBase on MDC's MDCSlidingTabIndicatorFoundation:\nhttps://github.com/material-components/material-components-web/blob/c0a11ef0d000a098fd0c372be8f12d6a99302855/packages/mdc-tab-indicator/sliding-foundation.ts

\n", + "args": [ + { + "name": "base", + "type": "T", + "deprecated": false, + "deprecationMessage": "" + } + ], + "jsdoctags": [ + { + "name": "base", + "type": "T", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + } + ], "ui/src/components/icon/icon-registery.ts": [ { "name": "cloneSvg", @@ -48265,8 +59749,8 @@ "jsdoctags": [ { "name": { - "pos": 1707, - "end": 1714, + "pos": 1649, + "end": 1656, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -48277,8 +59761,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 1701, - "end": 1706, + "pos": 1643, + "end": 1648, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -48309,8 +59793,8 @@ "jsdoctags": [ { "name": { - "pos": 1278, - "end": 1281, + "pos": 1235, + "end": 1238, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -48321,8 +59805,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 1272, - "end": 1277, + "pos": 1229, + "end": 1234, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -48995,6 +60479,66 @@ ] } ], + "ui/src/components/core/ripple/ripple-renderer.ts": [ + { + "name": "distanceToFurthestCorner", + "file": "ui/src/components/core/ripple/ripple-renderer.ts", + "ctype": "miscellaneous", + "subtype": "function", + "deprecated": false, + "deprecationMessage": "", + "description": "

Returns the distance from the point (x, y) to the furthest corner of a rectangle.

\n", + "args": [ + { + "name": "x", + "type": "number", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "y", + "type": "number", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "rect", + "type": "ClientRect", + "deprecated": false, + "deprecationMessage": "" + } + ], + "jsdoctags": [ + { + "name": "x", + "type": "number", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "y", + "type": "number", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "rect", + "type": "ClientRect", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + } + ], "ui/src/components/dialog/dialog-content.ts": [ { "name": "getClosestDialog", @@ -49020,8 +60564,8 @@ "jsdoctags": [ { "name": { - "pos": 10463, - "end": 10470, + "pos": 10099, + "end": 10106, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -49032,8 +60576,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 10457, - "end": 10462, + "pos": 10093, + "end": 10098, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -49044,8 +60588,8 @@ }, { "name": { - "pos": 10530, - "end": 10541, + "pos": 10165, + "end": 10176, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -49055,8 +60599,8 @@ "deprecated": false, "deprecationMessage": "", "tagName": { - "pos": 10524, - "end": 10529, + "pos": 10159, + "end": 10164, "flags": 4194304, "modifierFlagsCache": 0, "transformFlags": 0, @@ -49507,6 +61051,64 @@ ] } ], + "ui/src/components/core/common-behaviors/disable-ripple.ts": [ + { + "name": "mixinDisableRipple", + "file": "ui/src/components/core/common-behaviors/disable-ripple.ts", + "ctype": "miscellaneous", + "subtype": "function", + "deprecated": false, + "deprecationMessage": "", + "description": "

Mixin to augment a directive with a disableRipple property.

\n", + "args": [ + { + "name": "base", + "type": "T", + "deprecated": false, + "deprecationMessage": "" + } + ], + "jsdoctags": [ + { + "name": "base", + "type": "T", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + }, + { + "name": "mixinDisableRipple", + "file": "ui/src/components/core/common-behaviors/disable-ripple.ts", + "ctype": "miscellaneous", + "subtype": "function", + "deprecated": false, + "deprecationMessage": "", + "description": "", + "args": [ + { + "name": "base", + "type": "T", + "deprecated": false, + "deprecationMessage": "" + } + ], + "jsdoctags": [ + { + "name": "base", + "type": "T", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + } + ], "ui/src/components/core/common-behaviors/error-state.ts": [ { "name": "mixinErrorState", @@ -50017,6 +61619,39 @@ "file": "ui/src/components/core/keycodes/keycodes.ts" } ], + "ui/src/components/core/ripple/ripple-ref.ts": [ + { + "name": "RippleState", + "childs": [ + { + "name": "FADING_IN", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "VISIBLE", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "FADING_OUT", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "HIDDEN", + "deprecated": false, + "deprecationMessage": "" + } + ], + "ctype": "miscellaneous", + "subtype": "enum", + "deprecated": false, + "deprecationMessage": "", + "description": "", + "file": "ui/src/components/core/ripple/ripple-ref.ts" + } + ], "ui/src/components/checkbox/checkbox.ts": [ { "name": "TransitionCheckState", @@ -50047,6 +61682,30 @@ ] }, "groupedTypeAliases": { + "ui/src/components/core/common-behaviors/constructor.ts": [ + { + "name": "AbstractConstructor", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "", + "file": "ui/src/components/core/common-behaviors/constructor.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "", + "kind": 179 + }, + { + "name": "Constructor", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "", + "file": "ui/src/components/core/common-behaviors/constructor.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "", + "kind": 179 + } + ], "ui/src/components/sort/sort-header.ts": [ { "name": "ArrowViewState", @@ -50097,6 +61756,19 @@ "kind": 177 } ], + "ui/src/components/core/common-behaviors/disable-ripple.ts": [ + { + "name": "CanDisableRippleCtor", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "", + "file": "ui/src/components/core/common-behaviors/disable-ripple.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "", + "kind": 187 + } + ], "ui/src/components/button/progress.ts": [ { "name": "CanProgressCtor", @@ -50123,19 +61795,6 @@ "kind": 177 } ], - "ui/src/components/core/common-behaviors/constructor.ts": [ - { - "name": "Constructor", - "ctype": "miscellaneous", - "subtype": "typealias", - "rawtype": "", - "file": "ui/src/components/core/common-behaviors/constructor.ts", - "deprecated": false, - "deprecationMessage": "", - "description": "", - "kind": 179 - } - ], "ui/src/components/core/common-behaviors/initialized.ts": [ { "name": "HasInitializedCtor", @@ -50162,6 +61821,80 @@ "kind": 177 } ], + "ui/src/components/card/card.ts": [ + { + "name": "MatCardAppearance", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "\"outlined\" | \"raised\"", + "file": "ui/src/components/card/card.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "", + "kind": 186 + } + ], + "ui/src/components/tabs/paginated-tab-header.ts": [ + { + "name": "MatPaginatedTabHeaderItem", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "", + "file": "ui/src/components/tabs/paginated-tab-header.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "

Item inside a paginated tab header.

\n", + "kind": 187 + }, + { + "name": "ScrollDirection", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "\"after\" | \"before\"", + "file": "ui/src/components/tabs/paginated-tab-header.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "

The directions that scrolling can go in when the header's tabs exceed the header width. 'After'\nwill scroll the header towards the end of the tabs list and 'before' will scroll towards the\nbeginning of the list.

\n", + "kind": 186 + } + ], + "ui/src/components/tabs/tab-body.ts": [ + { + "name": "MatTabBodyOriginState", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "\"left\" | \"right\"", + "file": "ui/src/components/tabs/tab-body.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "

The origin state is an internally used state that is set on a new tab body indicating if it\nbegan to the left or right of the prior selected index. For example, if the selected index was\nset to 1, and a new tab is created and selected at index 2, then the tab body would have an\norigin of right because its index was greater than the prior selected index.

\n", + "kind": 186 + }, + { + "name": "MatTabBodyPositionState", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "\"left\" | \"center\" | \"right\" | \"left-origin-center\" | \"right-origin-center\"", + "file": "ui/src/components/tabs/tab-body.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "

These position states are used internally as animation states for the tab body. Setting the\nposition state to left, right, or center will transition the tab body from its current\nposition to its respective state. If there is not current position (void, in the case of a new\ntab body), then there will be no transition animation to its state.

\n

In the case of a new tab body that should immediately be centered with an animating transition,\nthen left-origin-center or right-origin-center can be used, which will use left or right as its\npseudo-prior state.

\n", + "kind": 186 + } + ], + "ui/src/components/tabs/tab-group.ts": [ + { + "name": "MatTabHeaderPosition", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "\"above\" | \"below\"", + "file": "ui/src/components/tabs/tab-group.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "

Possible positions for the tab header.

\n", + "kind": 186 + } + ], "ui/src/components/menu/menu-positions.ts": [ { "name": "MenuPositionX", @@ -50314,6 +62047,32 @@ "kind": 194 } ], + "ui/src/components/core/ripple/ripple-ref.ts": [ + { + "name": "RippleConfig", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "literal type", + "file": "ui/src/components/core/ripple/ripple-ref.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "", + "kind": 181 + } + ], + "ui/src/components/core/common-behaviors/common-module.ts": [ + { + "name": "SanityChecks", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "boolean | GranularSanityChecks", + "file": "ui/src/components/core/common-behaviors/common-module.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "

Possible sanity checks that can be enabled. If set to\ntrue/false, all checks will be enabled/disabled.

\n", + "kind": 186 + } + ], "ui/src/components/sort/sort-direction.ts": [ { "name": "SortDirection", @@ -50355,8 +62114,8 @@ }, "routes": [], "coverage": { - "count": 46, - "status": "medium", + "count": 51, + "status": "good", "files": [ { "filePath": "environments/environment.prod.ts", @@ -50608,6 +62367,161 @@ "coverageCount": "1/1", "status": "very-good" }, + { + "filePath": "ui/src/components/card/card.ts", + "type": "component", + "linktype": "component", + "name": "MatCard", + "coveragePercent": 33, + "coverageCount": "1/3", + "status": "medium" + }, + { + "filePath": "ui/src/components/card/card.ts", + "type": "component", + "linktype": "component", + "name": "MatCardHeader", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/card/card.ts", + "type": "component", + "linktype": "component", + "name": "MatCardTitleGroup", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/card/card.ts", + "type": "directive", + "linktype": "directive", + "name": "MatCardActions", + "coveragePercent": 100, + "coverageCount": "2/2", + "status": "very-good" + }, + { + "filePath": "ui/src/components/card/card.ts", + "type": "directive", + "linktype": "directive", + "name": "MatCardAvatar", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/card/card.ts", + "type": "directive", + "linktype": "directive", + "name": "MatCardContent", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/card/card.ts", + "type": "directive", + "linktype": "directive", + "name": "MatCardFooter", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/card/card.ts", + "type": "directive", + "linktype": "directive", + "name": "MatCardImage", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/card/card.ts", + "type": "directive", + "linktype": "directive", + "name": "MatCardLgImage", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/card/card.ts", + "type": "directive", + "linktype": "directive", + "name": "MatCardMdImage", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/card/card.ts", + "type": "directive", + "linktype": "directive", + "name": "MatCardSmImage", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/card/card.ts", + "type": "directive", + "linktype": "directive", + "name": "MatCardSubtitle", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/card/card.ts", + "type": "directive", + "linktype": "directive", + "name": "MatCardTitle", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/card/card.ts", + "type": "directive", + "linktype": "directive", + "name": "MatCardXlImage", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/card/card.ts", + "type": "interface", + "linktype": "interface", + "name": "MatCardConfig", + "coveragePercent": 100, + "coverageCount": "2/2", + "status": "very-good" + }, + { + "filePath": "ui/src/components/card/card.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "MAT_CARD_CONFIG", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/card/module.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "CARD_DIRECTIVES", + "coveragePercent": 0, + "coverageCount": "0/1", + "status": "low" + }, { "filePath": "ui/src/components/checkbox/checkbox.ts", "type": "component", @@ -50710,6 +62624,94 @@ "coverageCount": "1/1", "status": "very-good" }, + { + "filePath": "ui/src/components/core/common-behaviors/common-module.ts", + "type": "interface", + "linktype": "interface", + "name": "GranularSanityChecks", + "coveragePercent": 25, + "coverageCount": "1/4", + "status": "low" + }, + { + "filePath": "ui/src/components/core/common-behaviors/common-module.ts", + "type": "function", + "linktype": "miscellaneous", + "linksubtype": "function", + "name": "_checkCdkVersionMatch", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/core/common-behaviors/common-module.ts", + "type": "function", + "linktype": "miscellaneous", + "linksubtype": "function", + "name": "_checkDoctypeIsDefined", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/core/common-behaviors/common-module.ts", + "type": "function", + "linktype": "miscellaneous", + "linksubtype": "function", + "name": "_checkThemeIsPresent", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/core/common-behaviors/common-module.ts", + "type": "function", + "linktype": "miscellaneous", + "linksubtype": "function", + "name": "MATERIAL_SANITY_CHECKS_FACTORY", + "coveragePercent": 0, + "coverageCount": "0/1", + "status": "low" + }, + { + "filePath": "ui/src/components/core/common-behaviors/common-module.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "MATERIAL_SANITY_CHECKS", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/core/common-behaviors/disable-ripple.ts", + "type": "interface", + "linktype": "interface", + "name": "CanDisableRipple", + "coveragePercent": 50, + "coverageCount": "1/2", + "status": "medium" + }, + { + "filePath": "ui/src/components/core/common-behaviors/disable-ripple.ts", + "type": "function", + "linktype": "miscellaneous", + "linksubtype": "function", + "name": "mixinDisableRipple", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/core/common-behaviors/disable-ripple.ts", + "type": "function", + "linktype": "miscellaneous", + "linksubtype": "function", + "name": "mixinDisableRipple", + "coveragePercent": 0, + "coverageCount": "0/1", + "status": "low" + }, { "filePath": "ui/src/components/core/common-behaviors/disabled.ts", "type": "interface", @@ -50936,6 +62938,158 @@ "coverageCount": "1/1", "status": "very-good" }, + { + "filePath": "ui/src/components/core/ripple/ripple-event-manager.ts", + "type": "class", + "linktype": "classe", + "name": "RippleEventManager", + "coveragePercent": 80, + "coverageCount": "4/5", + "status": "very-good" + }, + { + "filePath": "ui/src/components/core/ripple/ripple-event-manager.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "passiveCapturingEventOptions", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/core/ripple/ripple-ref.ts", + "type": "class", + "linktype": "classe", + "name": "RippleRef", + "coveragePercent": 71, + "coverageCount": "5/7", + "status": "good" + }, + { + "filePath": "ui/src/components/core/ripple/ripple-ref.ts", + "type": "interface", + "linktype": "interface", + "name": "RippleAnimationConfig", + "coveragePercent": 100, + "coverageCount": "3/3", + "status": "very-good" + }, + { + "filePath": "ui/src/components/core/ripple/ripple-renderer.ts", + "type": "class", + "linktype": "classe", + "name": "RippleRenderer", + "coveragePercent": 88, + "coverageCount": "22/25", + "status": "very-good" + }, + { + "filePath": "ui/src/components/core/ripple/ripple-renderer.ts", + "type": "interface", + "linktype": "interface", + "name": "RippleEventListeners", + "coveragePercent": 33, + "coverageCount": "1/3", + "status": "medium" + }, + { + "filePath": "ui/src/components/core/ripple/ripple-renderer.ts", + "type": "interface", + "linktype": "interface", + "name": "RippleTarget", + "coveragePercent": 100, + "coverageCount": "3/3", + "status": "very-good" + }, + { + "filePath": "ui/src/components/core/ripple/ripple-renderer.ts", + "type": "function", + "linktype": "miscellaneous", + "linksubtype": "function", + "name": "distanceToFurthestCorner", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/core/ripple/ripple-renderer.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "defaultRippleAnimationConfig", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/core/ripple/ripple-renderer.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "ignoreMouseEventsTimeout", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/core/ripple/ripple-renderer.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "passiveCapturingEventOptions", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/core/ripple/ripple-renderer.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "pointerDownEvents", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/core/ripple/ripple-renderer.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "pointerUpEvents", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/core/ripple/ripple.ts", + "type": "directive", + "linktype": "directive", + "name": "MatRipple", + "coveragePercent": 68, + "coverageCount": "15/22", + "status": "good" + }, + { + "filePath": "ui/src/components/core/ripple/ripple.ts", + "type": "interface", + "linktype": "interface", + "name": "RippleGlobalOptions", + "coveragePercent": 100, + "coverageCount": "4/4", + "status": "very-good" + }, + { + "filePath": "ui/src/components/core/ripple/ripple.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "MAT_RIPPLE_GLOBAL_OPTIONS", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, { "filePath": "ui/src/components/core/selection/pseudo-checkbox/pseudo-checkbox.ts", "type": "component", @@ -51045,6 +63199,16 @@ "coverageCount": "1/1", "status": "very-good" }, + { + "filePath": "ui/src/components/core/version.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "VERSION", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, { "filePath": "ui/src/components/datepicker/calendar-body.ts", "type": "component", @@ -52680,8 +64844,8 @@ "type": "component", "linktype": "component", "name": "OuiSelectSearchComponent", - "coveragePercent": 27, - "coverageCount": "6/22", + "coveragePercent": 26, + "coverageCount": "6/23", "status": "medium" }, { @@ -52720,7 +64884,7 @@ "linktype": "component", "name": "OuiSelect", "coveragePercent": 81, - "coverageCount": "91/112", + "coverageCount": "92/113", "status": "very-good" }, { @@ -53243,6 +65407,387 @@ "coverageCount": "1/1", "status": "very-good" }, + { + "filePath": "ui/src/components/tabs/ink-bar.ts", + "type": "class", + "linktype": "classe", + "name": "MatInkBar", + "coveragePercent": 80, + "coverageCount": "4/5", + "status": "very-good" + }, + { + "filePath": "ui/src/components/tabs/ink-bar.ts", + "type": "interface", + "linktype": "interface", + "name": "_MatInkBarPositioner", + "coveragePercent": 50, + "coverageCount": "1/2", + "status": "medium" + }, + { + "filePath": "ui/src/components/tabs/ink-bar.ts", + "type": "interface", + "linktype": "interface", + "name": "MatInkBarItem", + "coveragePercent": 20, + "coverageCount": "1/5", + "status": "low" + }, + { + "filePath": "ui/src/components/tabs/ink-bar.ts", + "type": "function", + "linktype": "miscellaneous", + "linksubtype": "function", + "name": "_MAT_INK_BAR_POSITIONER_FACTORY", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/tabs/ink-bar.ts", + "type": "function", + "linktype": "miscellaneous", + "linksubtype": "function", + "name": "mixinInkBarItem", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/tabs/ink-bar.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "_MAT_INK_BAR_POSITIONER", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/tabs/ink-bar.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "ACTIVE_CLASS", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/tabs/ink-bar.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "NO_TRANSITION_CLASS", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/tabs/paginated-tab-header.ts", + "type": "directive", + "linktype": "directive", + "name": "MatPaginatedTabHeader", + "coveragePercent": 69, + "coverageCount": "34/49", + "status": "good" + }, + { + "filePath": "ui/src/components/tabs/paginated-tab-header.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "HEADER_SCROLL_DELAY", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/tabs/paginated-tab-header.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "HEADER_SCROLL_INTERVAL", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/tabs/paginated-tab-header.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "passiveEventListenerOptions", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/tabs/tab-body.ts", + "type": "component", + "linktype": "component", + "name": "MatTabBody", + "coveragePercent": 86, + "coverageCount": "20/23", + "status": "very-good" + }, + { + "filePath": "ui/src/components/tabs/tab-body.ts", + "type": "directive", + "linktype": "directive", + "name": "MatTabBodyPortal", + "coveragePercent": 83, + "coverageCount": "5/6", + "status": "very-good" + }, + { + "filePath": "ui/src/components/tabs/tab-config.ts", + "type": "interface", + "linktype": "interface", + "name": "MatTabsConfig", + "coveragePercent": 100, + "coverageCount": "8/8", + "status": "very-good" + }, + { + "filePath": "ui/src/components/tabs/tab-config.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "MAT_TABS_CONFIG", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/tabs/tab-content.ts", + "type": "directive", + "linktype": "directive", + "name": "MatTabContent", + "coveragePercent": 66, + "coverageCount": "2/3", + "status": "good" + }, + { + "filePath": "ui/src/components/tabs/tab-content.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "MAT_TAB_CONTENT", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/tabs/tab-group.ts", + "type": "component", + "linktype": "component", + "name": "MatTabGroup", + "coveragePercent": 63, + "coverageCount": "36/57", + "status": "good" + }, + { + "filePath": "ui/src/components/tabs/tab-group.ts", + "type": "class", + "linktype": "classe", + "name": "MatTabChangeEvent", + "coveragePercent": 75, + "coverageCount": "3/4", + "status": "good" + }, + { + "filePath": "ui/src/components/tabs/tab-group.ts", + "type": "interface", + "linktype": "interface", + "name": "MatTabGroupBaseHeader", + "coveragePercent": 0, + "coverageCount": "0/4", + "status": "low" + }, + { + "filePath": "ui/src/components/tabs/tab-group.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "_MatTabGroupMixinBase", + "coveragePercent": 0, + "coverageCount": "0/1", + "status": "low" + }, + { + "filePath": "ui/src/components/tabs/tab-group.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "nextId", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/tabs/tab-header.ts", + "type": "component", + "linktype": "component", + "name": "MatTabHeader", + "coveragePercent": 66, + "coverageCount": "34/51", + "status": "good" + }, + { + "filePath": "ui/src/components/tabs/tab-label-wrapper.ts", + "type": "directive", + "linktype": "directive", + "name": "MatTabLabelWrapper", + "coveragePercent": 40, + "coverageCount": "2/5", + "status": "medium" + }, + { + "filePath": "ui/src/components/tabs/tab-label-wrapper.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "_MatTabLabelWrapperMixinBase", + "coveragePercent": 0, + "coverageCount": "0/1", + "status": "low" + }, + { + "filePath": "ui/src/components/tabs/tab-label.ts", + "type": "directive", + "linktype": "directive", + "name": "MatTabLabel", + "coveragePercent": 33, + "coverageCount": "1/3", + "status": "medium" + }, + { + "filePath": "ui/src/components/tabs/tab-label.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "MAT_TAB", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/tabs/tab-label.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "MAT_TAB_LABEL", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/tabs/tab-nav-bar/tab-nav-bar.ts", + "type": "component", + "linktype": "component", + "name": "MatTabLink", + "coveragePercent": 35, + "coverageCount": "6/17", + "status": "medium" + }, + { + "filePath": "ui/src/components/tabs/tab-nav-bar/tab-nav-bar.ts", + "type": "component", + "linktype": "component", + "name": "MatTabNav", + "coveragePercent": 65, + "coverageCount": "41/63", + "status": "good" + }, + { + "filePath": "ui/src/components/tabs/tab-nav-bar/tab-nav-bar.ts", + "type": "component", + "linktype": "component", + "name": "MatTabNavPanel", + "coveragePercent": 100, + "coverageCount": "3/3", + "status": "very-good" + }, + { + "filePath": "ui/src/components/tabs/tab-nav-bar/tab-nav-bar.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "_MatTabLinkMixinBase", + "coveragePercent": 0, + "coverageCount": "0/1", + "status": "low" + }, + { + "filePath": "ui/src/components/tabs/tab-nav-bar/tab-nav-bar.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "nextUniqueId", + "coveragePercent": 0, + "coverageCount": "0/1", + "status": "low" + }, + { + "filePath": "ui/src/components/tabs/tab.ts", + "type": "component", + "linktype": "component", + "name": "MatTab", + "coveragePercent": 53, + "coverageCount": "14/26", + "status": "good" + }, + { + "filePath": "ui/src/components/tabs/tab.ts", + "type": "class", + "linktype": "classe", + "name": "OuiTabsBase", + "coveragePercent": 0, + "coverageCount": "0/3", + "status": "low" + }, + { + "filePath": "ui/src/components/tabs/tab.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "_MatTabMixinBase", + "coveragePercent": 0, + "coverageCount": "0/1", + "status": "low" + }, + { + "filePath": "ui/src/components/tabs/tab.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "DEFAULT_COLOR", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/tabs/tab.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "MAT_TAB_GROUP", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, + { + "filePath": "ui/src/components/tabs/tabs-animations.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "matTabsAnimations", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, { "filePath": "ui/src/components/tooltip/tooltip-animations.ts", "type": "variable", @@ -53435,6 +65980,15 @@ "coverageCount": "0/2", "status": "low" }, + { + "filePath": "ui/src/stories/card/card.component.ts", + "type": "component", + "linktype": "component", + "name": "MatCardStorybook", + "coveragePercent": 0, + "coverageCount": "0/2", + "status": "low" + }, { "filePath": "ui/src/stories/const.ts", "type": "variable", @@ -53762,6 +66316,15 @@ "coverageCount": "0/11", "status": "low" }, + { + "filePath": "ui/src/stories/tabs/tabs.component.ts", + "type": "component", + "linktype": "component", + "name": "MatTabStorybook", + "coveragePercent": 0, + "coverageCount": "0/2", + "status": "low" + }, { "filePath": "ui/src/stories/tooltip/tooltip.component.ts", "type": "component", diff --git a/package.json b/package.json index 549950b9c..17895713a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "oncehub-ui", - "version": "7.0.28", + "version": "7.0.30", "scripts": { "ng": "ng", "build": "ng build ui", @@ -62,7 +62,7 @@ "@storybook/manager-webpack5": "6.5.13", "@storybook/storybook-deployer": "2.8.16", "@types/jasmine": "4.3.0", - "@types/node": "17.0.25", + "@types/node": "^20.5.9", "@typescript-eslint/eslint-plugin": "5.45.0", "@typescript-eslint/parser": "5.45.0", "@webcomponents/custom-elements": "1.5.1", diff --git a/ui/package.json b/ui/package.json index 746d6f2d1..255bbea8d 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,6 +1,6 @@ { "name": "@oncehub/ui", - "version": "7.0.28", + "version": "7.0.30", "description": "Oncehub UI", "peerDependencies": {}, "repository": { diff --git a/ui/src/components/card/BUILD.bazel b/ui/src/components/card/BUILD.bazel new file mode 100644 index 000000000..4bd5abed5 --- /dev/null +++ b/ui/src/components/card/BUILD.bazel @@ -0,0 +1,66 @@ +load( + "//tools:defaults.bzl", + "markdown_to_html", + "ng_module", + "ng_test_library", + "ng_web_test_suite", + "sass_binary", + "sass_library", +) + +package(default_visibility = ["//visibility:public"]) + +ng_module( + name = "card", + srcs = glob( + ["**/*.ts"], + exclude = ["**/*.spec.ts"], + ), + assets = [":card_scss"] + glob(["**/*.html"]), + deps = [ + "//src/material/core", + ], +) + +sass_library( + name = "card_scss_lib", + srcs = glob(["**/_*.scss"]), + deps = [ + "//:mdc_sass_lib", + "//src/material/core:core_scss_lib", + ], +) + +sass_binary( + name = "card_scss", + src = "card.scss", + deps = [ + "//:mdc_sass_lib", + "//src/material/core:core_scss_lib", + ], +) + +ng_test_library( + name = "unit_test_sources", + srcs = glob( + ["**/*.spec.ts"], + ), + deps = [ + ":card", + ], +) + +ng_web_test_suite( + name = "unit_tests", + deps = [":unit_test_sources"], +) + +markdown_to_html( + name = "overview", + srcs = [":card.md"], +) + +filegroup( + name = "source-files", + srcs = glob(["**/*.ts"]), +) diff --git a/ui/src/components/card/README.md b/ui/src/components/card/README.md new file mode 100644 index 000000000..3ddeb569b --- /dev/null +++ b/ui/src/components/card/README.md @@ -0,0 +1 @@ +Please see the official documentation at https://material.angular.io/components/component/card diff --git a/ui/src/components/card/_card-theme.scss b/ui/src/components/card/_card-theme.scss new file mode 100644 index 000000000..a7f357beb --- /dev/null +++ b/ui/src/components/card/_card-theme.scss @@ -0,0 +1,133 @@ +@use 'sass:map'; +@use '../core/style/sass-utils'; +@use '../core/theming/theming'; +@use '../core/theming/inspection'; +@use '../core/typography/typography'; +@use '../core/tokens/token-utils'; +@use '../core/tokens/m2/mat/card' as tokens-mat-card; +@use '../core/tokens/m2/mdc/elevated-card' as tokens-mdc-elevated-card; +@use '../core/tokens/m2/mdc/outlined-card' as tokens-mdc-outlined-card; +@use '@material/card/elevated-card-theme' as mdc-elevated-card-theme; +@use '@material/card/outlined-card-theme' as mdc-outlined-card-theme; + +@mixin base($theme) { + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme, base)); + } + @else { + @include sass-utils.current-selector-or-root() { + @include mdc-elevated-card-theme.theme(tokens-mdc-elevated-card.get-unthemable-tokens()); + @include mdc-outlined-card-theme.theme(tokens-mdc-outlined-card.get-unthemable-tokens()); + @include token-utils.create-token-values( + tokens-mat-card.$prefix, tokens-mat-card.get-unthemable-tokens()); + } + } +} + +@mixin color($theme) { + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme, color)); + } + @else { + $mdc-elevated-card-color-tokens: token-utils.resolve-elevation( + tokens-mdc-elevated-card.get-color-tokens($theme), + container-elevation, + container-shadow-color + ); + $mdc-outlined-card-color-tokens: token-utils.resolve-elevation( + tokens-mdc-outlined-card.get-color-tokens($theme), + container-elevation, + container-shadow-color, + ); + $mat-card-color-tokens: tokens-mat-card.get-color-tokens($theme); + + // Add values for card tokens. + @include sass-utils.current-selector-or-root() { + @include mdc-elevated-card-theme.theme($mdc-elevated-card-color-tokens); + @include mdc-outlined-card-theme.theme($mdc-outlined-card-color-tokens); + @include token-utils.create-token-values(tokens-mat-card.$prefix, $mat-card-color-tokens); + } + } +} + +@mixin typography($theme) { + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme, typography)); + } + @else { + $mdc-elevated-card-typography-tokens: tokens-mdc-elevated-card.get-typography-tokens($theme); + $mdc-outlined-card-typography-tokens: tokens-mdc-outlined-card.get-typography-tokens($theme); + $mat-card-typography-tokens: tokens-mat-card.get-typography-tokens($theme); + + // Add values for card tokens. + @include sass-utils.current-selector-or-root() { + @include mdc-elevated-card-theme.theme($mdc-elevated-card-typography-tokens); + @include mdc-outlined-card-theme.theme($mdc-outlined-card-typography-tokens); + @include token-utils.create-token-values( + tokens-mat-card.$prefix, $mat-card-typography-tokens); + } + } +} + +@mixin density($theme) { + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme, density)); + } + @else { + $mdc-elevated-card-density-tokens: tokens-mdc-elevated-card.get-density-tokens($theme); + $mdc-outlined-card-density-tokens: tokens-mdc-outlined-card.get-density-tokens($theme); + $mat-card-density-tokens: tokens-mat-card.get-density-tokens($theme); + + // Add values for card tokens. + @include sass-utils.current-selector-or-root() { + @include mdc-elevated-card-theme.theme($mdc-elevated-card-density-tokens); + @include mdc-outlined-card-theme.theme($mdc-outlined-card-density-tokens); + @include token-utils.create-token-values(tokens-mat-card.$prefix, $mat-card-density-tokens); + } + } +} + +@mixin theme($theme-or-color-config) { + $theme: theming.private-legacy-get-theme($theme-or-color-config); + + @include theming.private-check-duplicate-theme-styles($theme, 'mat-card') { + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme)); + } + @else { + @include base($theme); + @if inspection.theme-has($theme, color) { + @include color($theme); + } + @if inspection.theme-has($theme, density) { + @include density($theme); + } + @if inspection.theme-has($theme, typography) { + @include typography($theme); + } + } + } +} + +@mixin _theme-from-tokens($tokens) { + @if ($tokens != ()) { + $elevated-card-tokens: map.get($tokens, tokens-mdc-elevated-card.$prefix); + // Work around a bug in MDC where the elevation is not resolved to an actual shadow value. + $elevated-card-tokens: token-utils.resolve-elevation( + $elevated-card-tokens, + container-elevation, + container-shadow-color + ); + $outlined-card-tokens: map.get($tokens, tokens-mdc-outlined-card.$prefix); + // Work around a bug in MDC where the elevation is not resolved to an actual shadow value. + $outlined-card-tokens: token-utils.resolve-elevation( + $outlined-card-tokens, + container-elevation, + container-shadow-color + ); + @include mdc-elevated-card-theme.theme($elevated-card-tokens); + @include mdc-outlined-card-theme.theme($outlined-card-tokens); + @include token-utils.create-token-values( + tokens-mat-card.$prefix, map.get($tokens, tokens-mat-card.$prefix)); + } +} diff --git a/ui/src/components/card/card-header.html b/ui/src/components/card/card-header.html new file mode 100644 index 000000000..b476f9327 --- /dev/null +++ b/ui/src/components/card/card-header.html @@ -0,0 +1,8 @@ + +
+ +
+ diff --git a/ui/src/components/card/card-title-group.html b/ui/src/components/card/card-title-group.html new file mode 100644 index 000000000..67c0eba6f --- /dev/null +++ b/ui/src/components/card/card-title-group.html @@ -0,0 +1,12 @@ +
+ +
+ + diff --git a/ui/src/components/card/card.html b/ui/src/components/card/card.html new file mode 100644 index 000000000..6dbc74306 --- /dev/null +++ b/ui/src/components/card/card.html @@ -0,0 +1 @@ + diff --git a/ui/src/components/card/card.md b/ui/src/components/card/card.md new file mode 100644 index 000000000..3e05919f6 --- /dev/null +++ b/ui/src/components/card/card.md @@ -0,0 +1,96 @@ +`` is a content container for text, photos, and actions in the context of a single subject. + + + +### Basic card sections + +The most basic card needs only an `` element with some content. However, Angular Material +provides a number of preset sections that you can use inside a ``: + +| Element | Description | +|--------------------------|----------------------------------------------------------------| +| `` | Section anchored to the top of the card (adds padding) | +| `` | Primary card content (adds padding) | +| `` | Card image. Stretches the image to the container width | +| `` | Container for buttons at the bottom of the card (adds padding) | +| `` | Section anchored to the bottom of the card | + +These elements primary serve as pre-styled content containers without any additional APIs. +However, the `align` property on `` can be used to position the actions at the +`'start'` or `'end'` of the container. + +### Card padding + +The `` element itself does not add any padding around its content. This allows developers +to customize the padding to their liking by applying padding to the elements they put in the card. + +In many cases developers may just want the standard padding specified in the Material Design spec. +In this case, the ``, ``, and `` sections can be +used. + +* `` adds standard padding along its sides, as well as along the top if it is the + first element in the ``, and along the bottom if it is the last element in the + ``. +* `` adds standard padding along its sides and top. +* `` adds padding appropriate for the action buttons at the bottom of a card. + +### Card headers + +A `` can contain any content, but there are several predefined elements +that can be used to create a rich header to a card. These include: + +| Element | Description | +|--------------------------|------------------------------------------------------| +| `` | A title within the header | +| `` | A subtitle within the header | +| `` | An image used as an avatar within the header | + +In addition to using `` and `` directly within the +``, they can be further nested inside a `` in order arrange +them with a (non-avatar) image. + +### Title groups + +`` can be used to combine a title, subtitle, and image into a single section. +This element can contain: +* `` +* `` +* One of: + * `` + * `` + * `` + +### Accessibility + +Cards serve a wide variety of scenarios and may contain many different types of content. +Due to this flexible nature, the appropriate accessibility treatment depends on how you use +``. + +#### Group, region, and landmarks + +There are several ARIA roles that communicate that a portion of the UI represents some semantically +meaningful whole. Depending on what the content of the card means to your application, you can apply +one of [`role="group"`][role-group], [`role="region"`][role-region], or +[one of the landmark roles][aria-landmarks] to the `` element. + +You do not need to apply a role when using a card as a purely decorative container that does not +convey a meaningful grouping of related content for a single subject. In these cases, the content +of the card should follow standard practices for document content. + +#### Focus + +Depending on how cards are used, it may be appropriate to apply a `tabindex` to the `` +element. + +* If cards are a primary mechanism through which user interacts with the application, `tabindex="0"` + may be appropriate. +* If attention can be sent to the card, but it's not part of the document flow, `tabindex="-1"` may + be appropriate. +* If the card acts as a purely decorative container, it does not need to be tabbable. In this case, + the card content should follow normal best practices for tab order. + +Always test your application to verify the behavior that works best for your users. + +[role-group]: https://www.w3.org/TR/wai-aria/#group +[role-region]: https://www.w3.org/TR/wai-aria/#region +[aria-landmarks]: https://www.w3.org/TR/wai-aria/#landmark diff --git a/ui/src/components/card/card.scss b/ui/src/components/card/card.scss new file mode 100644 index 000000000..d6942f0b8 --- /dev/null +++ b/ui/src/components/card/card.scss @@ -0,0 +1,223 @@ +// @import 'card-theme'; +// @use 'sass:map'; +// @use './card' as mdc-card; +// @use './card/elevated-card-theme' as mdc-elevated-card-theme; +// @use './card/outlined-card-theme' as mdc-outlined-card-theme; +// @use './theme/custom-properties' as mdc-custom-properties; +// @use '../core/tokens/token-utils'; +// @use '../core/tokens/m2/mat/card' as tokens-mat-card; +// @use '../core/tokens/m2/mdc/elevated-card' as tokens-mdc-elevated-card; +// @use '../core/tokens/m2/mdc/outlined-card' as tokens-mdc-outlined-card; + +// // TODO(jelbourn): move header and title-group styles to their own files. +// @include mdc-custom-properties.configure($emit-fallback-values: false, $emit-fallback-vars: false) { +// $mdc-elevated-card-token-slots: tokens-mdc-elevated-card.get-token-slots(); +// $mdc-outlined-card-token-slots: tokens-mdc-outlined-card.get-token-slots(); + +// // Add the MDC card static styles. +// @include mdc-card.static-styles(); + +// .mat-mdc-card { +// // Add the official slots for the MDC elevated-card. +// @include mdc-elevated-card-theme.theme-styles(map.merge($mdc-elevated-card-token-slots, ( +// // MDC emits the box-shadow on .mdc-card, so the full selector would be +// // `.mat-mdc-card .mdc-card` which is incorrect. We emit the elevation slot ourselves instead. +// container-elevation: null, +// container-shadow-color: null, +// ))); + +// // Emit the elevation slot directly on .mat-mdc-card. +// @include token-utils.use-tokens( +// tokens-mdc-elevated-card.$prefix, $mdc-elevated-card-token-slots) { +// @include token-utils.create-token-slot(box-shadow, container-elevation); +// } +// } + +// .mat-mdc-card-outlined { +// // Add the official slots for the MDC outlined-card. +// @include mdc-outlined-card-theme.theme-styles(map.merge($mdc-outlined-card-token-slots, ( +// // MDC emits the box-shadow on .mdc-card, so the full selector would be +// // `.mat-mdc-card-outlined .mdc-card` which is incorrect. We emit the elevation slot ourselves +// // instead. +// container-elevation: null, +// container-shadow-color: null, +// ))); + +// // Emit the elevation slot directly on .mat-mdc-card-outlined. +// @include token-utils.use-tokens( +// tokens-mdc-outlined-card.$prefix, $mdc-outlined-card-token-slots) { +// @include token-utils.create-token-slot(box-shadow, container-elevation); +// } +// } + +// // Add slots for custom Angular Material card tokens. +// @include token-utils.use-tokens(tokens-mat-card.$prefix, tokens-mat-card.get-token-slots()) { +// .mat-mdc-card-title { +// @include token-utils.create-token-slot(font-family, title-text-font); +// @include token-utils.create-token-slot(line-height, title-text-line-height); +// @include token-utils.create-token-slot(font-size, title-text-size); +// @include token-utils.create-token-slot(letter-spacing, title-text-tracking); +// @include token-utils.create-token-slot(font-weight, title-text-weight); +// } + +// .mat-mdc-card-subtitle { +// @include token-utils.create-token-slot(color, subtitle-text-color); +// @include token-utils.create-token-slot(font-family, subtitle-text-font); +// @include token-utils.create-token-slot(line-height, subtitle-text-line-height); +// @include token-utils.create-token-slot(font-size, subtitle-text-size); +// @include token-utils.create-token-slot(letter-spacing, subtitle-text-tracking); +// @include token-utils.create-token-slot(font-weight, subtitle-text-weight); +// } +// } +// } + +// // Size of the `mat-card-header` region custom to Angular Material. +// $mat-card-header-size: 40px !default; + +// // Default padding for text content within a card. +// $mat-card-default-padding: 16px !default; + +// .mat-mdc-card { +// position: relative; +// } + +// // Title text and subtitles text within a card. MDC doesn't have pre-made title sections for cards. +// // Maintained here for backwards compatibility with the previous generation MatCard. +// .mat-mdc-card-title, +// .mat-mdc-card-subtitle { +// // Custom elements default to `display: inline`. Reset to 'block'. +// display: block; + +// // Titles and subtitles can be set on native header elements which come with +// // their own margin. Clear it since the spacing is done using `padding`. +// margin: 0; + +// .mat-mdc-card-avatar ~ .mat-mdc-card-header-text & { +// // Apply default padding for a text content region. Omit any bottom padding because we assume +// // this region will be followed by another region that includes top padding. +// padding: $mat-card-default-padding $mat-card-default-padding 0; +// } +// } + +// // Header section at the top of a card. MDC does not have a pre-made header section for cards. +// // Maintained here for backwards compatibility with the previous generation MatCard. +// .mat-mdc-card-header { +// // The primary purpose of the card header is to lay out the title, subtitle, and image in the +// // correct order. The image will come first, followed by a single div that will contain (via +// // content projection) both the title and the subtitle. +// display: flex; + +// // Apply default padding for the header region. Omit any bottom padding because we assume +// // this region will be followed by another region that includes top padding. +// padding: $mat-card-default-padding $mat-card-default-padding 0; +// } + +// // Primary card content. MDC does not have a pre-made section for primary content. +// // Adds the default 16px padding to the content. Maintained here for backwards compatibility +// // with the previous generation MatCard. +// .mat-mdc-card-content { +// // Custom elements default to `display: inline`. Reset to 'block'. +// display: block; + +// // Apply default horizontal padding for a content region. +// padding: 0 $mat-card-default-padding; + +// // Only add vertical padding to the main content area if it's not preceeded/followed by another +// // element within the card. +// &:first-child { +// padding-top: $mat-card-default-padding; +// } + +// &:last-child { +// padding-bottom: $mat-card-default-padding; +// } +// } + +// // Title group within a card. MDC does not have a pre-made section for anything title-related. +// // Maintained here for backwards compatibility with the previous generation MatCard. +// .mat-mdc-card-title-group { +// // This element exists primary to lay out its children (title, subtitle, media). The title +// // and subtitle go first (projected into a single div), followed by the media. +// display: flex; +// justify-content: space-between; +// width: 100%; +// } + +// // Avatar image for a card. MDC does not specifically have a card avatar or a "common" avatar. +// // They *do* have a list avatar, but it uses a different size than we do here, which we preserve +// // to reduce breaking changes. Ultimately, the avatar styles just consist of a size and a +// // border-radius. +// .mat-mdc-card-avatar { +// height: $mat-card-header-size; +// width: $mat-card-header-size; +// border-radius: 50%; +// flex-shrink: 0; +// margin-bottom: $mat-card-default-padding; + +// // Makes `` tags behave like `background-size: cover`. Not supported +// // in IE, but we're using it as a progressive enhancement. +// object-fit: cover; + +// // When a title and subtitle are used alongside an avatar, +// // reduce the spacing between them to better align with the avatar. +// & ~ .mat-mdc-card-header-text { +// .mat-mdc-card-subtitle, +// .mat-mdc-card-title { +// line-height: normal; +// } +// } +// } + +// // Specifically sized small image, specific to Angular Material. +// .mat-mdc-card-sm-image { +// width: 80px; +// height: 80px; +// } + +// // Specifically sized medium image, specific to Angular Material. +// .mat-mdc-card-md-image { +// width: 112px; +// height: 112px; +// } + +// // Specifically sized large image, specific to Angular Material. +// .mat-mdc-card-lg-image { +// width: 152px; +// height: 152px; +// } + +// // Specifically sized extra-large image, specific to Angular Material. +// .mat-mdc-card-xl-image { +// width: 240px; +// height: 240px; +// } + +// // When both a title and a subtitle are used, eliminate the top padding of whichever comes second +// // because the first already covers the padding. +// // +// // Additionally, reset the padding for title and subtitle when used within `mat-card-header` or +// // `mat-card-title-group` since the padding is captured by parent container already. +// .mat-mdc-card-subtitle ~ .mat-mdc-card-title, +// .mat-mdc-card-title ~ .mat-mdc-card-subtitle, + +// // The `.mat-mdc-card-header-text` here is redundant since the header text +// // wrapper is always there in the header, but we need the extra specificity. +// .mat-mdc-card-header .mat-mdc-card-header-text .mat-mdc-card-title, +// .mat-mdc-card-header .mat-mdc-card-header-text .mat-mdc-card-subtitle, +// .mat-mdc-card-title-group .mat-mdc-card-title, +// .mat-mdc-card-title-group .mat-mdc-card-subtitle { +// padding-top: 0; +// } + +// // Remove the bottom margin from the last child of the card content. We intended to remove +// // margin from elements that have it built-in, such as `

`. We do this to avoid having too much +// // space between card content regions, as the space is already captured in the content region +// // element. +// .mat-mdc-card-content > :last-child:not(.mat-mdc-card-footer) { +// margin-bottom: 0; +// } + +// // Support for actions aligned to the end of the card. +// .mat-mdc-card-actions-align-end { +// justify-content: flex-end; +// } diff --git a/ui/src/components/card/card.spec.ts b/ui/src/components/card/card.spec.ts new file mode 100644 index 000000000..9005a0383 --- /dev/null +++ b/ui/src/components/card/card.spec.ts @@ -0,0 +1,55 @@ +import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {Component, Provider, Type, ViewChild} from '@angular/core'; +import {MatCardModule} from './module'; +import {MatCard, MAT_CARD_CONFIG} from './card'; + +describe('MDC-based MatCard', () => { + function createComponent(component: Type, providers: Provider[] = []): ComponentFixture { + TestBed.configureTestingModule({ + imports: [MatCardModule], + declarations: [component], + providers, + }).compileComponents(); + + return TestBed.createComponent(component); + } + + it('should default the card to the `raised` appearance', () => { + const fixture = createComponent(BasicCard); + fixture.detectChanges(); + const card = fixture.nativeElement.querySelector('.mat-mdc-card'); + expect(card.classList).not.toContain('mdc-card--outlined'); + }); + + it('should be able to change the card appearance', () => { + const fixture = createComponent(BasicCard); + fixture.detectChanges(); + const card = fixture.nativeElement.querySelector('.mat-mdc-card'); + + expect(card.classList).not.toContain('mdc-card--outlined'); + + fixture.componentInstance.card.appearance = 'outlined'; + fixture.detectChanges(); + + expect(card.classList).toContain('mdc-card--outlined'); + }); + + it('should be able to change the default card appearance using DI', () => { + const fixture = createComponent(BasicCard, [ + { + provide: MAT_CARD_CONFIG, + useValue: {appearance: 'outlined'}, + }, + ]); + fixture.detectChanges(); + const card = fixture.nativeElement.querySelector('.mat-mdc-card'); + expect(card.classList).toContain('mdc-card--outlined'); + }); +}); + +@Component({ + template: '', +}) +class BasicCard { + @ViewChild(MatCard) card: MatCard; +} diff --git a/ui/src/components/card/card.ts b/ui/src/components/card/card.ts new file mode 100644 index 000000000..587541bde --- /dev/null +++ b/ui/src/components/card/card.ts @@ -0,0 +1,236 @@ +/** + * @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 { + ChangeDetectionStrategy, + Component, + Directive, + Inject, + InjectionToken, + Input, + Optional, + ViewEncapsulation, +} from '@angular/core'; + +export type MatCardAppearance = 'outlined' | 'raised'; + +/** Object that can be used to configure the default options for the card module. */ +export interface MatCardConfig { + /** Default appearance for cards. */ + appearance?: MatCardAppearance; +} + +/** Injection token that can be used to provide the default options the card module. */ +export const MAT_CARD_CONFIG = new InjectionToken('MAT_CARD_CONFIG'); + +/** + * Material Design card component. Cards contain content and actions about a single subject. + * See https://material.io/design/components/cards.html + * + * MatCard provides no behaviors, instead serving as a purely visual treatment. + */ +@Component({ + selector: 'mat-card', + templateUrl: 'card.html', + styleUrls: ['./card.scss'], + host: { + 'class': 'mat-mdc-card mdc-card', + '[class.mat-mdc-card-outlined]': 'appearance === "outlined"', + '[class.mdc-card--outlined]': 'appearance === "outlined"', + }, + exportAs: 'matCard', + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MatCard { + @Input() appearance: MatCardAppearance; + + constructor(@Inject(MAT_CARD_CONFIG) @Optional() config?: MatCardConfig) { + this.appearance = config?.appearance || 'raised'; + } +} + +// TODO(jelbourn): add `MatActionCard`, which is a card that acts like a button (and has a ripple). +// Supported in MDC with `.mdc-card__primary-action`. Will require additional a11y docs for users. + +/** + * Title of a card, intended for use within ``. This component is an optional + * convenience for one variety of card title; any custom title element may be used in its place. + * + * MatCardTitle provides no behaviors, instead serving as a purely visual treatment. + */ +@Directive({ + selector: `mat-card-title, [mat-card-title], [matCardTitle]`, + host: {'class': 'mat-mdc-card-title'}, +}) +export class MatCardTitle {} + +/** + * Container intended to be used within the `` component. Can contain exactly one + * ``, one `` and one content image of any size + * (e.g. ``). + */ +@Component({ + selector: 'mat-card-title-group', + templateUrl: 'card-title-group.html', + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush, + host: {'class': 'mat-mdc-card-title-group'}, +}) +export class MatCardTitleGroup {} + +/** + * Content of a card, intended for use within ``. This component is an optional + * convenience for use with other convenience elements, such as ``; any custom + * content block element may be used in its place. + * + * MatCardContent provides no behaviors, instead serving as a purely visual treatment. + */ +@Directive({ + selector: 'mat-card-content', + host: {'class': 'mat-mdc-card-content'}, +}) +export class MatCardContent {} + +/** + * Sub-title of a card, intended for use within `` beneath a ``. This + * component is an optional convenience for use with other convenience elements, such as + * ``. + * + * MatCardSubtitle provides no behaviors, instead serving as a purely visual treatment. + */ +@Directive({ + selector: `mat-card-subtitle, [mat-card-subtitle], [matCardSubtitle]`, + host: {'class': 'mat-mdc-card-subtitle'}, +}) +export class MatCardSubtitle {} + +/** + * Bottom area of a card that contains action buttons, intended for use within ``. + * This component is an optional convenience for use with other convenience elements, such as + * ``; any custom action block element may be used in its place. + * + * MatCardActions provides no behaviors, instead serving as a purely visual treatment. + */ +@Directive({ + selector: 'mat-card-actions', + exportAs: 'matCardActions', + host: { + 'class': 'mat-mdc-card-actions mdc-card__actions', + '[class.mat-mdc-card-actions-align-end]': 'align === "end"', + }, +}) +export class MatCardActions { + // TODO(jelbourn): deprecate `align` in favor of `actionPosition` or `actionAlignment` + // as to not conflict with the native `align` attribute. + + /** Position of the actions inside the card. */ + @Input() align: 'start' | 'end' = 'start'; + + // TODO(jelbourn): support `.mdc-card__actions--full-bleed`. + + // TODO(jelbourn): support `.mdc-card__action-buttons` and `.mdc-card__action-icons`. + + // TODO(jelbourn): figure out how to use `.mdc-card__action`, `.mdc-card__action--button`, and + // `mdc-card__action--icon`. They're used primarily for positioning, which we might be able to + // do implicitly. +} + +/** + * Header region of a card, intended for use within ``. This header captures + * a card title, subtitle, and avatar. This component is an optional convenience for use with + * other convenience elements, such as ``; any custom header block element may be + * used in its place. + * + * MatCardHeader provides no behaviors, instead serving as a purely visual treatment. + */ +@Component({ + selector: 'mat-card-header', + templateUrl: 'card-header.html', + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush, + host: {'class': 'mat-mdc-card-header'}, +}) +export class MatCardHeader {} + +/** + * Footer area a card, intended for use within ``. + * This component is an optional convenience for use with other convenience elements, such as + * ``; any custom footer block element may be used in its place. + * + * MatCardFooter provides no behaviors, instead serving as a purely visual treatment. + */ +@Directive({ + selector: 'mat-card-footer', + host: {'class': 'mat-mdc-card-footer'}, +}) +export class MatCardFooter {} + +// TODO(jelbourn): deprecate the "image" selectors to replace with "media". + +// TODO(jelbourn): support `.mdc-card__media-content`. + +/** + * Primary image content for a card, intended for use within ``. Can be applied to + * any media element, such as `` or ``. + * + * This component is an optional convenience for use with other convenience elements, such as + * ``; any custom media element may be used in its place. + * + * MatCardImage provides no behaviors, instead serving as a purely visual treatment. + */ +@Directive({ + selector: '[mat-card-image], [matCardImage]', + host: {'class': 'mat-mdc-card-image mdc-card__media'}, +}) +export class MatCardImage { + // TODO(jelbourn): support `.mdc-card__media--square` and `.mdc-card__media--16-9`. +} + +/** Same as `MatCardImage`, but small. */ +@Directive({ + selector: '[mat-card-sm-image], [matCardImageSmall]', + host: {'class': 'mat-mdc-card-sm-image mdc-card__media'}, +}) +export class MatCardSmImage {} + +/** Same as `MatCardImage`, but medium. */ +@Directive({ + selector: '[mat-card-md-image], [matCardImageMedium]', + host: {'class': 'mat-mdc-card-md-image mdc-card__media'}, +}) +export class MatCardMdImage {} + +/** Same as `MatCardImage`, but large. */ +@Directive({ + selector: '[mat-card-lg-image], [matCardImageLarge]', + host: {'class': 'mat-mdc-card-lg-image mdc-card__media'}, +}) +export class MatCardLgImage {} + +/** Same as `MatCardImage`, but extra-large. */ +@Directive({ + selector: '[mat-card-xl-image], [matCardImageXLarge]', + host: {'class': 'mat-mdc-card-xl-image mdc-card__media'}, +}) +export class MatCardXlImage {} + +/** + * Avatar image content for a card, intended for use within ``. Can be applied to + * any media element, such as `` or ``. + * + * This component is an optional convenience for use with other convenience elements, such as + * ``; any custom media element may be used in its place. + * + * MatCardAvatar provides no behaviors, instead serving as a purely visual treatment. + */ +@Directive({ + selector: '[mat-card-avatar], [matCardAvatar]', + host: {'class': 'mat-mdc-card-avatar'}, +}) +export class MatCardAvatar {} diff --git a/ui/src/components/card/index.ts b/ui/src/components/card/index.ts new file mode 100644 index 000000000..676ca90f1 --- /dev/null +++ b/ui/src/components/card/index.ts @@ -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'; diff --git a/ui/src/components/card/migration.md b/ui/src/components/card/migration.md new file mode 100644 index 000000000..c1d7c311f --- /dev/null +++ b/ui/src/components/card/migration.md @@ -0,0 +1,2 @@ +# Migration notes for MDC-based `MatCard` +* Previous `MatCard` always set 16px padding, which the MDC card sets no padding. diff --git a/ui/src/components/card/module.ts b/ui/src/components/card/module.ts new file mode 100644 index 000000000..ba70cb9e4 --- /dev/null +++ b/ui/src/components/card/module.ts @@ -0,0 +1,51 @@ +/** + * @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 {CommonModule} from '@angular/common'; +import {NgModule} from '@angular/core'; +// import {MatCommonModule} from '../core/common-behaviors/common-module'; +import { + MatCard, + MatCardActions, + MatCardAvatar, + MatCardContent, + MatCardFooter, + MatCardHeader, + MatCardImage, + MatCardLgImage, + MatCardMdImage, + MatCardSmImage, + MatCardSubtitle, + MatCardTitle, + MatCardTitleGroup, + MatCardXlImage, +} from './card'; + +const CARD_DIRECTIVES = [ + MatCard, + MatCardActions, + MatCardAvatar, + MatCardContent, + MatCardFooter, + MatCardHeader, + MatCardImage, + MatCardLgImage, + MatCardMdImage, + MatCardSmImage, + MatCardSubtitle, + MatCardTitle, + MatCardTitleGroup, + MatCardXlImage, +]; + +@NgModule({ + imports: [CommonModule], + exports: [CARD_DIRECTIVES], + declarations: CARD_DIRECTIVES, +}) +export class MatCardModule {} diff --git a/ui/src/components/card/public-api.ts b/ui/src/components/card/public-api.ts new file mode 100644 index 000000000..78483f223 --- /dev/null +++ b/ui/src/components/card/public-api.ts @@ -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 './card'; +export * from './module'; diff --git a/ui/src/components/core/common-behaviors/common-module.ts b/ui/src/components/core/common-behaviors/common-module.ts new file mode 100644 index 000000000..7f85ff2e7 --- /dev/null +++ b/ui/src/components/core/common-behaviors/common-module.ts @@ -0,0 +1,153 @@ +/** + * @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 {HighContrastModeDetector} from '@angular/cdk/a11y'; +import {BidiModule} from '@angular/cdk/bidi'; +import {inject, Inject, InjectionToken, NgModule, Optional} from '@angular/core'; +import {VERSION as CDK_VERSION} from '@angular/cdk'; +import {DOCUMENT} from '@angular/common'; +import {Platform, _isTestEnvironment} from '@angular/cdk/platform'; +import {VERSION} from '../version'; +import { isDevMode } from '@angular/core'; + + +/** @docs-private */ +export function MATERIAL_SANITY_CHECKS_FACTORY(): SanityChecks { + return true; +} + +/** Injection token that configures whether the Material sanity checks are enabled. */ +export const MATERIAL_SANITY_CHECKS = new InjectionToken('mat-sanity-checks', { + providedIn: 'root', + factory: MATERIAL_SANITY_CHECKS_FACTORY, +}); + +/** + * Possible sanity checks that can be enabled. If set to + * true/false, all checks will be enabled/disabled. + */ +export type SanityChecks = boolean | GranularSanityChecks; + +/** Object that can be used to configure the sanity checks granularly. */ +export interface GranularSanityChecks { + doctype: boolean; + theme: boolean; + version: boolean; +} + +/** + * Module that captures anything that should be loaded and/or run for *all* Angular Material + * components. This includes Bidi, etc. + * + * This module should be imported to each top-level component module (e.g., MatTabsModule). + */ +@NgModule({ + imports: [BidiModule], + exports: [BidiModule], +}) +export class MatCommonModule { + /** Whether we've done the global sanity checks (e.g. a theme is loaded, there is a doctype). */ + private _hasDoneGlobalChecks = false; + + constructor( + highContrastModeDetector: HighContrastModeDetector, + @Optional() @Inject(MATERIAL_SANITY_CHECKS) private _sanityChecks: SanityChecks, + @Inject(DOCUMENT) private _document: Document, + ) { + // While A11yModule also does this, we repeat it here to avoid importing A11yModule + // in MatCommonModule. + highContrastModeDetector._applyBodyHighContrastModeCssClasses(); + + if (!this._hasDoneGlobalChecks) { + this._hasDoneGlobalChecks = true; + + if (typeof isDevMode === 'undefined' || isDevMode) { + // Inject in here so the reference to `Platform` can be removed in production mode. + const platform = inject(Platform, {optional: true}); + + if (this._checkIsEnabled('doctype')) { + _checkDoctypeIsDefined(this._document); + } + + if (this._checkIsEnabled('theme')) { + _checkThemeIsPresent(this._document, !!platform?.isBrowser); + } + + if (this._checkIsEnabled('version')) { + _checkCdkVersionMatch(); + } + } + } + } + + /** Gets whether a specific sanity check is enabled. */ + private _checkIsEnabled(name: keyof GranularSanityChecks): boolean { + if (_isTestEnvironment()) { + return false; + } + + if (typeof this._sanityChecks === 'boolean') { + return this._sanityChecks; + } + + return !!this._sanityChecks[name]; + } +} + +/** Checks that the page has a doctype. */ +function _checkDoctypeIsDefined(doc: Document): void { + if (!doc.doctype) { + console.warn( + 'Current document does not have a doctype. This may cause ' + + 'some Angular Material components not to behave as expected.', + ); + } +} + +/** Checks that a theme has been included. */ +function _checkThemeIsPresent(doc: Document, isBrowser: boolean): void { + // We need to assert that the `body` is defined, because these checks run very early + // and the `body` won't be defined if the consumer put their scripts in the `head`. + if (!doc.body || !isBrowser) { + return; + } + + const testElement = doc.createElement('div'); + testElement.classList.add('mat-theme-loaded-marker'); + doc.body.appendChild(testElement); + + const computedStyle = getComputedStyle(testElement); + + // In some situations the computed style of the test element can be null. For example in + // Firefox, the computed style is null if an application is running inside of a hidden iframe. + // See: https://bugzilla.mozilla.org/show_bug.cgi?id=548397 + if (computedStyle && computedStyle.display !== 'none') { + console.warn( + 'Could not find Angular Material core theme. Most Material ' + + 'components may not work as expected. For more info refer ' + + 'to the theming guide: https://material.angular.io/guide/theming', + ); + } + + testElement.remove(); +} + +/** Checks whether the Material version matches the CDK version. */ +function _checkCdkVersionMatch(): void { + if (VERSION.full !== CDK_VERSION.full) { + console.warn( + 'The Angular Material version (' + + VERSION.full + + ') does not match ' + + 'the Angular CDK version (' + + CDK_VERSION.full + + ').\n' + + 'Please ensure the versions of these two packages exactly match.', + ); + } +} diff --git a/ui/src/components/core/common-behaviors/constructor.ts b/ui/src/components/core/common-behaviors/constructor.ts index f758baa2e..ee3739cf7 100644 --- a/ui/src/components/core/common-behaviors/constructor.ts +++ b/ui/src/components/core/common-behaviors/constructor.ts @@ -1 +1,3 @@ export type Constructor = new (...args: any[]) => T; + +export type AbstractConstructor = abstract new (...args: any[]) => T; diff --git a/ui/src/components/core/common-behaviors/disable-ripple.spec.ts b/ui/src/components/core/common-behaviors/disable-ripple.spec.ts new file mode 100644 index 000000000..b60cb8dde --- /dev/null +++ b/ui/src/components/core/common-behaviors/disable-ripple.spec.ts @@ -0,0 +1,37 @@ +import {mixinDisableRipple} from './disable-ripple'; + +describe('mixinDisableRipple', () => { + it('should augment an existing class with a disableRipple property', () => { + const classWithMixin = mixinDisableRipple(TestClass); + const instance = new classWithMixin(); + + expect(instance.disableRipple) + .withContext('Expected the mixed-into class to have a disable-ripple property') + .toBe(false); + + instance.disableRipple = true; + + expect(instance.disableRipple) + .withContext('Expected the mixed-into class to have an updated disable-ripple property') + .toBe(true); + }); + + it('should coerce values being passed to the disableRipple property', () => { + const classWithMixin = mixinDisableRipple(TestClass); + const instance = new classWithMixin(); + + expect(instance.disableRipple) + .withContext('Expected disableRipple to be set to false initially') + .toBe(false); + + // Setting string values to the disableRipple property should be prevented by TypeScript's + // type checking, but developers can still set string values from their template bindings. + (instance as any).disableRipple = ''; + + expect(instance.disableRipple) + .withContext('Expected disableRipple to be set to true if an empty string is set as value') + .toBe(true); + }); +}); + +class TestClass {} diff --git a/ui/src/components/core/common-behaviors/disable-ripple.ts b/ui/src/components/core/common-behaviors/disable-ripple.ts new file mode 100644 index 000000000..8b55bc637 --- /dev/null +++ b/ui/src/components/core/common-behaviors/disable-ripple.ts @@ -0,0 +1,40 @@ +/** + * @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 {coerceBooleanProperty} from '@angular/cdk/coercion'; +import {AbstractConstructor, Constructor} from './constructor'; + +/** @docs-private */ +export interface CanDisableRipple { + /** Whether ripples are disabled. */ + disableRipple: boolean; +} + +type CanDisableRippleCtor = Constructor & AbstractConstructor; + +/** Mixin to augment a directive with a `disableRipple` property. */ +export function mixinDisableRipple>( + base: T, +): CanDisableRippleCtor & T; +export function mixinDisableRipple>(base: T): CanDisableRippleCtor & T { + return class extends base { + private _disableRipple: boolean = false; + + /** Whether the ripple effect is disabled or not. */ + get disableRipple(): boolean { + return this._disableRipple; + } + set disableRipple(value: any) { + this._disableRipple = coerceBooleanProperty(value); + } + + constructor(...args: any[]) { + super(...args); + } + }; +} diff --git a/ui/src/components/core/common-behaviors/index.ts b/ui/src/components/core/common-behaviors/index.ts index a59220893..c7995a96d 100644 --- a/ui/src/components/core/common-behaviors/index.ts +++ b/ui/src/components/core/common-behaviors/index.ts @@ -1,6 +1,8 @@ +export * from './common-module'; export * from './color'; export * from './disabled'; export * from './constructor'; export * from './tabIndex'; export * from './initialized'; export * from './error-state'; +export * from './disable-ripple'; diff --git a/ui/src/components/core/public-api.ts b/ui/src/components/core/public-api.ts index 2d710bddc..a6cf1e3f9 100644 --- a/ui/src/components/core/public-api.ts +++ b/ui/src/components/core/public-api.ts @@ -4,3 +4,4 @@ export * from './keycodes/index'; export * from './animation/animation'; export * from './error/error-options'; export * from './selection/index'; +export * from './ripple/index'; diff --git a/ui/src/components/core/ripple/_ripple-theme.scss b/ui/src/components/core/ripple/_ripple-theme.scss new file mode 100644 index 000000000..00def46ec --- /dev/null +++ b/ui/src/components/core/ripple/_ripple-theme.scss @@ -0,0 +1,29 @@ +@use 'sass:meta'; +@use '../theming/theming'; +@use '../theming/inspection'; + +// Colors for the ripple elements. +@mixin color($theme) { + $foreground-base: inspection.get-theme-color($theme, foreground, base); + $color-opacity: 0.1; + + .mat-ripple-element { + // If the ripple color is resolves to a color *type*, we can use it directly, otherwise + // (e.g. it resolves to a CSS variable) we fall back to using the color and setting an opacity. + @if (meta.type-of($foreground-base) == color) { + background-color: rgba($foreground-base, $color-opacity); + } + @else { + background-color: $foreground-base; + opacity: $color-opacity; + } + } +} + +@mixin theme($theme) { + @include theming.private-check-duplicate-theme-styles($theme, 'mat-ripple') { + @if inspection.theme-has($theme, color) { + @include color($theme); + } + } +} diff --git a/ui/src/components/core/ripple/_ripple.scss b/ui/src/components/core/ripple/_ripple.scss new file mode 100644 index 000000000..ca5b93f70 --- /dev/null +++ b/ui/src/components/core/ripple/_ripple.scss @@ -0,0 +1,43 @@ +@use '@angular/cdk'; + +@mixin ripple() { + // The host element of an mat-ripple directive should always have a position of "absolute" or + // "relative" so that the ripples inside are correctly positioned relatively to the container. + .mat-ripple { + overflow: hidden; + + // By default, every ripple container should have position: relative in favor of creating an + // easy API for developers using the MatRipple directive. + position: relative; + + // Promote containers that have ripples to a new layer. We want to target `:not(:empty)`, + // because we don't want all ripple containers to have their own layer since they're used in a + // lot of places and the layer is only relevant while animating. Note that ideally we'd use + // the `contain` property here (see #13175), because `:empty` can be broken by having extra + // text inside the element, but it isn't very well supported yet. + &:not(:empty) { + transform: translateZ(0); + } + } + + .mat-ripple.mat-ripple-unbounded { + overflow: visible; + } + + .mat-ripple-element { + position: absolute; + border-radius: 50%; + pointer-events: none; + + transition: opacity, transform 0ms cubic-bezier(0, 0, 0.2, 1); + + // We use a 3d transform here in order to avoid an issue in Safari where + // the ripples aren't clipped when inside the shadow DOM (see #24028). + transform: scale3d(0, 0, 0); + + // In high contrast mode the ripple is opaque, causing it to obstruct the content. + @include cdk.high-contrast(active, off) { + display: none; + } + } +} diff --git a/ui/src/components/core/ripple/index.ts b/ui/src/components/core/ripple/index.ts new file mode 100644 index 000000000..97b4bc7e7 --- /dev/null +++ b/ui/src/components/core/ripple/index.ts @@ -0,0 +1,22 @@ +/** + * @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 '../common-behaviors/common-module'; +import {MatRipple} from './ripple'; + +export * from './ripple'; +export * from './ripple-ref'; +export * from './ripple-renderer'; + +@NgModule({ + imports: [MatCommonModule], + exports: [MatRipple, MatCommonModule], + declarations: [MatRipple], +}) +export class MatRippleModule {} diff --git a/ui/src/components/core/ripple/ripple-event-manager.ts b/ui/src/components/core/ripple/ripple-event-manager.ts new file mode 100644 index 000000000..5cfb1cd5f --- /dev/null +++ b/ui/src/components/core/ripple/ripple-event-manager.ts @@ -0,0 +1,81 @@ +/** + * @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 {normalizePassiveListenerOptions, _getEventTarget} from '@angular/cdk/platform'; +import {NgZone} from '@angular/core'; + +/** Options used to bind a passive capturing event. */ +const passiveCapturingEventOptions = normalizePassiveListenerOptions({ + passive: true, + capture: true, +}); + +/** Manages events through delegation so that as few event handlers as possible are bound. */ +export class RippleEventManager { + private _events = new Map>>(); + + /** Adds an event handler. */ + addHandler(ngZone: NgZone, name: string, element: HTMLElement, handler: EventListenerObject) { + const handlersForEvent = this._events.get(name); + + if (handlersForEvent) { + const handlersForElement = handlersForEvent.get(element); + + if (handlersForElement) { + handlersForElement.add(handler); + } else { + handlersForEvent.set(element, new Set([handler])); + } + } else { + this._events.set(name, new Map([[element, new Set([handler])]])); + + ngZone.runOutsideAngular(() => { + document.addEventListener(name, this._delegateEventHandler, passiveCapturingEventOptions); + }); + } + } + + /** Removes an event handler. */ + removeHandler(name: string, element: HTMLElement, handler: EventListenerObject) { + const handlersForEvent = this._events.get(name); + + if (!handlersForEvent) { + return; + } + + const handlersForElement = handlersForEvent.get(element); + + if (!handlersForElement) { + return; + } + + handlersForElement.delete(handler); + + if (handlersForElement.size === 0) { + handlersForEvent.delete(element); + } + + if (handlersForEvent.size === 0) { + this._events.delete(name); + document.removeEventListener(name, this._delegateEventHandler, passiveCapturingEventOptions); + } + } + + /** Event handler that is bound and which dispatches the events to the different targets. */ + private _delegateEventHandler = (event: Event) => { + const target = _getEventTarget(event); + + if (target) { + this._events.get(event.type)?.forEach((handlers, element) => { + if (element === target || element.contains(target as Node)) { + handlers.forEach(handler => handler.handleEvent(event)); + } + }); + } + }; +} diff --git a/ui/src/components/core/ripple/ripple-ref.ts b/ui/src/components/core/ripple/ripple-ref.ts new file mode 100644 index 000000000..38887a71a --- /dev/null +++ b/ui/src/components/core/ripple/ripple-ref.ts @@ -0,0 +1,58 @@ +/** + * @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 + */ + +/** Possible states for a ripple element. */ +export const enum RippleState { + FADING_IN, + VISIBLE, + FADING_OUT, + HIDDEN, +} + +export type RippleConfig = { + color?: string; + centered?: boolean; + radius?: number; + persistent?: boolean; + animation?: RippleAnimationConfig; + terminateOnPointerUp?: boolean; +}; + +/** + * Interface that describes the configuration for the animation of a ripple. + * There are two animation phases with different durations for the ripples. + */ +export interface RippleAnimationConfig { + /** Duration in milliseconds for the enter animation (expansion from point of contact). */ + enterDuration?: number; + /** Duration in milliseconds for the exit animation (fade-out). */ + exitDuration?: number; +} + +/** + * Reference to a previously launched ripple element. + */ +export class RippleRef { + /** Current state of the ripple. */ + state: RippleState = RippleState.HIDDEN; + + constructor( + private _renderer: {fadeOutRipple(ref: RippleRef): void}, + /** Reference to the ripple HTML element. */ + public element: HTMLElement, + /** Ripple configuration used for the ripple. */ + public config: RippleConfig, + /* Whether animations are forcibly disabled for ripples through CSS. */ + public _animationForciblyDisabledThroughCss = false, + ) {} + + /** Fades out the ripple element. */ + fadeOut() { + this._renderer.fadeOutRipple(this); + } +} diff --git a/ui/src/components/core/ripple/ripple-renderer.ts b/ui/src/components/core/ripple/ripple-renderer.ts new file mode 100644 index 000000000..f3434e0ea --- /dev/null +++ b/ui/src/components/core/ripple/ripple-renderer.ts @@ -0,0 +1,448 @@ +/** + * @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 {ElementRef, NgZone} from '@angular/core'; +import {Platform, normalizePassiveListenerOptions, _getEventTarget} from '@angular/cdk/platform'; +import {isFakeMousedownFromScreenReader, isFakeTouchstartFromScreenReader} from '@angular/cdk/a11y'; +import {coerceElement} from '@angular/cdk/coercion'; +import {RippleRef, RippleState, RippleConfig} from './ripple-ref'; +import {RippleEventManager} from './ripple-event-manager'; + +/** + * Interface that describes the target for launching ripples. + * It defines the ripple configuration and disabled state for interaction ripples. + * @docs-private + */ +export interface RippleTarget { + /** Configuration for ripples that are launched on pointer down. */ + rippleConfig: RippleConfig; + /** Whether ripples on pointer down should be disabled. */ + rippleDisabled: boolean; +} + +/** Interfaces the defines ripple element transition event listeners. */ +interface RippleEventListeners { + onTransitionEnd: EventListener; + onTransitionCancel: EventListener; +} + +/** + * Default ripple animation configuration for ripples without an explicit + * animation config specified. + */ +export const defaultRippleAnimationConfig = { + enterDuration: 225, + exitDuration: 150, +}; + +/** + * Timeout for ignoring mouse events. Mouse events will be temporary ignored after touch + * events to avoid synthetic mouse events. + */ +const ignoreMouseEventsTimeout = 800; + +/** Options used to bind a passive capturing event. */ +const passiveCapturingEventOptions = normalizePassiveListenerOptions({ + passive: true, + capture: true, +}); + +/** Events that signal that the pointer is down. */ +const pointerDownEvents = ['mousedown', 'touchstart']; + +/** Events that signal that the pointer is up. */ +const pointerUpEvents = ['mouseup', 'mouseleave', 'touchend', 'touchcancel']; + +/** + * Helper service that performs DOM manipulations. Not intended to be used outside this module. + * The constructor takes a reference to the ripple directive's host element and a map of DOM + * event handlers to be installed on the element that triggers ripple animations. + * This will eventually become a custom renderer once Angular support exists. + * @docs-private + */ +export class RippleRenderer implements EventListenerObject { + /** Element where the ripples are being added to. */ + private _containerElement: HTMLElement; + + /** Element which triggers the ripple elements on mouse events. */ + private _triggerElement: HTMLElement | null; + + /** Whether the pointer is currently down or not. */ + private _isPointerDown = false; + + /** + * Map of currently active ripple references. + * The ripple reference is mapped to its element event listeners. + * The reason why `| null` is used is that event listeners are added only + * when the condition is truthy (see the `_startFadeOutTransition` method). + */ + private _activeRipples = new Map(); + + /** Latest non-persistent ripple that was triggered. */ + private _mostRecentTransientRipple: RippleRef | null; + + /** Time in milliseconds when the last touchstart event happened. */ + private _lastTouchStartEvent: number; + + /** Whether pointer-up event listeners have been registered. */ + private _pointerUpEventsRegistered = false; + + /** + * Cached dimensions of the ripple container. Set when the first + * ripple is shown and cleared once no more ripples are visible. + */ + private _containerRect: ClientRect | null; + + private static _eventManager = new RippleEventManager(); + + constructor( + private _target: RippleTarget, + private _ngZone: NgZone, + elementOrElementRef: HTMLElement | ElementRef, + private _platform: Platform, + ) { + // Only do anything if we're on the browser. + if (_platform.isBrowser) { + this._containerElement = coerceElement(elementOrElementRef); + } + } + + /** + * Fades in a ripple at the given coordinates. + * @param x Coordinate within the element, along the X axis at which to start the ripple. + * @param y Coordinate within the element, along the Y axis at which to start the ripple. + * @param config Extra ripple options. + */ + fadeInRipple(x: number, y: number, config: RippleConfig = {}): RippleRef { + const containerRect = (this._containerRect = + this._containerRect || this._containerElement.getBoundingClientRect()); + const animationConfig = {...defaultRippleAnimationConfig, ...config.animation}; + + if (config.centered) { + x = containerRect.left + containerRect.width / 2; + y = containerRect.top + containerRect.height / 2; + } + + const radius = config.radius || distanceToFurthestCorner(x, y, containerRect); + const offsetX = x - containerRect.left; + const offsetY = y - containerRect.top; + const enterDuration = animationConfig.enterDuration; + + const ripple = document.createElement('div'); + ripple.classList.add('mat-ripple-element'); + + ripple.style.left = `${offsetX - radius}px`; + ripple.style.top = `${offsetY - radius}px`; + ripple.style.height = `${radius * 2}px`; + ripple.style.width = `${radius * 2}px`; + + // If a custom color has been specified, set it as inline style. If no color is + // set, the default color will be applied through the ripple theme styles. + if (config.color != null) { + ripple.style.backgroundColor = config.color; + } + + ripple.style.transitionDuration = `${enterDuration}ms`; + + this._containerElement.appendChild(ripple); + + // By default the browser does not recalculate the styles of dynamically created + // ripple elements. This is critical to ensure that the `scale` animates properly. + // We enforce a style recalculation by calling `getComputedStyle` and *accessing* a property. + // See: https://gist.github.com/paulirish/5d52fb081b3570c81e3a + const computedStyles = window.getComputedStyle(ripple); + const userTransitionProperty = computedStyles.transitionProperty; + const userTransitionDuration = computedStyles.transitionDuration; + + // Note: We detect whether animation is forcibly disabled through CSS (e.g. through + // `transition: none` or `display: none`). This is technically unexpected since animations are + // controlled through the animation config, but this exists for backwards compatibility. This + // logic does not need to be super accurate since it covers some edge cases which can be easily + // avoided by users. + const animationForciblyDisabledThroughCss = + userTransitionProperty === 'none' || + // Note: The canonical unit for serialized CSS `

+ +
+``` + +By default, a ripple is activated when the host element of the `matRipple` directive receives +mouse or touch events. Upon being pressed, a ripple will begin fading in from the point of contact, +radiating to cover the host element. Each ripple will fade out only upon release of the mouse or touch. + +Ripples can also be triggered programmatically by getting a reference to the MatRipple directive +and calling its `launch` method. + + +### Ripple trigger + +By default ripples will fade in on interaction with the directive's host element. +In some situations, developers may want to show ripples on interaction with *some other* element, +but still want to have the ripples placed in another location. This can be done by specifying +the `matRippleTrigger` option that expects a reference to an `HTMLElement`. + +```html +
+
+ +
+ +
+
+``` + +### Manual ripples + +Ripples can be shown programmatically by getting a reference to the `MatRipple` directive. + +```ts +class MyComponent { + + /** Reference to the directive instance of the ripple. */ + @ViewChild(MatRipple) ripple: MatRipple; + + /** Shows a centered and persistent ripple. */ + launchRipple() { + const rippleRef = this.ripple.launch({ + persistent: true, + centered: true + }); + + // Fade out the ripple later. + rippleRef.fadeOut(); + } +} +``` + +In the example above, no specific coordinates have been passed, because the `centered` +ripple option has been set to `true` and the coordinates would not matter. + +Ripples that are being dispatched programmatically can be launched with the `persistent` option. +This means that the ripples will not fade out automatically, and need to be faded out using +the `RippleRef` (*useful for focus indicators*). + +In case, developers want to launch ripples at specific coordinates within the element, the +`launch()` method also accepts `x` and `y` coordinates as parameters. Those coordinates +are relative to the ripple container element. + +```ts +const rippleRef = this.ripple.launch(10, 10, {persistent: true}); +``` + +### Global options + +Developers are able to specify options for all ripples inside of their application. + +The speed of the ripples can be adjusted and the ripples can be disabled globally as well. + +Global ripple options can be specified by setting the `MAT_RIPPLE_GLOBAL_OPTIONS` provider. + +```ts +const globalRippleConfig: RippleGlobalOptions = { + disabled: true, + animation: { + enterDuration: 300, + exitDuration: 0 + } +}; + +@NgModule({ + providers: [ + {provide: MAT_RIPPLE_GLOBAL_OPTIONS, useValue: globalRippleConfig} + ] +}) +``` + +All available global options can be seen in the `RippleGlobalOptions` interface. + +### Disabling animation + +The animation of ripples can be disabled by using the `animation` global option. If the +`enterDuration` and `exitDuration` is being set to `0`, ripples will just appear without any +animation. + +This is specifically useful in combination with the `disabled` global option, because globally +disabling ripples won't affect the focus indicator ripples. If someone still wants to disable +those ripples for performance reasons, the duration can be set to `0`, to remove the ripple feel. + +```ts +const globalRippleConfig: RippleGlobalOptions = { + disabled: true, + animation: { + enterDuration: 0, + exitDuration: 0 + } +}; +``` + +**Note**: Ripples will also have no animation if the `NoopAnimationsModule` is being used. This +also means that the durations in the `animation` configuration won't be taken into account. + +### Animation behavior + +There are two different animation behaviors for the fade-out of ripples shown in the Material +Design specifications. + +By default, all ripples will start fading out if the mouse or touch is released and the enter +animation completed. The second possible behavior, which is also shown in the specifications, is +that ripples start to fade out immediately on mouse or touch release. + +In some scenarios, developers might prefer that behavior over the default and would like to have +the same for Angular Material. This behavior can be activated by specifying the +`terminateOnPointerUp` global ripple option. + +```ts +const globalRippleConfig: RippleGlobalOptions = { + terminateOnPointerUp: true +}; +``` + +### Updating global options at runtime + +To change global ripple options at runtime, just inject the `MAT_RIPPLE_GLOBAL_OPTIONS` +provider and update the desired options. + +There are various ways of injecting the global options. In order to make it easier to +inject and update options at runtime, it's recommended to create a service that implements +the `RippleGlobalOptions` interface. + +```ts +@Injectable({providedIn: 'root'}) +export class AppGlobalRippleOptions implements RippleGlobalOptions { + /** Whether ripples should be disabled globally. */ + disabled: boolean = false; +} +``` + +```ts +@NgModule({ + providers: [ + {provide: MAT_RIPPLE_GLOBAL_OPTIONS, useExisting: AppGlobalRippleOptions}, + ] +}) +export class MyModule {...} +``` + +Now that the global ripple options are set to a service we can inject, the service can be +used update any global ripple option at runtime. + +```ts +@Component(...) +export class MyComponent { + constructor(private _appRippleOptions: AppGlobalRippleOptions) {} + + disableRipples() { + this._appRippleOptions.disabled = true; + } +} +``` diff --git a/ui/src/components/core/ripple/ripple.spec.ts b/ui/src/components/core/ripple/ripple.spec.ts new file mode 100644 index 000000000..cc7a31120 --- /dev/null +++ b/ui/src/components/core/ripple/ripple.spec.ts @@ -0,0 +1,907 @@ +import {Platform} from '@angular/cdk/platform'; +import { + createMouseEvent, + createTouchEvent, + dispatchEvent, + dispatchFakeEvent, + dispatchMouseEvent, + dispatchTouchEvent, +} from '@angular/cdk/testing/private'; +import {Component, ViewChild, ViewEncapsulation} from '@angular/core'; +import {ComponentFixture, inject, TestBed} from '@angular/core/testing'; +import {NoopAnimationsModule} from '@angular/platform-browser/animations'; +import { + MAT_RIPPLE_GLOBAL_OPTIONS, + MatRipple, + MatRippleModule, + RippleAnimationConfig, + RippleGlobalOptions, + RippleState, +} from './index'; + +describe('MatRipple', () => { + let fixture: ComponentFixture; + let rippleTarget: HTMLElement; + let originalBodyMargin: string | null; + let platform: Platform; + + /** Extracts the numeric value of a pixel size string like '123px'. */ + const pxStringToFloat = (s: string | null) => (s ? parseFloat(s) : 0); + const startingWindowWidth = window.innerWidth; + const startingWindowHeight = window.innerHeight; + + /** Flushes the transition of the ripple element inside of the ripple target. */ + function flushTransition() { + dispatchFakeEvent(rippleTarget.querySelector('.mat-ripple-element')!, 'transitionend'); + } + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [MatRippleModule], + declarations: [ + BasicRippleContainer, + RippleContainerWithInputBindings, + RippleContainerWithoutBindings, + RippleContainerWithNgIf, + RippleCssTransitionNone, + RippleCssTransitionDurationZero, + RippleWithDomRemovalOnClick, + ], + }); + }); + + beforeEach(inject([Platform], (p: Platform) => { + platform = p; + + // Set body margin to 0 during tests so it doesn't mess up position calculations. + originalBodyMargin = document.body.style.margin; + document.body.style.margin = '0'; + })); + + afterEach(() => { + document.body.style.margin = originalBodyMargin!; + }); + + describe('basic ripple', () => { + let rippleDirective: MatRipple; + + const TARGET_HEIGHT = 200; + const TARGET_WIDTH = 300; + + beforeEach(() => { + fixture = TestBed.createComponent(BasicRippleContainer); + fixture.detectChanges(); + + rippleTarget = fixture.nativeElement.querySelector('.mat-ripple'); + rippleDirective = fixture.componentInstance.ripple; + }); + + it('sizes ripple to cover element', () => { + // This test is consistently flaky on iOS (vs. Safari on desktop and all other browsers). + // Temporarily skip this test on iOS until we can determine the source of the flakiness. + // TODO(jelbourn): determine the source of flakiness here + if (platform.IOS) { + return; + } + + let elementRect = rippleTarget.getBoundingClientRect(); + + // Dispatch a ripple at the following relative coordinates (X: 50| Y: 75) + dispatchMouseEvent(rippleTarget, 'mousedown', 50, 75); + dispatchMouseEvent(rippleTarget, 'mouseup'); + + // Calculate distance from the click to farthest edge of the ripple target. + let maxDistanceX = TARGET_WIDTH - 50; + let maxDistanceY = TARGET_HEIGHT - 75; + + // At this point the foreground ripple should be created with a div centered at the click + // location, and large enough to reach the furthest corner, which is 250px to the right + // and 125px down relative to the click position. + let expectedRadius = Math.sqrt(maxDistanceX * maxDistanceX + maxDistanceY * maxDistanceY); + let expectedLeft = elementRect.left + 50 - expectedRadius; + let expectedTop = elementRect.top + 75 - expectedRadius; + + let ripple = rippleTarget.querySelector('.mat-ripple-element') as HTMLElement; + + // Note: getBoundingClientRect won't work because there's a transform applied to make the + // ripple start out tiny. + expect(pxStringToFloat(ripple.style.left)).toBeCloseTo(expectedLeft, 1); + expect(pxStringToFloat(ripple.style.top)).toBeCloseTo(expectedTop, 1); + expect(pxStringToFloat(ripple.style.width)).toBeCloseTo(2 * expectedRadius, 1); + expect(pxStringToFloat(ripple.style.height)).toBeCloseTo(2 * expectedRadius, 1); + }); + + it('creates ripple on mousedown', () => { + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(2); + }); + + it('should launch ripples on touchstart', () => { + dispatchTouchEvent(rippleTarget, 'touchstart'); + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + + flushTransition(); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + + dispatchTouchEvent(rippleTarget, 'touchend'); + flushTransition(); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + }); + + it('should clear ripples if the touch sequence is cancelled', () => { + dispatchTouchEvent(rippleTarget, 'touchstart'); + flushTransition(); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + + dispatchTouchEvent(rippleTarget, 'touchcancel'); + flushTransition(); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + }); + + it('should launch multiple ripples for multi-touch', () => { + const touchEvent = createTouchEvent('touchstart'); + + Object.defineProperties(touchEvent, { + changedTouches: { + value: [ + {pageX: 0, pageY: 0}, + {pageX: 10, pageY: 10}, + {pageX: 20, pageY: 20}, + ], + }, + }); + + dispatchEvent(rippleTarget, touchEvent); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(3); + + const rippleElements = rippleTarget.querySelectorAll('.mat-ripple-element'); + + // Flush the fade-in transition of all three ripples. + dispatchFakeEvent(rippleElements[0], 'transitionend'); + dispatchFakeEvent(rippleElements[1], 'transitionend'); + dispatchFakeEvent(rippleElements[2], 'transitionend'); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(3); + + dispatchTouchEvent(rippleTarget, 'touchend'); + + // Flush the fade-out transition of all three ripples. + dispatchFakeEvent(rippleElements[0], 'transitionend'); + dispatchFakeEvent(rippleElements[1], 'transitionend'); + dispatchFakeEvent(rippleElements[2], 'transitionend'); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + }); + + it('should ignore synthetic mouse events after touchstart', () => { + dispatchTouchEvent(rippleTarget, 'touchstart'); + dispatchTouchEvent(rippleTarget, 'mousedown'); + + flushTransition(); + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + + dispatchTouchEvent(rippleTarget, 'touchend'); + + flushTransition(); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + }); + + it('should ignore fake mouse events from screen readers', () => { + const event = createMouseEvent('mousedown'); + Object.defineProperties(event, {offsetX: {get: () => 0}, offsetY: {get: () => 0}}); + + dispatchEvent(rippleTarget, event); + + expect(rippleTarget.querySelector('.mat-ripple-element')).toBeFalsy(); + }); + + it('removes ripple after timeout', () => { + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + + // Flush fade-in and fade-out transition. + flushTransition(); + flushTransition(); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + }); + + it('should remove ripples after mouseup', () => { + dispatchMouseEvent(rippleTarget, 'mousedown'); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + + // Flush the transition of fading in. Also flush the potential fading-out transition in + // order to make sure that the ripples didn't fade-out before mouseup. + flushTransition(); + flushTransition(); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + + dispatchMouseEvent(rippleTarget, 'mouseup'); + flushTransition(); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + }); + + it('creates ripples when manually triggered', () => { + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + + rippleDirective.launch(0, 0); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + }); + + it('creates manual ripples with the default ripple config', () => { + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + + // Calculate the diagonal distance and divide it by two for the center radius. + let radius = Math.sqrt(TARGET_HEIGHT * TARGET_HEIGHT + TARGET_WIDTH * TARGET_WIDTH) / 2; + + rippleDirective.centered = true; + rippleDirective.launch(0, 0); + + let rippleElement = rippleTarget.querySelector('.mat-ripple-element') as HTMLElement; + + expect(rippleElement).toBeTruthy(); + expect(parseFloat(rippleElement.style.left as string)).toBeCloseTo( + TARGET_WIDTH / 2 - radius, + 1, + ); + expect(parseFloat(rippleElement.style.top as string)).toBeCloseTo( + TARGET_HEIGHT / 2 - radius, + 1, + ); + }); + + it('cleans up the event handlers when the container gets destroyed', () => { + fixture = TestBed.createComponent(RippleContainerWithNgIf); + fixture.detectChanges(); + + rippleTarget = fixture.debugElement.nativeElement.querySelector('.mat-ripple'); + + fixture.componentInstance.isDestroyed = true; + fixture.detectChanges(); + + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + }); + + it('does not run events inside the NgZone', () => { + const spy = jasmine.createSpy('zone unstable callback'); + const subscription = fixture.ngZone!.onUnstable.subscribe(spy); + + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); + + expect(spy).not.toHaveBeenCalled(); + subscription.unsubscribe(); + }); + + it('should only persist the latest ripple on pointer down', () => { + dispatchMouseEvent(rippleTarget, 'mousedown'); + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + + dispatchMouseEvent(rippleTarget, 'mousedown'); + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(2); + + // Flush the fade-in transition. + flushTransition(); + // Flush the fade-out transition. + flushTransition(); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + }); + + describe('when page is scrolled', () => { + let veryLargeElement: HTMLDivElement = document.createElement('div'); + let pageScrollTop = 500; + let pageScrollLeft = 500; + + beforeEach(() => { + // Add a very large element to make the page scroll + veryLargeElement.style.width = '4000px'; + veryLargeElement.style.height = '4000px'; + + document.body.appendChild(veryLargeElement); + document.body.scrollTop = pageScrollTop; + document.body.scrollLeft = pageScrollLeft; + + // Firefox + document.documentElement!.scrollLeft = pageScrollLeft; + document.documentElement!.scrollTop = pageScrollTop; + + // Mobile safari + window.scrollTo(pageScrollLeft, pageScrollTop); + }); + + afterEach(() => { + veryLargeElement.remove(); + document.body.scrollTop = 0; + document.body.scrollLeft = 0; + + // Firefox + document.documentElement!.scrollLeft = 0; + document.documentElement!.scrollTop = 0; + + // Mobile safari + window.scrollTo(0, 0); + }); + + it('create ripple with correct position', () => { + let elementTop = 600; + let elementLeft = 750; + let left = 50; + let top = 75; + + rippleTarget.style.left = `${elementLeft}px`; + rippleTarget.style.top = `${elementTop}px`; + + // Simulate a keyboard-triggered click by setting event coordinates to 0. + dispatchMouseEvent( + rippleTarget, + 'mousedown', + left + elementLeft - pageScrollLeft, + top + elementTop - pageScrollTop, + ); + + let expectedRadius = Math.sqrt(250 * 250 + 125 * 125); + let expectedLeft = left - expectedRadius; + let expectedTop = top - expectedRadius; + + let ripple = rippleTarget.querySelector('.mat-ripple-element') as HTMLElement; + + // In the iOS simulator (BrowserStack & SauceLabs), adding the content to the + // body causes karma's iframe for the test to stretch to fit that content once we attempt to + // scroll the page. Setting width / height / maxWidth / maxHeight on the iframe does not + // successfully constrain its size. As such, skip assertions in environments where the + // window size has changed since the start of the test. + if (window.innerWidth > startingWindowWidth || window.innerHeight > startingWindowHeight) { + return; + } + + expect(pxStringToFloat(ripple.style.left)).toBeCloseTo(expectedLeft, 1); + expect(pxStringToFloat(ripple.style.top)).toBeCloseTo(expectedTop, 1); + expect(pxStringToFloat(ripple.style.width)).toBeCloseTo(2 * expectedRadius, 1); + expect(pxStringToFloat(ripple.style.height)).toBeCloseTo(2 * expectedRadius, 1); + }); + }); + }); + + describe('manual ripples', () => { + let rippleDirective: MatRipple; + + beforeEach(() => { + fixture = TestBed.createComponent(BasicRippleContainer); + fixture.detectChanges(); + + rippleTarget = fixture.nativeElement.querySelector('.mat-ripple'); + rippleDirective = fixture.componentInstance.ripple; + }); + + it('should allow persistent ripple elements', () => { + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + + let rippleRef = rippleDirective.launch(0, 0, {persistent: true}); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + + // Flush the fade-in transition. Additionally flush the potential fade-out transition + // in order to make sure that the ripple is persistent and won't fade-out. + flushTransition(); + flushTransition(); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + + rippleRef.fadeOut(); + flushTransition(); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + }); + + it('should remove ripples that are not done fading in', () => { + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + + rippleDirective.launch(0, 0); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + + // The ripple should still fade in right now. Now by calling `fadeOutAll` the ripple should + // immediately start fading out. We can verify this by just flushing the current transition + // and verifying if the ripple has been removed from the DOM. + rippleDirective.fadeOutAll(); + flushTransition(); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length) + .withContext('Expected no ripples to be active after calling fadeOutAll.') + .toBe(0); + }); + + it('should properly set ripple states', () => { + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + + let rippleRef = rippleDirective.launch(0, 0, {persistent: true}); + + expect(rippleRef.state).toBe(RippleState.FADING_IN); + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + + flushTransition(); + + expect(rippleRef.state).toBe(RippleState.VISIBLE); + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + + rippleRef.fadeOut(); + + expect(rippleRef.state).toBe(RippleState.FADING_OUT); + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + + flushTransition(); + + expect(rippleRef.state).toBe(RippleState.HIDDEN); + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + }); + + it('should allow setting a specific animation config for a ripple', () => { + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + + const rippleRef = rippleDirective.launch(0, 0, { + animation: {enterDuration: 120, exitDuration: 0}, + }); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + + // Since we cannot use `fakeAsync`, we manually verify that the element has + // the specified transition duration. + expect(rippleRef.element.style.transitionDuration).toBe('120ms'); + + // We still flush the 120ms transition and should check if the 0ms exit transition happened + // properly. + flushTransition(); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + }); + + it('should allow passing only a configuration', () => { + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + + const rippleRef = rippleDirective.launch({persistent: true}); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + + // Flush the fade-in transition. Additionally flush the potential fade-out transition + // in order to make sure that the ripple is persistent and won't fade-out. + flushTransition(); + flushTransition(); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + + rippleRef.fadeOut(); + flushTransition(); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + }); + }); + + describe('global ripple options', () => { + let rippleDirective: MatRipple; + + function createTestComponent( + rippleConfig: RippleGlobalOptions, + testComponent: any = BasicRippleContainer, + extraImports: any[] = [], + ) { + // Reset the previously configured testing module to be able set new providers. + // The testing module has been initialized in the root describe group for the ripples. + TestBed.resetTestingModule(); + TestBed.configureTestingModule({ + imports: [MatRippleModule, ...extraImports], + declarations: [testComponent], + providers: [{provide: MAT_RIPPLE_GLOBAL_OPTIONS, useValue: rippleConfig}], + }); + + fixture = TestBed.createComponent(testComponent); + fixture.detectChanges(); + + rippleTarget = fixture.nativeElement.querySelector('.mat-ripple'); + rippleDirective = fixture.componentInstance.ripple; + } + + it('should work without having any binding set', () => { + createTestComponent({disabled: true}, RippleContainerWithoutBindings); + + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + }); + + it('when disabled should not show any ripples on mousedown', () => { + createTestComponent({disabled: true}); + + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + }); + + it('when disabled should still allow manual ripples', () => { + createTestComponent({disabled: true}); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + + rippleDirective.launch(0, 0); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + }); + + it('should support changing the animation duration', () => { + createTestComponent({ + animation: {enterDuration: 100, exitDuration: 150}, + }); + + const rippleRef = rippleDirective.launch(0, 0); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + + expect(rippleRef.element.style.transitionDuration).toBe('100ms'); + flushTransition(); + + expect(rippleRef.element.style.transitionDuration).toBe('150ms'); + flushTransition(); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + }); + + it('should allow ripples to fade out immediately on pointer up', () => { + createTestComponent({ + terminateOnPointerUp: true, + }); + + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + + // Just flush the fade-out duration because we immediately fired the mouseup after the + // mousedown. This means that the ripple should just fade out, and there shouldn't be an + // enter animation. + flushTransition(); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + }); + + it('should not mutate the global options when NoopAnimationsModule is present', () => { + const options: RippleGlobalOptions = {}; + + createTestComponent(options, RippleContainerWithoutBindings, [NoopAnimationsModule]); + + expect(options.animation).toBeFalsy(); + }); + }); + + describe('with disabled animations', () => { + let rippleDirective: MatRipple; + + beforeEach(() => { + TestBed.resetTestingModule(); + TestBed.configureTestingModule({ + imports: [NoopAnimationsModule, MatRippleModule], + declarations: [BasicRippleContainer], + }); + + fixture = TestBed.createComponent(BasicRippleContainer); + fixture.detectChanges(); + + rippleTarget = fixture.nativeElement.querySelector('.mat-ripple'); + rippleDirective = fixture.componentInstance.ripple; + }); + + it('should set the animation durations to zero', () => { + expect(rippleDirective.rippleConfig.animation!.enterDuration).toBe(0); + expect(rippleDirective.rippleConfig.animation!.exitDuration).toBe(0); + }); + }); + + describe('configuring behavior', () => { + let controller: RippleContainerWithInputBindings; + + beforeEach(() => { + fixture = TestBed.createComponent(RippleContainerWithInputBindings); + fixture.detectChanges(); + + controller = fixture.debugElement.componentInstance; + rippleTarget = fixture.debugElement.nativeElement.querySelector('.mat-ripple'); + }); + + it('sets ripple color', () => { + const backgroundColor = 'rgba(12, 34, 56, 0.8)'; + + controller.color = backgroundColor; + fixture.detectChanges(); + + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); + + let ripple = rippleTarget.querySelector('.mat-ripple-element')!; + expect(window.getComputedStyle(ripple).backgroundColor).toBe(backgroundColor); + }); + + it('does not respond to events when disabled input is set', () => { + controller.disabled = true; + fixture.detectChanges(); + + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + + controller.disabled = false; + fixture.detectChanges(); + + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + }); + + it('fades out non-persistent ripples when disabled input is set', () => { + dispatchMouseEvent(rippleTarget, 'mousedown'); + controller.ripple.launch(0, 0, {persistent: true}); + + flushTransition(); + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(2); + + spyOn(controller.ripple, 'fadeOutAllNonPersistent').and.callThrough(); + controller.disabled = true; + fixture.detectChanges(); + + expect(controller.ripple.fadeOutAllNonPersistent).toHaveBeenCalled(); + + flushTransition(); + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + }); + + it('allows specifying custom trigger element', () => { + let alternateTrigger = fixture.debugElement.nativeElement.querySelector( + '.alternateTrigger', + ) as HTMLElement; + + dispatchMouseEvent(alternateTrigger, 'mousedown'); + dispatchMouseEvent(alternateTrigger, 'mouseup'); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + + // Set the trigger element, and now events should create ripples. + controller.trigger = alternateTrigger; + fixture.detectChanges(); + + dispatchMouseEvent(alternateTrigger, 'mousedown'); + dispatchMouseEvent(alternateTrigger, 'mouseup'); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + }); + + it('expands ripple from center if centered input is set', () => { + controller.centered = true; + fixture.detectChanges(); + + let elementRect = rippleTarget.getBoundingClientRect(); + + // Click the ripple element 50 px to the right and 75px down from its upper left. + dispatchMouseEvent(rippleTarget, 'mousedown', 50, 75); + dispatchMouseEvent(rippleTarget, 'mouseup'); + + // Because the centered input is true, the center of the ripple should be the midpoint of the + // bounding rect. The ripple should expand to cover the rect corners, which are 150px + // horizontally and 100px vertically from the midpoint. + let expectedRadius = Math.sqrt(150 * 150 + 100 * 100); + let expectedLeft = elementRect.left + elementRect.width / 2 - expectedRadius; + let expectedTop = elementRect.top + elementRect.height / 2 - expectedRadius; + + let ripple = rippleTarget.querySelector('.mat-ripple-element') as HTMLElement; + + expect(pxStringToFloat(ripple.style.left)).toBeCloseTo(expectedLeft, 1); + expect(pxStringToFloat(ripple.style.top)).toBeCloseTo(expectedTop, 1); + expect(pxStringToFloat(ripple.style.width)).toBeCloseTo(2 * expectedRadius, 1); + expect(pxStringToFloat(ripple.style.height)).toBeCloseTo(2 * expectedRadius, 1); + }); + + it('uses custom radius if set', () => { + let customRadius = 42; + + controller.radius = customRadius; + fixture.detectChanges(); + + let elementRect = rippleTarget.getBoundingClientRect(); + + // Click the ripple element 50 px to the right and 75px down from its upper left. + dispatchMouseEvent(rippleTarget, 'mousedown', 50, 75); + dispatchMouseEvent(rippleTarget, 'mouseup'); + + let expectedLeft = elementRect.left + 50 - customRadius; + let expectedTop = elementRect.top + 75 - customRadius; + + let ripple = rippleTarget.querySelector('.mat-ripple-element') as HTMLElement; + + expect(pxStringToFloat(ripple.style.left)).toBeCloseTo(expectedLeft, 1); + expect(pxStringToFloat(ripple.style.top)).toBeCloseTo(expectedTop, 1); + expect(pxStringToFloat(ripple.style.width)).toBeCloseTo(2 * customRadius, 1); + expect(pxStringToFloat(ripple.style.height)).toBeCloseTo(2 * customRadius, 1); + }); + + it('should be able to specify animation config through binding', () => { + controller.animationConfig = {enterDuration: 120, exitDuration: 150}; + fixture.detectChanges(); + + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + + const rippleElement = rippleTarget.querySelector('.mat-ripple-element')! as HTMLElement; + + expect(rippleElement.style.transitionDuration).toBe('120ms'); + flushTransition(); + + expect(rippleElement.style.transitionDuration).toBe('150ms'); + flushTransition(); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + }); + }); + + describe('edge cases', () => { + it('should handle forcibly disabled animations through CSS `transition: none`', async () => { + fixture = TestBed.createComponent(RippleCssTransitionNone); + fixture.detectChanges(); + + rippleTarget = fixture.nativeElement.querySelector('.mat-ripple'); + + dispatchMouseEvent(rippleTarget, 'mousedown'); + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + + dispatchMouseEvent(rippleTarget, 'mouseup'); + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + }); + + it('should handle forcibly disabled animations through CSS `transition-duration: 0ms`', async () => { + fixture = TestBed.createComponent(RippleCssTransitionDurationZero); + fixture.detectChanges(); + + rippleTarget = fixture.nativeElement.querySelector('.mat-ripple'); + + dispatchMouseEvent(rippleTarget, 'mousedown'); + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + + dispatchMouseEvent(rippleTarget, 'mouseup'); + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + }); + + it('should destroy the ripple if the transition is being canceled due to DOM removal', async () => { + fixture = TestBed.createComponent(RippleWithDomRemovalOnClick); + fixture.detectChanges(); + + rippleTarget = fixture.nativeElement.querySelector('.mat-ripple'); + + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); + dispatchMouseEvent(rippleTarget, 'click'); + + const fadingRipple = rippleTarget.querySelector('.mat-ripple-element'); + expect(fadingRipple).not.toBe(null); + + // The ripple animation is still on-going but the element is now removed from DOM as + // part of the change detecton (given `show` being set to `false` on click) + fixture.detectChanges(); + + // The `transitioncancel` event is emitted when a CSS transition is canceled due + // to e.g. DOM removal. More details in the CSS transitions spec: + // https://www.w3.org/TR/css-transitions-1/#:~:text=no%20longer%20in%20the%20document. + dispatchFakeEvent(fadingRipple!, 'transitioncancel'); + + // There should be no ripple element anymore because the fading-in ripple from + // before had its animation canceled due the DOM removal. + expect(rippleTarget.querySelector('.mat-ripple-element')).toBeNull(); + }); + }); +}); + +@Component({ + template: ` +
+
+ `, +}) +class BasicRippleContainer { + @ViewChild('ripple') ripple: MatRipple; +} + +@Component({ + template: ` +
+
+
+ `, +}) +class RippleContainerWithInputBindings { + animationConfig: RippleAnimationConfig; + trigger: HTMLElement; + centered = false; + disabled = false; + radius = 0; + color = ''; + @ViewChild(MatRipple) ripple: MatRipple; +} + +@Component({ + template: `
`, +}) +class RippleContainerWithoutBindings {} + +@Component({ + template: `
`, +}) +class RippleContainerWithNgIf { + @ViewChild(MatRipple) ripple: MatRipple; + isDestroyed = false; +} + +@Component({ + styles: [`* { transition: none !important; }`], + template: `
`, + encapsulation: ViewEncapsulation.None, +}) +class RippleCssTransitionNone {} + +@Component({ + styles: [`* { transition-duration: 0ms !important; }`], + template: `
`, + encapsulation: ViewEncapsulation.None, +}) +class RippleCssTransitionDurationZero {} + +@Component({ + template: ` +
+ Click to remove this element. +
+ `, +}) +class RippleWithDomRemovalOnClick { + show = true; +} diff --git a/ui/src/components/core/ripple/ripple.ts b/ui/src/components/core/ripple/ripple.ts new file mode 100644 index 000000000..ef7756f64 --- /dev/null +++ b/ui/src/components/core/ripple/ripple.ts @@ -0,0 +1,214 @@ +/** + * @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 {Platform} from '@angular/cdk/platform'; +import { + Directive, + ElementRef, + Inject, + InjectionToken, + Input, + NgZone, + OnDestroy, + OnInit, + Optional, +} from '@angular/core'; +import {RippleAnimationConfig, RippleConfig, RippleRef} from './ripple-ref'; +import {RippleRenderer, RippleTarget} from './ripple-renderer'; +import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations'; + +/** Configurable options for `matRipple`. */ +export interface RippleGlobalOptions { + /** + * Whether ripples should be disabled. Ripples can be still launched manually by using + * the `launch()` method. Therefore focus indicators will still show up. + */ + disabled?: boolean; + + /** + * Default configuration for the animation duration of the ripples. There are two phases with + * different durations for the ripples: `enter` and `leave`. The durations will be overwritten + * by the value of `matRippleAnimation` or if the `NoopAnimationsModule` is included. + */ + animation?: RippleAnimationConfig; + + /** + * Whether ripples should start fading out immediately after the mouse or touch is released. By + * default, ripples will wait for the enter animation to complete and for mouse or touch release. + */ + terminateOnPointerUp?: boolean; +} + +/** Injection token that can be used to specify the global ripple options. */ +export const MAT_RIPPLE_GLOBAL_OPTIONS = new InjectionToken( + 'mat-ripple-global-options', +); + +@Directive({ + selector: '[mat-ripple], [matRipple]', + exportAs: 'matRipple', + host: { + 'class': 'mat-ripple', + '[class.mat-ripple-unbounded]': 'unbounded', + }, +}) +export class MatRipple implements OnInit, OnDestroy, RippleTarget { + /** Custom color for all ripples. */ + @Input('matRippleColor') color: string; + + /** Whether the ripples should be visible outside the component's bounds. */ + @Input('matRippleUnbounded') unbounded: boolean; + + /** + * Whether the ripple always originates from the center of the host element's bounds, rather + * than originating from the location of the click event. + */ + @Input('matRippleCentered') centered: boolean; + + /** + * If set, the radius in pixels of foreground ripples when fully expanded. If unset, the radius + * will be the distance from the center of the ripple to the furthest corner of the host element's + * bounding rectangle. + */ + @Input('matRippleRadius') radius: number = 0; + + /** + * Configuration for the ripple animation. Allows modifying the enter and exit animation + * duration of the ripples. The animation durations will be overwritten if the + * `NoopAnimationsModule` is being used. + */ + @Input('matRippleAnimation') animation: RippleAnimationConfig; + + /** + * Whether click events will not trigger the ripple. Ripples can be still launched manually + * by using the `launch()` method. + */ + @Input('matRippleDisabled') + get disabled() { + return this._disabled; + } + set disabled(value: boolean) { + if (value) { + this.fadeOutAllNonPersistent(); + } + this._disabled = value; + this._setupTriggerEventsIfEnabled(); + } + private _disabled: boolean = false; + + /** + * The element that triggers the ripple when click events are received. + * Defaults to the directive's host element. + */ + @Input('matRippleTrigger') + get trigger() { + return this._trigger || this._elementRef.nativeElement; + } + set trigger(trigger: HTMLElement) { + this._trigger = trigger; + this._setupTriggerEventsIfEnabled(); + } + private _trigger: HTMLElement; + + /** Renderer for the ripple DOM manipulations. */ + private _rippleRenderer: RippleRenderer; + + /** Options that are set globally for all ripples. */ + private _globalOptions: RippleGlobalOptions; + + /** @docs-private Whether ripple directive is initialized and the input bindings are set. */ + _isInitialized: boolean = false; + + constructor( + private _elementRef: ElementRef, + ngZone: NgZone, + platform: Platform, + @Optional() @Inject(MAT_RIPPLE_GLOBAL_OPTIONS) globalOptions?: RippleGlobalOptions, + @Optional() @Inject(ANIMATION_MODULE_TYPE) private _animationMode?: string, + ) { + this._globalOptions = globalOptions || {}; + this._rippleRenderer = new RippleRenderer(this, ngZone, _elementRef, platform); + } + + ngOnInit() { + this._isInitialized = true; + this._setupTriggerEventsIfEnabled(); + } + + ngOnDestroy() { + this._rippleRenderer._removeTriggerEvents(); + } + + /** Fades out all currently showing ripple elements. */ + fadeOutAll() { + this._rippleRenderer.fadeOutAll(); + } + + /** Fades out all currently showing non-persistent ripple elements. */ + fadeOutAllNonPersistent() { + this._rippleRenderer.fadeOutAllNonPersistent(); + } + + /** + * Ripple configuration from the directive's input values. + * @docs-private Implemented as part of RippleTarget + */ + get rippleConfig(): RippleConfig { + return { + centered: this.centered, + radius: this.radius, + color: this.color, + animation: { + ...this._globalOptions.animation, + ...(this._animationMode === 'NoopAnimations' ? {enterDuration: 0, exitDuration: 0} : {}), + ...this.animation, + }, + terminateOnPointerUp: this._globalOptions.terminateOnPointerUp, + }; + } + + /** + * Whether ripples on pointer-down are disabled or not. + * @docs-private Implemented as part of RippleTarget + */ + get rippleDisabled(): boolean { + return this.disabled || !!this._globalOptions.disabled; + } + + /** Sets up the trigger event listeners if ripples are enabled. */ + private _setupTriggerEventsIfEnabled() { + if (!this.disabled && this._isInitialized) { + this._rippleRenderer.setupTriggerEvents(this.trigger); + } + } + + /** + * Launches a manual ripple using the specified ripple configuration. + * @param config Configuration for the manual ripple. + */ + launch(config: RippleConfig): RippleRef; + + /** + * Launches a manual ripple at the specified coordinates relative to the viewport. + * @param x Coordinate along the X axis at which to fade-in the ripple. Coordinate + * should be relative to the viewport. + * @param y Coordinate along the Y axis at which to fade-in the ripple. Coordinate + * should be relative to the viewport. + * @param config Optional ripple configuration for the manual ripple. + */ + launch(x: number, y: number, config?: RippleConfig): RippleRef; + + /** Launches a manual ripple at the specified coordinated or just by the ripple config. */ + launch(configOrX: number | RippleConfig, y: number = 0, config?: RippleConfig): RippleRef { + if (typeof configOrX === 'number') { + return this._rippleRenderer.fadeInRipple(configOrX, y, {...this.rippleConfig, ...config}); + } else { + return this._rippleRenderer.fadeInRipple(0, 0, {...this.rippleConfig, ...configOrX}); + } + } +} diff --git a/ui/src/components/core/style/_elevation.scss b/ui/src/components/core/style/_elevation.scss new file mode 100644 index 000000000..aff4b9020 --- /dev/null +++ b/ui/src/components/core/style/_elevation.scss @@ -0,0 +1,105 @@ +// @use 'sass:map'; +// @use 'sass:meta'; +// @use 'variables'; +// // @use '@material/elevation/elevation-theme' as mdc-elevation; + +// // A collection of mixins and CSS classes that can be used to apply elevation to a material +// // element. +// // See: https://material.io/design/environment/elevation.html +// // Examples: +// // +// // +// // .mat-foo { +// // @include $mat-elevation(2); +// // +// // &:active { +// // @include $mat-elevation(8); +// // } +// // } +// // +// //

Some content

+// // +// // For an explanation of the design behind how elevation is implemented, see the design doc at +// // https://goo.gl/Kq0k9Z. + +// // The default duration value for elevation transitions. +// $transition-duration: 280ms !default; + +// // The default easing value for elevation transitions. +// // $transition-timing-function: variables.$fast-out-slow-in-timing-function; + +// // The default color for elevation shadows. +// $color: black !default; + +// // Prefix for elevation-related selectors. +// $prefix: 'mat-elevation-z'; + +// // Applies the correct css rules to an element to give it the elevation specified by $zValue. +// // The $zValue must be between 0 and 24. +// @mixin elevation($zValue, $color: $color, $opacity: null) { +// @if meta.type-of($color) == color and $opacity == null { +// // @include mdc-elevation.elevation($zValue, $color); +// } +// @else { +// // Copied from @material/elevation/_elevation-theme.scss#_box-shadow +// // TODO(mmalerba): Add support for graceful handling of CSS var color to MDC. +// $umbra-z-value: map.get(mdc-elevation.$umbra-map, $zValue); +// $penumbra-z-value: map.get(mdc-elevation.$penumbra-map, $zValue); +// $ambient-z-value: map.get(mdc-elevation.$ambient-map, $zValue); + +// $color-opacity: if($opacity != null, $opacity, 1); +// $umbra-color: _compute-color-opacity($color, mdc-elevation.$umbra-opacity * $color-opacity); +// $penumbra-color: +// _compute-color-opacity($color, mdc-elevation.$penumbra-opacity * $color-opacity); +// $ambient-color: _compute-color-opacity($color, mdc-elevation.$ambient-opacity * $color-opacity); + +// $box-shadow: ( +// #{'#{$umbra-z-value} #{$umbra-color}'}, +// #{'#{$penumbra-z-value} #{$penumbra-color}'}, +// #{$ambient-z-value} $ambient-color +// ); +// @include mdc-elevation.shadow($box-shadow); +// } +// } + +// // Applies the elevation to an element in a manner that allows +// // consumers to override it via the Material elevation classes. +// @mixin overridable-elevation($zValue, $color: $color, $opacity: null) { +// &:not([class*='#{$prefix}']) { +// @include elevation($zValue, $color, $opacity); +// } +// } + +// // Returns a string that can be used as the value for a transition property for elevation. +// // Calling this function directly is useful in situations where a component needs to transition +// // more than one property. +// // +// // .foo { +// // transition: mat-elevation-transition-property-value(), opacity 100ms ease; +// // } +// @function private-transition-property-value( +// $duration: $transition-duration, +// $easing: $transition-timing-function) { +// @return box-shadow #{$duration} #{$easing}; +// } + +// // Applies the correct css rules needed to have an element transition between elevations. +// // This mixin should be applied to elements whose elevation values will change depending on their +// // context (e.g. when active or disabled). +// // +// // NOTE(traviskaufman): Both this mixin and the above function use default parameters so they can +// // be used in the same way by clients. +// @mixin elevation-transition( +// $duration: $transition-duration, +// $easing: $transition-timing-function) { +// transition: private-transition-property-value($duration, $easing); +// } + +// @function _compute-color-opacity($color, $opacity) { +// @if meta.type-of($color) == color and $opacity != null { +// @return rgba($color, $opacity); +// } +// @else { +// @return $color; +// } +// } diff --git a/ui/src/components/core/style/_layout-common.scss b/ui/src/components/core/style/_layout-common.scss new file mode 100644 index 000000000..eeadafebe --- /dev/null +++ b/ui/src/components/core/style/_layout-common.scss @@ -0,0 +1,8 @@ +// This mixin ensures an element spans to fill the nearest ancestor with defined positioning. +@mixin fill { + top: 0; + left: 0; + right: 0; + bottom: 0; + position: absolute; +} diff --git a/ui/src/components/core/style/_private.scss b/ui/src/components/core/style/_private.scss new file mode 100644 index 000000000..a5844f500 --- /dev/null +++ b/ui/src/components/core/style/_private.scss @@ -0,0 +1,30 @@ +@use './elevation'; +// @use '../theming/inspection'; + +@mixin private-theme-elevation($zValue, $theme) { + $elevation-color: inspection.get-theme-color($theme, foreground, elevation); + $elevation-color-or-default: if($elevation-color == null, elevation.$color, $elevation-color); + + @include elevation.elevation($zValue, $elevation-color-or-default); +} + +@mixin private-theme-overridable-elevation($zValue, $theme) { + $elevation-color: inspection.get-theme-color($theme, foreground, elevation); + $elevation-color-or-default: if($elevation-color == null, elevation.$color, $elevation-color); + + @include elevation.overridable-elevation($zValue, $elevation-color-or-default); +} + +// If the mat-animation-noop class is present on the components root element, +// prevent non css animations from running. +// NOTE: Currently this mixin should only be used with components that do not +// have any projected content. +@mixin private-animation-noop() { + &._mat-animation-noopable { + // Use !important here since we don't know what context this mixin will + // be included in and MDC can have some really specific selectors. + transition: none !important; + animation: none !important; + @content; + } +} diff --git a/ui/src/components/core/theming/_all-theme.scss b/ui/src/components/core/theming/_all-theme.scss index 115bff73f..d7ae6e8fe 100644 --- a/ui/src/components/core/theming/_all-theme.scss +++ b/ui/src/components/core/theming/_all-theme.scss @@ -11,7 +11,9 @@ @import '../../datepicker/datepicker-theme'; @import '../../checkbox/checkbox-theme'; @import '../../radio/radio-theme'; +@import '../../tabs/tabs-theme'; @import '../cdk/overlay/overlay-prebuilt'; // Loading overlay + // Create a theme. @mixin once-ui-theme($theme) { @include oui-button-theme($theme); @@ -26,4 +28,5 @@ @include oui-checkbox-theme($theme); @include oui-radio-theme($theme); @include oui-datepicker-theme($theme); + @include oui-tabs-theme($theme); } diff --git a/ui/src/components/core/version.ts b/ui/src/components/core/version.ts new file mode 100644 index 000000000..e983426f6 --- /dev/null +++ b/ui/src/components/core/version.ts @@ -0,0 +1,12 @@ +/** + * @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 {Version} from '@angular/core'; + +/** Current version of Angular Material. */ +export const VERSION = new Version('0.0.0-PLACEHOLDER'); diff --git a/ui/src/components/index.ts b/ui/src/components/index.ts index a401c87b4..796e935b1 100644 --- a/ui/src/components/index.ts +++ b/ui/src/components/index.ts @@ -17,3 +17,5 @@ export * from './progress-bar/public-api'; export * from './panel/public-api'; export * from './select/public-api'; export * from './slide-toggle/public-api'; +export * from './card/public-api'; +export * from './tabs/public-api'; diff --git a/ui/src/components/tabs/BUILD.bazel b/ui/src/components/tabs/BUILD.bazel new file mode 100644 index 000000000..17fdf740f --- /dev/null +++ b/ui/src/components/tabs/BUILD.bazel @@ -0,0 +1,124 @@ +load( + "//tools:defaults.bzl", + "markdown_to_html", + "ng_module", + "ng_test_library", + "ng_web_test_suite", + "sass_binary", + "sass_library", +) + +package(default_visibility = ["//visibility:public"]) + +ng_module( + name = "tabs", + srcs = glob( + ["**/*.ts"], + exclude = ["**/*.spec.ts"], + ), + assets = [ + ":tab-body.scss", + ":tab-header.scss", + ":tab-group.scss", + ":tab-nav-bar/tab-nav-bar.scss", + ":tab-nav-bar/tab-link.scss", + ] + glob(["**/*.html"]), + deps = [ + "//src:dev_mode_types", + "//src/cdk/a11y", + "//src/cdk/bidi", + "//src/cdk/coercion", + "//src/cdk/keycodes", + "//src/cdk/observers", + "//src/cdk/platform", + "//src/cdk/portal", + "//src/cdk/scrolling", + "//src/material/core", + "@npm//@angular/animations", + "@npm//@angular/common", + "@npm//@angular/core", + ], +) + +sass_library( + name = "tabs_scss_lib", + srcs = glob(["**/_*.scss"]), + deps = [ + "//:mdc_sass_lib", + "//src/material/core:core_scss_lib", + ], +) + +sass_binary( + name = "mdc_tab_body_scss", + src = "_tab-body.scss", + deps = [ + "//src/material/core:core_scss_lib", + ], +) + +sass_binary( + name = "mdc_tab_header_scss", + src = "tab-header.scss", + deps = [":tabs_scss_lib"], +) + +sass_binary( + name = "mdc_tab_group_scss", + src = "tab-group.scss", + deps = [ + ":tabs_scss_lib", + ], +) + +sass_binary( + name = "mdc_tab_nav_bar_scss", + src = "tab-nav-bar/tab-nav-bar.scss", + deps = [":tabs_scss_lib"], +) + +sass_binary( + name = "mdc_tab_link_scss", + src = "tab-nav-bar/tab-link.scss", + deps = [ + ":tabs_scss_lib", + ], +) + +ng_test_library( + name = "tabs_tests_lib", + srcs = glob( + ["**/*.spec.ts"], + ), + deps = [ + ":tabs", + "//src/cdk/bidi", + "//src/cdk/keycodes", + "//src/cdk/observers", + "//src/cdk/portal", + "//src/cdk/scrolling", + "//src/cdk/testing/private", + "//src/cdk/testing/testbed", + "//src/material/core", + "@npm//@angular/common", + "@npm//@angular/platform-browser", + "@npm//rxjs", + ], +) + +ng_web_test_suite( + name = "unit_tests", + deps = [ + ":tabs_tests_lib", + ], +) + +markdown_to_html( + name = "overview", + srcs = [":tabs.md"], +) + +filegroup( + name = "source-files", + srcs = glob(["**/*.ts"]), +) diff --git a/ui/src/components/tabs/README.md b/ui/src/components/tabs/README.md new file mode 100644 index 000000000..5f068875e --- /dev/null +++ b/ui/src/components/tabs/README.md @@ -0,0 +1 @@ +Please see the official documentation at https://material.angular.io/components/component/tabs diff --git a/ui/src/components/tabs/_tabs-common.scss b/ui/src/components/tabs/_tabs-common.scss new file mode 100644 index 000000000..06b5c3cd0 --- /dev/null +++ b/ui/src/components/tabs/_tabs-common.scss @@ -0,0 +1,514 @@ +// @use '../core/ripple' as mdc-ripple; +// @use '@material/tab' as mdc-tab; +// @use '@material/tab-indicator' as mdc-tab-indicator; +// @use '@material/tab-indicator/tab-indicator-theme' as mdc-tab-indicator-theme; +// @use '@material/tab/tab-theme' as mdc-tab-theme; +// @use '@material/theme/custom-properties' as mdc-custom-properties; +// @use '../core/mdc-helpers/mdc-helpers'; +// @use '../core/style/vendor-prefixes'; +// @use '../core/tokens/m2/mdc/tab-indicator' as tokens-mdc-tab-indicator; +// @use '../core/tokens/m2/mdc/tab' as tokens-mdc-tab; +// @use '../core/tokens/m2/mat/tab-header' as tokens-mat-tab-header; +// @use '../core/tokens/m2/mat/tab-header-with-background' as tokens-mat-tab-header-with-background; +// @use '../core/tokens/token-utils'; +@use 'sass:map'; +@use './tabs-theme'; + + +$mat-tab-animation-duration: 500ms !default; + +// Combines the various structural styles we need for the tab group and tab nav bar. +@mixin structural-styles { +// @include mdc-custom-properties.configure( +// $emit-fallback-values: false, +// $emit-fallback-vars: false +// ) { +// @include mdc-tab.static-styles($query: mdc-helpers.$mdc-base-styles-query); +// @include mdc-tab-indicator.static-styles($query: mdc-helpers.$mdc-base-styles-query); +// } + + .mat-mdc-tab-ripple { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + pointer-events: none; + } +} +.oui-tab { + &.oui-primary { + .mdc-tab-indicator--active .mdc-tab-indicator__content { + border-color: #006bb1; + } + } +} +.chatonce { + .oui-tab { + &.oui-primary { + .mdc-tab-indicator--active .mdc-tab-indicator__content { + border-color: #b731a9; + } + } + } +} +.inviteonce { + .oui-tab { + &.oui-primary { + .mdc-tab-indicator--active .mdc-tab-indicator__content { + border-color: #197439; + } + } + } +} +@mixin tab { +// @include mdc-custom-properties.configure( +// $emit-fallback-values: false, +// $emit-fallback-vars: false +// ) { +// @include mdc-tab-indicator-theme.theme-styles(tokens-mdc-tab-indicator.get-token-slots()); +// @include mdc-tab-theme.secondary-navigation-tab-theme-styles(tokens-mdc-tab.get-token-slots()); +// } + + + -webkit-tap-highlight-color: transparent; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-decoration: none; + + // Tabs might be `button` elements so we have to reset the user agent styling. + background: none; + + &.mdc-tab { + min-width: 90px; + padding-right: 24px; + padding-left: 24px; + display: flex; + flex: 1 0 auto; + justify-content: center; + box-sizing: border-box; + margin: 0; + padding-top: 0; + padding-bottom: 0; + border: none; + outline: none; + text-align: center; + white-space: nowrap; + cursor: pointer; + -webkit-appearance: none; + z-index: 1; + // MDC's tabs stretch to fit the header by default, whereas stretching on our current ones + // is an opt-in behavior. Also technically we don't need to combine the two classes, but + // we need the extra specificity to avoid issues with CSS insertion order. + flex-grow: 0; + } + + .mdc-tab__text-label { + transition: 150ms color linear; + display: inline-block; + line-height: 1; + z-index: 2; + } + + .mdc-tab--active .mdc-tab__text-label, .mdc-tab--active .mdc-tab__icon { + transition-delay: 100ms; + } + +// @include token-utils.use-tokens( +// tokens-mat-tab-header.$prefix, +// tokens-mat-tab-header.get-token-slots() +// ) { +// // @include token-utils.create-token-slot(font-family, label-text-font); +// // @include token-utils.create-token-slot(font-size, label-text-size); +// // @include token-utils.create-token-slot(letter-spacing, label-text-tracking); +// // @include token-utils.create-token-slot(line-height, label-text-line-height); +// // @include token-utils.create-token-slot(font-weight, label-text-weight); + +// &:hover .mdc-tab__text-label { +// @include token-utils.create-token-slot(color, inactive-hover-label-text-color); +// } + +// &:focus .mdc-tab__text-label { +// @include token-utils.create-token-slot(color, inactive-focus-label-text-color); +// } + +// &.mdc-tab--active { +// .mdc-tab__text-label { +// @include token-utils.create-token-slot(color, active-label-text-color); +// } + +// .mdc-tab__ripple::before, +// .mat-ripple-element { +// @include token-utils.create-token-slot(background-color, active-ripple-color); +// } + +// &:hover { +// .mdc-tab__text-label { +// @include token-utils.create-token-slot(color, active-hover-label-text-color); +// } + +// .mdc-tab-indicator__content--underline { +// @include token-utils.create-token-slot(border-color, active-hover-indicator-color); +// } +// } + +// &:focus { +// .mdc-tab__text-label { +// @include token-utils.create-token-slot(color, active-focus-label-text-color); +// } + +// .mdc-tab-indicator__content--underline { +// @include token-utils.create-token-slot(border-color, active-focus-indicator-color); +// } +// } +// } +// } + + &.mat-mdc-tab-disabled { + // MDC doesn't support disabled tabs so we need to improvise. + opacity: 0.4; + + // We use `pointer-events` to make the element unclickable when it's disabled, rather than + // preventing the default action through JS, because we can't prevent the action reliably + // due to other directives potentially registering their events earlier. This shouldn't cause + // the user to click through, because we always have a header behind the tab. Furthermore, this + // saves us some CSS, because we don't have to add `:not(.mat-mdc-tab-disabled)` to all the + // hover and focus selectors. + pointer-events: none; + + // We also need to prevent content from being clickable. + .mdc-tab__content { + pointer-events: none; + } + + // @include token-utils.use-tokens( + // tokens-mat-tab-header.$prefix, + // tokens-mat-tab-header.get-token-slots() + // ) { + // .mdc-tab__ripple::before, + // .mat-ripple-element { + // @include token-utils.create-token-slot(background-color, disabled-ripple-color); + // } + // } + } + + // Used to render out the background tint when hovered/focused. Usually this is done by + // MDC's ripple styles, however we're using our own ripples due to size concerns. + .mdc-tab__ripple::before { + content: ''; + display: block; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + opacity: 0; + pointer-events: none; + + // @include token-utils.use-tokens( + // tokens-mat-tab-header.$prefix, + // tokens-mat-tab-header.get-token-slots() + // ) { + // @include token-utils.create-token-slot(background-color, inactive-ripple-color); + // } + } + + .mdc-tab__text-label { + // @include token-utils.use-tokens( + // tokens-mat-tab-header.$prefix, + // tokens-mat-tab-header.get-token-slots() + // ) { + // @include token-utils.create-token-slot(color, inactive-label-text-color); + // } + + // We support projecting icons into the tab. These styles ensure that they're centered. + display: inline-flex; + align-items: center; + } + + .mdc-tab__content { + // Required for `fitInkBarToContent` to work. This used to be included with MDC's + // `without-ripple` mixin, but that no longer appears to be the case with `static-styles`. + // Since the latter is ~10kb smaller, we include this one extra style ourselves. + position: relative; + + // MDC sets `pointer-events: none` on the content which prevents interactions with the + // nested content. Re-enable it since we allow nesting any content in the tab (see #26195). + pointer-events: auto; + } + + // We need to handle the hover and focus indication ourselves, because we don't use MDC's ripple. + &:hover .mdc-tab__ripple::before { + // opacity: map.get(mdc-ripple.$dark-ink-opacities, hover); + } + + &.cdk-program-focused, + &.cdk-keyboard-focused { + .mdc-tab__ripple::before { + // opacity: map.get(mdc-ripple.$dark-ink-opacities, focus); + } + } + + .mat-ripple-element { + // opacity: map.get(mdc-ripple.$dark-ink-opacities, press); + + // @include token-utils.use-tokens( + // tokens-mat-tab-header.$prefix, + // tokens-mat-tab-header.get-token-slots() + // ) { + // @include token-utils.create-token-slot(background-color, inactive-ripple-color); + // } + } +} + +// Structural styles for a tab header. Used by both `mat-tab-header` and `mat-tab-nav-bar`. +// We need this styles on top of MDC's, because MDC doesn't support pagination like ours. +@mixin paginated-tab-header { + .mat-mdc-tab-header { + display: flex; + overflow: hidden; + position: relative; + flex-shrink: 0; + height: 48px; + + // @include mdc-tab-indicator-theme.theme(tokens-mdc-tab-indicator.get-unthemable-tokens()); + // @include mdc-tab-theme.secondary-navigation-tab-theme(tokens-mdc-tab.get-unthemable-tokens()); + // @include token-utils.create-token-values( + // tokens-mat-tab-header.$prefix, tokens-mat-tab-header.get-unthemable-tokens()); + } + + .mdc-tab-indicator .mdc-tab-indicator__content { + transition-duration: var(500ms, 250ms); + } + + .mdc-tab-indicator { + display: flex; + position: absolute; + top: 0; + left: 0; + justify-content: center; + width: 100%; + height: 100%; + pointer-events: none; + z-index: 1; + } + + .mat-mdc-tab:not(.mdc-tab--stacked) { + height: 48px; + } + + .mat-mdc-tab-header-pagination { + // @include vendor-prefixes.user-select(none); + position: relative; + display: none; + justify-content: center; + align-items: center; + min-width: 32px; + cursor: pointer; + z-index: 2; + -webkit-tap-highlight-color: transparent; + touch-action: none; + box-sizing: content-box; + background: none; + border: none; + outline: 0; + padding: 0; + + &::-moz-focus-inner { + border: 0; + } + + .mat-ripple-element { + // opacity: map.get(mdc-ripple.$dark-ink-opacities, press); + + // @include token-utils.use-tokens( + // tokens-mat-tab-header.$prefix, + // tokens-mat-tab-header.get-token-slots() + // ) { + // @include token-utils.create-token-slot(background-color, inactive-ripple-color); + // } + } + + .mat-mdc-tab-header-pagination-controls-enabled & { + display: flex; + } + } + + // The pagination control that is displayed on the left side of the tab header. + .mat-mdc-tab-header-pagination-before, + .mat-mdc-tab-header-rtl .mat-mdc-tab-header-pagination-after { + padding-left: 4px; + .mat-mdc-tab-header-pagination-chevron { + transform: rotate(-135deg); + } + } + + // The pagination control that is displayed on the right side of the tab header. + .mat-mdc-tab-header-rtl .mat-mdc-tab-header-pagination-before, + .mat-mdc-tab-header-pagination-after { + padding-right: 4px; + .mat-mdc-tab-header-pagination-chevron { + transform: rotate(45deg); + } + } + + .mat-mdc-tab-header-pagination-chevron { + border-style: solid; + border-width: 2px 2px 0 0; + height: 8px; + width: 8px; + + // @include token-utils.use-tokens( + // tokens-mat-tab-header.$prefix, + // tokens-mat-tab-header.get-token-slots() + // ) { + // @include token-utils.create-token-slot(border-color, pagination-icon-color); + // } + } + + .mat-mdc-tab-header-pagination-disabled { + box-shadow: none; + cursor: default; + pointer-events: none; + + .mat-mdc-tab-header-pagination-chevron { + opacity: 0.4; + } + } + + .mdc-tab-indicator__content--underline { + align-self: flex-end; + box-sizing: border-box; + width: 100%; + border-top-style: solid; + } + + .mdc-tab-indicator__content { + transform-origin: left; + opacity: 0; + } + + .mdc-tab-indicator .mdc-tab-indicator__content { + transition: 250ms transform cubic-bezier(0.4, 0, 0.2, 1); + } + + .mdc-tab-indicator--active .mdc-tab-indicator__content { + opacity: 1; + // border-color: $background; + } + + .mat-mdc-tab-list { + flex-grow: 1; + position: relative; + transition: transform 500ms cubic-bezier(0.35, 0, 0.25, 1); + + ._mat-animation-noopable & { + transition: none; + } + } + + // The `span` is in the selector in order to increase the specificity, ensuring + // that it's always higher than the selector that declares the transition. + ._mat-animation-noopable { + span.mdc-tab-indicator__content, + span.mdc-tab__text-label { + transition: none; + } + } +} + +// Structural styles for the element that wraps the paginated header items. +@mixin paginated-tab-header-item-wrapper($parent) { + display: flex; + flex: 1 0 auto; + + // We need to set the parent here explicitly, in order to prevent the alignment + // from any parent tab groups from propagating down to the children when nesting. + // Note that these are used as inputs so they shouldn't be changed to `mat-mdc-`. + [mat-align-tabs='center'] > #{$parent} & { + justify-content: center; + } + + [mat-align-tabs='end'] > #{$parent} & { + justify-content: flex-end; + } +} + +// Structural styles for the element that wraps the paginated container's content. +@mixin paginated-tab-header-container { + display: flex; + flex-grow: 1; + overflow: hidden; + z-index: 1; +} + +.mat-mdc-tab .mdc-tab-indicator__content--underline { + // @include oui-input-base(); +} + +/*common properties in all inputs*/ +@mixin oui-input-base { +} + +@mixin paginated-tab-header-with-background($header-selector, $tab-selector) { + &.mat-tabs-with-background { + // @include token-utils.create-token-values( + // tokens-mat-tab-header-with-background.$prefix, + // tokens-mat-tab-header-with-background.get-unthemable-tokens() + // ); + + // @include token-utils.use-tokens( + // tokens-mat-tab-header-with-background.$prefix, + // tokens-mat-tab-header-with-background.get-token-slots() + // ) { + // // Note that these selectors target direct descendants so + // // that the styles don't apply to any nested tab groups. + // > #{$header-selector}, > .mat-mdc-tab-header-pagination { + // // Set background color for the tab group + // @include token-utils.create-token-slot(background-color, background-color); + // } + + // // Note: this is only scoped to primary, because the legacy tabs had the incorrect behavior + // // where setting both a background and `mat-accent` would add the background, but keep + // // accent on the selected tab. There are some internal apps whose design depends on this now + // // so we have to replicate it here. + // &.mat-primary > #{$header-selector} { + // // Set labels to contrast against background + // #{$tab-selector} .mdc-tab__text-label { + // @include token-utils.create-token-slot(color, foreground-color); + // } + + // .mdc-tab-indicator__content--underline { + // @include token-utils.create-token-slot(border-color, foreground-color); + // } + // } + + // &:not(.mat-primary) > #{$header-selector} { + // #{$tab-selector}:not(.mdc-tab--active) { + // .mdc-tab__text-label { + // @include token-utils.create-token-slot(color, foreground-color); + // } + + // .mdc-tab-indicator__content--underline { + // @include token-utils.create-token-slot(border-color, foreground-color); + // } + // } + // } + + // > #{$header-selector}, > .mat-mdc-tab-header-pagination { + // .mat-mdc-tab-header-pagination-chevron, + // .mat-mdc-focus-indicator::before { + // @include token-utils.create-token-slot(border-color, foreground-color); + // } + + // .mat-ripple-element, .mdc-tab__ripple::before { + // @include token-utils.create-token-slot(background-color, foreground-color); + // } + + // .mat-mdc-tab-header-pagination-chevron { + // @include token-utils.create-token-slot(color, foreground-color); + // } + // } + // } + } +} diff --git a/ui/src/components/tabs/_tabs-theme.scss b/ui/src/components/tabs/_tabs-theme.scss new file mode 100644 index 000000000..bfdd60219 --- /dev/null +++ b/ui/src/components/tabs/_tabs-theme.scss @@ -0,0 +1,48 @@ +@mixin oui-tabs-theme($theme) { + $primary: map-get($theme, primary); + $accent: map-get($theme, accent); + $warn: map-get($theme, warn); + $background: map-get($theme, background); + $foreground: map-get($theme, foreground); + .oui-tab { + @include oui-tabs-theme-palette($theme); + } +} +/*generating themes for oui-button(Primary buttons)*/ +@mixin oui-tabs-theme-palette($theme) { + $primary: map-get($theme, primary); + $accent: map-get($theme, accent); + $warn: map-get($theme, warn); + $border-color: map-get($theme, background); + &.oui-primary { + .mdc-tab-indicator--active { + border-color: map-get($primary, default); + } + &:hover, + &[class^='cdk'], + &.cdk-keyboard-focused { + border-color: map-get($primary, darker); + } + } + &.oui-accent { + border-color: map-get($accent, default); + &:hover, + &[class^='cdk'], + &.cdk-keyboard-focused { + border-color: map-get($accent, darker); + } + } + &.oui-warn { + border-color: map-get($warn, default); + &:hover, + &[class^='cdk'], + &.cdk-keyboard-focused { + border-color: map-get($warn, darker); + } + } + &.oui-primary[disabled], + &.oui-accent[disabled], + &.oui-warn[disabled] { + border-color: map-get($background, disabled-button); + } +} diff --git a/ui/src/components/tabs/index.ts b/ui/src/components/tabs/index.ts new file mode 100644 index 000000000..676ca90f1 --- /dev/null +++ b/ui/src/components/tabs/index.ts @@ -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'; diff --git a/ui/src/components/tabs/ink-bar.ts b/ui/src/components/tabs/ink-bar.ts new file mode 100644 index 000000000..c68cf1fb4 --- /dev/null +++ b/ui/src/components/tabs/ink-bar.ts @@ -0,0 +1,217 @@ +/** + * @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 {BooleanInput, coerceBooleanProperty} from '@angular/cdk/coercion'; +import {ElementRef, InjectionToken, OnDestroy, OnInit, QueryList} from '@angular/core'; +import { isDevMode } from '@angular/core'; + + +/** + * Item inside a tab header relative to which the ink bar can be aligned. + * @docs-private + */ +export interface MatInkBarItem extends OnInit, OnDestroy { + elementRef: ElementRef; + activateInkBar(previousIndicatorClientRect?: ClientRect): void; + deactivateInkBar(): void; + fitInkBarToContent: boolean; +} + +/** Class that is applied when a tab indicator is active. */ +const ACTIVE_CLASS = 'mdc-tab-indicator--active'; + +/** Class that is applied when the tab indicator should not transition. */ +const NO_TRANSITION_CLASS = 'mdc-tab-indicator--no-transition'; + +/** + * Abstraction around the MDC tab indicator that acts as the tab header's ink bar. + * @docs-private + */ +export class MatInkBar { + /** Item to which the ink bar is aligned currently. */ + private _currentItem: MatInkBarItem | undefined; + + constructor(private _items: QueryList) {} + + /** Hides the ink bar. */ + hide() { + this._items.forEach(item => item.deactivateInkBar()); + } + + /** Aligns the ink bar to a DOM node. */ + alignToElement(element: HTMLElement) { + const correspondingItem = this._items.find(item => item.elementRef.nativeElement === element); + const currentItem = this._currentItem; + + if (correspondingItem === currentItem) { + return; + } + + currentItem?.deactivateInkBar(); + + if (correspondingItem) { + const clientRect = currentItem?.elementRef.nativeElement.getBoundingClientRect?.(); + + // The ink bar won't animate unless we give it the `ClientRect` of the previous item. + correspondingItem.activateInkBar(clientRect); + this._currentItem = correspondingItem; + } + } +} + +/** + * Mixin that can be used to apply the `MatInkBarItem` behavior to a class. + * Base on MDC's `MDCSlidingTabIndicatorFoundation`: + * https://github.com/material-components/material-components-web/blob/c0a11ef0d000a098fd0c372be8f12d6a99302855/packages/mdc-tab-indicator/sliding-foundation.ts + * @docs-private + */ +export function mixinInkBarItem< + T extends new (...args: any[]) => {elementRef: ElementRef}, +>(base: T): T & (new (...args: any[]) => MatInkBarItem) { + return class extends base { + constructor(...args: any[]) { + super(...args); + } + + private _inkBarElement: HTMLElement | null; + private _inkBarContentElement: HTMLElement | null; + private _fitToContent = false; + + /** Whether the ink bar should fit to the entire tab or just its content. */ + get fitInkBarToContent(): boolean { + return this._fitToContent; + } + set fitInkBarToContent(v: BooleanInput) { + const newValue = coerceBooleanProperty(v); + + if (this._fitToContent !== newValue) { + this._fitToContent = newValue; + + if (this._inkBarElement) { + this._appendInkBarElement(); + } + } + } + + /** Aligns the ink bar to the current item. */ + activateInkBar(previousIndicatorClientRect?: ClientRect) { + const element = this.elementRef.nativeElement; + + // Early exit if no indicator is present to handle cases where an indicator + // may be activated without a prior indicator state + if ( + !previousIndicatorClientRect || + !element.getBoundingClientRect || + !this._inkBarContentElement + ) { + element.classList.add(ACTIVE_CLASS); + return; + } + + // This animation uses the FLIP approach. You can read more about it at the link below: + // https://aerotwist.com/blog/flip-your-animations/ + + // Calculate the dimensions based on the dimensions of the previous indicator + const currentClientRect = element.getBoundingClientRect(); + const widthDelta = previousIndicatorClientRect.width / currentClientRect.width; + const xPosition = previousIndicatorClientRect.left - currentClientRect.left; + element.classList.add(NO_TRANSITION_CLASS); + this._inkBarContentElement.style.setProperty( + 'transform', + `translateX(${xPosition}px) scaleX(${widthDelta})`, + ); + + // Force repaint before updating classes and transform to ensure the transform properly takes effect + element.getBoundingClientRect(); + + element.classList.remove(NO_TRANSITION_CLASS); + element.classList.add(ACTIVE_CLASS); + this._inkBarContentElement.style.setProperty('transform', ''); + } + + /** Removes the ink bar from the current item. */ + deactivateInkBar() { + this.elementRef.nativeElement.classList.remove(ACTIVE_CLASS); + } + + /** Initializes the foundation. */ + ngOnInit() { + this._createInkBarElement(); + } + + /** Destroys the foundation. */ + ngOnDestroy() { + this._inkBarElement?.remove(); + this._inkBarElement = this._inkBarContentElement = null!; + } + + /** Creates and appends the ink bar element. */ + private _createInkBarElement() { + const documentNode = this.elementRef.nativeElement.ownerDocument || document; + this._inkBarElement = documentNode.createElement('span'); + this._inkBarContentElement = documentNode.createElement('span'); + + this._inkBarElement.className = 'mdc-tab-indicator'; + this._inkBarContentElement.className = + 'mdc-tab-indicator__content mdc-tab-indicator__content--underline'; + + this._inkBarElement.appendChild(this._inkBarContentElement); + this._appendInkBarElement(); + } + + /** + * Appends the ink bar to the tab host element or content, depending on whether + * the ink bar should fit to content. + */ + private _appendInkBarElement() { + if (!this._inkBarElement && (typeof isDevMode === 'undefined' || isDevMode)) { + throw Error('Ink bar element has not been created and cannot be appended'); + } + + const parentElement = this._fitToContent + ? this.elementRef.nativeElement.querySelector('.mdc-tab__content') + : this.elementRef.nativeElement; + + if (!parentElement && (typeof isDevMode === 'undefined' || isDevMode)) { + throw Error('Missing element to host the ink bar'); + } + + parentElement!.appendChild(this._inkBarElement!); + } + }; +} + +/** + * Interface for a MatInkBar positioner method, defining the positioning and width of the ink + * bar in a set of tabs. + */ +export interface _MatInkBarPositioner { + (element: HTMLElement): {left: string; width: string}; +} + +/** + * The default positioner function for the MatInkBar. + * @docs-private + */ +export function _MAT_INK_BAR_POSITIONER_FACTORY(): _MatInkBarPositioner { + const method = (element: HTMLElement) => ({ + left: element ? (element.offsetLeft || 0) + 'px' : '0', + width: element ? (element.offsetWidth || 0) + 'px' : '0', + }); + + return method; +} + +/** Injection token for the MatInkBar's Positioner. */ +export const _MAT_INK_BAR_POSITIONER = new InjectionToken<_MatInkBarPositioner>( + 'MatInkBarPositioner', + { + providedIn: 'root', + factory: _MAT_INK_BAR_POSITIONER_FACTORY, + }, +); diff --git a/ui/src/components/tabs/module.ts b/ui/src/components/tabs/module.ts new file mode 100644 index 000000000..1052c5a34 --- /dev/null +++ b/ui/src/components/tabs/module.ts @@ -0,0 +1,60 @@ +/** + * @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 {CommonModule} from '@angular/common'; +import {NgModule} from '@angular/core'; +import { MatRippleModule} from '../core'; +import {PortalModule} from '@angular/cdk/portal'; +import {ObserversModule} from '@angular/cdk/observers'; +import {A11yModule} from '@angular/cdk/a11y'; +import {MatTabBodyPortal} from './tab-body'; +import {MatTabContent} from './tab-content'; +import {MatTabLabel} from './tab-label'; +import {MatTabLabelWrapper} from './tab-label-wrapper'; +import {MatTab} from './tab'; +import {MatTabHeader} from './tab-header'; +import {MatTabGroup} from './tab-group'; +import {MatTabNav, MatTabNavPanel, MatTabLink} from './tab-nav-bar/tab-nav-bar'; +import { MatTabBody } from './tab-body'; + +@NgModule({ + imports: [ + CommonModule, + PortalModule, + MatRippleModule, + ObserversModule, + A11yModule, + ], + exports: [ + MatTabContent, + MatTabLabel, + MatTab, + MatTabGroup, + MatTabNav, + MatTabNavPanel, + MatTabLink, + MatTabBodyPortal, + MatTabBody + ], + declarations: [ + MatTabContent, + MatTabLabel, + MatTab, + MatTabGroup, + MatTabNav, + MatTabNavPanel, + MatTabLink, + + // Private directives, should not be exported. + MatTabLabelWrapper, + MatTabHeader, + MatTabBody, + MatTabBodyPortal, + ], +}) +export class MatTabsModule {} diff --git a/ui/src/components/tabs/paginated-tab-header.ts b/ui/src/components/tabs/paginated-tab-header.ts new file mode 100644 index 000000000..9547a8c3d --- /dev/null +++ b/ui/src/components/tabs/paginated-tab-header.ts @@ -0,0 +1,658 @@ +/** + * @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 { + ChangeDetectorRef, + ElementRef, + NgZone, + Optional, + QueryList, + EventEmitter, + AfterContentChecked, + AfterContentInit, + AfterViewInit, + OnDestroy, + Directive, + Inject, + Input, +} from '@angular/core'; +import {Direction, Directionality} from '@angular/cdk/bidi'; +import { + BooleanInput, + coerceBooleanProperty, + coerceNumberProperty, + NumberInput, +} from '@angular/cdk/coercion'; +import {ViewportRuler} from '@angular/cdk/scrolling'; +import {FocusKeyManager, FocusableOption} from '@angular/cdk/a11y'; +import {ENTER, SPACE, hasModifierKey} from '@angular/cdk/keycodes'; +import { + merge, + of as observableOf, + Subject, + EMPTY, + Observer, + Observable, + timer, + fromEvent, +} from 'rxjs'; +import {take, switchMap, startWith, skip, takeUntil, filter} from 'rxjs/operators'; +import {Platform, normalizePassiveListenerOptions} from '@angular/cdk/platform'; +import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations'; + +/** Config used to bind passive event listeners */ +const passiveEventListenerOptions = normalizePassiveListenerOptions({ + passive: true, +}) as EventListenerOptions; + +/** + * The directions that scrolling can go in when the header's tabs exceed the header width. 'After' + * will scroll the header towards the end of the tabs list and 'before' will scroll towards the + * beginning of the list. + */ +export type ScrollDirection = 'after' | 'before'; + +/** + * Amount of milliseconds to wait before starting to scroll the header automatically. + * Set a little conservatively in order to handle fake events dispatched on touch devices. + */ +const HEADER_SCROLL_DELAY = 650; + +/** + * Interval in milliseconds at which to scroll the header + * while the user is holding their pointer. + */ +const HEADER_SCROLL_INTERVAL = 100; + +/** Item inside a paginated tab header. */ +export type MatPaginatedTabHeaderItem = FocusableOption & {elementRef: ElementRef}; + +/** + * Base class for a tab header that supported pagination. + * @docs-private + */ +@Directive() +export abstract class MatPaginatedTabHeader + implements AfterContentChecked, AfterContentInit, AfterViewInit, OnDestroy +{ + abstract _items: QueryList; + abstract _inkBar: {hide: () => void; alignToElement: (element: HTMLElement) => void}; + abstract _tabListContainer: ElementRef; + abstract _tabList: ElementRef; + abstract _tabListInner: ElementRef; + abstract _nextPaginator: ElementRef; + abstract _previousPaginator: ElementRef; + + /** The distance in pixels that the tab labels should be translated to the left. */ + private _scrollDistance = 0; + + /** Whether the header should scroll to the selected index after the view has been checked. */ + private _selectedIndexChanged = false; + + /** Emits when the component is destroyed. */ + protected readonly _destroyed = new Subject(); + + /** Whether the controls for pagination should be displayed */ + _showPaginationControls = false; + + /** Whether the tab list can be scrolled more towards the end of the tab label list. */ + _disableScrollAfter = true; + + /** Whether the tab list can be scrolled more towards the beginning of the tab label list. */ + _disableScrollBefore = true; + + /** + * The number of tab labels that are displayed on the header. When this changes, the header + * should re-evaluate the scroll position. + */ + private _tabLabelCount: number; + + /** Whether the scroll distance has changed and should be applied after the view is checked. */ + private _scrollDistanceChanged: boolean; + + /** Used to manage focus between the tabs. */ + private _keyManager: FocusKeyManager; + + /** Cached text content of the header. */ + private _currentTextContent: string; + + /** Stream that will stop the automated scrolling. */ + private _stopScrolling = new Subject(); + + /** + * Whether pagination should be disabled. This can be used to avoid unnecessary + * layout recalculations if it's known that pagination won't be required. + */ + @Input() + get disablePagination(): boolean { + return this._disablePagination; + } + set disablePagination(value: BooleanInput) { + this._disablePagination = coerceBooleanProperty(value); + } + private _disablePagination: boolean = false; + + /** The index of the active tab. */ + get selectedIndex(): number { + return this._selectedIndex; + } + set selectedIndex(value: NumberInput) { + value = coerceNumberProperty(value); + + if (this._selectedIndex != value) { + this._selectedIndexChanged = true; + this._selectedIndex = value; + + if (this._keyManager) { + this._keyManager.updateActiveItem(value); + } + } + } + private _selectedIndex: number = 0; + + /** Event emitted when the option is selected. */ + readonly selectFocusedIndex: EventEmitter = new EventEmitter(); + + /** Event emitted when a label is focused. */ + readonly indexFocused: EventEmitter = new EventEmitter(); + + constructor( + protected _elementRef: ElementRef, + protected _changeDetectorRef: ChangeDetectorRef, + private _viewportRuler: ViewportRuler, + @Optional() private _dir: Directionality, + private _ngZone: NgZone, + private _platform: Platform, + @Optional() @Inject(ANIMATION_MODULE_TYPE) public _animationMode?: string, + ) { + // Bind the `mouseleave` event on the outside since it doesn't change anything in the view. + _ngZone.runOutsideAngular(() => { + fromEvent(_elementRef.nativeElement, 'mouseleave') + .pipe(takeUntil(this._destroyed)) + .subscribe(() => { + this._stopInterval(); + }); + }); + } + + /** Called when the user has selected an item via the keyboard. */ + protected abstract _itemSelected(event: KeyboardEvent): void; + + ngAfterViewInit() { + // We need to handle these events manually, because we want to bind passive event listeners. + fromEvent(this._previousPaginator.nativeElement, 'touchstart', passiveEventListenerOptions) + .pipe(takeUntil(this._destroyed)) + .subscribe(() => { + this._handlePaginatorPress('before'); + }); + + fromEvent(this._nextPaginator.nativeElement, 'touchstart', passiveEventListenerOptions) + .pipe(takeUntil(this._destroyed)) + .subscribe(() => { + this._handlePaginatorPress('after'); + }); + } + + ngAfterContentInit() { + const dirChange = this._dir ? this._dir.change : observableOf('ltr'); + const resize = this._viewportRuler.change(150); + const realign = () => { + this.updatePagination(); + this._alignInkBarToSelectedTab(); + }; + + this._keyManager = new FocusKeyManager(this._items) + .withHorizontalOrientation(this._getLayoutDirection()) + .withHomeAndEnd() + .withWrap() + // Allow focus to land on disabled tabs, as per https://w3c.github.io/aria-practices/#kbd_disabled_controls + .skipPredicate(() => false); + + this._keyManager.updateActiveItem(this._selectedIndex); + + // Defer the first call in order to allow for slower browsers to lay out the elements. + // This helps in cases where the user lands directly on a page with paginated tabs. + // Note that we use `onStable` instead of `requestAnimationFrame`, because the latter + // can hold up tests that are in a background tab. + this._ngZone.onStable.pipe(take(1)).subscribe(realign); + + // On dir change or window resize, realign the ink bar and update the orientation of + // the key manager if the direction has changed. + merge(dirChange, resize, this._items.changes, this._itemsResized()) + .pipe(takeUntil(this._destroyed)) + .subscribe(() => { + // We need to defer this to give the browser some time to recalculate + // the element dimensions. The call has to be wrapped in `NgZone.run`, + // because the viewport change handler runs outside of Angular. + this._ngZone.run(() => { + Promise.resolve().then(() => { + // Clamp the scroll distance, because it can change with the number of tabs. + this._scrollDistance = Math.max( + 0, + Math.min(this._getMaxScrollDistance(), this._scrollDistance), + ); + realign(); + }); + }); + this._keyManager.withHorizontalOrientation(this._getLayoutDirection()); + }); + + // If there is a change in the focus key manager we need to emit the `indexFocused` + // event in order to provide a public event that notifies about focus changes. Also we realign + // the tabs container by scrolling the new focused tab into the visible section. + this._keyManager.change.subscribe(newFocusIndex => { + this.indexFocused.emit(newFocusIndex); + this._setTabFocus(newFocusIndex); + }); + } + + /** Sends any changes that could affect the layout of the items. */ + private _itemsResized(): Observable { + if (typeof ResizeObserver !== 'function') { + return EMPTY; + } + + return this._items.changes.pipe( + startWith(this._items), + switchMap( + (tabItems: QueryList) => + new Observable((observer: Observer) => + this._ngZone.runOutsideAngular(() => { + const resizeObserver = new ResizeObserver(entries => observer.next(entries)); + tabItems.forEach(item => resizeObserver.observe(item.elementRef.nativeElement)); + return () => { + resizeObserver.disconnect(); + }; + }), + ), + ), + // Skip the first emit since the resize observer emits when an item + // is observed for new items when the tab is already inserted + skip(1), + // Skip emissions where all the elements are invisible since we don't want + // the header to try and re-render with invalid measurements. See #25574. + filter(entries => entries.some(e => e.contentRect.width > 0 && e.contentRect.height > 0)), + ); + } + + ngAfterContentChecked(): void { + // If the number of tab labels have changed, check if scrolling should be enabled + if (this._tabLabelCount != this._items.length) { + this.updatePagination(); + this._tabLabelCount = this._items.length; + this._changeDetectorRef.markForCheck(); + } + + // If the selected index has changed, scroll to the label and check if the scrolling controls + // should be disabled. + if (this._selectedIndexChanged) { + this._scrollToLabel(this._selectedIndex); + this._checkScrollingControls(); + this._alignInkBarToSelectedTab(); + this._selectedIndexChanged = false; + this._changeDetectorRef.markForCheck(); + } + + // If the scroll distance has been changed (tab selected, focused, scroll controls activated), + // then translate the header to reflect this. + if (this._scrollDistanceChanged) { + this._updateTabScrollPosition(); + this._scrollDistanceChanged = false; + this._changeDetectorRef.markForCheck(); + } + } + + ngOnDestroy() { + // this._keyManager?.destroy(); + this._destroyed.next(); + this._destroyed.complete(); + this._stopScrolling.complete(); + } + + /** Handles keyboard events on the header. */ + _handleKeydown(event: KeyboardEvent) { + // We don't handle any key bindings with a modifier key. + if (hasModifierKey(event)) { + return; + } + + switch (event.keyCode) { + case ENTER: + case SPACE: + if (this.focusIndex !== this.selectedIndex) { + const item = this._items.get(this.focusIndex); + + if (item && !item.disabled) { + this.selectFocusedIndex.emit(this.focusIndex); + this._itemSelected(event); + } + } + break; + default: + this._keyManager.onKeydown(event); + } + } + + /** + * Callback for when the MutationObserver detects that the content has changed. + */ + _onContentChanges() { + const textContent = this._elementRef.nativeElement.textContent; + + // We need to diff the text content of the header, because the MutationObserver callback + // will fire even if the text content didn't change which is inefficient and is prone + // to infinite loops if a poorly constructed expression is passed in (see #14249). + if (textContent !== this._currentTextContent) { + this._currentTextContent = textContent || ''; + + // The content observer runs outside the `NgZone` by default, which + // means that we need to bring the callback back in ourselves. + this._ngZone.run(() => { + this.updatePagination(); + this._alignInkBarToSelectedTab(); + this._changeDetectorRef.markForCheck(); + }); + } + } + + /** + * Updates the view whether pagination should be enabled or not. + * + * WARNING: Calling this method can be very costly in terms of performance. It should be called + * as infrequently as possible from outside of the Tabs component as it causes a reflow of the + * page. + */ + updatePagination() { + this._checkPaginationEnabled(); + this._checkScrollingControls(); + this._updateTabScrollPosition(); + } + + /** Tracks which element has focus; used for keyboard navigation */ + get focusIndex(): number { + return this._keyManager ? this._keyManager.activeItemIndex! : 0; + } + + /** When the focus index is set, we must manually send focus to the correct label */ + set focusIndex(value: number) { + if (!this._isValidIndex(value) || this.focusIndex === value || !this._keyManager) { + return; + } + + this._keyManager.setActiveItem(value); + } + + /** + * Determines if an index is valid. If the tabs are not ready yet, we assume that the user is + * providing a valid index and return true. + */ + _isValidIndex(index: number): boolean { + return this._items ? !!this._items.toArray()[index] : true; + } + + /** + * Sets focus on the HTML element for the label wrapper and scrolls it into the view if + * scrolling is enabled. + */ + _setTabFocus(tabIndex: number) { + if (this._showPaginationControls) { + this._scrollToLabel(tabIndex); + } + + if (this._items && this._items.length) { + this._items.toArray()[tabIndex].focus(); + + // Do not let the browser manage scrolling to focus the element, this will be handled + // by using translation. In LTR, the scroll left should be 0. In RTL, the scroll width + // should be the full width minus the offset width. + const containerEl = this._tabListContainer.nativeElement; + const dir = this._getLayoutDirection(); + + if (dir == 'ltr') { + containerEl.scrollLeft = 0; + } else { + containerEl.scrollLeft = containerEl.scrollWidth - containerEl.offsetWidth; + } + } + } + + /** The layout direction of the containing app. */ + _getLayoutDirection(): Direction { + return this._dir && this._dir.value === 'rtl' ? 'rtl' : 'ltr'; + } + + /** Performs the CSS transformation on the tab list that will cause the list to scroll. */ + _updateTabScrollPosition() { + if (this.disablePagination) { + return; + } + + const scrollDistance = this.scrollDistance; + const translateX = this._getLayoutDirection() === 'ltr' ? -scrollDistance : scrollDistance; + + // Don't use `translate3d` here because we don't want to create a new layer. A new layer + // seems to cause flickering and overflow in Internet Explorer. For example, the ink bar + // and ripples will exceed the boundaries of the visible tab bar. + // See: https://github.com/angular/components/issues/10276 + // We round the `transform` here, because transforms with sub-pixel precision cause some + // browsers to blur the content of the element. + this._tabList.nativeElement.style.transform = `translateX(${Math.round(translateX)}px)`; + + // Setting the `transform` on IE will change the scroll offset of the parent, causing the + // position to be thrown off in some cases. We have to reset it ourselves to ensure that + // it doesn't get thrown off. Note that we scope it only to IE and Edge, because messing + // with the scroll position throws off Chrome 71+ in RTL mode (see #14689). + if (this._platform.TRIDENT || this._platform.EDGE) { + this._tabListContainer.nativeElement.scrollLeft = 0; + } + } + + /** Sets the distance in pixels that the tab header should be transformed in the X-axis. */ + get scrollDistance(): number { + return this._scrollDistance; + } + set scrollDistance(value: number) { + this._scrollTo(value); + } + + /** + * Moves the tab list in the 'before' or 'after' direction (towards the beginning of the list or + * the end of the list, respectively). The distance to scroll is computed to be a third of the + * length of the tab list view window. + * + * This is an expensive call that forces a layout reflow to compute box and scroll metrics and + * should be called sparingly. + */ + _scrollHeader(direction: ScrollDirection) { + const viewLength = this._tabListContainer.nativeElement.offsetWidth; + + // Move the scroll distance one-third the length of the tab list's viewport. + const scrollAmount = ((direction == 'before' ? -1 : 1) * viewLength) / 3; + + return this._scrollTo(this._scrollDistance + scrollAmount); + } + + /** Handles click events on the pagination arrows. */ + _handlePaginatorClick(direction: ScrollDirection) { + this._stopInterval(); + this._scrollHeader(direction); + } + + /** + * Moves the tab list such that the desired tab label (marked by index) is moved into view. + * + * This is an expensive call that forces a layout reflow to compute box and scroll metrics and + * should be called sparingly. + */ + _scrollToLabel(labelIndex: number) { + if (this.disablePagination) { + return; + } + + const selectedLabel = this._items ? this._items.toArray()[labelIndex] : null; + + if (!selectedLabel) { + return; + } + + // The view length is the visible width of the tab labels. + const viewLength = this._tabListContainer.nativeElement.offsetWidth; + const {offsetLeft, offsetWidth} = selectedLabel.elementRef.nativeElement; + + let labelBeforePos: number, labelAfterPos: number; + if (this._getLayoutDirection() == 'ltr') { + labelBeforePos = offsetLeft; + labelAfterPos = labelBeforePos + offsetWidth; + } else { + labelAfterPos = this._tabListInner.nativeElement.offsetWidth - offsetLeft; + labelBeforePos = labelAfterPos - offsetWidth; + } + + const beforeVisiblePos = this.scrollDistance; + const afterVisiblePos = this.scrollDistance + viewLength; + + if (labelBeforePos < beforeVisiblePos) { + // Scroll header to move label to the before direction + this.scrollDistance -= beforeVisiblePos - labelBeforePos; + } else if (labelAfterPos > afterVisiblePos) { + // Scroll header to move label to the after direction + this.scrollDistance += Math.min( + labelAfterPos - afterVisiblePos, + labelBeforePos - beforeVisiblePos, + ); + } + } + + /** + * Evaluate whether the pagination controls should be displayed. If the scroll width of the + * tab list is wider than the size of the header container, then the pagination controls should + * be shown. + * + * This is an expensive call that forces a layout reflow to compute box and scroll metrics and + * should be called sparingly. + */ + _checkPaginationEnabled() { + if (this.disablePagination) { + this._showPaginationControls = false; + } else { + const isEnabled = + this._tabListInner.nativeElement.scrollWidth > this._elementRef.nativeElement.offsetWidth; + + if (!isEnabled) { + this.scrollDistance = 0; + } + + if (isEnabled !== this._showPaginationControls) { + this._changeDetectorRef.markForCheck(); + } + + this._showPaginationControls = isEnabled; + } + } + + /** + * Evaluate whether the before and after controls should be enabled or disabled. + * If the header is at the beginning of the list (scroll distance is equal to 0) then disable the + * before button. If the header is at the end of the list (scroll distance is equal to the + * maximum distance we can scroll), then disable the after button. + * + * This is an expensive call that forces a layout reflow to compute box and scroll metrics and + * should be called sparingly. + */ + _checkScrollingControls() { + if (this.disablePagination) { + this._disableScrollAfter = this._disableScrollBefore = true; + } else { + // Check if the pagination arrows should be activated. + this._disableScrollBefore = this.scrollDistance == 0; + this._disableScrollAfter = this.scrollDistance == this._getMaxScrollDistance(); + this._changeDetectorRef.markForCheck(); + } + } + + /** + * Determines what is the maximum length in pixels that can be set for the scroll distance. This + * is equal to the difference in width between the tab list container and tab header container. + * + * This is an expensive call that forces a layout reflow to compute box and scroll metrics and + * should be called sparingly. + */ + _getMaxScrollDistance(): number { + const lengthOfTabList = this._tabListInner.nativeElement.scrollWidth; + const viewLength = this._tabListContainer.nativeElement.offsetWidth; + return lengthOfTabList - viewLength || 0; + } + + /** Tells the ink-bar to align itself to the current label wrapper */ + _alignInkBarToSelectedTab(): void { + const selectedItem = + this._items && this._items.length ? this._items.toArray()[this.selectedIndex] : null; + const selectedLabelWrapper = selectedItem ? selectedItem.elementRef.nativeElement : null; + + if (selectedLabelWrapper) { + this._inkBar.alignToElement(selectedLabelWrapper); + } else { + this._inkBar.hide(); + } + } + + /** Stops the currently-running paginator interval. */ + _stopInterval() { + this._stopScrolling.next(); + } + + /** + * Handles the user pressing down on one of the paginators. + * Starts scrolling the header after a certain amount of time. + * @param direction In which direction the paginator should be scrolled. + */ + _handlePaginatorPress(direction: ScrollDirection, mouseEvent?: MouseEvent) { + // Don't start auto scrolling for right mouse button clicks. Note that we shouldn't have to + // null check the `button`, but we do it so we don't break tests that use fake events. + if (mouseEvent && mouseEvent.button != null && mouseEvent.button !== 0) { + return; + } + + // Avoid overlapping timers. + this._stopInterval(); + + // Start a timer after the delay and keep firing based on the interval. + timer(HEADER_SCROLL_DELAY, HEADER_SCROLL_INTERVAL) + // Keep the timer going until something tells it to stop or the component is destroyed. + .pipe(takeUntil(merge(this._stopScrolling, this._destroyed))) + .subscribe(() => { + const {maxScrollDistance, distance} = this._scrollHeader(direction); + + // Stop the timer if we've reached the start or the end. + if (distance === 0 || distance >= maxScrollDistance) { + this._stopInterval(); + } + }); + } + + /** + * Scrolls the header to a given position. + * @param position Position to which to scroll. + * @returns Information on the current scroll distance and the maximum. + */ + private _scrollTo(position: number) { + if (this.disablePagination) { + return {maxScrollDistance: 0, distance: 0}; + } + + const maxScrollDistance = this._getMaxScrollDistance(); + this._scrollDistance = Math.max(0, Math.min(maxScrollDistance, position)); + + // Mark that the scroll distance has changed so that after the view is checked, the CSS + // transformation can move the header. + this._scrollDistanceChanged = true; + this._checkScrollingControls(); + + return {maxScrollDistance, distance: this._scrollDistance}; + } +} diff --git a/ui/src/components/tabs/public-api.ts b/ui/src/components/tabs/public-api.ts new file mode 100644 index 000000000..17175d3d6 --- /dev/null +++ b/ui/src/components/tabs/public-api.ts @@ -0,0 +1,21 @@ +/** + * @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 './module'; +export * from './paginated-tab-header'; +export * from './tab'; +export * from './tab-body'; +export * from './tab-config'; +export * from './tab-content'; +export * from './tab-label'; +export * from './ink-bar'; +export * from './tab-header'; +export * from './tab-group'; +export * from './tab-nav-bar/tab-nav-bar'; +export * from './tabs-animations'; +export * from './tab-label-wrapper'; diff --git a/ui/src/components/tabs/tab-body.html b/ui/src/components/tabs/tab-body.html new file mode 100644 index 000000000..0d750b86b --- /dev/null +++ b/ui/src/components/tabs/tab-body.html @@ -0,0 +1,6 @@ +
+ + +
diff --git a/ui/src/components/tabs/tab-body.scss b/ui/src/components/tabs/tab-body.scss new file mode 100644 index 000000000..5d24ace1d --- /dev/null +++ b/ui/src/components/tabs/tab-body.scss @@ -0,0 +1,45 @@ +@use '../core/style/layout-common'; + +// Wraps each tab body. We need to add these styles ourselves, +// because MDC only provides styling for the tab header. +.mat-mdc-tab-body { + @include layout-common.fill(); + display: block; + overflow: hidden; + outline: 0; + + // Fix for auto content wrapping in IE11 + flex-basis: 100%; + + &.mat-mdc-tab-body-active { + position: relative; + overflow-x: hidden; + overflow-y: auto; + z-index: 1; + flex-grow: 1; + } + + .mat-mdc-tab-group.mat-mdc-tab-group-dynamic-height &.mat-mdc-tab-body-active { + overflow-y: hidden; + } +} + +.mat-mdc-tab-body-content { + height: 100%; + overflow: auto; + + .mat-mdc-tab-group-dynamic-height & { + overflow: hidden; + } + + // Usually the `visibility: hidden` added by the animation is enough to prevent focus from + // entering the collapsed content, but children with their own `visibility` can override it. + // This is a fallback that completely hides the content when the element becomes hidden. + // Note that we can't do this in the animation definition, because the style gets recomputed too + // late, breaking the animation because Angular didn't have time to figure out the target height. + // This can also be achieved with JS, but it has issues when starting an animation before + // the previous one has finished. + &[style*='visibility: hidden'] { + display: none; + } +} diff --git a/ui/src/components/tabs/tab-body.spec.ts b/ui/src/components/tabs/tab-body.spec.ts new file mode 100644 index 000000000..70040a5f1 --- /dev/null +++ b/ui/src/components/tabs/tab-body.spec.ts @@ -0,0 +1,218 @@ +import {Direction, Directionality} from '@angular/cdk/bidi'; +import {PortalModule, TemplatePortal} from '@angular/cdk/portal'; +import {CommonModule} from '@angular/common'; +import {AfterContentInit, Component, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core'; +import {waitForAsync, ComponentFixture, TestBed} from '@angular/core/testing'; +import {MatRippleModule} from '../core'; +import {NoopAnimationsModule} from '@angular/platform-browser/animations'; +import {CdkScrollable, ScrollingModule} from '@angular/cdk/scrolling'; +import {MatTabBody, MatTabBodyPortal} from './tab-body'; +import {By} from '@angular/platform-browser'; +import {Subject} from 'rxjs'; + +describe('MDC-based MatTabBody', () => { + let dir: Direction = 'ltr'; + let dirChange: Subject = new Subject(); + + beforeEach(waitForAsync(() => { + dir = 'ltr'; + TestBed.configureTestingModule({ + imports: [CommonModule, PortalModule, MatRippleModule, NoopAnimationsModule], + declarations: [MatTabBody, MatTabBodyPortal, SimpleTabBodyApp], + providers: [{provide: Directionality, useFactory: () => ({value: dir, change: dirChange})}], + }); + + TestBed.compileComponents(); + })); + + describe('when initialized as center', () => { + let fixture: ComponentFixture; + + it('should be center position if origin is unchanged', () => { + fixture = TestBed.createComponent(SimpleTabBodyApp); + fixture.componentInstance.position = 0; + fixture.detectChanges(); + + expect(fixture.componentInstance.tabBody._position).toBe('center'); + }); + + it('should be center position if origin is explicitly set to null', () => { + fixture = TestBed.createComponent(SimpleTabBodyApp); + fixture.componentInstance.position = 0; + + // It can happen that the `origin` is explicitly set to null through the Angular input + // binding. This test should ensure that the body does properly such origin value. + // The `MatTab` class sets the origin by default to null. See related issue: #12455 + fixture.componentInstance.origin = null; + fixture.detectChanges(); + + expect(fixture.componentInstance.tabBody._position).toBe('center'); + }); + + describe('in LTR direction', () => { + beforeEach(() => { + dir = 'ltr'; + fixture = TestBed.createComponent(SimpleTabBodyApp); + }); + it('should be left-origin-center position with negative or zero origin', () => { + fixture.componentInstance.position = 0; + fixture.componentInstance.origin = 0; + fixture.detectChanges(); + + expect(fixture.componentInstance.tabBody._position).toBe('left-origin-center'); + }); + + it('should be right-origin-center position with positive nonzero origin', () => { + fixture.componentInstance.position = 0; + fixture.componentInstance.origin = 1; + fixture.detectChanges(); + + expect(fixture.componentInstance.tabBody._position).toBe('right-origin-center'); + }); + }); + + describe('in RTL direction', () => { + beforeEach(() => { + dir = 'rtl'; + fixture = TestBed.createComponent(SimpleTabBodyApp); + }); + + it('should be right-origin-center position with negative or zero origin', () => { + fixture.componentInstance.position = 0; + fixture.componentInstance.origin = 0; + fixture.detectChanges(); + + expect(fixture.componentInstance.tabBody._position).toBe('right-origin-center'); + }); + + it('should be left-origin-center position with positive nonzero origin', () => { + fixture.componentInstance.position = 0; + fixture.componentInstance.origin = 1; + fixture.detectChanges(); + + expect(fixture.componentInstance.tabBody._position).toBe('left-origin-center'); + }); + }); + }); + + describe('should properly set the position in LTR', () => { + let fixture: ComponentFixture; + + beforeEach(() => { + dir = 'ltr'; + fixture = TestBed.createComponent(SimpleTabBodyApp); + fixture.detectChanges(); + }); + + it('to be left position with negative position', () => { + fixture.componentInstance.position = -1; + fixture.detectChanges(); + + expect(fixture.componentInstance.tabBody._position).toBe('left'); + }); + + it('to be center position with zero position', () => { + fixture.componentInstance.position = 0; + fixture.detectChanges(); + + expect(fixture.componentInstance.tabBody._position).toBe('center'); + }); + + it('to be left position with positive position', () => { + fixture.componentInstance.position = 1; + fixture.detectChanges(); + + expect(fixture.componentInstance.tabBody._position).toBe('right'); + }); + }); + + describe('should properly set the position in RTL', () => { + let fixture: ComponentFixture; + + beforeEach(() => { + dir = 'rtl'; + fixture = TestBed.createComponent(SimpleTabBodyApp); + fixture.detectChanges(); + }); + + it('to be right position with negative position', () => { + fixture.componentInstance.position = -1; + fixture.detectChanges(); + + expect(fixture.componentInstance.tabBody._position).toBe('right'); + }); + + it('to be center position with zero position', () => { + fixture.componentInstance.position = 0; + fixture.detectChanges(); + + expect(fixture.componentInstance.tabBody._position).toBe('center'); + }); + + it('to be left position with positive position', () => { + fixture.componentInstance.position = 1; + fixture.detectChanges(); + + expect(fixture.componentInstance.tabBody._position).toBe('left'); + }); + }); + + it('should update position if direction changed at runtime', () => { + const fixture = TestBed.createComponent(SimpleTabBodyApp); + + fixture.componentInstance.position = 1; + fixture.detectChanges(); + + expect(fixture.componentInstance.tabBody._position).toBe('right'); + + dirChange.next('rtl'); + dir = 'rtl'; + + fixture.detectChanges(); + + expect(fixture.componentInstance.tabBody._position).toBe('left'); + }); + + it('should mark the tab body content as a scrollable container', () => { + TestBed.resetTestingModule() + .configureTestingModule({ + imports: [ + CommonModule, + PortalModule, + MatRippleModule, + NoopAnimationsModule, + ScrollingModule, + ], + declarations: [MatTabBody, MatTabBodyPortal, SimpleTabBodyApp], + }) + .compileComponents(); + + const fixture = TestBed.createComponent(SimpleTabBodyApp); + const tabBodyContent = fixture.nativeElement.querySelector('.mat-mdc-tab-body-content'); + const scrollable = fixture.debugElement.query(By.directive(CdkScrollable)); + + expect(scrollable).toBeTruthy(); + expect(scrollable.nativeElement).toBe(tabBodyContent); + }); +}); + +@Component({ + template: ` + Tab Body Content + + `, +}) +class SimpleTabBodyApp implements AfterContentInit { + content: TemplatePortal; + position: number; + origin: number | null; + + @ViewChild(MatTabBody) tabBody: MatTabBody; + @ViewChild(TemplateRef) template: TemplateRef; + + constructor(private _viewContainerRef: ViewContainerRef) {} + + ngAfterContentInit() { + this.content = new TemplatePortal(this.template, this._viewContainerRef); + } +} diff --git a/ui/src/components/tabs/tab-body.ts b/ui/src/components/tabs/tab-body.ts new file mode 100644 index 000000000..6b7f67297 --- /dev/null +++ b/ui/src/components/tabs/tab-body.ts @@ -0,0 +1,271 @@ +/** + * @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 { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ComponentFactoryResolver, + Directive, + ElementRef, + EventEmitter, + // forwardRef, + Inject, + Input, + OnDestroy, + OnInit, + Optional, + Output, + ViewChild, + ViewContainerRef, + ViewEncapsulation, +} from '@angular/core'; +import {CdkPortalOutlet} from '@angular/cdk/portal'; +import {Direction, Directionality} from '@angular/cdk/bidi'; +import {DOCUMENT} from '@angular/common'; +import {Subject, Subscription} from 'rxjs'; +import {distinctUntilChanged, + // startWith +} from 'rxjs/operators'; +import {AnimationEvent} from '@angular/animations'; +import {matTabsAnimations} from './tabs-animations'; + +/** + * The portal host directive for the contents of the tab. + * @docs-private + */ + +@Directive({ + selector: `MatTabBodyHost` +}) +export class MatTabBodyPortal extends CdkPortalOutlet implements OnInit, OnDestroy { + /** Subscription to events for when the tab body begins centering. */ + private _centeringSub = Subscription.EMPTY; + /** Subscription to events for when the tab body finishes leaving from center position. */ + private _leavingSub = Subscription.EMPTY; + + constructor( + componentFactoryResolver: ComponentFactoryResolver, + viewContainerRef: ViewContainerRef, + // @Inject(forwardRef(() => MatTabBody)) private _host: MatTabBody, + @Inject(DOCUMENT) _document: any, + ) { + super(componentFactoryResolver, viewContainerRef, _document); + } + + /** Set initial visibility or set up subscription for changing visibility. */ + override ngOnInit(): void { + super.ngOnInit(); + + console.log('in directive...') + // this._centeringSub = this._host._beforeCentering + // .pipe(startWith(this._host._isCenterPosition(this._host._position))) + // .subscribe((isCentering: boolean) => { + // if (isCentering && !this.hasAttached()) { + // this.attach(this._host._content); + // } + // }); + + // this._leavingSub = this._host._afterLeavingCenter.subscribe(() => { + // if (!this._host.preserveContent) { + // this.detach(); + // } + // }); + } + + /** Clean up centering subscription. */ + override ngOnDestroy(): void { + super.ngOnDestroy(); + this._centeringSub.unsubscribe(); + this._leavingSub.unsubscribe(); + } +} + +/** + * These position states are used internally as animation states for the tab body. Setting the + * position state to left, right, or center will transition the tab body from its current + * position to its respective state. If there is not current position (void, in the case of a new + * tab body), then there will be no transition animation to its state. + * + * In the case of a new tab body that should immediately be centered with an animating transition, + * then left-origin-center or right-origin-center can be used, which will use left or right as its + * pseudo-prior state. + */ +export type MatTabBodyPositionState = + | 'left' + | 'center' + | 'right' + | 'left-origin-center' + | 'right-origin-center'; + +/** + * Wrapper for the contents of a tab. + * @docs-private + */ +@Component({ + selector: 'mat-tab-body', + templateUrl: 'tab-body.html', + // styleUrls: ['tab-body.css'], + encapsulation: ViewEncapsulation.None, + // tslint:disable-next-line:validate-decorators + changeDetection: ChangeDetectionStrategy.Default, + animations: [matTabsAnimations.translateTab], + host: { + 'class': 'mat-mdc-tab-body', + }, +}) +export class MatTabBody implements OnInit, OnDestroy { + /** Current position of the tab-body in the tab-group. Zero means that the tab is visible. */ + private _positionIndex: number; + + /** Subscription to the directionality change observable. */ + private _dirChangeSubscription = Subscription.EMPTY; + + /** Tab body position state. Used by the animation trigger for the current state. */ + _position: MatTabBodyPositionState; + + /** Emits when an animation on the tab is complete. */ + readonly _translateTabComplete = new Subject(); + + /** Event emitted when the tab begins to animate towards the center as the active tab. */ + @Output() readonly _onCentering: EventEmitter = new EventEmitter(); + + /** Event emitted before the centering of the tab begins. */ + @Output() readonly _beforeCentering: EventEmitter = new EventEmitter(); + + /** Event emitted before the centering of the tab begins. */ + @Output() readonly _afterLeavingCenter: EventEmitter = new EventEmitter(); + + /** Event emitted when the tab completes its animation towards the center. */ + @Output() readonly _onCentered: EventEmitter = new EventEmitter(true); + + /** The portal host inside of this container into which the tab body content will be loaded. */ + @ViewChild(CdkPortalOutlet) _portalHost: CdkPortalOutlet; + + /** The tab body content to display. */ + @Input('content') _content: any; + + /** Position that will be used when the tab is immediately becoming visible after creation. */ + @Input() origin: number | null; + + // Note that the default value will always be overwritten by `MatTabBody`, but we need one + // anyway to prevent the animations module from throwing an error if the body is used on its own. + /** Duration for the tab's animation. */ + @Input() animationDuration: string = '0'; + + /** Whether the tab's content should be kept in the DOM while it's off-screen. */ + @Input() preserveContent: boolean = false; + + /** The shifted index position of the tab body, where zero represents the active center tab. */ + @Input() + set position(position: number) { + this._positionIndex = position; + this._computePositionAnimationState(); + } + + constructor( + private _elementRef: ElementRef, + @Optional() private _dir: Directionality, + changeDetectorRef: ChangeDetectorRef, + ) { + if (_dir) { + this._dirChangeSubscription = _dir.change.subscribe((dir: Direction) => { + this._computePositionAnimationState(dir); + changeDetectorRef.markForCheck(); + }); + } + + // Ensure that we get unique animation events, because the `.done` callback can get + // invoked twice in some browsers. See https://github.com/angular/angular/issues/24084. + this._translateTabComplete + .pipe( + distinctUntilChanged((x, y) => { + return x.fromState === y.fromState && x.toState === y.toState; + }), + ) + .subscribe(event => { + // If the transition to the center is complete, emit an event. + if (this._isCenterPosition(event.toState) && this._isCenterPosition(this._position)) { + this._onCentered.emit(); + } + + if (this._isCenterPosition(event.fromState) && !this._isCenterPosition(this._position)) { + this._afterLeavingCenter.emit(); + } + }); + } + + /** + * After initialized, check if the content is centered and has an origin. If so, set the + * special position states that transition the tab from the left or right before centering. + */ + ngOnInit() { + if (this._position == 'center' && this.origin != null) { + this._position = this._computePositionFromOrigin(this.origin); + } + console.log('sssssssss', this._content) + } + + ngOnDestroy() { + this._dirChangeSubscription.unsubscribe(); + this._translateTabComplete.complete(); + } + + _onTranslateTabStarted(event: AnimationEvent): void { + const isCentering = this._isCenterPosition(event.toState); + this._beforeCentering.emit(isCentering); + if (isCentering) { + this._onCentering.emit(this._elementRef.nativeElement.clientHeight); + } + } + + /** The text direction of the containing app. */ + _getLayoutDirection(): Direction { + return this._dir && this._dir.value === 'rtl' ? 'rtl' : 'ltr'; + } + + /** Whether the provided position state is considered center, regardless of origin. */ + _isCenterPosition(position: MatTabBodyPositionState | string): boolean { + return ( + position == 'center' || position == 'left-origin-center' || position == 'right-origin-center' + ); + } + + /** Computes the position state that will be used for the tab-body animation trigger. */ + private _computePositionAnimationState(dir: Direction = this._getLayoutDirection()) { + if (this._positionIndex < 0) { + this._position = dir == 'ltr' ? 'left' : 'right'; + } else if (this._positionIndex > 0) { + this._position = dir == 'ltr' ? 'right' : 'left'; + } else { + this._position = 'center'; + } + } + + /** + * Computes the position state based on the specified origin position. This is used if the + * tab is becoming visible immediately after creation. + */ + private _computePositionFromOrigin(origin: number): MatTabBodyPositionState { + const dir = this._getLayoutDirection(); + + if ((dir == 'ltr' && origin <= 0) || (dir == 'rtl' && origin > 0)) { + return 'left-origin-center'; + } + + return 'right-origin-center'; + } +} + +/** + * The origin state is an internally used state that is set on a new tab body indicating if it + * began to the left or right of the prior selected index. For example, if the selected index was + * set to 1, and a new tab is created and selected at index 2, then the tab body would have an + * origin of right because its index was greater than the prior selected index. + */ +export type MatTabBodyOriginState = 'left' | 'right'; diff --git a/ui/src/components/tabs/tab-config.ts b/ui/src/components/tabs/tab-config.ts new file mode 100644 index 000000000..414b722d1 --- /dev/null +++ b/ui/src/components/tabs/tab-config.ts @@ -0,0 +1,45 @@ +/** + * @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 {InjectionToken} from '@angular/core'; + +/** Object that can be used to configure the default options for the tabs module. */ +export interface MatTabsConfig { + /** Duration for the tab animation. Must be a valid CSS value (e.g. 600ms). */ + animationDuration?: string; + + /** + * Whether pagination should be disabled. This can be used to avoid unnecessary + * layout recalculations if it's known that pagination won't be required. + */ + disablePagination?: boolean; + + /** + * Whether the ink bar should fit its width to the size of the tab label content. + * This only applies to the MDC-based tabs. + */ + fitInkBarToContent?: boolean; + + /** Whether the tab group should grow to the size of the active tab. */ + dynamicHeight?: boolean; + + /** `tabindex` to be set on the inner element that wraps the tab content. */ + contentTabIndex?: number; + + /** + * By default tabs remove their content from the DOM while it's off-screen. + * Setting this to `true` will keep it in the DOM which will prevent elements + * like iframes and videos from reloading next time it comes back into the view. + */ + preserveContent?: boolean; + + /** Whether tabs should be stretched to fill the header. */ + stretchTabs?: boolean; +} + +/** Injection token that can be used to provide the default options the tabs module. */ +export const MAT_TABS_CONFIG = new InjectionToken('MAT_TABS_CONFIG'); diff --git a/ui/src/components/tabs/tab-content.ts b/ui/src/components/tabs/tab-content.ts new file mode 100644 index 000000000..03f097d32 --- /dev/null +++ b/ui/src/components/tabs/tab-content.ts @@ -0,0 +1,25 @@ +/** + * @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, InjectionToken, TemplateRef} from '@angular/core'; + +/** + * Injection token that can be used to reference instances of `MatTabContent`. It serves as + * alternative token to the actual `MatTabContent` class which could cause unnecessary + * retention of the class and its directive metadata. + */ +export const MAT_TAB_CONTENT = new InjectionToken('MatTabContent'); + +/** Decorates the `ng-template` tags and reads out the template from it. */ +@Directive({ + selector: '[matTabContent]', + providers: [{provide: MAT_TAB_CONTENT, useExisting: MatTabContent}], +}) +export class MatTabContent { + constructor(/** Content for the tab. */ public template: TemplateRef) {} +} diff --git a/ui/src/components/tabs/tab-group.html b/ui/src/components/tabs/tab-group.html new file mode 100644 index 000000000..6ff53cf31 --- /dev/null +++ b/ui/src/components/tabs/tab-group.html @@ -0,0 +1,72 @@ + + + + + +
+ + +
diff --git a/ui/src/components/tabs/tab-group.scss b/ui/src/components/tabs/tab-group.scss new file mode 100644 index 000000000..29fd298ca --- /dev/null +++ b/ui/src/components/tabs/tab-group.scss @@ -0,0 +1,56 @@ +@use '../core/style/private'; +@use '../core/style/variables'; +@use './tabs-common'; + +@include tabs-common.structural-styles; + +.mat-mdc-tab { + @include tabs-common.tab; + + // Note that we only want to target direct descendant tabs. + .mat-mdc-tab-group.mat-mdc-tab-group-stretch-tabs > .mat-mdc-tab-header & { + flex-grow: 1; + } +} + +.mat-mdc-focus-indicator { + position: relative; +} + +.mdc-tab__content { + display: flex; + align-items: center; + justify-content: center; + height: inherit; + pointer-events: none; +} + +.mat-mdc-tab.mdc-tab--active .mdc-tab__text-label { + color: #333; +} + +.mat-mdc-tab-group { +// @include tabs-common.paginated-tab-header-with-background('.mat-mdc-tab-header', '.mat-mdc-tab'); + display: flex; + flex-direction: column; + + // Fixes pagination issues inside flex containers (see #23157). + max-width: 100%; + + &.mat-mdc-tab-group-inverted-header { + flex-direction: column-reverse; + + .mdc-tab-indicator__content--underline { + align-self: flex-start; + } + } +} + +// The bottom section of the view; contains the tab bodies +.mat-mdc-tab-body-wrapper { +// @include private.private-animation-noop(); + position: relative; + overflow: hidden; + display: flex; +// transition: height tabs-common.$mat-tab-animation-duration variables.$ease-in-out-curve-function; +} diff --git a/ui/src/components/tabs/tab-group.spec.ts b/ui/src/components/tabs/tab-group.spec.ts new file mode 100644 index 000000000..0b092d81f --- /dev/null +++ b/ui/src/components/tabs/tab-group.spec.ts @@ -0,0 +1,1409 @@ +import {LEFT_ARROW, RIGHT_ARROW} from '@angular/cdk/keycodes'; +import {dispatchFakeEvent, dispatchKeyboardEvent} from '../../cdk/testing/private'; +import {Component, DebugElement, OnInit, QueryList, ViewChild, ViewChildren} from '@angular/core'; +import { + ComponentFixture, + fakeAsync, + flush, + TestBed, + tick, + waitForAsync, +} from '@angular/core/testing'; +import {By} from '@angular/platform-browser'; +import {BrowserAnimationsModule, NoopAnimationsModule} from '@angular/platform-browser/animations'; +import {CommonModule} from '@angular/common'; +import {Observable} from 'rxjs'; +import { + MAT_TABS_CONFIG, + MatTab, + MatTabGroup, + MatTabHeader, + MatTabHeaderPosition, + MatTabsModule, +} from './index'; + +describe('MDC-based MatTabGroup', () => { + beforeEach(fakeAsync(() => { + TestBed.configureTestingModule({ + imports: [MatTabsModule, CommonModule, NoopAnimationsModule], + declarations: [ + SimpleTabsTestApp, + SimpleDynamicTabsTestApp, + BindedTabsTestApp, + AsyncTabsTestApp, + DisabledTabsTestApp, + TabGroupWithSimpleApi, + TemplateTabs, + TabGroupWithAriaInputs, + TabGroupWithIsActiveBinding, + NestedTabs, + TabGroupWithIndirectDescendantTabs, + TabGroupWithSpaceAbove, + NestedTabGroupWithLabel, + TabsWithClassesTestApp, + ], + }); + + TestBed.compileComponents(); + })); + + describe('basic behavior', () => { + let fixture: ComponentFixture; + let element: HTMLElement; + + beforeEach(() => { + fixture = TestBed.createComponent(SimpleTabsTestApp); + element = fixture.nativeElement; + }); + + it('should default to the first tab', () => { + checkSelectedIndex(1, fixture); + }); + + it('will properly load content on first change detection pass', () => { + fixture.detectChanges(); + const tabBodies = element.querySelectorAll('.mat-mdc-tab-body'); + expect(tabBodies[1].querySelectorAll('span').length).toBe(3); + }); + + it('should change selected index on click', () => { + let component = fixture.debugElement.componentInstance; + component.selectedIndex = 0; + checkSelectedIndex(0, fixture); + + // select the second tab + let tabLabel = fixture.debugElement.queryAll(By.css('.mat-mdc-tab'))[1]; + tabLabel.nativeElement.click(); + checkSelectedIndex(1, fixture); + + // select the third tab + tabLabel = fixture.debugElement.queryAll(By.css('.mat-mdc-tab'))[2]; + tabLabel.nativeElement.click(); + checkSelectedIndex(2, fixture); + }); + + it('should support two-way binding for selectedIndex', fakeAsync(() => { + let component = fixture.componentInstance; + component.selectedIndex = 0; + + fixture.detectChanges(); + + let tabLabel = fixture.debugElement.queryAll(By.css('.mat-mdc-tab'))[1]; + tabLabel.nativeElement.click(); + fixture.detectChanges(); + tick(); + + expect(component.selectedIndex).toBe(1); + })); + + // Note: needs to be `async` in order to fail when we expect it to. + it('should set to correct tab on fast change', waitForAsync(() => { + let component = fixture.componentInstance; + component.selectedIndex = 0; + fixture.detectChanges(); + + setTimeout(() => { + component.selectedIndex = 1; + fixture.detectChanges(); + + setTimeout(() => { + component.selectedIndex = 0; + fixture.detectChanges(); + fixture.whenStable().then(() => { + expect(component.selectedIndex).toBe(0); + }); + }, 1); + }, 1); + })); + + it('should change tabs based on selectedIndex', fakeAsync(() => { + let component = fixture.componentInstance; + let tabComponent = fixture.debugElement.query(By.css('mat-tab-group')).componentInstance; + + spyOn(component, 'handleSelection').and.callThrough(); + + checkSelectedIndex(1, fixture); + + tabComponent.selectedIndex = 2; + + checkSelectedIndex(2, fixture); + tick(); + + expect(component.handleSelection).toHaveBeenCalledTimes(1); + expect(component.selectEvent.index).toBe(2); + })); + + it('should update tab positions when selected index is changed', () => { + fixture.detectChanges(); + const component: MatTabGroup = fixture.debugElement.query( + By.css('mat-tab-group'), + ).componentInstance; + const tabs: MatTab[] = component._tabs.toArray(); + + expect(tabs[0].position).toBeLessThan(0); + expect(tabs[1].position).toBe(0); + expect(tabs[2].position).toBeGreaterThan(0); + + // Move to third tab + component.selectedIndex = 2; + fixture.detectChanges(); + expect(tabs[0].position).toBeLessThan(0); + expect(tabs[1].position).toBeLessThan(0); + expect(tabs[2].position).toBe(0); + + // Move to the first tab + component.selectedIndex = 0; + fixture.detectChanges(); + expect(tabs[0].position).toBe(0); + expect(tabs[1].position).toBeGreaterThan(0); + expect(tabs[2].position).toBeGreaterThan(0); + }); + + it('should clamp the selected index to the size of the number of tabs', () => { + fixture.detectChanges(); + const component: MatTabGroup = fixture.debugElement.query( + By.css('mat-tab-group'), + ).componentInstance; + + // Set the index to be negative, expect first tab selected + fixture.componentInstance.selectedIndex = -1; + fixture.detectChanges(); + expect(component.selectedIndex).toBe(0); + + // Set the index beyond the size of the tabs, expect last tab selected + fixture.componentInstance.selectedIndex = 3; + fixture.detectChanges(); + expect(component.selectedIndex).toBe(2); + }); + + it('should not crash when setting the selected index to NaN', () => { + let component = fixture.debugElement.componentInstance; + + expect(() => { + component.selectedIndex = NaN; + fixture.detectChanges(); + }).not.toThrow(); + }); + + it('should show ripples for tab-group labels', () => { + fixture.detectChanges(); + + const testElement = fixture.nativeElement; + const tabLabel = fixture.debugElement.queryAll(By.css('.mat-mdc-tab'))[1]; + + expect(testElement.querySelectorAll('.mat-ripple-element').length) + .withContext('Expected no ripples to show up initially.') + .toBe(0); + + dispatchFakeEvent(tabLabel.nativeElement, 'mousedown'); + + expect(testElement.querySelectorAll('.mat-ripple-element').length) + .withContext('Expected one ripple to show up on label mousedown.') + .toBe(1); + }); + + it('should allow disabling ripples for tab-group labels', () => { + fixture.componentInstance.disableRipple = true; + fixture.detectChanges(); + + const testElement = fixture.nativeElement; + const tabLabel = fixture.debugElement.queryAll(By.css('.mat-mdc-tab'))[1]; + + expect(testElement.querySelectorAll('.mat-ripple-element').length) + .withContext('Expected no ripples to show up initially.') + .toBe(0); + + dispatchFakeEvent(tabLabel.nativeElement, 'mousedown'); + dispatchFakeEvent(tabLabel.nativeElement, 'mouseup'); + + expect(testElement.querySelectorAll('.mat-ripple-element').length) + .withContext('Expected no ripple to show up on label mousedown.') + .toBe(0); + }); + + it('should set the isActive flag on each of the tabs', fakeAsync(() => { + fixture.detectChanges(); + tick(); + + const tabs = fixture.componentInstance.tabs.toArray(); + + expect(tabs[0].isActive).toBe(false); + expect(tabs[1].isActive).toBe(true); + expect(tabs[2].isActive).toBe(false); + + fixture.componentInstance.selectedIndex = 2; + fixture.detectChanges(); + tick(); + + expect(tabs[0].isActive).toBe(false); + expect(tabs[1].isActive).toBe(false); + expect(tabs[2].isActive).toBe(true); + })); + + it('should fire animation done event', fakeAsync(() => { + fixture.detectChanges(); + + spyOn(fixture.componentInstance, 'animationDone'); + let tabLabel = fixture.debugElement.queryAll(By.css('.mat-mdc-tab'))[1]; + tabLabel.nativeElement.click(); + fixture.detectChanges(); + tick(); + + expect(fixture.componentInstance.animationDone).toHaveBeenCalledTimes(1); + })); + + it('should add the proper `aria-setsize` and `aria-posinset`', () => { + fixture.detectChanges(); + + const labels = Array.from(element.querySelectorAll('.mat-mdc-tab')); + + expect(labels.map(label => label.getAttribute('aria-posinset'))).toEqual(['1', '2', '3']); + expect(labels.every(label => label.getAttribute('aria-setsize') === '3')).toBe(true); + }); + + it('should emit focusChange event on click', () => { + spyOn(fixture.componentInstance, 'handleFocus'); + fixture.detectChanges(); + + const tabLabels = fixture.debugElement.queryAll(By.css('.mat-mdc-tab')); + + expect(fixture.componentInstance.handleFocus).toHaveBeenCalledTimes(0); + + tabLabels[2].nativeElement.click(); + fixture.detectChanges(); + + expect(fixture.componentInstance.handleFocus).toHaveBeenCalledTimes(1); + expect(fixture.componentInstance.handleFocus).toHaveBeenCalledWith( + jasmine.objectContaining({index: 2}), + ); + }); + + it('should emit focusChange on arrow key navigation', () => { + spyOn(fixture.componentInstance, 'handleFocus'); + fixture.detectChanges(); + + const tabLabels = fixture.debugElement.queryAll(By.css('.mat-mdc-tab')); + const tabLabelContainer = fixture.debugElement.query(By.css('.mat-mdc-tab-label-container')) + .nativeElement as HTMLElement; + + expect(fixture.componentInstance.handleFocus).toHaveBeenCalledTimes(0); + + // In order to verify that the `focusChange` event also fires with the correct + // index, we focus the third tab before testing the keyboard navigation. + tabLabels[2].nativeElement.click(); + fixture.detectChanges(); + + expect(fixture.componentInstance.handleFocus).toHaveBeenCalledTimes(1); + + dispatchKeyboardEvent(tabLabelContainer, 'keydown', LEFT_ARROW); + + expect(fixture.componentInstance.handleFocus).toHaveBeenCalledTimes(2); + expect(fixture.componentInstance.handleFocus).toHaveBeenCalledWith( + jasmine.objectContaining({index: 1}), + ); + }); + + it('should clean up the tabs QueryList on destroy', () => { + const component: MatTabGroup = fixture.debugElement.query( + By.css('mat-tab-group'), + )!.componentInstance; + const spy = jasmine.createSpy('complete spy'); + const subscription = component._tabs.changes.subscribe({complete: spy}); + + fixture.destroy(); + + expect(spy).toHaveBeenCalled(); + subscription.unsubscribe(); + }); + + it('should have a focus indicator', () => { + const tabLabelNativeElements = [ + ...fixture.debugElement.nativeElement.querySelectorAll('.mat-mdc-tab'), + ]; + + expect( + tabLabelNativeElements.every(el => el.classList.contains('mat-mdc-focus-indicator')), + ).toBe(true); + }); + + it('should emit focusChange when a tab receives focus', fakeAsync(() => { + spyOn(fixture.componentInstance, 'handleFocus'); + fixture.detectChanges(); + + const tabLabels = fixture.debugElement.queryAll(By.css('.mat-mdc-tab')); + + expect(fixture.componentInstance.handleFocus).toHaveBeenCalledTimes(0); + + // In order to verify that the `focusChange` event also fires with the correct + // index, we focus the second tab before testing the keyboard navigation. + dispatchFakeEvent(tabLabels[2].nativeElement, 'focus'); + fixture.detectChanges(); + flush(); + fixture.detectChanges(); + + expect(fixture.componentInstance.handleFocus).toHaveBeenCalledTimes(1); + expect(fixture.componentInstance.handleFocus).toHaveBeenCalledWith( + jasmine.objectContaining({index: 2}), + ); + })); + + it('should be able to programmatically focus a particular tab', () => { + fixture.detectChanges(); + const tabGroup: MatTabGroup = fixture.debugElement.query( + By.css('mat-tab-group'), + ).componentInstance; + const tabHeader: MatTabHeader = fixture.debugElement.query( + By.css('mat-tab-header'), + ).componentInstance; + + expect(tabHeader.focusIndex).not.toBe(3); + + tabGroup.focusTab(3); + fixture.detectChanges(); + + expect(tabHeader.focusIndex).not.toBe(3); + }); + + it('should be able to set a tabindex on the inner content element', () => { + fixture.componentInstance.contentTabIndex = 1; + fixture.detectChanges(); + const contentElements = Array.from( + fixture.nativeElement.querySelectorAll('mat-tab-body'), + ); + + expect(contentElements.map(e => e.getAttribute('tabindex'))).toEqual([null, '1', null]); + + fixture.componentInstance.selectedIndex = 0; + fixture.detectChanges(); + + expect(contentElements.map(e => e.getAttribute('tabindex'))).toEqual(['1', null, null]); + }); + + it('should update the tabindex of the labels when navigating via keyboard', () => { + fixture.detectChanges(); + + const tabLabels = fixture.debugElement + .queryAll(By.css('.mat-mdc-tab')) + .map(label => label.nativeElement); + const tabLabelContainer = fixture.debugElement.query(By.css('.mat-mdc-tab-label-container')) + .nativeElement as HTMLElement; + + expect(tabLabels.map(label => label.getAttribute('tabindex'))).toEqual(['-1', '0', '-1']); + + dispatchKeyboardEvent(tabLabelContainer, 'keydown', RIGHT_ARROW); + fixture.detectChanges(); + + expect(tabLabels.map(label => label.getAttribute('tabindex'))).toEqual(['-1', '-1', '0']); + }); + }); + + describe('aria labelling', () => { + let fixture: ComponentFixture; + let tab: HTMLElement; + + beforeEach(fakeAsync(() => { + fixture = TestBed.createComponent(TabGroupWithAriaInputs); + fixture.detectChanges(); + tick(); + tab = fixture.nativeElement.querySelector('.mat-mdc-tab'); + })); + + it('should not set aria-label or aria-labelledby attributes if they are not passed in', () => { + expect(tab.hasAttribute('aria-label')).toBe(false); + expect(tab.hasAttribute('aria-labelledby')).toBe(false); + }); + + it('should set the aria-label attribute', () => { + fixture.componentInstance.ariaLabel = 'Fruit'; + fixture.detectChanges(); + + expect(tab.getAttribute('aria-label')).toBe('Fruit'); + }); + + it('should set the aria-labelledby attribute', () => { + fixture.componentInstance.ariaLabelledby = 'fruit-label'; + fixture.detectChanges(); + + expect(tab.getAttribute('aria-labelledby')).toBe('fruit-label'); + }); + + it('should not be able to set both an aria-label and aria-labelledby', () => { + fixture.componentInstance.ariaLabel = 'Fruit'; + fixture.componentInstance.ariaLabelledby = 'fruit-label'; + fixture.detectChanges(); + + expect(tab.getAttribute('aria-label')).toBe('Fruit'); + expect(tab.hasAttribute('aria-labelledby')).toBe(false); + + fixture.componentInstance.ariaLabel = 'Veggie'; + fixture.detectChanges(); + expect(tab.getAttribute('aria-label')).toBe('Veggie'); + }); + }); + + describe('disable tabs', () => { + let fixture: ComponentFixture; + + beforeEach(() => { + fixture = TestBed.createComponent(DisabledTabsTestApp); + }); + + it('should have one disabled tab', () => { + fixture.detectChanges(); + const labels = fixture.debugElement.queryAll(By.css('.mat-mdc-tab-disabled')); + expect(labels.length).toBe(1); + expect(labels[0].nativeElement.getAttribute('aria-disabled')).toBe('true'); + }); + + it('should set the disabled flag on tab', () => { + fixture.detectChanges(); + + const tabs = fixture.componentInstance.tabs.toArray(); + let labels = fixture.debugElement.queryAll(By.css('.mat-mdc-tab-disabled')); + expect(tabs[2].disabled).toBe(false); + expect(labels.length).toBe(1); + expect(labels[0].nativeElement.getAttribute('aria-disabled')).toBe('true'); + + fixture.componentInstance.isDisabled = true; + fixture.detectChanges(); + + expect(tabs[2].disabled).toBe(true); + labels = fixture.debugElement.queryAll(By.css('.mat-mdc-tab-disabled')); + expect(labels.length).toBe(2); + expect( + labels.every(label => label.nativeElement.getAttribute('aria-disabled') === 'true'), + ).toBe(true); + }); + }); + + describe('dynamic binding tabs', () => { + let fixture: ComponentFixture; + + beforeEach(fakeAsync(() => { + fixture = TestBed.createComponent(SimpleDynamicTabsTestApp); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + })); + + it('should be able to add a new tab, select it, and have correct origin position', fakeAsync(() => { + const component: MatTabGroup = fixture.debugElement.query( + By.css('mat-tab-group'), + ).componentInstance; + + let tabs: MatTab[] = component._tabs.toArray(); + expect(tabs[0].origin).toBe(null); + expect(tabs[1].origin).toBe(0); + expect(tabs[2].origin).toBe(null); + + // Add a new tab on the right and select it, expect an origin >= than 0 (animate right) + fixture.componentInstance.tabs.push({label: 'New tab', content: 'to right of index'}); + fixture.componentInstance.selectedIndex = 4; + fixture.detectChanges(); + tick(); + + tabs = component._tabs.toArray(); + expect(tabs[3].origin).toBeGreaterThanOrEqual(0); + + // Add a new tab in the beginning and select it, expect an origin < than 0 (animate left) + fixture.componentInstance.selectedIndex = 0; + fixture.detectChanges(); + tick(); + + fixture.componentInstance.tabs.push({label: 'New tab', content: 'to left of index'}); + fixture.detectChanges(); + tick(); + + tabs = component._tabs.toArray(); + expect(tabs[0].origin).toBeLessThan(0); + })); + + it('should update selected index if the last tab removed while selected', fakeAsync(() => { + const component: MatTabGroup = fixture.debugElement.query( + By.css('mat-tab-group'), + ).componentInstance; + + const numberOfTabs = component._tabs.length; + fixture.componentInstance.selectedIndex = numberOfTabs - 1; + fixture.detectChanges(); + tick(); + + // Remove last tab while last tab is selected, expect next tab over to be selected + fixture.componentInstance.tabs.pop(); + fixture.detectChanges(); + tick(); + + expect(component.selectedIndex).toBe(numberOfTabs - 2); + })); + + it('should maintain the selected tab if a new tab is added', () => { + fixture.detectChanges(); + const component: MatTabGroup = fixture.debugElement.query( + By.css('mat-tab-group'), + ).componentInstance; + + fixture.componentInstance.selectedIndex = 1; + fixture.detectChanges(); + + // Add a new tab at the beginning. + fixture.componentInstance.tabs.unshift({label: 'New tab', content: 'at the start'}); + fixture.detectChanges(); + + expect(component.selectedIndex).toBe(2); + expect(component._tabs.toArray()[2].isActive).toBe(true); + }); + + it('should maintain the selected tab if a tab is removed', () => { + // Select the second tab. + fixture.componentInstance.selectedIndex = 1; + fixture.detectChanges(); + + const component: MatTabGroup = fixture.debugElement.query( + By.css('mat-tab-group'), + ).componentInstance; + + // Remove the first tab that is right before the selected one. + fixture.componentInstance.tabs.splice(0, 1); + fixture.detectChanges(); + + // Since the first tab has been removed and the second one was selected before, the selected + // tab moved one position to the right. Meaning that the tab is now the first tab. + expect(component.selectedIndex).toBe(0); + expect(component._tabs.toArray()[0].isActive).toBe(true); + }); + + it('should be able to select a new tab after creation', fakeAsync(() => { + fixture.detectChanges(); + const component: MatTabGroup = fixture.debugElement.query( + By.css('mat-tab-group'), + ).componentInstance; + + fixture.componentInstance.tabs.push({label: 'Last tab', content: 'at the end'}); + fixture.componentInstance.selectedIndex = 3; + + fixture.detectChanges(); + tick(); + + expect(component.selectedIndex).toBe(3); + expect(component._tabs.toArray()[3].isActive).toBe(true); + })); + + it('should not fire `selectedTabChange` when the amount of tabs changes', fakeAsync(() => { + fixture.detectChanges(); + fixture.componentInstance.selectedIndex = 1; + fixture.detectChanges(); + + // Add a new tab at the beginning. + spyOn(fixture.componentInstance, 'handleSelection'); + fixture.componentInstance.tabs.unshift({label: 'New tab', content: 'at the start'}); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + + expect(fixture.componentInstance.handleSelection).not.toHaveBeenCalled(); + })); + + it('should update the newly-selected tab if the previously-selected tab is replaced', fakeAsync(() => { + const component: MatTabGroup = fixture.debugElement.query( + By.css('mat-tab-group'), + )!.componentInstance; + + spyOn(fixture.componentInstance, 'handleSelection'); + + fixture.componentInstance.tabs[fixture.componentInstance.selectedIndex] = { + label: 'New', + content: 'New', + }; + fixture.detectChanges(); + tick(); + + expect(component._tabs.get(1)?.isActive).toBe(true); + expect(fixture.componentInstance.handleSelection).toHaveBeenCalledWith( + jasmine.objectContaining({index: 1}), + ); + })); + + it('should be able to disable the pagination', fakeAsync(() => { + fixture.componentInstance.disablePagination = true; + fixture.detectChanges(); + tick(); + + for (let i = 0; i < 50; i++) { + fixture.componentInstance.tabs.push({label: `Extra ${i}`, content: ''}); + } + + fixture.detectChanges(); + tick(); + + expect( + fixture.nativeElement.querySelector('.mat-mdc-tab-header-pagination-controls-enabled'), + ).toBeFalsy(); + })); + }); + + describe('async tabs', () => { + let fixture: ComponentFixture; + + it('should show tabs when they are available', fakeAsync(() => { + fixture = TestBed.createComponent(AsyncTabsTestApp); + + expect(fixture.debugElement.queryAll(By.css('.mat-mdc-tab')).length).toBe(0); + + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + tick(); + + expect(fixture.debugElement.queryAll(By.css('.mat-mdc-tab')).length).toBe(2); + })); + }); + + describe('with simple api', () => { + let fixture: ComponentFixture; + let tabGroup: MatTabGroup; + + beforeEach(fakeAsync(() => { + fixture = TestBed.createComponent(TabGroupWithSimpleApi); + fixture.detectChanges(); + tick(); + + tabGroup = fixture.debugElement.query(By.directive(MatTabGroup)) + .componentInstance as MatTabGroup; + })); + + it('should support a tab-group with the simple api', fakeAsync(() => { + expect(getSelectedLabel(fixture).textContent).toMatch('Junk food'); + expect(getSelectedContent(fixture).textContent).toMatch('Pizza, fries'); + + tabGroup.selectedIndex = 2; + fixture.detectChanges(); + tick(); + + expect(getSelectedLabel(fixture).textContent).toMatch('Fruit'); + expect(getSelectedContent(fixture).textContent).toMatch('Apples, grapes'); + + fixture.componentInstance.otherLabel = 'Chips'; + fixture.componentInstance.otherContent = 'Salt, vinegar'; + fixture.detectChanges(); + + expect(getSelectedLabel(fixture).textContent).toMatch('Chips'); + expect(getSelectedContent(fixture).textContent).toMatch('Salt, vinegar'); + })); + + it('should support @ViewChild in the tab content', () => { + expect(fixture.componentInstance.legumes).toBeTruthy(); + }); + + it('should only have the active tab in the DOM', fakeAsync(() => { + expect(fixture.nativeElement.textContent).toContain('Pizza, fries'); + expect(fixture.nativeElement.textContent).not.toContain('Peanuts'); + + tabGroup.selectedIndex = 3; + fixture.detectChanges(); + tick(); + + expect(fixture.nativeElement.textContent).not.toContain('Pizza, fries'); + expect(fixture.nativeElement.textContent).toContain('Peanuts'); + })); + + it('should support setting the header position', () => { + let tabGroupNode = fixture.debugElement.query(By.css('mat-tab-group')).nativeElement; + + expect(tabGroupNode.classList).not.toContain('mat-mdc-tab-group-inverted-header'); + + tabGroup.headerPosition = 'below'; + fixture.detectChanges(); + + expect(tabGroupNode.classList).toContain('mat-mdc-tab-group-inverted-header'); + }); + + it('should be able to opt into keeping the inactive tab content in the DOM', fakeAsync(() => { + fixture.componentInstance.preserveContent = true; + fixture.detectChanges(); + + expect(fixture.nativeElement.textContent).toContain('Pizza, fries'); + expect(fixture.nativeElement.textContent).not.toContain('Peanuts'); + + tabGroup.selectedIndex = 3; + fixture.detectChanges(); + tick(); + + expect(fixture.nativeElement.textContent).toContain('Pizza, fries'); + expect(fixture.nativeElement.textContent).toContain('Peanuts'); + })); + + it('should visibly hide the content of inactive tabs', fakeAsync(() => { + const contentElements: HTMLElement[] = Array.from( + fixture.nativeElement.querySelectorAll('.mat-mdc-tab-body-content'), + ); + + expect(contentElements.map(element => element.style.visibility)).toEqual([ + '', + 'hidden', + 'hidden', + 'hidden', + ]); + + tabGroup.selectedIndex = 2; + fixture.detectChanges(); + tick(); + + expect(contentElements.map(element => element.style.visibility)).toEqual([ + 'hidden', + 'hidden', + '', + 'hidden', + ]); + + tabGroup.selectedIndex = 1; + fixture.detectChanges(); + tick(); + + expect(contentElements.map(element => element.style.visibility)).toEqual([ + 'hidden', + '', + 'hidden', + 'hidden', + ]); + })); + }); + + describe('lazy loaded tabs', () => { + it('should lazy load the second tab', fakeAsync(() => { + const fixture = TestBed.createComponent(TemplateTabs); + fixture.detectChanges(); + tick(); + + const secondLabel = fixture.debugElement.queryAll(By.css('.mat-mdc-tab'))[1]; + secondLabel.nativeElement.click(); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + + const child = fixture.debugElement.query(By.css('.child')); + expect(child.nativeElement).toBeDefined(); + })); + }); + + describe('special cases', () => { + it('should not throw an error when binding isActive to the view', fakeAsync(() => { + const fixture = TestBed.createComponent(TabGroupWithIsActiveBinding); + + expect(() => { + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + }).not.toThrow(); + + expect(fixture.nativeElement.textContent).toContain('pizza is active'); + })); + + it('should not pick up mat-tab-label from a child tab', fakeAsync(() => { + const fixture = TestBed.createComponent(NestedTabGroupWithLabel); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + + const labels = fixture.nativeElement.querySelectorAll('.mdc-tab__text-label'); + const contents = Array.from(labels).map(label => label.textContent?.trim()); + + expect(contents).toEqual([ + 'Parent 1', + 'Parent 2', + 'Parent 3', + 'Child 1', + 'Child 2', + 'Child 3', + ]); + })); + }); + + describe('nested tabs', () => { + it('should not pick up the tabs from descendant tab groups', fakeAsync(() => { + const fixture = TestBed.createComponent(NestedTabs); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + + const groups = fixture.componentInstance.groups.toArray(); + + expect(groups.length).toBe(2); + expect(groups[0]._tabs.map((tab: MatTab) => tab.textLabel)).toEqual(['One', 'Two']); + expect(groups[1]._tabs.map((tab: MatTab) => tab.textLabel)).toEqual([ + 'Inner tab one', + 'Inner tab two', + ]); + })); + + it('should pick up indirect descendant tabs', fakeAsync(() => { + const fixture = TestBed.createComponent(TabGroupWithIndirectDescendantTabs); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + + const tabs = fixture.componentInstance.tabGroup._tabs; + expect(tabs.map((tab: MatTab) => tab.textLabel)).toEqual(['One', 'Two']); + })); + }); + + describe('tall tabs', () => { + beforeEach(() => { + window.scrollTo({top: 0}); + }); + + it('should not scroll when changing tabs by clicking', fakeAsync(() => { + const fixture = TestBed.createComponent(TabGroupWithSpaceAbove); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + + window.scrollBy(0, 250); + expect(window.scrollY).toBe(250); + + // select the second tab + let tabLabel = fixture.debugElement.queryAll(By.css('.mat-mdc-tab'))[1]; + tabLabel.nativeElement.click(); + checkSelectedIndex(1, fixture); + + expect(window.scrollY).toBe(250); + tick(); + })); + + it('should not scroll when changing tabs programatically', fakeAsync(() => { + const fixture = TestBed.createComponent(TabGroupWithSpaceAbove); + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + + window.scrollBy(0, 250); + expect(window.scrollY).toBe(250); + + fixture.componentInstance.tabGroup.selectedIndex = 1; + fixture.detectChanges(); + + expect(window.scrollY).toBe(250); + tick(); + })); + }); + + describe('tabs with custom css classes', () => { + let fixture: ComponentFixture; + let labelElements: DebugElement[]; + let bodyElements: DebugElement[]; + + beforeEach(() => { + fixture = TestBed.createComponent(TabsWithClassesTestApp); + fixture.detectChanges(); + labelElements = fixture.debugElement.queryAll(By.css('.mdc-tab')); + bodyElements = fixture.debugElement.queryAll(By.css('mat-tab-body')); + }); + + it('should apply label/body classes', () => { + expect(labelElements[1].nativeElement.classList).toContain('hardcoded-label-class'); + expect(bodyElements[1].nativeElement.classList).toContain('hardcoded-body-class'); + }); + + it('should set classes as strings dynamically', () => { + expect(labelElements[0].nativeElement.classList).not.toContain('custom-label-class'); + expect(bodyElements[0].nativeElement.classList).not.toContain('custom-body-class'); + + fixture.componentInstance.labelClassList = 'custom-label-class'; + fixture.componentInstance.bodyClassList = 'custom-body-class'; + fixture.detectChanges(); + + expect(labelElements[0].nativeElement.classList).toContain('custom-label-class'); + expect(bodyElements[0].nativeElement.classList).toContain('custom-body-class'); + + delete fixture.componentInstance.labelClassList; + delete fixture.componentInstance.bodyClassList; + fixture.detectChanges(); + + expect(labelElements[0].nativeElement.classList).not.toContain('custom-label-class'); + expect(bodyElements[0].nativeElement.classList).not.toContain('custom-body-class'); + }); + + it('should set classes as strings array dynamically', () => { + expect(labelElements[0].nativeElement.classList).not.toContain('custom-label-class'); + expect(bodyElements[0].nativeElement.classList).not.toContain('custom-body-class'); + + fixture.componentInstance.labelClassList = ['custom-label-class']; + fixture.componentInstance.bodyClassList = ['custom-body-class']; + fixture.detectChanges(); + + expect(labelElements[0].nativeElement.classList).toContain('custom-label-class'); + expect(bodyElements[0].nativeElement.classList).toContain('custom-body-class'); + + delete fixture.componentInstance.labelClassList; + delete fixture.componentInstance.bodyClassList; + fixture.detectChanges(); + + expect(labelElements[0].nativeElement.classList).not.toContain('custom-label-class'); + expect(bodyElements[0].nativeElement.classList).not.toContain('custom-body-class'); + }); + }); + + /** + * Checks that the `selectedIndex` has been updated; checks that the label and body have their + * respective `active` classes + */ + function checkSelectedIndex(expectedIndex: number, fixture: ComponentFixture) { + fixture.detectChanges(); + + let tabComponent: MatTabGroup = fixture.debugElement.query( + By.css('mat-tab-group'), + ).componentInstance; + expect(tabComponent.selectedIndex).toBe(expectedIndex); + + let tabLabelElement = fixture.debugElement.query( + By.css(`.mat-mdc-tab:nth-of-type(${expectedIndex + 1})`), + ).nativeElement; + expect(tabLabelElement.classList.contains('mdc-tab--active')).toBe(true); + + let tabContentElement = fixture.debugElement.query( + By.css(`mat-tab-body:nth-of-type(${expectedIndex + 1})`), + ).nativeElement; + expect(tabContentElement.classList.contains('mat-mdc-tab-body-active')).toBe(true); + } + + function getSelectedLabel(fixture: ComponentFixture): HTMLElement { + return fixture.nativeElement.querySelector('.mdc-tab--active'); + } + + function getSelectedContent(fixture: ComponentFixture): HTMLElement { + return fixture.nativeElement.querySelector('.mat-mdc-tab-body-active'); + } +}); + +describe('nested MatTabGroup with enabled animations', () => { + beforeEach(fakeAsync(() => { + TestBed.configureTestingModule({ + imports: [MatTabsModule, BrowserAnimationsModule], + declarations: [NestedTabs, TabsWithCustomAnimationDuration], + }); + + TestBed.compileComponents(); + })); + + it('should not throw when creating a component with nested tab groups', fakeAsync(() => { + expect(() => { + let fixture = TestBed.createComponent(NestedTabs); + fixture.detectChanges(); + tick(); + }).not.toThrow(); + })); + + it('should not throw when setting an animationDuration without units', fakeAsync(() => { + expect(() => { + let fixture = TestBed.createComponent(TabsWithCustomAnimationDuration); + fixture.detectChanges(); + tick(); + }).not.toThrow(); + })); + + it('should set appropiate css variable given a specified animationDuration', fakeAsync(() => { + let fixture = TestBed.createComponent(TabsWithCustomAnimationDuration); + fixture.detectChanges(); + tick(); + + const tabGroup = fixture.nativeElement.querySelector('.mat-mdc-tab-group'); + expect(tabGroup.style.getPropertyValue('--mat-tab-animation-duration')).toBe('500ms'); + })); +}); + +describe('MatTabGroup with ink bar fit to content', () => { + let fixture: ComponentFixture; + + beforeEach(fakeAsync(() => { + TestBed.configureTestingModule({ + imports: [MatTabsModule, BrowserAnimationsModule], + declarations: [TabGroupWithInkBarFitToContent], + }); + + TestBed.compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TabGroupWithInkBarFitToContent); + fixture.detectChanges(); + }); + + it('should properly nest the ink bar when fit to content', () => { + const tabElement = fixture.nativeElement.querySelector('.mdc-tab'); + const contentElement = tabElement.querySelector('.mdc-tab__content'); + const indicatorElement = tabElement.querySelector('.mdc-tab-indicator'); + expect(indicatorElement.parentElement).toBeTruthy(); + expect(indicatorElement.parentElement).toBe(contentElement); + }); + + it('should be able to move the ink bar between content and full', () => { + fixture.componentInstance.fitInkBarToContent = false; + fixture.detectChanges(); + + const tabElement = fixture.nativeElement.querySelector('.mdc-tab'); + const indicatorElement = tabElement.querySelector('.mdc-tab-indicator'); + expect(indicatorElement.parentElement).toBeTruthy(); + expect(indicatorElement.parentElement).toBe(tabElement); + + fixture.componentInstance.fitInkBarToContent = true; + fixture.detectChanges(); + + const contentElement = tabElement.querySelector('.mdc-tab__content'); + expect(indicatorElement.parentElement).toBeTruthy(); + expect(indicatorElement.parentElement).toBe(contentElement); + }); +}); + +describe('MatTabNavBar with a default config', () => { + let fixture: ComponentFixture; + + beforeEach(fakeAsync(() => { + TestBed.configureTestingModule({ + imports: [MatTabsModule, BrowserAnimationsModule], + declarations: [SimpleTabsTestApp], + providers: [ + { + provide: MAT_TABS_CONFIG, + useValue: {fitInkBarToContent: true, dynamicHeight: true}, + }, + ], + }); + + TestBed.compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SimpleTabsTestApp); + fixture.detectChanges(); + }); + + it('should set whether the ink bar fits to content', () => { + const tabElement = fixture.nativeElement.querySelector('.mdc-tab'); + const contentElement = tabElement.querySelector('.mdc-tab__content'); + const indicatorElement = tabElement.querySelector('.mdc-tab-indicator'); + expect(indicatorElement.parentElement).toBeTruthy(); + expect(indicatorElement.parentElement).toBe(contentElement); + }); + + it('should set whether the height of the tab group is dynamic', () => { + expect(fixture.componentInstance.tabGroup.dynamicHeight).toBe(true); + }); +}); + +@Component({ + template: ` + + + Tab One + Tab one content + + + Tab Two + Tab twocontent + + + Tab Three + Tab three content + + + `, +}) +class SimpleTabsTestApp { + @ViewChild(MatTabGroup) tabGroup: MatTabGroup; + @ViewChildren(MatTab) tabs: QueryList; + selectedIndex: number = 1; + focusEvent: any; + selectEvent: any; + disableRipple: boolean = false; + contentTabIndex: number | null = null; + headerPosition: MatTabHeaderPosition = 'above'; + handleFocus(event: any) { + this.focusEvent = event; + } + handleSelection(event: any) { + this.selectEvent = event; + } + animationDone() {} +} + +@Component({ + template: ` + + + {{tab.label}} + {{tab.content}} + + + `, +}) +class SimpleDynamicTabsTestApp { + tabs = [ + {label: 'Label 1', content: 'Content 1'}, + {label: 'Label 2', content: 'Content 2'}, + {label: 'Label 3', content: 'Content 3'}, + ]; + selectedIndex: number = 1; + focusEvent: any; + selectEvent: any; + disablePagination = false; + handleFocus(event: any) { + this.focusEvent = event; + } + handleSelection(event: any) { + this.selectEvent = event; + } +} + +@Component({ + template: ` + + + {{tab.content}} + + + `, +}) +class BindedTabsTestApp { + tabs = [ + {label: 'one', content: 'one'}, + {label: 'two', content: 'two'}, + ]; + selectedIndex = 0; + + addNewActiveTab(): void { + this.tabs.push({ + label: 'new tab', + content: 'new content', + }); + this.selectedIndex = this.tabs.length - 1; + } +} + +@Component({ + template: ` + + + Tab One + Tab one content + + + Tab Two + Tab two content + + + Tab Three + Tab three content + + + `, +}) +class DisabledTabsTestApp { + @ViewChildren(MatTab) tabs: QueryList; + isDisabled = false; +} + +@Component({ + template: ` + + + {{ tab.label }} + {{ tab.content }} + + + `, +}) +class AsyncTabsTestApp implements OnInit { + private _tabs = [ + {label: 'one', content: 'one'}, + {label: 'two', content: 'two'}, + ]; + + tabs: Observable; + + ngOnInit() { + // Use ngOnInit because there is some issue with scheduling the async task in the constructor. + this.tabs = new Observable((observer: any) => { + setTimeout(() => observer.next(this._tabs)); + }); + } +} + +@Component({ + template: ` + + Pizza, fries + Broccoli, spinach + {{otherContent}} +

Peanuts

+
+ `, +}) +class TabGroupWithSimpleApi { + preserveContent = false; + otherLabel = 'Fruit'; + otherContent = 'Apples, grapes'; + @ViewChild('legumes') legumes: any; +} + +@Component({ + template: ` + + Tab one content + + Tab two content + + Inner content one + Inner content two + + + + `, +}) +class NestedTabs { + @ViewChildren(MatTabGroup) groups: QueryList; +} + +@Component({ + template: ` + + + Eager + + + +
Hi
+
+
+
+ `, +}) +class TemplateTabs {} + +@Component({ + template: ` + + + + `, +}) +class TabGroupWithAriaInputs { + ariaLabel: string; + ariaLabelledby: string; +} + +@Component({ + template: ` + + Pizza, fries + Broccoli, spinach + + +
pizza is active
+ `, +}) +class TabGroupWithIsActiveBinding {} + +@Component({ + template: ` + + Tab one content + Tab two content + + `, +}) +class TabsWithCustomAnimationDuration {} + +@Component({ + template: ` + + + Tab one content + Tab two content + + + `, +}) +class TabGroupWithIndirectDescendantTabs { + @ViewChild(MatTabGroup) tabGroup: MatTabGroup; +} + +@Component({ + template: ` + + Tab one content + Tab two content + + `, +}) +class TabGroupWithInkBarFitToContent { + fitInkBarToContent = true; +} + +@Component({ + template: ` +
+ Top Content here +
+ + + +
+
+ +
+
+
+
+ `, +}) +class TabGroupWithSpaceAbove { + @ViewChild(MatTabGroup) tabGroup: MatTabGroup; +} + +@Component({ + template: ` + + + + Content 1 + + Child 2 + Content 2 + + Child 3 + + + Parent 2 + Parent 3 + + `, +}) +class NestedTabGroupWithLabel {} + +@Component({ + template: ` + + + Tab one content + + + Tab two content + + + `, +}) +class TabsWithClassesTestApp { + labelClassList?: string | string[]; + bodyClassList?: string | string[]; +} diff --git a/ui/src/components/tabs/tab-group.ts b/ui/src/components/tabs/tab-group.ts new file mode 100644 index 000000000..1b1846dac --- /dev/null +++ b/ui/src/components/tabs/tab-group.ts @@ -0,0 +1,592 @@ +/** + * @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 { + AfterContentChecked, + AfterContentInit, + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ContentChildren, + ElementRef, + EventEmitter, + Inject, + Input, + OnDestroy, + Optional, + Output, + QueryList, + ViewChild, + ViewEncapsulation, +} from '@angular/core'; +import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations'; +import {MAT_TAB_GROUP, MatTab} from './tab'; +import {MatTabHeader} from './tab-header'; +import { + BooleanInput, + coerceBooleanProperty, + coerceNumberProperty, + NumberInput, +} from '@angular/cdk/coercion'; +import { + CanColor, + CanDisableRipple, + mixinColor, + mixinDisableRipple, + ThemePalette, +} from '../core'; +import {merge, Subscription} from 'rxjs'; +import {MAT_TABS_CONFIG, MatTabsConfig} from './tab-config'; +import {startWith} from 'rxjs/operators'; +import {FocusOrigin} from '@angular/cdk/a11y'; + +/** Used to generate unique ID's for each tab component */ +let nextId = 0; + +// Boilerplate for applying mixins to MatTabGroup. +/** @docs-private */ +const _MatTabGroupMixinBase = mixinColor( + mixinDisableRipple( + class { + constructor(public _elementRef: ElementRef) {} + }, + ), + 'primary', +); + +/** @docs-private */ +export interface MatTabGroupBaseHeader { + _alignInkBarToSelectedTab(): void; + updatePagination(): void; + focusIndex: number; +} + +/** Possible positions for the tab header. */ +export type MatTabHeaderPosition = 'above' | 'below'; + +/** + * Material design tab-group component. Supports basic tab pairs (label + content) and includes + * animated ink-bar, keyboard navigation, and screen reader. + * See: https://material.io/design/components/tabs.html + */ +@Component({ + selector: 'mat-tab-group', + exportAs: 'matTabGroup', + templateUrl: 'tab-group.html', + styleUrls: ['tab-group.scss'], + encapsulation: ViewEncapsulation.None, + // tslint:disable-next-line:validate-decorators + changeDetection: ChangeDetectionStrategy.Default, + inputs: ['color', 'disableRipple'], + providers: [ + { + provide: MAT_TAB_GROUP, + useExisting: MatTabGroup, + }, + ], + host: { + 'ngSkipHydration': '', + 'class': 'mat-mdc-tab-group oui-tab', + '[class.mat-mdc-tab-group-dynamic-height]': 'dynamicHeight', + '[class.mat-mdc-tab-group-inverted-header]': 'headerPosition === "below"', + '[class.mat-mdc-tab-group-stretch-tabs]': 'stretchTabs', + '[style.--mat-tab-animation-duration]': 'animationDuration', + }, +}) +export class MatTabGroup + extends _MatTabGroupMixinBase + implements AfterContentInit, AfterContentChecked, OnDestroy, CanColor, CanDisableRipple +{ + /** + * All tabs inside the tab group. This includes tabs that belong to groups that are nested + * inside the current one. We filter out only the tabs that belong to this group in `_tabs`. + */ + @ContentChildren(MatTab, {descendants: true}) _allTabs: QueryList; + @ViewChild('tabBodyWrapper') _tabBodyWrapper: ElementRef; + @ViewChild('tabHeader') _tabHeader: MatTabHeader; + + /** All of the tabs that belong to the group. */ + _tabs: QueryList = new QueryList(); + + /** The tab index that should be selected after the content has been checked. */ + private _indexToSelect: number | null = 0; + + /** Index of the tab that was focused last. */ + private _lastFocusedTabIndex: number | null = null; + + /** Snapshot of the height of the tab body wrapper before another tab is activated. */ + private _tabBodyWrapperHeight: number = 0; + + /** Subscription to tabs being added/removed. */ + private _tabsSubscription = Subscription.EMPTY; + + /** Subscription to changes in the tab labels. */ + private _tabLabelSubscription = Subscription.EMPTY; + + /** Whether the ink bar should fit its width to the size of the tab label content. */ + @Input() + get fitInkBarToContent(): boolean { + return this._fitInkBarToContent; + } + set fitInkBarToContent(v: BooleanInput) { + this._fitInkBarToContent = coerceBooleanProperty(v); + this._changeDetectorRef.markForCheck(); + } + private _fitInkBarToContent = false; + + /** Whether tabs should be stretched to fill the header. */ + @Input('mat-stretch-tabs') + get stretchTabs(): boolean { + return this._stretchTabs; + } + set stretchTabs(v: BooleanInput) { + this._stretchTabs = coerceBooleanProperty(v); + } + private _stretchTabs = true; + + /** Whether the tab group should grow to the size of the active tab. */ + @Input() + get dynamicHeight(): boolean { + return this._dynamicHeight; + } + + set dynamicHeight(value: BooleanInput) { + this._dynamicHeight = coerceBooleanProperty(value); + } + + private _dynamicHeight: boolean = false; + + /** The index of the active tab. */ + @Input() + get selectedIndex(): number | null { + return this._selectedIndex; + } + + set selectedIndex(value: NumberInput) { + this._indexToSelect = coerceNumberProperty(value, null); + } + + private _selectedIndex: number | null = null; + + /** Position of the tab header. */ + @Input() headerPosition: MatTabHeaderPosition = 'above'; + + /** Duration for the tab animation. Will be normalized to milliseconds if no units are set. */ + @Input() + get animationDuration(): string { + return this._animationDuration; + } + + set animationDuration(value: NumberInput) { + this._animationDuration = /^\d+$/.test(value + '') ? value + 'ms' : (value as string); + } + + private _animationDuration: string; + + /** + * `tabindex` to be set on the inner element that wraps the tab content. Can be used for improved + * accessibility when the tab does not have focusable elements or if it has scrollable content. + * The `tabindex` will be removed automatically for inactive tabs. + * Read more at https://www.w3.org/TR/wai-aria-practices/examples/tabs/tabs-2/tabs.html + */ + @Input() + get contentTabIndex(): number | null { + return this._contentTabIndex; + } + + set contentTabIndex(value: NumberInput) { + this._contentTabIndex = coerceNumberProperty(value, null); + } + + private _contentTabIndex: number | null; + + /** + * Whether pagination should be disabled. This can be used to avoid unnecessary + * layout recalculations if it's known that pagination won't be required. + */ + @Input() + get disablePagination(): boolean { + return this._disablePagination; + } + + set disablePagination(value: BooleanInput) { + this._disablePagination = coerceBooleanProperty(value); + } + + private _disablePagination: boolean = false; + + /** + * By default tabs remove their content from the DOM while it's off-screen. + * Setting this to `true` will keep it in the DOM which will prevent elements + * like iframes and videos from reloading next time it comes back into the view. + */ + @Input() + get preserveContent(): boolean { + return this._preserveContent; + } + + set preserveContent(value: BooleanInput) { + this._preserveContent = coerceBooleanProperty(value); + } + + private _preserveContent: boolean = false; + + /** Background color of the tab group. */ + @Input() + get backgroundColor(): ThemePalette { + return this._backgroundColor; + } + + set backgroundColor(value: ThemePalette) { + // console.log('value value', value); + const classList: DOMTokenList = this._elementRef.nativeElement.classList; + + classList.remove('mat-tabs-with-background', `mat-background-${this.backgroundColor}`); + + if (value) { + classList.add('mat-tabs-with-background', `mat-background-${value}`); + } + + this._backgroundColor = value; + } + + private _backgroundColor: ThemePalette; + + /** Output to enable support for two-way binding on `[(selectedIndex)]` */ + @Output() readonly selectedIndexChange: EventEmitter = new EventEmitter(); + + /** Event emitted when focus has changed within a tab group. */ + @Output() readonly focusChange: EventEmitter = + new EventEmitter(); + + /** Event emitted when the body animation has completed */ + @Output() readonly animationDone: EventEmitter = new EventEmitter(); + + /** Event emitted when the tab selection has changed. */ + @Output() readonly selectedTabChange: EventEmitter = + new EventEmitter(true); + + private _groupId: number; + getHTMLText: any; + updatedTabHTML: any; + + + constructor( + elementRef: ElementRef, + private _changeDetectorRef: ChangeDetectorRef, + @Inject(MAT_TABS_CONFIG) @Optional() defaultConfig?: MatTabsConfig, + @Optional() @Inject(ANIMATION_MODULE_TYPE) public _animationMode?: string, + ) { + super(elementRef); + // console.log(elementRef) + this._groupId = nextId++; + this.animationDuration = + defaultConfig && defaultConfig.animationDuration ? defaultConfig.animationDuration : '500ms'; + this.disablePagination = + defaultConfig && defaultConfig.disablePagination != null + ? defaultConfig.disablePagination + : false; + this.dynamicHeight = + defaultConfig && defaultConfig.dynamicHeight != null ? defaultConfig.dynamicHeight : false; + this.contentTabIndex = defaultConfig?.contentTabIndex ?? null; + this.preserveContent = !!defaultConfig?.preserveContent; + // console.log(this.preserveContent) + this.fitInkBarToContent = + defaultConfig && defaultConfig.fitInkBarToContent != null + ? defaultConfig.fitInkBarToContent + : false; + this.stretchTabs = + defaultConfig && defaultConfig.stretchTabs != null ? defaultConfig.stretchTabs : true; + } + + /** + * After the content is checked, this component knows what tabs have been defined + * and what the selected index should be. This is where we can know exactly what position + * each tab should be in according to the new selected index, and additionally we know how + * a new selected tab should transition in (from the left or right). + */ + ngAfterContentChecked() { + // Don't clamp the `indexToSelect` immediately in the setter because it can happen that + // the amount of tabs changes before the actual change detection runs. + const indexToSelect = (this._indexToSelect = this._clampTabIndex(this._indexToSelect)); + + // If there is a change in selected index, emit a change event. Should not trigger if + // the selected index has not yet been initialized. + if (this._selectedIndex != indexToSelect) { + const isFirstRun = this._selectedIndex == null; + + if (!isFirstRun) { + this.selectedTabChange.emit(this._createChangeEvent(indexToSelect)); + // Preserve the height so page doesn't scroll up during tab change. + // Fixes https://stackblitz.com/edit/mat-tabs-scroll-page-top-on-tab-change + const wrapper = this._tabBodyWrapper.nativeElement; + wrapper.style.minHeight = wrapper.clientHeight + 'px'; + } + + // Changing these values after change detection has run + // since the checked content may contain references to them. + Promise.resolve().then(() => { + this._tabs.forEach((tab, index) => (tab.isActive = index === indexToSelect)); + + if (!isFirstRun) { + this.selectedIndexChange.emit(indexToSelect); + // Clear the min-height, this was needed during tab change to avoid + // unnecessary scrolling. + this._tabBodyWrapper.nativeElement.style.minHeight = ''; + } + }); + } + + // Setup the position for each tab and optionally setup an origin on the next selected tab. + this._tabs.forEach((tab: MatTab, index: number) => { + tab.position = index - indexToSelect; + + // If there is already a selected tab, then set up an origin for the next selected tab + // if it doesn't have one already. + if (this._selectedIndex != null && tab.position == 0 && !tab.origin) { + tab.origin = indexToSelect - this._selectedIndex; + } + }); + + if (this._selectedIndex !== indexToSelect) { + this._selectedIndex = indexToSelect; + this._lastFocusedTabIndex = null; + this._changeDetectorRef.markForCheck(); + } + } + + ngAfterContentInit() { + // console.log(';;;;;;;;;;;;', this._tabs) + this._subscribeToAllTabChanges(); + this._subscribeToTabLabels(); + // console.log(this._tabs) + // Subscribe to changes in the amount of tabs, in order to be + // able to re-render the content as new tabs are added or removed. + this._tabsSubscription = this._tabs.changes.subscribe(() => { + // console.log("change") + const indexToSelect = this._clampTabIndex(this._indexToSelect); + + // Maintain the previously-selected tab if a new tab is added or removed and there is no + // explicit change that selects a different tab. + // console.log('>>>>>>>>>>>>>', indexToSelect, this._selectedIndex) + if (indexToSelect === this._selectedIndex) { + const tabs = this._tabs.toArray(); + let selectedTab: MatTab | undefined; + + for (let i = 0; i < tabs.length; i++) { + console.log('tabsi',tabs[i]) + if (tabs[i].isActive) { + // Assign both to the `_indexToSelect` and `_selectedIndex` so we don't fire a changed + // event, otherwise the consumer may end up in an infinite loop in some edge cases like + // adding a tab within the `selectedIndexChange` event. + this._indexToSelect = this._selectedIndex = i; + this._lastFocusedTabIndex = null; + selectedTab = tabs[i]; + break; + } + } + + // If we haven't found an active tab and a tab exists at the selected index, it means + // that the active tab was swapped out. Since this won't be picked up by the rendering + // loop in `ngAfterContentChecked`, we need to sync it up manually. + if (!selectedTab && tabs[indexToSelect]) { + Promise.resolve().then(() => { + tabs[indexToSelect].isActive = true; + this.selectedTabChange.emit(this._createChangeEvent(indexToSelect)); + }); + } + } + + this._changeDetectorRef.markForCheck(); + }); + } + + /** Listens to changes in all of the tabs. */ + private _subscribeToAllTabChanges() { + // Since we use a query with `descendants: true` to pick up the tabs, we may end up catching + // some that are inside of nested tab groups. We filter them out manually by checking that + // the closest group to the tab is the current one. + // console.log("_subscribeToAllTabChanges", this._allTabs) + this.getHTMLText = this._allTabs['_results'][0].givenText; + this.updatedTabHTML = this.getHTMLText; + this._allTabs['_results'].forEach((tabData)=>{ + this._tabs.myKey = tabData.givenText; + }) + this._allTabs.changes.pipe(startWith(this._allTabs)).subscribe((tabs: QueryList) => { + this._tabs.reset( + tabs.filter(tab => { + // console.log(tab) + return tab._closestTabGroup === this || !tab._closestTabGroup; + }), + ); + this._tabs.notifyOnChanges(); + }); + } + + ngOnDestroy() { + this._tabs.destroy(); + this._tabsSubscription.unsubscribe(); + this._tabLabelSubscription.unsubscribe(); + } + + /** Re-aligns the ink bar to the selected tab element. */ + realignInkBar() { + if (this._tabHeader) { + this._tabHeader._alignInkBarToSelectedTab(); + } + } + + /** + * Recalculates the tab group's pagination dimensions. + * + * WARNING: Calling this method can be very costly in terms of performance. It should be called + * as infrequently as possible from outside of the Tabs component as it causes a reflow of the + * page. + */ + updatePagination() { + if (this._tabHeader) { + this._tabHeader.updatePagination(); + } + } + + /** + * Sets focus to a particular tab. + * @param index Index of the tab to be focused. + */ + focusTab(index: number) { + const header = this._tabHeader; + + if (header) { + header.focusIndex = index; + } + } + + _focusChanged(index: number) { + this._lastFocusedTabIndex = index; + this.focusChange.emit(this._createChangeEvent(index)); + } + + private _createChangeEvent(index: number): MatTabChangeEvent { + const event = new MatTabChangeEvent(); + event.index = index; + if (this._tabs && this._tabs.length) { + event.tab = this._tabs.toArray()[index]; + // console.log('_createChangeEvent', event, this._tabs); + this.updatedTabHTML = event.tab.givenText; + // console.log(this.updatedTabHTML) + } + return event; + } + + + _handleEnter() { + this.getHTMLText = this.updatedTabHTML; + } + + /** + * Subscribes to changes in the tab labels. This is needed, because the @Input for the label is + * on the MatTab component, whereas the data binding is inside the MatTabGroup. In order for the + * binding to be updated, we need to subscribe to changes in it and trigger change detection + * manually. + */ + private _subscribeToTabLabels() { + if (this._tabLabelSubscription) { + this._tabLabelSubscription.unsubscribe(); + } + + this._tabLabelSubscription = merge(...this._tabs.map(tab => tab._stateChanges)).subscribe(() => + this._changeDetectorRef.markForCheck(), + ); + } + + /** Clamps the given index to the bounds of 0 and the tabs length. */ + private _clampTabIndex(index: number | null): number { + // Note the `|| 0`, which ensures that values like NaN can't get through + // and which would otherwise throw the component into an infinite loop + // (since Math.max(NaN, 0) === NaN). + return Math.min(this._tabs.length - 1, Math.max(index || 0, 0)); + } + + /** Returns a unique id for each tab label element */ + _getTabLabelId(i: number): string { + return `mat-tab-label-${this._groupId}-${i}`; + } + + /** Returns a unique id for each tab content element */ + _getTabContentId(i: number): string { + return `mat-tab-content-${this._groupId}-${i}`; + } + + /** + * Sets the height of the body wrapper to the height of the activating tab if dynamic + * height property is true. + */ + _setTabBodyWrapperHeight(tabHeight: number): void { + if (!this._dynamicHeight || !this._tabBodyWrapperHeight) { + return; + } + + const wrapper: HTMLElement = this._tabBodyWrapper.nativeElement; + + wrapper.style.height = this._tabBodyWrapperHeight + 'px'; + + // This conditional forces the browser to paint the height so that + // the animation to the new height can have an origin. + if (this._tabBodyWrapper.nativeElement.offsetHeight) { + wrapper.style.height = tabHeight + 'px'; + } + } + + /** Removes the height of the tab body wrapper. */ + _removeTabBodyWrapperHeight(): void { + const wrapper = this._tabBodyWrapper.nativeElement; + this._tabBodyWrapperHeight = wrapper.clientHeight; + wrapper.style.height = ''; + this.animationDone.emit(); + } + + /** Handle click events, setting new selected index if appropriate. */ + _handleClick(tab: MatTab, tabHeader: MatTabGroupBaseHeader, index: number) { + // console.log("click") + tabHeader.focusIndex = index; + this.getHTMLText = this.updatedTabHTML; + + if (!tab.disabled) { + // console.log('ppppppppppp.p') + this.selectedIndex = index; + } + } + + /** Retrieves the tabindex for the tab. */ + _getTabIndex(index: number): number { + // console.log('get tab index') + const targetIndex = this._lastFocusedTabIndex ?? this.selectedIndex; + return index === targetIndex ? 0 : -1; + } + + /** Callback for when the focused state of a tab has changed. */ + _tabFocusChanged(focusOrigin: FocusOrigin, index: number) { + // console.log('focusssss', focusOrigin) + // Mouse/touch focus happens during the `mousedown`/`touchstart` phase which + // can cause the tab to be moved out from under the pointer, interrupting the + // click sequence (see #21898). We don't need to scroll the tab into view for + // such cases anyway, because it will be done when the tab becomes selected. + if (focusOrigin && focusOrigin !== 'mouse' && focusOrigin !== 'touch') { + this._tabHeader.focusIndex = index; + } + } +} + +/** A simple change event emitted on focus or selection changes. */ +export class MatTabChangeEvent { + /** Index of the currently-selected tab. */ + index: number; + /** Reference to the currently-selected tab. */ + tab: MatTab; + event: Event; +} diff --git a/ui/src/components/tabs/tab-header.html b/ui/src/components/tabs/tab-header.html new file mode 100644 index 000000000..104cd2df7 --- /dev/null +++ b/ui/src/components/tabs/tab-header.html @@ -0,0 +1,47 @@ + + + +
+
+
+ +
+
+
+ + + diff --git a/ui/src/components/tabs/tab-header.scss b/ui/src/components/tabs/tab-header.scss new file mode 100644 index 000000000..58036aa79 --- /dev/null +++ b/ui/src/components/tabs/tab-header.scss @@ -0,0 +1,29 @@ +@use '@angular/cdk'; +@use './tabs-theme'; +@use './tabs-common'; + +@include tabs-common.paginated-tab-header; + +.mat-mdc-tab-label-container { + @include tabs-common.paginated-tab-header-container; +} + +.mat-mdc-tab-labels { + @include tabs-common.paginated-tab-header-item-wrapper('.mat-mdc-tab-header'); +} + +.mat-mdc-tab { + // For the tab element, default inset/offset values are necessary to ensure that + // the focus indicator is sufficiently contrastive and renders appropriately. + &::before { + margin: 5px; + } + + @include cdk.high-contrast(active, off) { + // When a tab is disabled in high contrast mode, set the text color to the disabled + // color, which is (unintuitively) named "GrayText". + &[aria-disabled='true'] { + color: GrayText; + } + } +} diff --git a/ui/src/components/tabs/tab-header.spec.ts b/ui/src/components/tabs/tab-header.spec.ts new file mode 100644 index 000000000..153f71c36 --- /dev/null +++ b/ui/src/components/tabs/tab-header.spec.ts @@ -0,0 +1,771 @@ +import {Direction} from '@angular/cdk/bidi'; +import {END, ENTER, HOME, LEFT_ARROW, RIGHT_ARROW, SPACE} from '@angular/cdk/keycodes'; +import {PortalModule} from '@angular/cdk/portal'; +import {ScrollingModule, ViewportRuler} from '@angular/cdk/scrolling'; +import { + dispatchFakeEvent, + dispatchKeyboardEvent, + createKeyboardEvent, + dispatchEvent, + createMouseEvent, +} from '../../cdk/testing/private'; +import {CommonModule} from '@angular/common'; +import {Component, ViewChild} from '@angular/core'; +import { + waitForAsync, + ComponentFixture, + discardPeriodicTasks, + fakeAsync, + TestBed, + tick, +} from '@angular/core/testing'; +import {MatRippleModule} from '../core'; +import {By} from '@angular/platform-browser'; +import {MatTabHeader} from './tab-header'; +import {MatTabLabelWrapper} from './tab-label-wrapper'; +import {ObserversModule, MutationObserverFactory} from '@angular/cdk/observers'; + +describe('MDC-based MatTabHeader', () => { + let fixture: ComponentFixture; + let appComponent: SimpleTabHeaderApp; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [CommonModule, PortalModule, MatRippleModule, ScrollingModule, ObserversModule], + declarations: [MatTabHeader, MatTabLabelWrapper, SimpleTabHeaderApp], + providers: [ViewportRuler], + }); + + TestBed.compileComponents(); + })); + + describe('focusing', () => { + let tabListContainer: HTMLElement; + + beforeEach(() => { + fixture = TestBed.createComponent(SimpleTabHeaderApp); + fixture.detectChanges(); + + appComponent = fixture.componentInstance; + tabListContainer = appComponent.tabHeader._tabListContainer.nativeElement; + }); + + it('should initialize to the selected index', () => { + // Recreate the fixture so we can test that it works with a non-default selected index + fixture.destroy(); + fixture = TestBed.createComponent(SimpleTabHeaderApp); + fixture.componentInstance.selectedIndex = 1; + fixture.detectChanges(); + appComponent = fixture.componentInstance; + tabListContainer = appComponent.tabHeader._tabListContainer.nativeElement; + + expect(appComponent.tabHeader.focusIndex).toBe(1); + }); + + it('should send focus change event', () => { + appComponent.tabHeader.focusIndex = 2; + fixture.detectChanges(); + expect(appComponent.tabHeader.focusIndex).toBe(2); + }); + + it('should be able to focus a disabled tab', () => { + appComponent.tabHeader.focusIndex = 0; + fixture.detectChanges(); + expect(appComponent.tabHeader.focusIndex).toBe(0); + + appComponent.tabHeader.focusIndex = appComponent.disabledTabIndex; + fixture.detectChanges(); + expect(appComponent.tabHeader.focusIndex).toBe(appComponent.disabledTabIndex); + }); + + it('should move focus right including over disabled tabs', () => { + appComponent.tabHeader.focusIndex = 0; + fixture.detectChanges(); + expect(appComponent.tabHeader.focusIndex).toBe(0); + + expect(appComponent.disabledTabIndex).toBe(1); + dispatchKeyboardEvent(tabListContainer, 'keydown', RIGHT_ARROW); + fixture.detectChanges(); + expect(appComponent.tabHeader.focusIndex).toBe(1); + + dispatchKeyboardEvent(tabListContainer, 'keydown', RIGHT_ARROW); + fixture.detectChanges(); + expect(appComponent.tabHeader.focusIndex).toBe(2); + }); + + it('should move focus left including over disabled tabs', () => { + appComponent.tabHeader.focusIndex = 3; + fixture.detectChanges(); + expect(appComponent.tabHeader.focusIndex).toBe(3); + + // Move focus left to index 3 + dispatchKeyboardEvent(tabListContainer, 'keydown', LEFT_ARROW); + fixture.detectChanges(); + expect(appComponent.tabHeader.focusIndex).toBe(2); + + expect(appComponent.disabledTabIndex).toBe(1); + dispatchKeyboardEvent(tabListContainer, 'keydown', LEFT_ARROW); + fixture.detectChanges(); + expect(appComponent.tabHeader.focusIndex).toBe(1); + }); + + it('should support key down events to move and select focus', () => { + appComponent.tabHeader.focusIndex = 0; + fixture.detectChanges(); + expect(appComponent.tabHeader.focusIndex).toBe(0); + + // Move focus right to 1 + dispatchKeyboardEvent(tabListContainer, 'keydown', RIGHT_ARROW); + fixture.detectChanges(); + expect(appComponent.tabHeader.focusIndex).toBe(1); + + // Try to select 1. Should not work since it's disabled. + expect(appComponent.selectedIndex).toBe(0); + const firstEnterEvent = dispatchKeyboardEvent(tabListContainer, 'keydown', ENTER); + fixture.detectChanges(); + expect(appComponent.selectedIndex).toBe(0); + expect(firstEnterEvent.defaultPrevented).toBe(false); + + // Move focus right to 2 + dispatchKeyboardEvent(tabListContainer, 'keydown', RIGHT_ARROW); + fixture.detectChanges(); + expect(appComponent.tabHeader.focusIndex).toBe(2); + + // Select 2 which is enabled. + expect(appComponent.selectedIndex).toBe(0); + const secondEnterEvent = dispatchKeyboardEvent(tabListContainer, 'keydown', ENTER); + fixture.detectChanges(); + expect(appComponent.selectedIndex).toBe(2); + expect(secondEnterEvent.defaultPrevented).toBe(true); + + // Move focus left to 1 + dispatchKeyboardEvent(tabListContainer, 'keydown', LEFT_ARROW); + fixture.detectChanges(); + expect(appComponent.tabHeader.focusIndex).toBe(1); + + // Move again to 0 + dispatchKeyboardEvent(tabListContainer, 'keydown', LEFT_ARROW); + fixture.detectChanges(); + expect(appComponent.tabHeader.focusIndex).toBe(0); + + // Select the focused 0 using space. + expect(appComponent.selectedIndex).toBe(2); + const spaceEvent = dispatchKeyboardEvent(tabListContainer, 'keydown', SPACE); + fixture.detectChanges(); + expect(appComponent.selectedIndex).toBe(0); + expect(spaceEvent.defaultPrevented).toBe(true); + }); + + it('should not prevent the default space/enter action if the current is selected', () => { + appComponent.tabHeader.focusIndex = appComponent.tabHeader.selectedIndex = 0; + fixture.detectChanges(); + + const spaceEvent = dispatchKeyboardEvent(tabListContainer, 'keydown', SPACE); + fixture.detectChanges(); + expect(spaceEvent.defaultPrevented).toBe(false); + + const enterEvent = dispatchKeyboardEvent(tabListContainer, 'keydown', ENTER); + fixture.detectChanges(); + expect(enterEvent.defaultPrevented).toBe(false); + }); + + it('should move focus to the first tab when pressing HOME', () => { + appComponent.tabHeader.focusIndex = 3; + fixture.detectChanges(); + expect(appComponent.tabHeader.focusIndex).toBe(3); + + const event = dispatchKeyboardEvent(tabListContainer, 'keydown', HOME); + fixture.detectChanges(); + + expect(appComponent.tabHeader.focusIndex).toBe(0); + expect(event.defaultPrevented).toBe(true); + }); + + it('should focus disabled items when moving focus using HOME', () => { + appComponent.tabHeader.focusIndex = 3; + appComponent.tabs[0].disabled = true; + fixture.detectChanges(); + expect(appComponent.tabHeader.focusIndex).toBe(3); + + dispatchKeyboardEvent(tabListContainer, 'keydown', HOME); + fixture.detectChanges(); + + expect(appComponent.tabHeader.focusIndex).toBe(0); + }); + + it('should move focus to the last tab when pressing END', () => { + appComponent.tabHeader.focusIndex = 0; + fixture.detectChanges(); + expect(appComponent.tabHeader.focusIndex).toBe(0); + + const event = dispatchKeyboardEvent(tabListContainer, 'keydown', END); + fixture.detectChanges(); + + expect(appComponent.tabHeader.focusIndex).toBe(3); + expect(event.defaultPrevented).toBe(true); + }); + + it('should focus disabled items when moving focus using END', () => { + appComponent.tabHeader.focusIndex = 0; + appComponent.tabs[3].disabled = true; + fixture.detectChanges(); + expect(appComponent.tabHeader.focusIndex).toBe(0); + + dispatchKeyboardEvent(tabListContainer, 'keydown', END); + fixture.detectChanges(); + + expect(appComponent.tabHeader.focusIndex).toBe(3); + }); + + it('should not do anything if a modifier key is pressed', () => { + const rightArrowEvent = createKeyboardEvent('keydown', RIGHT_ARROW, undefined, {shift: true}); + const enterEvent = createKeyboardEvent('keydown', ENTER, undefined, {shift: true}); + + appComponent.tabHeader.focusIndex = 0; + fixture.detectChanges(); + expect(appComponent.tabHeader.focusIndex).toBe(0); + + dispatchEvent(tabListContainer, rightArrowEvent); + fixture.detectChanges(); + expect(appComponent.tabHeader.focusIndex).toBe(0); + expect(rightArrowEvent.defaultPrevented).toBe(false); + + expect(appComponent.selectedIndex).toBe(0); + dispatchEvent(tabListContainer, enterEvent); + fixture.detectChanges(); + expect(appComponent.selectedIndex).toBe(0); + expect(enterEvent.defaultPrevented).toBe(false); + }); + }); + + describe('pagination', () => { + describe('ltr', () => { + beforeEach(() => { + fixture = TestBed.createComponent(SimpleTabHeaderApp); + appComponent = fixture.componentInstance; + appComponent.dir = 'ltr'; + fixture.detectChanges(); + }); + + it('should show width when tab list width exceeds container', () => { + fixture.detectChanges(); + expect(appComponent.tabHeader._showPaginationControls).toBe(false); + + // Add enough tabs that it will obviously exceed the width + appComponent.addTabsForScrolling(); + fixture.detectChanges(); + + expect(appComponent.tabHeader._showPaginationControls).toBe(true); + }); + + it('should scroll to show the focused tab label', () => { + appComponent.addTabsForScrolling(); + fixture.detectChanges(); + expect(appComponent.tabHeader.scrollDistance).toBe(0); + + // Focus on the last tab, expect this to be the maximum scroll distance. + appComponent.tabHeader.focusIndex = appComponent.tabs.length - 1; + fixture.detectChanges(); + const {offsetLeft, offsetWidth} = appComponent.getSelectedLabel( + appComponent.tabHeader.focusIndex, + ); + const viewLength = appComponent.getViewLength(); + expect(appComponent.tabHeader.scrollDistance).toBe(offsetLeft + offsetWidth - viewLength); + + // Focus on the first tab, expect this to be the maximum scroll distance. + appComponent.tabHeader.focusIndex = 0; + fixture.detectChanges(); + expect(appComponent.tabHeader.scrollDistance).toBe(0); + }); + + it('should show ripples for pagination buttons', () => { + appComponent.addTabsForScrolling(); + fixture.detectChanges(); + + expect(appComponent.tabHeader._showPaginationControls).toBe(true); + + const buttonAfter = fixture.debugElement.query( + By.css('.mat-mdc-tab-header-pagination-after'), + ); + + expect(fixture.nativeElement.querySelectorAll('.mat-ripple-element').length) + .withContext('Expected no ripple to show up initially.') + .toBe(0); + + dispatchFakeEvent(buttonAfter.nativeElement, 'mousedown'); + dispatchFakeEvent(buttonAfter.nativeElement, 'mouseup'); + + expect(fixture.nativeElement.querySelectorAll('.mat-ripple-element').length) + .withContext('Expected one ripple to show up after mousedown') + .toBe(1); + }); + + it('should allow disabling ripples for pagination buttons', () => { + appComponent.addTabsForScrolling(); + appComponent.disableRipple = true; + fixture.detectChanges(); + + expect(appComponent.tabHeader._showPaginationControls).toBe(true); + + const buttonAfter = fixture.debugElement.query( + By.css('.mat-mdc-tab-header-pagination-after'), + ); + + expect(fixture.nativeElement.querySelectorAll('.mat-ripple-element').length) + .withContext('Expected no ripple to show up initially.') + .toBe(0); + + dispatchFakeEvent(buttonAfter.nativeElement, 'mousedown'); + dispatchFakeEvent(buttonAfter.nativeElement, 'mouseup'); + + expect(fixture.nativeElement.querySelectorAll('.mat-ripple-element').length) + .withContext('Expected no ripple to show up after mousedown') + .toBe(0); + }); + + it('should update the scroll distance if a tab is removed and no tabs are selected', fakeAsync(() => { + appComponent.selectedIndex = 0; + appComponent.addTabsForScrolling(); + fixture.detectChanges(); + + // Focus the last tab so the header scrolls to the end. + appComponent.tabHeader.focusIndex = appComponent.tabs.length - 1; + fixture.detectChanges(); + const {offsetLeft, offsetWidth} = appComponent.getSelectedLabel( + appComponent.tabHeader.focusIndex, + ); + const viewLength = appComponent.getViewLength(); + expect(appComponent.tabHeader.scrollDistance).toBe(offsetLeft + offsetWidth - viewLength); + + // Remove the first two tabs which includes the selected tab. + appComponent.tabs = appComponent.tabs.slice(2); + fixture.detectChanges(); + tick(); + + expect(appComponent.tabHeader.scrollDistance).toBe( + appComponent.tabHeader._getMaxScrollDistance(), + ); + })); + }); + + describe('rtl', () => { + beforeEach(() => { + fixture = TestBed.createComponent(SimpleTabHeaderApp); + appComponent = fixture.componentInstance; + appComponent.dir = 'rtl'; + fixture.detectChanges(); + }); + + it('should scroll to show the focused tab label', () => { + appComponent.addTabsForScrolling(); + fixture.detectChanges(); + expect(appComponent.tabHeader.scrollDistance).toBe(0); + + // Focus on the last tab, expect this to be the maximum scroll distance. + appComponent.tabHeader.focusIndex = appComponent.tabs.length - 1; + fixture.detectChanges(); + const {offsetLeft} = appComponent.getSelectedLabel(appComponent.tabHeader.focusIndex); + expect(offsetLeft).toBe(0); + + // Focus on the first tab, expect this to be the maximum scroll distance. + appComponent.tabHeader.focusIndex = 0; + fixture.detectChanges(); + expect(appComponent.tabHeader.scrollDistance).toBe(0); + }); + }); + + describe('scrolling when holding paginator', () => { + let nextButton: HTMLElement; + let prevButton: HTMLElement; + let header: MatTabHeader; + let headerElement: HTMLElement; + + beforeEach(() => { + fixture = TestBed.createComponent(SimpleTabHeaderApp); + fixture.componentInstance.disableRipple = true; + fixture.detectChanges(); + + fixture.componentInstance.addTabsForScrolling(50); + fixture.detectChanges(); + + nextButton = fixture.nativeElement.querySelector('.mat-mdc-tab-header-pagination-after'); + prevButton = fixture.nativeElement.querySelector('.mat-mdc-tab-header-pagination-before'); + header = fixture.componentInstance.tabHeader; + headerElement = fixture.nativeElement.querySelector('.mat-mdc-tab-header'); + }); + + it('should scroll towards the end while holding down the next button using a mouse', fakeAsync(() => { + assertNextButtonScrolling('mousedown', 'click'); + })); + + it('should scroll towards the start while holding down the prev button using a mouse', fakeAsync(() => { + assertPrevButtonScrolling('mousedown', 'click'); + })); + + it('should scroll towards the end while holding down the next button using touch', fakeAsync(() => { + assertNextButtonScrolling('touchstart', 'touchend'); + })); + + it('should scroll towards the start while holding down the prev button using touch', fakeAsync(() => { + assertPrevButtonScrolling('touchstart', 'touchend'); + })); + + it('should not scroll if the sequence is interrupted quickly', fakeAsync(() => { + expect(header.scrollDistance).withContext('Expected to start off not scrolled.').toBe(0); + + dispatchFakeEvent(nextButton, 'mousedown'); + fixture.detectChanges(); + + tick(100); + + dispatchFakeEvent(headerElement, 'mouseleave'); + fixture.detectChanges(); + + tick(3000); + + expect(header.scrollDistance) + .withContext('Expected not to have scrolled after a while.') + .toBe(0); + })); + + it('should clear the timeouts on destroy', fakeAsync(() => { + dispatchFakeEvent(nextButton, 'mousedown'); + fixture.detectChanges(); + fixture.destroy(); + + // No need to assert. If fakeAsync doesn't throw, it means that the timers were cleared. + })); + + it('should clear the timeouts on click', fakeAsync(() => { + dispatchFakeEvent(nextButton, 'mousedown'); + fixture.detectChanges(); + + dispatchFakeEvent(nextButton, 'click'); + fixture.detectChanges(); + + // No need to assert. If fakeAsync doesn't throw, it means that the timers were cleared. + })); + + it('should clear the timeouts on touchend', fakeAsync(() => { + dispatchFakeEvent(nextButton, 'touchstart'); + fixture.detectChanges(); + + dispatchFakeEvent(nextButton, 'touchend'); + fixture.detectChanges(); + + // No need to assert. If fakeAsync doesn't throw, it means that the timers were cleared. + })); + + it('should clear the timeouts when reaching the end', fakeAsync(() => { + dispatchFakeEvent(nextButton, 'mousedown'); + fixture.detectChanges(); + + // Simulate a very long timeout. + tick(60000); + + // No need to assert. If fakeAsync doesn't throw, it means that the timers were cleared. + })); + + it('should clear the timeouts when reaching the start', fakeAsync(() => { + header.scrollDistance = Infinity; + fixture.detectChanges(); + + dispatchFakeEvent(prevButton, 'mousedown'); + fixture.detectChanges(); + + // Simulate a very long timeout. + tick(60000); + + // No need to assert. If fakeAsync doesn't throw, it means that the timers were cleared. + })); + + it('should stop scrolling if the pointer leaves the header', fakeAsync(() => { + expect(header.scrollDistance).withContext('Expected to start off not scrolled.').toBe(0); + + dispatchFakeEvent(nextButton, 'mousedown'); + fixture.detectChanges(); + tick(300); + + expect(header.scrollDistance) + .withContext('Expected not to scroll after short amount of time.') + .toBe(0); + + tick(1000); + + expect(header.scrollDistance) + .withContext('Expected to scroll after some time.') + .toBeGreaterThan(0); + + let previousDistance = header.scrollDistance; + + dispatchFakeEvent(headerElement, 'mouseleave'); + fixture.detectChanges(); + tick(100); + + expect(header.scrollDistance).toBe(previousDistance); + })); + + it('should not scroll when pressing the right mouse button', fakeAsync(() => { + expect(header.scrollDistance).withContext('Expected to start off not scrolled.').toBe(0); + + dispatchEvent( + nextButton, + createMouseEvent('mousedown', undefined, undefined, undefined, undefined, 2), + ); + fixture.detectChanges(); + tick(3000); + + expect(header.scrollDistance) + .withContext('Expected not to have scrolled after a while.') + .toBe(0); + })); + + /** + * Asserts that auto scrolling using the next button works. + * @param startEventName Name of the event that is supposed to start the scrolling. + * @param endEventName Name of the event that is supposed to end the scrolling. + */ + function assertNextButtonScrolling(startEventName: string, endEventName: string) { + expect(header.scrollDistance).withContext('Expected to start off not scrolled.').toBe(0); + + dispatchFakeEvent(nextButton, startEventName); + fixture.detectChanges(); + tick(300); + + expect(header.scrollDistance) + .withContext('Expected not to scroll after short amount of time.') + .toBe(0); + + tick(1000); + + expect(header.scrollDistance) + .withContext('Expected to scroll after some time.') + .toBeGreaterThan(0); + + let previousDistance = header.scrollDistance; + + tick(100); + + expect(header.scrollDistance) + .withContext('Expected to scroll again after some more time.') + .toBeGreaterThan(previousDistance); + + dispatchFakeEvent(nextButton, endEventName); + } + + /** + * Asserts that auto scrolling using the previous button works. + * @param startEventName Name of the event that is supposed to start the scrolling. + * @param endEventName Name of the event that is supposed to end the scrolling. + */ + function assertPrevButtonScrolling(startEventName: string, endEventName: string) { + header.scrollDistance = Infinity; + fixture.detectChanges(); + + let currentScroll = header.scrollDistance; + + expect(currentScroll).withContext('Expected to start off scrolled.').toBeGreaterThan(0); + + dispatchFakeEvent(prevButton, startEventName); + fixture.detectChanges(); + tick(300); + + expect(header.scrollDistance) + .withContext('Expected not to scroll after short amount of time.') + .toBe(currentScroll); + + tick(1000); + + expect(header.scrollDistance) + .withContext('Expected to scroll after some time.') + .toBeLessThan(currentScroll); + + currentScroll = header.scrollDistance; + + tick(100); + + expect(header.scrollDistance) + .withContext('Expected to scroll again after some more time.') + .toBeLessThan(currentScroll); + + dispatchFakeEvent(nextButton, endEventName); + } + }); + + describe('disabling pagination', () => { + it('should not show the pagination controls if pagination is disabled', () => { + fixture = TestBed.createComponent(SimpleTabHeaderApp); + appComponent = fixture.componentInstance; + appComponent.disablePagination = true; + fixture.detectChanges(); + expect(appComponent.tabHeader._showPaginationControls).toBe(false); + + // Add enough tabs that it will obviously exceed the width + appComponent.addTabsForScrolling(); + fixture.detectChanges(); + + expect(appComponent.tabHeader._showPaginationControls).toBe(false); + }); + + it('should not change the scroll position if pagination is disabled', () => { + fixture = TestBed.createComponent(SimpleTabHeaderApp); + appComponent = fixture.componentInstance; + appComponent.disablePagination = true; + fixture.detectChanges(); + appComponent.addTabsForScrolling(); + fixture.detectChanges(); + expect(appComponent.tabHeader.scrollDistance).toBe(0); + + appComponent.tabHeader.focusIndex = appComponent.tabs.length - 1; + fixture.detectChanges(); + expect(appComponent.tabHeader.scrollDistance).toBe(0); + + appComponent.tabHeader.focusIndex = 0; + fixture.detectChanges(); + expect(appComponent.tabHeader.scrollDistance).toBe(0); + }); + }); + + it('should re-align the ink bar when the direction changes', fakeAsync(() => { + fixture = TestBed.createComponent(SimpleTabHeaderApp); + fixture.detectChanges(); + + const inkBar = fixture.componentInstance.tabHeader._inkBar; + spyOn(inkBar, 'alignToElement'); + + fixture.detectChanges(); + + fixture.componentInstance.dir = 'rtl'; + fixture.detectChanges(); + tick(); + + expect(inkBar.alignToElement).toHaveBeenCalled(); + })); + + it('should re-align the ink bar when the window is resized', fakeAsync(() => { + fixture = TestBed.createComponent(SimpleTabHeaderApp); + fixture.detectChanges(); + + const inkBar = fixture.componentInstance.tabHeader._inkBar; + + spyOn(inkBar, 'alignToElement'); + + dispatchFakeEvent(window, 'resize'); + tick(150); + fixture.detectChanges(); + + expect(inkBar.alignToElement).toHaveBeenCalled(); + discardPeriodicTasks(); + })); + + it('should update arrows when the window is resized', fakeAsync(() => { + fixture = TestBed.createComponent(SimpleTabHeaderApp); + + const header = fixture.componentInstance.tabHeader; + + spyOn(header, '_checkPaginationEnabled'); + + dispatchFakeEvent(window, 'resize'); + tick(10); + fixture.detectChanges(); + + expect(header._checkPaginationEnabled).toHaveBeenCalled(); + discardPeriodicTasks(); + })); + + it('should update the pagination state if the content of the labels changes', () => { + const mutationCallbacks: Function[] = []; + TestBed.overrideProvider(MutationObserverFactory, { + useValue: { + // Stub out the MutationObserver since the native one is async. + create: function (callback: Function) { + mutationCallbacks.push(callback); + return {observe: () => {}, disconnect: () => {}}; + }, + }, + }); + + fixture = TestBed.createComponent(SimpleTabHeaderApp); + fixture.detectChanges(); + + const tabHeaderElement: HTMLElement = + fixture.nativeElement.querySelector('.mat-mdc-tab-header'); + const labels = Array.from( + fixture.nativeElement.querySelectorAll('.label-content'), + ); + const extraText = new Array(100).fill('w').join(); + const enabledClass = 'mat-mdc-tab-header-pagination-controls-enabled'; + + expect(tabHeaderElement.classList).not.toContain(enabledClass); + + labels.forEach(label => { + label.style.width = ''; + label.textContent += extraText; + }); + + mutationCallbacks.forEach(callback => callback()); + fixture.detectChanges(); + + expect(tabHeaderElement.classList).toContain(enabledClass); + }); + }); +}); + +interface Tab { + label: string; + disabled?: boolean; +} + +@Component({ + template: ` +
+ +
+ {{tab.label}} +
+
+
+ `, + styles: [ + ` + :host { + width: 130px; + } + `, + ], +}) +class SimpleTabHeaderApp { + disableRipple: boolean = false; + selectedIndex: number = 0; + focusedIndex: number; + disabledTabIndex = 1; + tabs: Tab[] = [{label: 'tab one'}, {label: 'tab one'}, {label: 'tab one'}, {label: 'tab one'}]; + dir: Direction = 'ltr'; + disablePagination: boolean; + + @ViewChild(MatTabHeader, {static: true}) tabHeader: MatTabHeader; + + constructor() { + this.tabs[this.disabledTabIndex].disabled = true; + } + + addTabsForScrolling(amount = 4) { + for (let i = 0; i < amount; i++) { + this.tabs.push({label: 'new'}); + } + } + + getViewLength() { + return this.tabHeader._tabListContainer.nativeElement.offsetWidth; + } + + getSelectedLabel(index: number) { + return this.tabHeader._items.toArray()[this.tabHeader.focusIndex].elementRef.nativeElement; + } +} diff --git a/ui/src/components/tabs/tab-header.ts b/ui/src/components/tabs/tab-header.ts new file mode 100644 index 000000000..eb4386f1d --- /dev/null +++ b/ui/src/components/tabs/tab-header.ts @@ -0,0 +1,102 @@ +/** + * @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 { + AfterContentChecked, + AfterContentInit, + AfterViewInit, + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ContentChildren, + ElementRef, + Inject, + Input, + NgZone, + OnDestroy, + Optional, + QueryList, + ViewChild, + ViewEncapsulation, +} from '@angular/core'; +import {ViewportRuler} from '@angular/cdk/scrolling'; +import {Platform} from '@angular/cdk/platform'; +import {Directionality} from '@angular/cdk/bidi'; +import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations'; +import {MatTabLabelWrapper} from './tab-label-wrapper'; +import {MatInkBar} from './ink-bar'; +import {MatPaginatedTabHeader} from './paginated-tab-header'; +import {BooleanInput, coerceBooleanProperty} from '@angular/cdk/coercion'; + +/** + * The header of the tab group which displays a list of all the tabs in the tab group. Includes + * an ink bar that follows the currently selected tab. When the tabs list's width exceeds the + * width of the header container, then arrows will be displayed to allow the user to scroll + * left and right across the header. + * @docs-private + */ +@Component({ + selector: 'mat-tab-header', + templateUrl: 'tab-header.html', + styleUrls: ['tab-header.scss'], + inputs: ['selectedIndex'], + outputs: ['selectFocusedIndex', 'indexFocused'], + encapsulation: ViewEncapsulation.None, + // tslint:disable-next-line:validate-decorators + changeDetection: ChangeDetectionStrategy.Default, + host: { + 'class': 'mat-mdc-tab-header', + '[class.mat-mdc-tab-header-pagination-controls-enabled]': '_showPaginationControls', + '[class.mat-mdc-tab-header-rtl]': "_getLayoutDirection() == 'rtl'", + }, +}) +export class MatTabHeader + extends MatPaginatedTabHeader + implements AfterContentChecked, AfterContentInit, AfterViewInit, OnDestroy +{ + @ContentChildren(MatTabLabelWrapper, {descendants: false}) _items: QueryList; + @ViewChild('tabListContainer', {static: true}) _tabListContainer: ElementRef; + @ViewChild('tabList', {static: true}) _tabList: ElementRef; + @ViewChild('tabListInner', {static: true}) _tabListInner: ElementRef; + @ViewChild('nextPaginator') _nextPaginator: ElementRef; + @ViewChild('previousPaginator') _previousPaginator: ElementRef; + _inkBar: MatInkBar; + + /** Whether the ripple effect is disabled or not. */ + @Input() + get disableRipple(): boolean { + return this._disableRipple; + } + + set disableRipple(value: BooleanInput) { + this._disableRipple = coerceBooleanProperty(value); + } + + private _disableRipple: boolean = false; + + constructor( + elementRef: ElementRef, + changeDetectorRef: ChangeDetectorRef, + viewportRuler: ViewportRuler, + @Optional() dir: Directionality, + ngZone: NgZone, + platform: Platform, + @Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string, + ) { + super(elementRef, changeDetectorRef, viewportRuler, dir, ngZone, platform, animationMode); + } + + override ngAfterContentInit() { + this._inkBar = new MatInkBar(this._items); + super.ngAfterContentInit(); + } + + protected _itemSelected(event: KeyboardEvent) { + event.preventDefault(); + } +} diff --git a/ui/src/components/tabs/tab-label-wrapper.ts b/ui/src/components/tabs/tab-label-wrapper.ts new file mode 100644 index 000000000..e13978624 --- /dev/null +++ b/ui/src/components/tabs/tab-label-wrapper.ts @@ -0,0 +1,52 @@ +/** + * @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, ElementRef} from '@angular/core'; +import {mixinInkBarItem} from './ink-bar'; +import {CanDisable, mixinDisabled} from '../core'; + +// Boilerplate for applying mixins to MatTabLabelWrapper. +/** @docs-private */ +const _MatTabLabelWrapperMixinBase = mixinInkBarItem( + mixinDisabled( + class { + elementRef: ElementRef; + }, + ), +); + +/** + * Used in the `mat-tab-group` view to display tab labels. + * @docs-private + */ +@Directive({ + selector: '[matTabLabelWrapper]', + inputs: ['disabled', 'fitInkBarToContent'], + host: { + '[class.mat-mdc-tab-disabled]': 'disabled', + '[attr.aria-disabled]': '!!disabled', + }, +}) +export class MatTabLabelWrapper extends _MatTabLabelWrapperMixinBase implements CanDisable { + constructor(override elementRef: ElementRef) { + super(); + } + + /** Sets focus on the wrapper element */ + focus(): void { + this.elementRef.nativeElement.focus(); + } + + getOffsetLeft(): number { + return this.elementRef.nativeElement.offsetLeft; + } + + getOffsetWidth(): number { + return this.elementRef.nativeElement.offsetWidth; + } +} diff --git a/ui/src/components/tabs/tab-label.ts b/ui/src/components/tabs/tab-label.ts new file mode 100644 index 000000000..9f2bc58d7 --- /dev/null +++ b/ui/src/components/tabs/tab-label.ts @@ -0,0 +1,45 @@ +/** + * @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, + Inject, + InjectionToken, + Optional, + TemplateRef, + ViewContainerRef, +} from '@angular/core'; +import {CdkPortal} from '@angular/cdk/portal'; + +/** + * Injection token that can be used to reference instances of `MatTabLabel`. It serves as + * alternative token to the actual `MatTabLabel` class which could cause unnecessary + * retention of the class and its directive metadata. + */ +export const MAT_TAB_LABEL = new InjectionToken('MatTabLabel'); + +/** + * Used to provide a tab label to a tab without causing a circular dependency. + * @docs-private + */ +export const MAT_TAB = new InjectionToken('MAT_TAB'); + +/** Used to flag tab labels for use with the portal directive */ +@Directive({ + selector: '[mat-tab-label], [matTabLabel]', + providers: [{provide: MAT_TAB_LABEL, useExisting: MatTabLabel}], +}) +export class MatTabLabel extends CdkPortal { + constructor( + templateRef: TemplateRef, + viewContainerRef: ViewContainerRef, + @Inject(MAT_TAB) @Optional() public _closestTab: any, + ) { + super(templateRef, viewContainerRef); + } +} diff --git a/ui/src/components/tabs/tab-nav-bar/tab-link.html b/ui/src/components/tabs/tab-nav-bar/tab-link.html new file mode 100644 index 000000000..be3c90598 --- /dev/null +++ b/ui/src/components/tabs/tab-nav-bar/tab-link.html @@ -0,0 +1,14 @@ + + +
+ + + + + + + diff --git a/ui/src/components/tabs/tab-nav-bar/tab-link.scss b/ui/src/components/tabs/tab-nav-bar/tab-link.scss new file mode 100644 index 000000000..d6ed48198 --- /dev/null +++ b/ui/src/components/tabs/tab-nav-bar/tab-link.scss @@ -0,0 +1,24 @@ +@use '../../core/style/variables'; +@use '../tabs-common'; + +// Wraps each link in the header +.mat-mdc-tab-link { + // @include tabs-common.tab; + + // Note that we only want to target direct descendant tabs. + .mat-mdc-tab-header.mat-mdc-tab-nav-bar-stretch-tabs & { + flex-grow: 1; + } + + // For the tab-link element, default inset/offset values are necessary to ensure that + // the focus indicator is sufficiently contrastive and renders appropriately. + &::before { + margin: 5px; + } +} + +// @media (variables.$xsmall) { +// .mat-mdc-tab-link { +// min-width: 72px; +// } +// } diff --git a/ui/src/components/tabs/tab-nav-bar/tab-nav-bar.html b/ui/src/components/tabs/tab-nav-bar/tab-nav-bar.html new file mode 100644 index 000000000..f01013da5 --- /dev/null +++ b/ui/src/components/tabs/tab-nav-bar/tab-nav-bar.html @@ -0,0 +1,39 @@ + + + + + + + diff --git a/ui/src/components/tabs/tab-nav-bar/tab-nav-bar.scss b/ui/src/components/tabs/tab-nav-bar/tab-nav-bar.scss new file mode 100644 index 000000000..f10a54407 --- /dev/null +++ b/ui/src/components/tabs/tab-nav-bar/tab-nav-bar.scss @@ -0,0 +1,17 @@ +// @use '../tabs-common'; + +// @include tabs-common.structural-styles; +// @include tabs-common.paginated-tab-header; + +// .mat-mdc-tab-links { +// @include tabs-common.paginated-tab-header-item-wrapper('.mat-mdc-tab-link-container'); +// } + +// .mat-mdc-tab-link-container { +// @include tabs-common.paginated-tab-header-container; +// } + +// .mat-mdc-tab-nav-bar { +// @include tabs-common.paginated-tab-header-with-background('.mat-mdc-tab-link-container', +// '.mat-mdc-tab-link'); +// } diff --git a/ui/src/components/tabs/tab-nav-bar/tab-nav-bar.spec.ts b/ui/src/components/tabs/tab-nav-bar/tab-nav-bar.spec.ts new file mode 100644 index 000000000..2a457d6d5 --- /dev/null +++ b/ui/src/components/tabs/tab-nav-bar/tab-nav-bar.spec.ts @@ -0,0 +1,576 @@ +import {ENTER, SPACE} from '@angular/cdk/keycodes'; +import {waitForAsync, ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing'; +import {Component, QueryList, ViewChild, ViewChildren} from '@angular/core'; +import {MAT_RIPPLE_GLOBAL_OPTIONS, RippleGlobalOptions} from '../../core'; +import {By} from '@angular/platform-browser'; +import { + dispatchFakeEvent, + dispatchKeyboardEvent, + dispatchMouseEvent, +} from '../../../cdk/testing/private'; +import {Direction, Directionality} from '@angular/cdk/bidi'; +import {Subject} from 'rxjs'; +import {MatTabsModule} from '../module'; +import {MatTabLink, MatTabNav} from './tab-nav-bar'; +import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; +import {MAT_TABS_CONFIG} from '../index'; + +describe('MDC-based MatTabNavBar', () => { + let dir: Direction = 'ltr'; + let dirChange = new Subject(); + let globalRippleOptions: RippleGlobalOptions; + + beforeEach(waitForAsync(() => { + globalRippleOptions = {}; + + TestBed.configureTestingModule({ + imports: [MatTabsModule], + declarations: [SimpleTabNavBarTestApp, TabLinkWithNgIf, TabBarWithInactiveTabsOnInit], + providers: [ + {provide: MAT_RIPPLE_GLOBAL_OPTIONS, useFactory: () => globalRippleOptions}, + {provide: Directionality, useFactory: () => ({value: dir, change: dirChange})}, + ], + }); + + TestBed.compileComponents(); + })); + + describe('basic behavior', () => { + let fixture: ComponentFixture; + + beforeEach(() => { + fixture = TestBed.createComponent(SimpleTabNavBarTestApp); + fixture.detectChanges(); + }); + + it('should change active index on click', () => { + // select the second link + let tabLink = fixture.debugElement.queryAll(By.css('a'))[1]; + tabLink.nativeElement.click(); + expect(fixture.componentInstance.activeIndex).toBe(1); + + // select the third link + tabLink = fixture.debugElement.queryAll(By.css('a'))[2]; + tabLink.nativeElement.click(); + expect(fixture.componentInstance.activeIndex).toBe(2); + }); + + it('should add the active class if active', () => { + let tabLink1 = fixture.debugElement.queryAll(By.css('a'))[0]; + let tabLink2 = fixture.debugElement.queryAll(By.css('a'))[1]; + const tabLinkElements = fixture.debugElement + .queryAll(By.css('a')) + .map(tabLinkDebugEl => tabLinkDebugEl.nativeElement); + + tabLink1.nativeElement.click(); + fixture.detectChanges(); + expect(tabLinkElements[0].classList.contains('mdc-tab--active')).toBeTruthy(); + expect(tabLinkElements[1].classList.contains('mdc-tab--active')).toBeFalsy(); + + tabLink2.nativeElement.click(); + fixture.detectChanges(); + expect(tabLinkElements[0].classList.contains('mdc-tab--active')).toBeFalsy(); + expect(tabLinkElements[1].classList.contains('mdc-tab--active')).toBeTruthy(); + }); + + it('should update aria-disabled if disabled', () => { + const tabLinkElements = fixture.debugElement + .queryAll(By.css('a')) + .map(tabLinkDebugEl => tabLinkDebugEl.nativeElement); + + expect(tabLinkElements.every(tabLink => tabLink.getAttribute('aria-disabled') === 'false')) + .withContext('Expected aria-disabled to be set to "false" by default.') + .toBe(true); + + fixture.componentInstance.disabled = true; + fixture.detectChanges(); + + expect(tabLinkElements.every(tabLink => tabLink.getAttribute('aria-disabled') === 'true')) + .withContext('Expected aria-disabled to be set to "true" if link is disabled.') + .toBe(true); + }); + + it('should update the tabindex if links are disabled', () => { + const tabLinkElements = fixture.debugElement + .queryAll(By.css('a')) + .map(tabLinkDebugEl => tabLinkDebugEl.nativeElement); + + expect(tabLinkElements.map(tabLink => tabLink.tabIndex)) + .withContext('Expected first element to be keyboard focusable by default') + .toEqual([0, -1, -1]); + + fixture.componentInstance.disabled = true; + fixture.detectChanges(); + + expect(tabLinkElements.every(tabLink => tabLink.tabIndex === -1)) + .withContext('Expected element to no longer be keyboard focusable if disabled.') + .toBe(true); + }); + + it('should mark disabled links', () => { + const tabLinkElement = fixture.debugElement.query(By.css('a')).nativeElement; + + expect(tabLinkElement.classList).not.toContain('mat-mdc-tab-disabled'); + + fixture.componentInstance.disabled = true; + fixture.detectChanges(); + + expect(tabLinkElement.classList).toContain('mat-mdc-tab-disabled'); + }); + + it('should prevent default keyboard actions on disabled links', () => { + const link = fixture.debugElement.query(By.css('a')).nativeElement; + fixture.componentInstance.disabled = true; + fixture.detectChanges(); + + const spaceEvent = dispatchKeyboardEvent(link, 'keydown', SPACE); + fixture.detectChanges(); + expect(spaceEvent.defaultPrevented).toBe(true); + + const enterEvent = dispatchKeyboardEvent(link, 'keydown', ENTER); + fixture.detectChanges(); + expect(enterEvent.defaultPrevented).toBe(true); + }); + + it('should re-align the ink bar when the direction changes', fakeAsync(() => { + const inkBar = fixture.componentInstance.tabNavBar._inkBar; + + spyOn(inkBar, 'alignToElement'); + + dirChange.next(); + tick(); + fixture.detectChanges(); + + expect(inkBar.alignToElement).toHaveBeenCalled(); + })); + + it('should re-align the ink bar when the tabs list change', fakeAsync(() => { + const inkBar = fixture.componentInstance.tabNavBar._inkBar; + + spyOn(inkBar, 'alignToElement'); + + fixture.componentInstance.tabs = [1, 2, 3, 4]; + fixture.detectChanges(); + tick(); + + expect(inkBar.alignToElement).toHaveBeenCalled(); + })); + + it('should re-align the ink bar when the tab labels change the width', done => { + const inkBar = fixture.componentInstance.tabNavBar._inkBar; + + const spy = spyOn(inkBar, 'alignToElement').and.callFake(() => { + expect(spy.calls.any()).toBe(true); + done(); + }); + + fixture.componentInstance.label = 'label change'; + fixture.detectChanges(); + + expect(spy.calls.any()).toBe(false); + }); + + it('should re-align the ink bar when the window is resized', fakeAsync(() => { + const inkBar = fixture.componentInstance.tabNavBar._inkBar; + + spyOn(inkBar, 'alignToElement'); + + dispatchFakeEvent(window, 'resize'); + tick(150); + fixture.detectChanges(); + + expect(inkBar.alignToElement).toHaveBeenCalled(); + })); + + it('should hide the ink bar when all the links are inactive', () => { + const inkBar = fixture.componentInstance.tabNavBar._inkBar; + + spyOn(inkBar, 'hide'); + + fixture.componentInstance.tabLinks.forEach(link => (link.active = false)); + fixture.detectChanges(); + + expect(inkBar.hide).toHaveBeenCalled(); + }); + + it('should update the focusIndex when a tab receives focus directly', () => { + const thirdLink = fixture.debugElement.queryAll(By.css('a'))[2]; + dispatchFakeEvent(thirdLink.nativeElement, 'focus'); + fixture.detectChanges(); + + expect(fixture.componentInstance.tabNavBar.focusIndex).toBe(2); + }); + }); + + it('should hide the ink bar if no tabs are active on init', fakeAsync(() => { + const fixture = TestBed.createComponent(TabBarWithInactiveTabsOnInit); + fixture.detectChanges(); + tick(20); // Angular turns rAF calls into 16.6ms timeouts in tests. + fixture.detectChanges(); + + expect(fixture.nativeElement.querySelectorAll('.mdc-tab-indicator--active').length).toBe(0); + })); + + it('should clean up the ripple event handlers on destroy', () => { + let fixture: ComponentFixture = TestBed.createComponent(TabLinkWithNgIf); + fixture.detectChanges(); + + let link = fixture.debugElement.nativeElement.querySelector('.mat-mdc-tab-link'); + + fixture.componentInstance.isDestroyed = true; + fixture.detectChanges(); + + dispatchMouseEvent(link, 'mousedown'); + + expect(link.querySelector('.mat-ripple-element')) + .withContext('Expected no ripple to be created when ripple target is destroyed.') + .toBeFalsy(); + }); + + it('should select the proper tab, if the tabs come in after init', () => { + const fixture = TestBed.createComponent(SimpleTabNavBarTestApp); + const instance = fixture.componentInstance; + + instance.tabs = []; + instance.activeIndex = 1; + fixture.detectChanges(); + + expect(instance.tabNavBar.selectedIndex).toBe(-1); + + instance.tabs = [0, 1, 2]; + fixture.detectChanges(); + + expect(instance.tabNavBar.selectedIndex).toBe(1); + }); + + it('should have the proper roles', () => { + const fixture = TestBed.createComponent(SimpleTabNavBarTestApp); + fixture.detectChanges(); + + const tabBar = fixture.nativeElement.querySelector('.mat-mdc-tab-nav-bar')!; + expect(tabBar.getAttribute('role')).toBe('tablist'); + + const tabLinks = fixture.nativeElement.querySelectorAll('.mat-mdc-tab-link'); + + expect(tabLinks[0].getAttribute('role')).toBe('tab'); + expect(tabLinks[1].getAttribute('role')).toBe('tab'); + expect(tabLinks[2].getAttribute('role')).toBe('tab'); + + const tabPanel = fixture.nativeElement.querySelector('.mat-mdc-tab-nav-panel')!; + expect(tabPanel.getAttribute('role')).toBe('tabpanel'); + }); + + it('should manage tabindex properly', () => { + const fixture = TestBed.createComponent(SimpleTabNavBarTestApp); + fixture.detectChanges(); + + const tabLinks = fixture.nativeElement.querySelectorAll('.mat-mdc-tab-link'); + expect(tabLinks[0].tabIndex).toBe(0); + expect(tabLinks[1].tabIndex).toBe(-1); + expect(tabLinks[2].tabIndex).toBe(-1); + + tabLinks[1].click(); + fixture.detectChanges(); + + expect(tabLinks[0].tabIndex).toBe(-1); + expect(tabLinks[1].tabIndex).toBe(0); + expect(tabLinks[2].tabIndex).toBe(-1); + }); + + it('should setup aria-controls properly', () => { + const fixture = TestBed.createComponent(SimpleTabNavBarTestApp); + fixture.detectChanges(); + + const tabLinks = fixture.nativeElement.querySelectorAll('.mat-mdc-tab-link'); + expect(tabLinks[0].getAttribute('aria-controls')).toBe('tab-panel'); + expect(tabLinks[1].getAttribute('aria-controls')).toBe('tab-panel'); + expect(tabLinks[2].getAttribute('aria-controls')).toBe('tab-panel'); + }); + + it('should not manage aria-current', () => { + const fixture = TestBed.createComponent(SimpleTabNavBarTestApp); + fixture.detectChanges(); + + const tabLinks = fixture.nativeElement.querySelectorAll('.mat-mdc-tab-link'); + expect(tabLinks[0].getAttribute('aria-current')).toBe(null); + expect(tabLinks[1].getAttribute('aria-current')).toBe(null); + expect(tabLinks[2].getAttribute('aria-current')).toBe(null); + }); + + it('should manage aria-selected properly', () => { + const fixture = TestBed.createComponent(SimpleTabNavBarTestApp); + fixture.detectChanges(); + + const tabLinks = fixture.nativeElement.querySelectorAll('.mat-mdc-tab-link'); + expect(tabLinks[0].getAttribute('aria-selected')).toBe('true'); + expect(tabLinks[1].getAttribute('aria-selected')).toBe('false'); + expect(tabLinks[2].getAttribute('aria-selected')).toBe('false'); + + tabLinks[1].click(); + fixture.detectChanges(); + + expect(tabLinks[0].getAttribute('aria-selected')).toBe('false'); + expect(tabLinks[1].getAttribute('aria-selected')).toBe('true'); + expect(tabLinks[2].getAttribute('aria-selected')).toBe('false'); + }); + + it('should activate a link when space is pressed', () => { + const fixture = TestBed.createComponent(SimpleTabNavBarTestApp); + fixture.detectChanges(); + + const tabLinks = fixture.nativeElement.querySelectorAll('.mat-mdc-tab-link'); + expect(tabLinks[1].classList.contains('mdc-tab--active')).toBe(false); + + dispatchKeyboardEvent(tabLinks[1], 'keydown', SPACE); + fixture.detectChanges(); + + expect(tabLinks[1].classList.contains('mdc-tab--active')).toBe(true); + }); + + describe('ripples', () => { + let fixture: ComponentFixture; + + beforeEach(() => { + fixture = TestBed.createComponent(SimpleTabNavBarTestApp); + fixture.detectChanges(); + }); + + it('should be disabled on all tab links when they are disabled on the nav bar', () => { + expect(fixture.componentInstance.tabLinks.toArray().every(tabLink => !tabLink.rippleDisabled)) + .withContext('Expected every tab link to have ripples enabled') + .toBe(true); + + fixture.componentInstance.disableRippleOnBar = true; + fixture.detectChanges(); + + expect(fixture.componentInstance.tabLinks.toArray().every(tabLink => tabLink.rippleDisabled)) + .withContext('Expected every tab link to have ripples disabled') + .toBe(true); + }); + + it('should have the `disableRipple` from the tab take precedence over the nav bar', () => { + const firstTab = fixture.componentInstance.tabLinks.first; + + expect(firstTab.rippleDisabled) + .withContext('Expected ripples to be enabled on first tab') + .toBe(false); + + firstTab.disableRipple = true; + fixture.componentInstance.disableRippleOnBar = false; + fixture.detectChanges(); + + expect(firstTab.rippleDisabled) + .withContext('Expected ripples to be disabled on first tab') + .toBe(true); + }); + + it('should show up for tab link elements on mousedown', () => { + const tabLink = fixture.debugElement.nativeElement.querySelector('.mat-mdc-tab-link'); + + dispatchMouseEvent(tabLink, 'mousedown'); + dispatchMouseEvent(tabLink, 'mouseup'); + + expect(tabLink.querySelectorAll('.mat-ripple-element').length) + .withContext('Expected one ripple to show up if user clicks on tab link.') + .toBe(1); + }); + + it('should be able to disable ripples on an individual tab link', () => { + const tabLinkDebug = fixture.debugElement.query(By.css('a')); + const tabLinkElement = tabLinkDebug.nativeElement; + + fixture.componentInstance.disableRippleOnLink = true; + fixture.detectChanges(); + + dispatchMouseEvent(tabLinkElement, 'mousedown'); + dispatchMouseEvent(tabLinkElement, 'mouseup'); + + expect(tabLinkElement.querySelectorAll('.mat-ripple-element').length) + .withContext('Expected no ripple to show up if ripples are disabled.') + .toBe(0); + }); + + it('should be able to disable ripples through global options at runtime', () => { + expect(fixture.componentInstance.tabLinks.toArray().every(tabLink => !tabLink.rippleDisabled)) + .withContext('Expected every tab link to have ripples enabled') + .toBe(true); + + globalRippleOptions.disabled = true; + + expect(fixture.componentInstance.tabLinks.toArray().every(tabLink => tabLink.rippleDisabled)) + .withContext('Expected every tab link to have ripples disabled') + .toBe(true); + }); + + it('should have a focus indicator', () => { + const tabLinkNativeElements = [ + ...fixture.debugElement.nativeElement.querySelectorAll('.mat-mdc-tab-link'), + ]; + + expect( + tabLinkNativeElements.every(element => + element.classList.contains('mat-mdc-focus-indicator'), + ), + ).toBe(true); + }); + }); + + describe('with the ink bar fit to content', () => { + let fixture: ComponentFixture; + + beforeEach(() => { + fixture = TestBed.createComponent(SimpleTabNavBarTestApp); + fixture.componentInstance.fitInkBarToContent = true; + fixture.detectChanges(); + }); + + it('should properly nest the ink bar when fit to content', () => { + const tabElement = fixture.nativeElement.querySelector('.mdc-tab'); + const contentElement = tabElement.querySelector('.mdc-tab__content'); + const indicatorElement = tabElement.querySelector('.mdc-tab-indicator'); + expect(indicatorElement.parentElement).toBeTruthy(); + expect(indicatorElement.parentElement).toBe(contentElement); + }); + + it('should be able to move the ink bar between content and full', () => { + fixture.componentInstance.fitInkBarToContent = false; + fixture.detectChanges(); + + const tabElement = fixture.nativeElement.querySelector('.mdc-tab'); + const indicatorElement = tabElement.querySelector('.mdc-tab-indicator'); + expect(indicatorElement.parentElement).toBeTruthy(); + expect(indicatorElement.parentElement).toBe(tabElement); + + fixture.componentInstance.fitInkBarToContent = true; + fixture.detectChanges(); + + const contentElement = tabElement.querySelector('.mdc-tab__content'); + expect(indicatorElement.parentElement).toBeTruthy(); + expect(indicatorElement.parentElement).toBe(contentElement); + }); + }); +}); + +describe('MatTabNavBar with a default config', () => { + let fixture: ComponentFixture; + + beforeEach(fakeAsync(() => { + TestBed.configureTestingModule({ + imports: [MatTabsModule, BrowserAnimationsModule], + declarations: [TabLinkWithNgIf], + providers: [{provide: MAT_TABS_CONFIG, useValue: {fitInkBarToContent: true}}], + }); + + TestBed.compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TabLinkWithNgIf); + fixture.detectChanges(); + }); + + it('should set whether the ink bar fits to content', () => { + const tabElement = fixture.nativeElement.querySelector('.mdc-tab'); + const contentElement = tabElement.querySelector('.mdc-tab__content'); + const indicatorElement = tabElement.querySelector('.mdc-tab-indicator'); + expect(indicatorElement.parentElement).toBeTruthy(); + expect(indicatorElement.parentElement).toBe(contentElement); + }); +}); + +describe('MatTabNavBar with enabled animations', () => { + beforeEach(fakeAsync(() => { + TestBed.configureTestingModule({ + imports: [MatTabsModule, BrowserAnimationsModule], + declarations: [TabsWithCustomAnimationDuration], + }); + + TestBed.compileComponents(); + })); + + it('should not throw when setting an animationDuration without units', fakeAsync(() => { + expect(() => { + let fixture = TestBed.createComponent(TabsWithCustomAnimationDuration); + fixture.detectChanges(); + tick(); + }).not.toThrow(); + })); + + it('should set appropiate css variable given a specified animationDuration', fakeAsync(() => { + let fixture = TestBed.createComponent(TabsWithCustomAnimationDuration); + fixture.detectChanges(); + tick(); + + const tabNavBar = fixture.nativeElement.querySelector('.mat-mdc-tab-nav-bar'); + expect(tabNavBar.style.getPropertyValue('--mat-tab-animation-duration')).toBe('500ms'); + })); +}); + +@Component({ + selector: 'test-app', + template: ` + + Tab panel + `, +}) +class SimpleTabNavBarTestApp { + @ViewChild(MatTabNav) tabNavBar: MatTabNav; + @ViewChildren(MatTabLink) tabLinks: QueryList; + + label = ''; + disabled = false; + disableRippleOnBar = false; + disableRippleOnLink = false; + tabs = [0, 1, 2]; + fitInkBarToContent = false; + + activeIndex = 0; +} + +@Component({ + template: ` + + Tab panel + `, +}) +class TabLinkWithNgIf { + isDestroyed = false; +} + +@Component({ + template: ` + + Tab panel + `, +}) +class TabBarWithInactiveTabsOnInit { + tabs = [0, 1, 2]; +} + +@Component({ + template: ` + + , + `, +}) +class TabsWithCustomAnimationDuration { + links = ['First', 'Second', 'Third']; +} diff --git a/ui/src/components/tabs/tab-nav-bar/tab-nav-bar.ts b/ui/src/components/tabs/tab-nav-bar/tab-nav-bar.ts new file mode 100644 index 000000000..2a09c12a0 --- /dev/null +++ b/ui/src/components/tabs/tab-nav-bar/tab-nav-bar.ts @@ -0,0 +1,450 @@ +/** + * @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 { + AfterContentChecked, + AfterContentInit, + AfterViewInit, + Attribute, + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ContentChildren, + ElementRef, + forwardRef, + Inject, + Input, + NgZone, + OnDestroy, + Optional, + QueryList, + ViewChild, + ViewEncapsulation, +} from '@angular/core'; +import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations'; +import { + CanDisable, + CanDisableRipple, + HasTabIndex, + MAT_RIPPLE_GLOBAL_OPTIONS, + mixinDisabled, + mixinDisableRipple, + mixinTabIndex, + RippleConfig, + RippleGlobalOptions, + RippleTarget, + ThemePalette, +} from '../../core'; +import {FocusableOption, FocusMonitor} from '@angular/cdk/a11y'; +import {Directionality} from '@angular/cdk/bidi'; +import {ViewportRuler} from '@angular/cdk/scrolling'; +import {Platform} from '@angular/cdk/platform'; +import {MatInkBar, mixinInkBarItem} from '../ink-bar'; +import {BooleanInput, coerceBooleanProperty, NumberInput} from '@angular/cdk/coercion'; +import {BehaviorSubject, Subject} from 'rxjs'; +import {startWith, takeUntil} from 'rxjs/operators'; +import {ENTER, SPACE} from '@angular/cdk/keycodes'; +import {MAT_TABS_CONFIG, MatTabsConfig} from '../tab-config'; +import {MatPaginatedTabHeader} from '../paginated-tab-header'; +import { isDevMode } from '@angular/core'; + +// Increasing integer for generating unique ids for tab nav components. +let nextUniqueId = 0; + +/** + * Navigation component matching the styles of the tab group header. + * Provides anchored navigation with animated ink bar. + */ +@Component({ + selector: '[mat-tab-nav-bar]', + exportAs: 'matTabNavBar, matTabNav', + inputs: ['color'], + templateUrl: 'tab-nav-bar.html', + styleUrls: ['tab-nav-bar.scss'], + host: { + '[attr.role]': '_getRole()', + 'class': 'mat-mdc-tab-nav-bar mat-mdc-tab-header', + '[class.mat-mdc-tab-header-pagination-controls-enabled]': '_showPaginationControls', + '[class.mat-mdc-tab-header-rtl]': "_getLayoutDirection() == 'rtl'", + '[class.mat-mdc-tab-nav-bar-stretch-tabs]': 'stretchTabs', + '[class.mat-primary]': 'color !== "warn" && color !== "accent"', + '[class.mat-accent]': 'color === "accent"', + '[class.mat-warn]': 'color === "warn"', + '[class._mat-animation-noopable]': '_animationMode === "NoopAnimations"', + '[style.--mat-tab-animation-duration]': 'animationDuration', + }, + encapsulation: ViewEncapsulation.None, + // tslint:disable-next-line:validate-decorators + changeDetection: ChangeDetectionStrategy.Default, +}) +export class MatTabNav + extends MatPaginatedTabHeader + implements AfterContentChecked, AfterContentInit, OnDestroy, AfterViewInit +{ + /** Whether the ink bar should fit its width to the size of the tab label content. */ + @Input() + get fitInkBarToContent(): boolean { + return this._fitInkBarToContent.value; + } + set fitInkBarToContent(v: BooleanInput) { + this._fitInkBarToContent.next(coerceBooleanProperty(v)); + this._changeDetectorRef.markForCheck(); + } + _fitInkBarToContent = new BehaviorSubject(false); + + /** Whether tabs should be stretched to fill the header. */ + @Input('mat-stretch-tabs') + get stretchTabs(): boolean { + return this._stretchTabs; + } + set stretchTabs(v: BooleanInput) { + this._stretchTabs = coerceBooleanProperty(v); + } + private _stretchTabs = true; + + @Input() + get animationDuration(): string { + return this._animationDuration; + } + + set animationDuration(value: NumberInput) { + this._animationDuration = /^\d+$/.test(value + '') ? value + 'ms' : (value as string); + } + + private _animationDuration: string; + + /** Query list of all tab links of the tab navigation. */ + @ContentChildren(forwardRef(() => MatTabLink), {descendants: true}) _items: QueryList; + + /** Background color of the tab nav. */ + @Input() + get backgroundColor(): ThemePalette { + return this._backgroundColor; + } + + set backgroundColor(value: ThemePalette) { + const classList = this._elementRef.nativeElement.classList; + classList.remove('mat-tabs-with-background', `mat-background-${this.backgroundColor}`); + + if (value) { + classList.add('mat-tabs-with-background', `mat-background-${value}`); + } + + this._backgroundColor = value; + } + + private _backgroundColor: ThemePalette; + + /** Whether the ripple effect is disabled or not. */ + @Input() + get disableRipple(): boolean { + return this._disableRipple; + } + + set disableRipple(value: BooleanInput) { + this._disableRipple = coerceBooleanProperty(value); + } + + private _disableRipple: boolean = false; + + /** Theme color of the nav bar. */ + @Input() color: ThemePalette = 'accent'; + + /** + * Associated tab panel controlled by the nav bar. If not provided, then the nav bar + * follows the ARIA link / navigation landmark pattern. If provided, it follows the + * ARIA tabs design pattern. + */ + @Input() tabPanel?: MatTabNavPanel; + + @ViewChild('tabListContainer', {static: true}) _tabListContainer: ElementRef; + @ViewChild('tabList', {static: true}) _tabList: ElementRef; + @ViewChild('tabListInner', {static: true}) _tabListInner: ElementRef; + @ViewChild('nextPaginator') _nextPaginator: ElementRef; + @ViewChild('previousPaginator') _previousPaginator: ElementRef; + _inkBar: MatInkBar; + + constructor( + elementRef: ElementRef, + @Optional() dir: Directionality, + ngZone: NgZone, + changeDetectorRef: ChangeDetectorRef, + viewportRuler: ViewportRuler, + platform: Platform, + @Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string, + @Optional() @Inject(MAT_TABS_CONFIG) defaultConfig?: MatTabsConfig, + ) { + super(elementRef, changeDetectorRef, viewportRuler, dir, ngZone, platform, animationMode); + this.disablePagination = + defaultConfig && defaultConfig.disablePagination != null + ? defaultConfig.disablePagination + : false; + this.fitInkBarToContent = + defaultConfig && defaultConfig.fitInkBarToContent != null + ? defaultConfig.fitInkBarToContent + : false; + this.stretchTabs = + defaultConfig && defaultConfig.stretchTabs != null ? defaultConfig.stretchTabs : true; + + } + + protected _itemSelected() { + // noop + } + + override ngAfterContentInit() { + this._inkBar = new MatInkBar(this._items); + // We need this to run before the `changes` subscription in parent to ensure that the + // selectedIndex is up-to-date by the time the super class starts looking for it. + this._items.changes.pipe(startWith(null), takeUntil(this._destroyed)).subscribe(() => { + this.updateActiveLink(); + }); + + super.ngAfterContentInit(); + } + + override ngAfterViewInit() { + if (!this.tabPanel && (typeof isDevMode === 'undefined' || isDevMode)) { + throw new Error('A mat-tab-nav-panel must be specified via [tabPanel].'); + } + super.ngAfterViewInit(); + } + + /** Notifies the component that the active link has been changed. */ + updateActiveLink() { + if (!this._items) { + return; + } + + const items = this._items.toArray(); + + for (let i = 0; i < items.length; i++) { + if (items[i].active) { + this.selectedIndex = i; + this._changeDetectorRef.markForCheck(); + + if (this.tabPanel) { + this.tabPanel._activeTabId = items[i].id; + } + + return; + } + } + + // The ink bar should hide itself if no items are active. + this.selectedIndex = -1; + this._inkBar.hide(); + } + + _getRole(): string | null { + return this.tabPanel ? 'tablist' : this._elementRef.nativeElement.getAttribute('role'); + } +} + +// Boilerplate for applying mixins to MatTabLink. +const _MatTabLinkMixinBase = mixinInkBarItem( + mixinTabIndex( + mixinDisableRipple( + mixinDisabled( + class { + elementRef: ElementRef; + }, + ), + ), + ), +); + +/** + * Link inside a `mat-tab-nav-bar`. + */ +@Component({ + selector: '[mat-tab-link], [matTabLink]', + exportAs: 'matTabLink', + inputs: ['disabled', 'disableRipple', 'tabIndex', 'active', 'id'], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + templateUrl: 'tab-link.html', + styleUrls: ['tab-link.scss'], + host: { + 'class': 'mdc-tab mat-mdc-tab-link mat-mdc-focus-indicator', + '[attr.aria-controls]': '_getAriaControls()', + '[attr.aria-current]': '_getAriaCurrent()', + '[attr.aria-disabled]': 'disabled', + '[attr.aria-selected]': '_getAriaSelected()', + '[attr.id]': 'id', + '[attr.tabIndex]': '_getTabIndex()', + '[attr.role]': '_getRole()', + '[class.mat-mdc-tab-disabled]': 'disabled', + '[class.mdc-tab--active]': 'active', + '(focus)': '_handleFocus()', + '(keydown)': '_handleKeydown($event)', + }, +}) +export class MatTabLink + extends _MatTabLinkMixinBase + implements + AfterViewInit, + OnDestroy, + CanDisable, + CanDisableRipple, + HasTabIndex, + RippleTarget, + FocusableOption +{ + private readonly _destroyed = new Subject(); + + /** Whether the tab link is active or not. */ + protected _isActive: boolean = false; + + /** Whether the link is active. */ + @Input() + get active(): boolean { + return this._isActive; + } + + set active(value: BooleanInput) { + const newValue = coerceBooleanProperty(value); + + if (newValue !== this._isActive) { + this._isActive = newValue; + this._tabNavBar.updateActiveLink(); + } + } + + /** + * Ripple configuration for ripples that are launched on pointer down. The ripple config + * is set to the global ripple options since we don't have any configurable options for + * the tab link ripples. + * @docs-private + */ + rippleConfig: RippleConfig & RippleGlobalOptions; + + /** + * Whether ripples are disabled on interaction. + * @docs-private + */ + get rippleDisabled(): boolean { + return ( + this.disabled || + this.disableRipple || + this._tabNavBar.disableRipple || + !!this.rippleConfig.disabled + ); + } + + /** Unique id for the tab. */ + @Input() id = `mat-tab-link-${nextUniqueId++}`; + + constructor( + private _tabNavBar: MatTabNav, + /** @docs-private */ + override elementRef: ElementRef, + @Optional() @Inject(MAT_RIPPLE_GLOBAL_OPTIONS) globalRippleOptions: RippleGlobalOptions | null, + @Attribute('tabindex') tabIndex: string, + private _focusMonitor: FocusMonitor, + @Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string, + ) { + super(); + + this.rippleConfig = globalRippleOptions || {}; + this.tabIndex = parseInt(tabIndex) || 0; + + if (animationMode === 'NoopAnimations') { + this.rippleConfig.animation = {enterDuration: 0, exitDuration: 0}; + } + + _tabNavBar._fitInkBarToContent + .pipe(takeUntil(this._destroyed)) + .subscribe(fitInkBarToContent => { + this.fitInkBarToContent = fitInkBarToContent; + }); + } + + /** Focuses the tab link. */ + focus() { + this.elementRef.nativeElement.focus(); + } + + ngAfterViewInit() { + this._focusMonitor.monitor(this.elementRef); + } + + override ngOnDestroy() { + this._destroyed.next(); + this._destroyed.complete(); + super.ngOnDestroy(); + this._focusMonitor.stopMonitoring(this.elementRef); + } + + _handleFocus() { + // Since we allow navigation through tabbing in the nav bar, we + // have to update the focused index whenever the link receives focus. + this._tabNavBar.focusIndex = this._tabNavBar._items.toArray().indexOf(this); + } + + _handleKeydown(event: KeyboardEvent) { + if (this.disabled && (event.keyCode === SPACE || event.keyCode === ENTER)) { + event.preventDefault(); + } else if (this._tabNavBar.tabPanel && event.keyCode === SPACE) { + this.elementRef.nativeElement.click(); + } + } + + _getAriaControls(): string | null { + return this._tabNavBar.tabPanel + ? this._tabNavBar.tabPanel?.id + : this.elementRef.nativeElement.getAttribute('aria-controls'); + } + + _getAriaSelected(): string | null { + if (this._tabNavBar.tabPanel) { + return this.active ? 'true' : 'false'; + } else { + return this.elementRef.nativeElement.getAttribute('aria-selected'); + } + } + + _getAriaCurrent(): string | null { + return this.active && !this._tabNavBar.tabPanel ? 'page' : null; + } + + _getRole(): string | null { + return this._tabNavBar.tabPanel ? 'tab' : this.elementRef.nativeElement.getAttribute('role'); + } + + _getTabIndex(): number { + if (this._tabNavBar.tabPanel) { + return this._isActive && !this.disabled ? 0 : -1; + } else { + return this.tabIndex; + } + } +} + +/** + * Tab panel component associated with MatTabNav. + */ +@Component({ + selector: 'mat-tab-nav-panel', + exportAs: 'matTabNavPanel', + template: '', + host: { + '[attr.aria-labelledby]': '_activeTabId', + '[attr.id]': 'id', + 'class': 'mat-mdc-tab-nav-panel', + 'role': 'tabpanel', + }, + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MatTabNavPanel { + /** Unique id for the tab panel. */ + @Input() id = `mat-tab-nav-panel-${nextUniqueId++}`; + + /** Id of the active tab in the nav bar. */ + _activeTabId?: string; +} diff --git a/ui/src/components/tabs/tab.html b/ui/src/components/tabs/tab.html new file mode 100644 index 000000000..99442c1f1 --- /dev/null +++ b/ui/src/components/tabs/tab.html @@ -0,0 +1,4 @@ + + diff --git a/ui/src/components/tabs/tab.ts b/ui/src/components/tabs/tab.ts new file mode 100644 index 000000000..b8bdf439f --- /dev/null +++ b/ui/src/components/tabs/tab.ts @@ -0,0 +1,201 @@ +/** + * @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 { + ChangeDetectionStrategy, + Component, + ContentChild, + ElementRef, + Inject, + InjectionToken, + Input, + OnChanges, + OnDestroy, + OnInit, + Optional, + SimpleChanges, + TemplateRef, + ViewChild, + ViewContainerRef, + ViewEncapsulation, +} from '@angular/core'; +import {MatTabContent} from './tab-content'; +import {MAT_TAB, MatTabLabel} from './tab-label'; +import {CanDisable, mixinColor} from '../core'; +import {TemplatePortal} from '@angular/cdk/portal'; +import {Subject} from 'rxjs'; + + +export class OuiTabsBase { + constructor(public _elementRef: ElementRef) {} +} +// Boilerplate for applying mixins to MatTab. +/** @docs-private */ +const _MatTabMixinBase: typeof OuiTabsBase = mixinColor(OuiTabsBase); + +/** + * Used to provide a tab group to a tab without causing a circular dependency. + * @docs-private + */ +export const MAT_TAB_GROUP = new InjectionToken('MAT_TAB_GROUP'); + +/** Default color palette for the tab */ +const DEFAULT_COLOR = 'primary'; + +@Component({ + selector: 'mat-tab', + + // Note that usually we'd go through a bit more trouble and set up another class so that + // the inlined template of `MatTab` isn't duplicated, however the template is small enough + // that creating the extra class will generate more code than just duplicating the template. + templateUrl: 'tab.html', + inputs: ['disabled'], + // tslint:disable-next-line:validate-decorators + changeDetection: ChangeDetectionStrategy.Default, + encapsulation: ViewEncapsulation.None, + exportAs: 'MatTab', + providers: [{provide: MAT_TAB, useExisting: MatTab}], +}) +export class MatTab extends _MatTabMixinBase implements CanDisable, OnInit, OnChanges, OnDestroy { + /** Content for the tab label given by ``. */ + private _templateLabel: MatTabLabel; + disabled: any; + @ContentChild(MatTabLabel) + get templateLabel(): MatTabLabel { + return this._templateLabel; + } + set templateLabel(value: MatTabLabel) { + console.log(value) + this._setTemplateLabelInput(value); + } + + /** + * Template provided in the tab content that will be used if present, used to enable lazy-loading + */ + @ContentChild(MatTabContent, {read: TemplateRef, static: true}) + // We need an initializer here to avoid a TS error. The value will be set in `ngAfterViewInit`. + private _explicitContent: TemplateRef = undefined!; + + /** Template inside the MatTab view that contains an ``. */ + @ViewChild(TemplateRef, {static: true}) _implicitContent: TemplateRef; + + /** Plain text label for the tab, used when there is no template label. */ + @Input('label') textLabel: string = ''; + + @Input('text') givenText: string = ''; + + /** Aria label for the tab. */ + @Input('aria-label') ariaLabel: string; + + @Input() color = 'accent'; + + /** + * Reference to the element that the tab is labelled by. + * Will be cleared if `aria-label` is set at the same time. + */ + @Input('aria-labelledby') ariaLabelledby: string; + + /** + * Classes to be passed to the tab label inside the mat-tab-header container. + * Supports string and string array values, same as `ngClass`. + */ + @Input() labelClass: string | string[]; + + /** + * Classes to be passed to the tab mat-tab-body container. + * Supports string and string array values, same as `ngClass`. + */ + @Input() bodyClass: string | string[]; + + /** Portal that will be the hosted content of the tab */ + private _contentPortal: TemplatePortal | null = null; + + /** @docs-private */ + get content(): TemplatePortal | null { + return this._contentPortal; + } + + /** Emits whenever the internal state of the tab changes. */ + readonly _stateChanges = new Subject(); + + /** + * The relatively indexed position where 0 represents the center, negative is left, and positive + * represents the right. + */ + position: number | null = null; + + /** + * The initial relatively index origin of the tab if it was created and selected after there + * was already a selected tab. Provides context of what position the tab should originate from. + */ + origin: number | null = null; + + /** + * Whether the tab is currently active. + */ + isActive = false; + + @ViewChild('tab1') _tab1: ElementRef; + constructor( + private _viewContainerRef: ViewContainerRef, + @Inject(MAT_TAB_GROUP) @Optional() public _closestTabGroup: any, + _elementRef: ElementRef + ) { + super(_elementRef); + this.addThemeColor(); + } + + ngOnChanges(changes: SimpleChanges): void { + if (changes.hasOwnProperty('textLabel') || changes.hasOwnProperty('disabled')) { + this._stateChanges.next(); + } + if (changes.hasOwnProperty('_tab2') || changes.hasOwnProperty('disabled')) { + this._stateChanges.next(); + } + } + + addThemeColor() { + console.log('sssssss123', this.color) + if (!this.color) { + this.color = DEFAULT_COLOR; + } + console.log('after', this.color) + } + + ngAfterViewInit() { + console.log("this._tab1.nativeElement.innerText;", this._tab1); + } + + ngOnDestroy(): void { + this._stateChanges.complete(); + } + + ngOnInit(): void { + this._contentPortal = new TemplatePortal( + this._explicitContent || this._implicitContent, + this._viewContainerRef, this._tab1 + ); + console.log('_tab1', this._contentPortal); + } + + /** + * This has been extracted to a util because of TS 4 and VE. + * View Engine doesn't support property rename inheritance. + * TS 4.0 doesn't allow properties to override accessors or vice-versa. + * @docs-private + */ + private _setTemplateLabelInput(value: MatTabLabel | undefined) { + // Only update the label if the query managed to find one. This works around an issue where a + // user may have manually set `templateLabel` during creation mode, which would then get + // clobbered by `undefined` when the query resolves. Also note that we check that the closest + // tab matches the current one so that we don't pick up labels from nested tabs. + if (value && value._closestTab === this) { + this._templateLabel = value; + } + } +} diff --git a/ui/src/components/tabs/tabs-animations.ts b/ui/src/components/tabs/tabs-animations.ts new file mode 100644 index 000000000..5e7955d4a --- /dev/null +++ b/ui/src/components/tabs/tabs-animations.ts @@ -0,0 +1,66 @@ +/** + * @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 { + animate, + state, + style, + transition, + trigger, + AnimationTriggerMetadata, +} from '@angular/animations'; + +/** + * Animations used by the Material tabs. + * @docs-private + */ +export const matTabsAnimations: { + readonly translateTab: AnimationTriggerMetadata; +} = { + /** Animation translates a tab along the X axis. */ + translateTab: trigger('translateTab', [ + // Transitions to `none` instead of 0, because some browsers might blur the content. + state('center, void, left-origin-center, right-origin-center', style({transform: 'none'})), + + // If the tab is either on the left or right, we additionally add a `min-height` of 1px + // in order to ensure that the element has a height before its state changes. This is + // necessary because Chrome does seem to skip the transition in RTL mode if the element does + // not have a static height and is not rendered. See related issue: #9465 + state( + 'left', + style({ + transform: 'translate3d(-100%, 0, 0)', + minHeight: '1px', + + // Normally this is redundant since we detach the content from the DOM, but if the user + // opted into keeping the content in the DOM, we have to hide it so it isn't focusable. + visibility: 'hidden', + }), + ), + state( + 'right', + style({ + transform: 'translate3d(100%, 0, 0)', + minHeight: '1px', + visibility: 'hidden', + }), + ), + + transition( + '* => left, * => right, left => center, right => center', + animate('{{animationDuration}} cubic-bezier(0.35, 0, 0.25, 1)'), + ), + transition('void => left-origin-center', [ + style({transform: 'translate3d(-100%, 0, 0)', visibility: 'hidden'}), + animate('{{animationDuration}} cubic-bezier(0.35, 0, 0.25, 1)'), + ]), + transition('void => right-origin-center', [ + style({transform: 'translate3d(100%, 0, 0)', visibility: 'hidden'}), + animate('{{animationDuration}} cubic-bezier(0.35, 0, 0.25, 1)'), + ]), + ]), +}; diff --git a/ui/src/components/tabs/tabs.md b/ui/src/components/tabs/tabs.md new file mode 100644 index 000000000..ff098e32d --- /dev/null +++ b/ui/src/components/tabs/tabs.md @@ -0,0 +1,124 @@ +Angular Material tabs organize content into separate views where only one view can be +visible at a time. Each tab's label is shown in the tab header and the active +tab's label is designated with the animated ink bar. When the list of tab labels exceeds the width +of the header, pagination controls appear to let the user scroll left and right across the labels. + +The active tab may be set using the `selectedIndex` input or when the user selects one of the +tab labels in the header. + + + +### Events + +The `selectedTabChange` output event is emitted when the active tab changes. + +The `focusChange` output event is emitted when the user puts focus on any of the tab labels in +the header, usually through keyboard navigation. + +### Labels + +If a tab's label is only text then the simple tab-group API can be used. + + + +For more complex labels, add a template with the `mat-tab-label` directive inside the `mat-tab`. + + + +### Dynamic Height + +By default, the tab group will not change its height to the height of the currently active tab. To +change this, set the `dynamicHeight` input to true. The tab body will animate its height according + to the height of the active tab. + + + +### Tabs and navigation +While `` is used to switch between views within a single route, `