@energycap/components 0.39.17-ECAP-23124-menu-item-divider-improvements.20240528-1347 → 0.39.17-ECAP-25250-eclipse-formula-builder-proto.20240529-1651

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1479,229 +1479,205 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImpor
1479
1479
  }] } });
1480
1480
 
1481
1481
  /**
1482
- * Service to help with interfacing with the window object
1483
- * and navigating around the application (going outside of the Angular 2+ router)
1482
+ * Primitive directive that popups a container using PopperJS
1483
+ *
1484
+ * @export
1484
1485
  */
1485
- class WindowService {
1486
- /**
1487
- * Tracks if there are any unsaved changes that the user could lose.
1488
- *
1489
- * It is set up as `get` only because it is set with `addNavigateAwayPrompt`.
1490
- *
1491
- * This also includes adding a prompt to the window itself (in addition to
1492
- * working with the `CanDeactivateUnsavedChanges` guard) to cover page reloads
1493
- * which do not trigger router events.
1494
- */
1495
- get hasUnsavedChanges() {
1496
- return this._hasUnsavedChanges;
1497
- }
1486
+ class PopupContainerDirective {
1498
1487
  /**
1499
- * Expose the innerWidth on the window global. Protects against errors when code
1500
- * is running on a non-browser platform.
1488
+ * Creates an instance of PopupContainerDirective.
1489
+ * @param templateRef Reference to the popup template
1490
+ * @param viewContainer Reference to the view container
1491
+ * @param document Reference to Document
1492
+ * @memberof PopupContainerDirective
1501
1493
  */
1502
- get innerWidth() {
1503
- return window ? window.innerWidth : undefined;
1504
- }
1505
- constructor(router, activatedRoute) {
1506
- this.router = router;
1507
- this.activatedRoute = activatedRoute;
1508
- this._hasUnsavedChanges = false;
1494
+ constructor(templateRef, viewContainer, document, renderer) {
1495
+ this.templateRef = templateRef;
1496
+ this.viewContainer = viewContainer;
1497
+ this.document = document;
1498
+ this.renderer = renderer;
1509
1499
  /**
1510
- * Function called when the window `beforeunload` event is fired.
1511
- *
1512
- * A reference to the function that was passed to `window.addEventListener`
1513
- * must be retained for `window.removeEventListener` to function properly.
1514
- *
1515
- * Some browsers require the event's `returnValue` to be set to show the confirmation
1516
- * dialog.
1517
- * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event
1500
+ * Emit the {@link PopupStatus} when it changes
1518
1501
  */
1519
- this.beforeUnloadFunction = (event) => {
1520
- // Cancel the event as stated by the standard.
1521
- event.preventDefault();
1522
- // Chrome requires returnValue to be set.
1523
- event.returnValue = '';
1524
- };
1525
- if (window) {
1526
- this.resized = fromEvent(window, 'resize');
1527
- }
1502
+ this.popperStatusChange = new EventEmitter();
1528
1503
  }
1529
1504
  /**
1530
- * Navigates to the previous page the user had visited
1505
+ * Angular onInit lifecycle hook
1506
+ * @see https://angular.io/guide/lifecycle-hooks
1531
1507
  */
1532
- goBack() {
1533
- window.history.back();
1508
+ ngOnInit() {
1509
+ this.templateViewRef = this.viewContainer.createEmbeddedView(this.templateRef);
1534
1510
  }
1535
- /**An abstraction around the browsers window history length.
1536
- * Returns zero if unable to access or running outside a browser context
1511
+ /**
1512
+ * Angular onDestroy lifecycle hook. Close and delete references. Unsubscribe observables
1513
+ * @see https://angular.io/guide/lifecycle-hooks
1537
1514
  */
1538
- getHistoryLength() {
1539
- return window?.history?.length || 0;
1515
+ ngOnDestroy() {
1516
+ this.hide();
1540
1517
  }
1541
1518
  /**
1542
- * Navigate to any url you know the path to
1543
- * @param url The URL to navigate to
1519
+ * Displays the templateRef as a popup
1544
1520
  *
1545
- * @deprecated For legacy support only; use `router.navigateByUrl` instead
1521
+ * @memberof PopupContainerDirective
1546
1522
  */
1547
- async navigateToUrl(url) {
1548
- try {
1549
- if (url.indexOf('/app/') === 0) {
1550
- await this.router.navigateByUrl(url.substring(5));
1551
- }
1552
- else {
1553
- await this.router.navigateByUrl(url);
1523
+ show() {
1524
+ if (PopupContainerDirective.GlobalPopupRef) {
1525
+ if (PopupContainerDirective.GlobalPopupRef != this) {
1526
+ PopupContainerDirective.GlobalPopupRef.hide();
1527
+ PopupContainerDirective.GlobalPopupRef = undefined;
1554
1528
  }
1555
1529
  }
1556
- catch (e) {
1557
- // If the router throws we will try to navigate to the fully qualified url as a last ditch effort.
1558
- // This can happen if we missed a link that needs to be converted to ng5 or our ng1Href directive
1559
- // didn't handle a link correctly
1560
- window.location.href = url;
1530
+ if (!this.globalCloseSubscription) {
1531
+ this.globalCloseSubscription = fromEvent(this.document.body, "click").subscribe((event) => {
1532
+ this.hide();
1533
+ });
1534
+ }
1535
+ if (!this.popperRef) {
1536
+ // Add the popper template as an embedded view since PopperJS
1537
+ // manipulates DOM elements.
1538
+ this.popupViewRef = this.viewContainer.createEmbeddedView(this.popup);
1539
+ // Since popper needs real DOM elements, grab the first non-comment
1540
+ // DOM element to use as our anchor.
1541
+ let anchorElement = this.popupViewRef.rootNodes.find(elem => { return elem.nodeName !== "#text"; });
1542
+ // Use the parents elements as our DOM elements to Popper
1543
+ this.popperRef = new Popper(this.templateViewRef.rootNodes[0], anchorElement, this.popperOptions);
1544
+ PopupContainerDirective.GlobalPopupRef = this;
1545
+ this.popperStatusChange.emit('visible');
1561
1546
  }
1562
1547
  }
1563
1548
  /**
1564
- * Adds a `beforeunload` function to the window to alert the user that there are about to leave
1565
- * the current page and ask if they'd like to leave or stay
1549
+ * Hides the templateRef
1550
+ *
1551
+ * @memberof PopupContainerDirective
1566
1552
  */
1567
- addNavigateAwayPrompt() {
1568
- this._hasUnsavedChanges = true;
1569
- window.addEventListener("beforeunload", this.beforeUnloadFunction);
1553
+ hide() {
1554
+ if (this.globalCloseSubscription) {
1555
+ this.globalCloseSubscription.unsubscribe();
1556
+ this.globalCloseSubscription = undefined;
1557
+ }
1558
+ if (this.popperRef && this.popupViewRef) {
1559
+ this.popupViewRef.destroy();
1560
+ this.popperRef.destroy();
1561
+ this.popperRef = undefined;
1562
+ this.popperStatusChange.emit('hidden');
1563
+ }
1570
1564
  }
1571
1565
  /**
1572
- * Removes the `beforeunload` function added to the window
1566
+ * Updates the popup container position
1573
1567
  */
1574
- removeNavigateAwayPrompt() {
1575
- this._hasUnsavedChanges = false;
1576
- window.removeEventListener("beforeunload", this.beforeUnloadFunction);
1568
+ update() {
1569
+ if (this.popperRef) {
1570
+ this.popperRef.update();
1571
+ }
1572
+ }
1573
+ fixPosition(minWidthNone, appendToBody = false) {
1574
+ if (this.popperRef && this.popperRef['reference'] && this.popperRef['popper']) {
1575
+ let popupEl = this.popperRef['popper'];
1576
+ // Reset width style previously assigned because the content may have
1577
+ // changed and the auto width would be different
1578
+ this.renderer.removeStyle(popupEl, 'width');
1579
+ this.renderer.setStyle(popupEl, 'position', 'fixed');
1580
+ if (appendToBody) {
1581
+ const bodyEl = this.document.querySelector('body');
1582
+ const popupParent = this.renderer.parentNode(popupEl);
1583
+ if (popupParent !== bodyEl) {
1584
+ this.renderer.appendChild(bodyEl, popupEl);
1585
+ }
1586
+ }
1587
+ let toggleEl = this.popperRef['reference'];
1588
+ let width = popupEl.offsetWidth;
1589
+ let boundaries = popupEl.getBoundingClientRect();
1590
+ let left = boundaries.left;
1591
+ let coords = toggleEl.getBoundingClientRect();
1592
+ // Set the top of our menu to the bottom of the toggle element
1593
+ let top = coords.bottom;
1594
+ if (this.popperOptions && this.popperOptions.placement) {
1595
+ if (this.popperOptions.placement === 'bottom-start' || this.popperOptions.placement === 'top-start') {
1596
+ left = coords.left;
1597
+ }
1598
+ else {
1599
+ left = coords.right - ((minWidthNone || width > coords.width) ? width : coords.width);
1600
+ }
1601
+ }
1602
+ // if it won't fit (with 10px space before hitting the window edge), flip it
1603
+ if (boundaries.height + top + 10 > window.innerHeight) {
1604
+ top = coords.top - boundaries.height;
1605
+ }
1606
+ this.renderer.setStyle(popupEl, 'transform', 'none');
1607
+ this.renderer.setStyle(popupEl, 'left', left + 'px');
1608
+ this.renderer.setStyle(popupEl, 'top', top + 'px');
1609
+ this.renderer.setStyle(popupEl, 'width', width + 'px');
1610
+ if (!minWidthNone) {
1611
+ this.renderer.setStyle(popupEl, 'min-width', coords.width + 'px');
1612
+ }
1613
+ }
1577
1614
  }
1615
+ }
1616
+ /**
1617
+ * Global reference to the currently displayed popup; only
1618
+ * one popup directive can be in `show` state at a given time.
1619
+ *
1620
+ * @memberof PopupContainerDirective
1621
+ */
1622
+ PopupContainerDirective.GlobalPopupRef = undefined;
1623
+ PopupContainerDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: PopupContainerDirective, deps: [{ token: i0.TemplateRef }, { token: i0.ViewContainerRef }, { token: DOCUMENT }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive });
1624
+ PopupContainerDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: PopupContainerDirective, selector: "[ecPopup]", inputs: { popup: ["ecPopup", "popup"], popperOptions: ["options", "popperOptions"] }, ngImport: i0 });
1625
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: PopupContainerDirective, decorators: [{
1626
+ type: Directive,
1627
+ args: [{ selector: '[ecPopup]' }]
1628
+ }], ctorParameters: function () { return [{ type: i0.TemplateRef }, { type: i0.ViewContainerRef }, { type: undefined, decorators: [{
1629
+ type: Inject,
1630
+ args: [DOCUMENT]
1631
+ }] }, { type: i0.Renderer2 }]; }, propDecorators: { popup: [{
1632
+ type: Input,
1633
+ args: ['ecPopup']
1634
+ }], popperOptions: [{
1635
+ type: Input,
1636
+ args: ['options']
1637
+ }] } });
1638
+
1639
+ class ScrollService {
1640
+ constructor() { }
1578
1641
  /**
1579
- * Send data to another window.
1580
- *
1581
- * __SECURITY RISK__ - Always use a specific target origin. Failing to provide a specific target origin can allow
1582
- * malicious sites to receive the message.
1642
+ * Given a container and the target element to scroll to, we will set the scroll top on
1643
+ * the container to bring the target into view.
1583
1644
  *
1584
- * @param targetWindow - Window to send the message to
1585
- * @param message - Data to send
1586
- * @param targetOrigin - What the URI of the target window must be for the message to be sent.
1587
- * If sending data to another EnergyCAP window, this should always be `window.location.origin` to ensure
1588
- * that only instances of EnergyCAP app receive the message.
1645
+ * @param scrollContainerSelector A valid CSS selector string for the scroll container.
1646
+ * @param targetElementSelector A valid CSS selector string for the target element.
1647
+ * @param topPadding The amount of space to leave above the target
1648
+ * to keep it from being pinned to the top of the scrollContainer. Defaults
1649
+ * to 32px, the default height of a menu item.
1589
1650
  */
1590
- postMessage(targetWindow, message, targetOrigin) {
1591
- targetWindow.postMessage(message, targetOrigin);
1651
+ scrollToItem(scrollContainerSelector, targetElementSelector, topPadding = 32) {
1652
+ let scrollContainer = document.querySelector(scrollContainerSelector);
1653
+ if (!scrollContainer) {
1654
+ return;
1655
+ }
1656
+ let targetElement = scrollContainer.querySelector(targetElementSelector);
1657
+ if (!targetElement) {
1658
+ return;
1659
+ }
1660
+ let targetRect = targetElement.getBoundingClientRect();
1661
+ let containerRect = scrollContainer.getBoundingClientRect();
1662
+ // Only scroll if the target is outside of the view bounds of the container
1663
+ if (targetRect.bottom > containerRect.bottom || targetRect.top < containerRect.top) {
1664
+ scrollContainer.scrollTop =
1665
+ (targetRect.top - containerRect.top) + scrollContainer.scrollTop - topPadding;
1666
+ }
1592
1667
  }
1593
1668
  /**
1594
- * Open a new window
1595
- * @param url - The URL of the resource to be loaded
1669
+ * Return the value of the scrollTop property for an HTMLElement that matches the selector
1670
+ * @param scrollContainerSelector A valid CSS selector
1596
1671
  */
1597
- openNew(url) {
1598
- window.open(url, '_blank');
1599
- }
1600
- /**
1601
- * A wrapper around the router for changing the query params for the current url
1602
- * without creating a new history entry or removing any existing query parameters.
1603
- * The provided params are updated if they already exist or added to the url if they don't
1604
- *
1605
- * @returns a promise that resolves to true if the navigation succeeds, false if something (like a guard) blocks it.
1606
- * In normal use, the navigation should succeed unless we use query params to block access to a route the user is already on
1607
- */
1608
- async modifyHistoryQueryParamsSubset(queryParams) {
1609
- return this.router.navigate([], {
1610
- relativeTo: this.activatedRoute,
1611
- replaceUrl: true,
1612
- queryParams: queryParams,
1613
- queryParamsHandling: 'merge',
1614
- });
1615
- }
1616
- /**A wrapper around the default javascript confirm dialog to allow us to unit test dependent code.
1617
- * Of course eventually we'd like to have pretty confirmations for everything, but in some cases it wasn't worth the extra time
1618
- * so we're using this instead.
1619
- */
1620
- confirm(prompt) {
1621
- return Promise.resolve(confirm(prompt));
1622
- }
1623
- /**
1624
- * Close the current window or a window instance if one is provided
1625
- * @param windowInstance - Window to close (optional)
1626
- */
1627
- closeWindow(windowInstance) {
1628
- if (windowInstance) {
1629
- windowInstance.close();
1630
- }
1631
- else {
1632
- window.close();
1633
- }
1634
- }
1635
- getLocation() {
1636
- return window.location.pathname + window.location.hash;
1637
- }
1638
- /** Get the current value of the full url, including protocol, host and path */
1639
- getFullUrl() {
1640
- return window.location.href;
1641
- }
1642
- /** Get the current value of the base url, including protocol, domain and port (if explicitly specified) */
1643
- getBaseUrl() {
1644
- return window.location.origin;
1645
- }
1646
- /**
1647
- * Reloads the browser window.
1648
- * NOT RECOMMENDED. Seek other options for reloading content within Angular before resorting to this.
1649
- */
1650
- reloadWindow() {
1651
- window.location.reload();
1652
- }
1653
- }
1654
- WindowService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: WindowService, deps: [{ token: i1$2.Router }, { token: i1$2.ActivatedRoute }], target: i0.ɵɵFactoryTarget.Injectable });
1655
- WindowService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: WindowService, providedIn: 'root' });
1656
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: WindowService, decorators: [{
1657
- type: Injectable,
1658
- args: [{
1659
- providedIn: 'root'
1660
- }]
1661
- }], ctorParameters: function () { return [{ type: i1$2.Router }, { type: i1$2.ActivatedRoute }]; } });
1662
-
1663
- class ScrollService {
1664
- constructor() { }
1665
- /**
1666
- * Given a container and the target element to scroll to, we will set the scroll top on
1667
- * the container to bring the target into view.
1668
- *
1669
- * @param scrollContainerSelector A valid CSS selector string for the scroll container.
1670
- * @param targetElementSelector A valid CSS selector string for the target element.
1671
- * @param topPadding The amount of space to leave above the target
1672
- * to keep it from being pinned to the top of the scrollContainer. Defaults
1673
- * to 32px, the default height of a menu item.
1674
- */
1675
- scrollToItem(scrollContainerSelector, targetElementSelector, topPadding = 32) {
1676
- let scrollContainer = document.querySelector(scrollContainerSelector);
1677
- if (!scrollContainer) {
1678
- return;
1679
- }
1680
- let targetElement = scrollContainer.querySelector(targetElementSelector);
1681
- if (!targetElement) {
1682
- return;
1683
- }
1684
- let targetRect = targetElement.getBoundingClientRect();
1685
- let containerRect = scrollContainer.getBoundingClientRect();
1686
- // Only scroll if the target is outside of the view bounds of the container
1687
- if (targetRect.bottom > containerRect.bottom || targetRect.top < containerRect.top) {
1688
- scrollContainer.scrollTop =
1689
- (targetRect.top - containerRect.top) + scrollContainer.scrollTop - topPadding;
1690
- }
1691
- }
1692
- /**
1693
- * Return the value of the scrollTop property for an HTMLElement that matches the selector
1694
- * @param scrollContainerSelector A valid CSS selector
1695
- */
1696
- getCurrentScrollPosition(scrollContainerSelector) {
1697
- let scrollContainer = document.querySelector(scrollContainerSelector);
1698
- if (scrollContainer) {
1699
- return scrollContainer.scrollTop;
1700
- }
1701
- else {
1702
- console.error(`Scroll container '${scrollContainerSelector}' does not exist.`);
1703
- return 0;
1704
- }
1672
+ getCurrentScrollPosition(scrollContainerSelector) {
1673
+ let scrollContainer = document.querySelector(scrollContainerSelector);
1674
+ if (scrollContainer) {
1675
+ return scrollContainer.scrollTop;
1676
+ }
1677
+ else {
1678
+ console.error(`Scroll container '${scrollContainerSelector}' does not exist.`);
1679
+ return 0;
1680
+ }
1705
1681
  }
1706
1682
  /**
1707
1683
  * Set the scrollTop of an HTMLElement that matches the selector to a specific position
@@ -1747,931 +1723,922 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImpor
1747
1723
  }]
1748
1724
  }], ctorParameters: function () { return []; } });
1749
1725
 
1750
- class NavItemActiveDirective {
1751
- /**
1752
- * Determines whether the directive will try to make an exact match on the url or not
1753
- * If false, the directive will add the active class if the first part of the url matches
1754
- * the active route.
1755
- * see: https://angular.io/api/router/Router#isactive
1756
- */
1757
- set exact(value) {
1758
- if (value === undefined) {
1759
- this._exact = true;
1726
+ /** Advanced validation for textbox form controls */
1727
+ const textboxValidation = (validatorParams) => {
1728
+ return (control) => {
1729
+ let validators = [];
1730
+ // Innocent until proven guilty
1731
+ validatorParams.valid = true;
1732
+ if (validatorParams.required) {
1733
+ validators.push(Validators.required);
1734
+ }
1735
+ if (validatorParams.minLength !== undefined) {
1736
+ validators.push(Validators.minLength(validatorParams.minLength));
1737
+ }
1738
+ if (validatorParams.maxLength !== undefined) {
1739
+ validators.push(Validators.maxLength(validatorParams.maxLength));
1740
+ }
1741
+ if (validatorParams.pattern !== undefined) {
1742
+ validators.push(Validators.pattern(validatorParams.pattern));
1743
+ }
1744
+ validators.forEach(validator => {
1745
+ let validationResult = validator(control);
1746
+ if (validationResult) {
1747
+ validatorParams.valid = false;
1748
+ }
1749
+ });
1750
+ if (validatorParams.valid) {
1751
+ return null;
1760
1752
  }
1761
1753
  else {
1762
- this._exact = value;
1754
+ return {
1755
+ textbox: validatorParams
1756
+ };
1763
1757
  }
1764
- this.update();
1758
+ };
1759
+ };
1760
+ const phoneNumberValidationPattern = '^\\s*(?:\\+?(\\d{1,3}))?[-. (]*(\\d{3})[-. )]*(\\d{3})[-. ]*(\\d{4})(?: *x(\\d+))?\\s*$';
1761
+ const urlValidationPattern = '([A-Za-z])+(:\/\/)+[^\\s]*';
1762
+ class TextboxComponent extends FormControlBase {
1763
+ constructor(validationMessageService, formGroupHelper, translate) {
1764
+ super(validationMessageService, formGroupHelper);
1765
+ this.validationMessageService = validationMessageService;
1766
+ this.formGroupHelper = formGroupHelper;
1767
+ this.translate = translate;
1768
+ /**
1769
+ * Set the value of the input's autocomplete attribute
1770
+ */
1771
+ this.autocomplete = 'off';
1772
+ /**
1773
+ * The textbox type
1774
+ */
1775
+ this.type = "text";
1776
+ /**
1777
+ * The value of the rows attribute for a textarea. Only applies to multi-line type
1778
+ */
1779
+ this.rows = 3;
1780
+ /**
1781
+ * If set to true, we will select all text within the input if
1782
+ * autofocus is also set to true
1783
+ */
1784
+ this.selectOnAutofocus = false;
1785
+ /**
1786
+ * If set to true, we will upper case on focus out
1787
+ */
1788
+ this.upperCase = false;
1789
+ /**
1790
+ * Validation pattern for the input. This is determined on the input type specified
1791
+ */
1792
+ this.validationPattern = '';
1793
+ }
1794
+ ngOnChanges(changes) {
1795
+ super.ngOnChanges(changes);
1765
1796
  }
1766
1797
  /**
1767
- * The url of the NavItem to check for activeness. Convert the item url into a
1768
- * UrlTree relative to the ActivatedRoute so router#isActive works even with relative urls.
1769
- * See Angular's [routerLink](https://github.com/angular/angular/blob/8282e15c2becbe42a49befa07d6407247e8243d8/packages/router/src/directives/router_link.ts#L249)
1770
- * and [routerLinkActive](https://github.com/angular/angular/blob/8282e15c2becbe42a49befa07d6407247e8243d8/packages/router/src/directives/router_link_active.ts#L139)
1771
- * for a similiar implementation.
1798
+ * The angular onInit lifecycle hook
1772
1799
  */
1773
- set url(value) {
1774
- if (value !== null && value !== undefined) {
1775
- this._url = this.router.createUrlTree([value], { relativeTo: this.route, queryParams: this.queryParams });
1776
- this.update();
1777
- }
1778
- }
1779
- constructor(router, el, renderer, route) {
1780
- this.router = router;
1781
- this.el = el;
1782
- this.renderer = renderer;
1783
- this.route = route;
1784
- this._exact = true;
1785
- /** Emits when the url becomes active */
1786
- this.routerLinkActivated = new EventEmitter();
1787
- /** Subject that emits when component is destroyed to unsubscribe from any subscriptions */
1788
- this.destroyed = new Subject();
1789
- }
1790
- /** Check if url is active on NavigationEnd events */
1791
1800
  ngOnInit() {
1792
- this.router.events.pipe(takeUntil(this.destroyed), filter(e => e instanceof NavigationEnd)).subscribe(() => {
1793
- this.update();
1794
- });
1801
+ super.ngOnInit();
1802
+ this.validationPattern = '';
1803
+ if (this.type === 'tel') {
1804
+ this.validationPattern = phoneNumberValidationPattern;
1805
+ }
1806
+ else if (this.type === 'url') {
1807
+ this.validationPattern = urlValidationPattern;
1808
+ }
1809
+ if (this.placeholder) {
1810
+ this.translate.get(this.placeholder)
1811
+ .subscribe((translated) => {
1812
+ this.placeholder = translated;
1813
+ });
1814
+ }
1795
1815
  }
1796
- ngOnDestroy() {
1797
- this.destroyed.next();
1798
- this.destroyed.unsubscribe();
1816
+ /**
1817
+ * The angular afterViewInit lifecycle hook
1818
+ */
1819
+ ngAfterViewInit() {
1820
+ if (this.autofocus) {
1821
+ this.setFocus(this.selectOnAutofocus);
1822
+ }
1799
1823
  }
1800
- /** If url is active apply the defined class to the element, otherwise remove it */
1801
- update() {
1802
- if (this._url && this.classValue) {
1803
- if (this.router.isActive(this._url, { matrixParams: 'ignored', queryParams: this._exact ? 'exact' : 'subset', paths: this._exact ? 'exact' : 'subset', fragment: 'ignored' })) {
1804
- this.renderer.addClass(this.el.nativeElement, this.classValue);
1805
- this.routerLinkActivated.emit(new Event('routerLinkActivated'));
1806
- }
1807
- else {
1808
- this.renderer.removeClass(this.el.nativeElement, this.classValue);
1809
- }
1824
+ /**
1825
+ * Function to set focus on an input programmatically after the page
1826
+ * had been rendered. The highlight text flag will select the text
1827
+ * within the input if passed in and true
1828
+ */
1829
+ setFocus(highlightText) {
1830
+ this.inputElement.nativeElement.focus();
1831
+ if (highlightText) {
1832
+ this.inputElement.nativeElement.select();
1810
1833
  }
1811
1834
  }
1812
- }
1813
- NavItemActiveDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NavItemActiveDirective, deps: [{ token: i1$2.Router }, { token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i1$2.ActivatedRoute }], target: i0.ɵɵFactoryTarget.Directive });
1814
- NavItemActiveDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: NavItemActiveDirective, selector: "[ecNavItemActive]", inputs: { classValue: ["ecNavItemActive", "classValue"], exact: ["ecNavItemActiveExactMatch", "exact"], queryParams: ["ecNavItemActiveQueryParams", "queryParams"], url: ["ecNavItemActiveUrl", "url"] }, outputs: { routerLinkActivated: "routerLinkActivated" }, ngImport: i0 });
1815
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NavItemActiveDirective, decorators: [{
1816
- type: Directive,
1817
- args: [{
1818
- selector: '[ecNavItemActive]'
1819
- }]
1820
- }], ctorParameters: function () { return [{ type: i1$2.Router }, { type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i1$2.ActivatedRoute }]; }, propDecorators: { classValue: [{
1821
- type: Input,
1822
- args: ['ecNavItemActive']
1823
- }], exact: [{
1824
- type: Input,
1825
- args: ['ecNavItemActiveExactMatch']
1826
- }], queryParams: [{
1827
- type: Input,
1828
- args: ['ecNavItemActiveQueryParams']
1829
- }], url: [{
1830
- type: Input,
1831
- args: ['ecNavItemActiveUrl']
1832
- }], routerLinkActivated: [{
1833
- type: Output
1835
+ /**
1836
+ * Focus out event handler
1837
+ * will upper case and trim value if upperCase is true (this is what we do on the apis)
1838
+ */
1839
+ focusOutEvent() {
1840
+ if (this.upperCase && this.formModel.value) {
1841
+ this.formModel.setValue(this.formModel.value.toUpperCase().trim());
1842
+ }
1843
+ }
1844
+ }
1845
+ TextboxComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TextboxComponent, deps: [{ token: ValidationMessageService }, { token: FormGroupHelper }, { token: i2.TranslateService }], target: i0.ɵɵFactoryTarget.Component });
1846
+ TextboxComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: TextboxComponent, selector: "ec-textbox", inputs: { autocomplete: "autocomplete", type: "type", placeholder: "placeholder", maxlength: "maxlength", minlength: "minlength", rows: "rows", selectOnAutofocus: "selectOnAutofocus", upperCase: "upperCase" }, viewQueries: [{ propertyName: "inputElement", first: true, predicate: ["textboxInput"], descendants: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"control control-label-{{labelPosition}}\"\r\n [ngClass]=\"{'is-readonly': readonly}\">\r\n\r\n <label *ngIf=\"label\">\r\n <span>{{label | translate}}</span>\r\n <span *ngIf=\"validationErrors.length > 0 && formModel.touched && formModel.invalid\">&nbsp;{{validationErrors |\r\n translate}}</span>\r\n <ec-help-popover id=\"{{id}}_helpPopover\"\r\n *ngIf=\"helpPopover\"\r\n class=\"d-inline-block my-n3 mx-n1\"\r\n text=\"{{helpPopover | translate}}\"\r\n contentPosition=\"{{helpPopoverPosition}}\">\r\n </ec-help-popover>\r\n </label>\r\n\r\n <div class=\"input-wrapper control-input\">\r\n <input *ngIf=\"type !== 'multi_line'\"\r\n #textboxInput\r\n email=\"{{type === 'email' ? true : false}}\"\r\n pattern=\"{{validationPattern}}\"\r\n type=\"{{type}}\"\r\n tabindex=\"{{tabindex}}\"\r\n title=\"{{tooltip}}\"\r\n [attr.id]=\"inputId\"\r\n [attr.autocomplete]=\"autocomplete\"\r\n [attr.placeholder]=\"placeholder\"\r\n [attr.maxlength]=\"maxlength\"\r\n [attr.minlength]=\"minlength\"\r\n [attr.required]=\"required ? required : null\"\r\n [formControl]=\"formModel\"\r\n [ngClass]=\"{'is-empty': !formModel?.value, 'is-pending': pending, 'is-uppercase': upperCase}\"\r\n (focusout)=\"focusOutEvent()\"\r\n [attr.cdkFocusInitial]=\"autofocus || null\">\r\n\r\n <textarea *ngIf=\"type === 'multi_line'\"\r\n [attr.rows]=\"rows\"\r\n #textboxInput\r\n tabindex=\"{{tabindex}}\"\r\n [attr.id]=\"inputId\"\r\n [attr.placeholder]=\"placeholder\"\r\n [attr.maxlength]=\"maxlength\"\r\n [attr.minlength]=\"minlength\"\r\n [attr.required]=\"required ? required : null\"\r\n [formControl]=\"formModel\"\r\n [ngClass]=\"{'is-empty': formModel?.value === '', 'is-pending': pending}\"\r\n [attr.cdkFocusInitial]=\"autofocus || null\">\r\n </textarea>\r\n\r\n <i class=\"ec-icon icon-required\"></i>\r\n <i class=\"ec-icon icon-invalid\"></i>\r\n <i class=\"ec-icon icon-loading\"></i>\r\n </div>\r\n</div>", styles: [":host{color:var(--ec-form-control-color);font-size:var(--ec-form-control-font-size);display:block;margin-bottom:1rem;width:100%}:host :host-context(.form-condensed){margin-bottom:.5rem}:host .control{width:100%;display:flex;flex-direction:column}:host .control.control-label-bottom{flex-direction:column-reverse}:host .control.control-label-left{flex-direction:row}:host .control.control-label-left label{margin-right:.25rem}:host .control.control-label-right{flex-direction:row-reverse}:host .control.control-label-right label{margin-left:.25rem}:host .control.control-label-left,:host .control.control-label-right{align-items:center}:host .control.control-label-left label,:host .control.control-label-right label{flex:1 1;margin-top:0;margin-bottom:0}:host .control.control-label-left .control-input,:host .control.control-label-right .control-input{flex:2 2}:host .control.is-readonly input,:host .control.is-readonly select,:host .control.is-readonly textarea{border-color:var(--ec-form-control-border-color-readonly);background-color:var(--ec-form-control-background-color-readonly);background-clip:border-box;background-image:none;color:var(--ec-form-control-color-readonly);opacity:1;-webkit-user-select:none;user-select:none;pointer-events:none;overflow:hidden;white-space:nowrap}:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-invalid,:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-valid{background-color:var(--ec-form-control-background-color-invalid);border-color:var(--ec-form-control-border-color-invalid);background-repeat:no-repeat;background-position:.5rem center;background-size:1rem,1rem;padding-left:1.75rem;background-image:none}:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-invalid:focus,:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-valid:focus{border-color:var(--ec-form-control-background-color-invalid)}:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-invalid~.icon-invalid,:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-valid~.icon-invalid{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-invalid~.icon-required,:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-valid~.icon-required{display:none}:host .control.invalid:not(.open) .textbox-group-btn-right ::ng-deep button{background-color:var(--ec-form-control-background-color-invalid)}:host .control.invalid:not(.open) .textbox-group-btn-right ::ng-deep button:not(:focus){border-color:var(--ec-form-control-border-color-invalid)}:host .textbox-group{display:flex;position:relative}:host textarea:focus,:host input:focus,:host select:focus{outline:none}:host label{color:var(--ec-form-control-label-color, var(--ec-color-secondary-dark));display:block;font-size:var(--ec-font-size-label);line-height:1;margin:calc(var(--ec-font-size-label) / 2) 0}:host input{background-color:var(--ec-form-control-background-color);border:1px solid var(--ec-form-control-border-color);border-radius:var(--ec-border-radius);background-image:none;background-clip:padding-box;width:100%;line-height:1.25rem;padding:.3125rem .5rem;height:2rem}:host input::selection{background-color:var(--ec-form-control-background-color-selection);color:var(--ec-form-control-color-selection)}:host input::-webkit-input-placeholder{color:var(--ec-form-control-color-placeholder)}:host input::-moz-placeholder{color:var(--ec-form-control-color-placeholder);opacity:1}:host input:-ms-input-placeholder{color:var(--ec-form-control-color-placeholder)}:host input:-moz-placeholder{color:var(--ec-form-control-color-placeholder);opacity:1}:host input~.icon-required,:host input~.icon-invalid{color:var(--ec-form-control-border-color-invalid)}:host input:required.is-empty{background-repeat:no-repeat;background-position:.5rem center;background-size:1rem,1rem;padding-left:1.75rem;background-image:none}:host input:required.is-empty~.icon-required{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host input.ng-invalid.ng-touched{background-color:var(--ec-form-control-background-color-invalid);border-color:var(--ec-form-control-border-color-invalid);background-repeat:no-repeat;background-position:.5rem center;background-size:1rem,1rem;padding-left:1.75rem;background-image:none}:host input.ng-invalid.ng-touched:focus{border-color:var(--ec-form-control-background-color-invalid)}:host input.ng-invalid.ng-touched~.icon-invalid{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host input.ng-invalid.ng-touched~.icon-required{display:none}:host input.is-pending.ng-valid,:host input.is-pending.ng-invalid,:host input.is-pending.ng-pending{background-image:\"\";background-repeat:no-repeat;background-position:.5rem center;background-size:1rem,1rem;padding-left:1.75rem}:host input.is-pending.ng-valid~.icon-loading,:host input.is-pending.ng-invalid~.icon-loading,:host input.is-pending.ng-pending~.icon-loading{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host input.is-pending.ng-valid~.icon-required,:host input.is-pending.ng-valid~.icon-invalid,:host input.is-pending.ng-invalid~.icon-required,:host input.is-pending.ng-invalid~.icon-invalid,:host input.is-pending.ng-pending~.icon-required,:host input.is-pending.ng-pending~.icon-invalid{display:none}:host input:focus,:host input:focus.is-empty{border-color:var(--ec-form-control-border-color-focus);box-shadow:var(--ec-form-control-box-shadow-focus);position:relative;z-index:1}:host input:disabled{background-color:var(--ec-form-control-background-color-disabled);border-color:var(--ec-form-control-border-color-disabled);color:var(--ec-form-control-color-disabled);opacity:var(--ec-form-control-opacity-disabled)}:host input:disabled:required,:host input:disabled:required.is-empty{background-image:none;padding-left:.5rem;background-color:var(--ec-form-control-background-color-disabled);border-color:var(--ec-form-control-border-color-disabled)}:host input:disabled:required+.icon-required,:host input:disabled:required.is-empty+.icon-required{display:none}:host input.is-uppercase:not(.is-empty){text-transform:uppercase}:host textarea{background-color:var(--ec-form-control-background-color);border:1px solid var(--ec-form-control-border-color);border-radius:var(--ec-border-radius);background-image:none;background-clip:padding-box;width:100%;line-height:1.25rem;padding:.3125rem .5rem;height:auto;resize:none;display:block}:host textarea::selection{background-color:var(--ec-form-control-background-color-selection);color:var(--ec-form-control-color-selection)}:host textarea::-webkit-input-placeholder{color:var(--ec-form-control-color-placeholder)}:host textarea::-moz-placeholder{color:var(--ec-form-control-color-placeholder);opacity:1}:host textarea:-ms-input-placeholder{color:var(--ec-form-control-color-placeholder)}:host textarea:-moz-placeholder{color:var(--ec-form-control-color-placeholder);opacity:1}:host textarea~.icon-required,:host textarea~.icon-invalid{color:var(--ec-form-control-border-color-invalid)}:host textarea:required.is-empty{background-repeat:no-repeat;background-position:.5rem .5rem;background-size:1rem,1rem;padding-left:1.75rem;background-image:none}:host textarea:required.is-empty~.icon-required{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host textarea.ng-invalid.ng-touched{background-color:var(--ec-form-control-background-color-invalid);border-color:var(--ec-form-control-border-color-invalid);background-repeat:no-repeat;background-position:.5rem .5rem;background-size:1rem,1rem;padding-left:1.75rem;background-image:none}:host textarea.ng-invalid.ng-touched:focus{border-color:var(--ec-form-control-background-color-invalid)}:host textarea.ng-invalid.ng-touched~.icon-invalid{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host textarea.ng-invalid.ng-touched~.icon-required{display:none}:host textarea.is-pending.ng-valid,:host textarea.is-pending.ng-invalid,:host textarea.is-pending.ng-pending{background-image:\"\";background-repeat:no-repeat;background-position:.5rem .5rem;background-size:1rem,1rem;padding-left:1.75rem}:host textarea.is-pending.ng-valid~.icon-loading,:host textarea.is-pending.ng-invalid~.icon-loading,:host textarea.is-pending.ng-pending~.icon-loading{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host textarea.is-pending.ng-valid~.icon-required,:host textarea.is-pending.ng-valid~.icon-invalid,:host textarea.is-pending.ng-invalid~.icon-required,:host textarea.is-pending.ng-invalid~.icon-invalid,:host textarea.is-pending.ng-pending~.icon-required,:host textarea.is-pending.ng-pending~.icon-invalid{display:none}:host textarea:focus,:host textarea:focus.is-empty{border-color:var(--ec-form-control-border-color-focus);box-shadow:var(--ec-form-control-box-shadow-focus);position:relative;z-index:1}:host textarea:disabled{background-color:var(--ec-form-control-background-color-disabled);border-color:var(--ec-form-control-border-color-disabled);color:var(--ec-form-control-color-disabled);opacity:var(--ec-form-control-opacity-disabled)}:host textarea:disabled:required,:host textarea:disabled:required.is-empty{background-image:none;padding-left:.5rem;background-color:var(--ec-form-control-background-color-disabled);border-color:var(--ec-form-control-border-color-disabled)}:host textarea:disabled:required+.icon-required,:host textarea:disabled:required.is-empty+.icon-required{display:none}:host textarea.is-uppercase:not(.is-empty){text-transform:uppercase}.input-wrapper{position:relative}.input-wrapper>.ec-icon{display:none}:host(.textbox-group-input:not(:last-child)){flex:1 1 0%;width:1px}:host(.textbox-group-input:not(:last-child)) .control{margin-bottom:0}:host(.textbox-group-input:not(:last-child)) .control.is-readonly input{border-right-width:1px}:host(.textbox-group-input:not(:last-child)) input{border-top-right-radius:0;border-bottom-right-radius:0;border-right-width:0}:host(.textbox-group-input:not(:last-child)) input:focus{position:relative;z-index:1;border-right-width:1px}:host(.text-truncate) input{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host(.is-monospace) input,:host(.is-monospace) textarea,:host-context(.is-monospace) input,:host-context(.is-monospace) textarea{font-family:var(--ec-font-family-monospace)}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i4.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i4.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i4.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i4.EmailValidator, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: ["email"] }, { kind: "directive", type: i4.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: HelpPopoverComponent, selector: "ec-help-popover", inputs: ["id", "text", "contentPosition", "maxWidth"] }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }] });
1847
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TextboxComponent, decorators: [{
1848
+ type: Component,
1849
+ args: [{ selector: 'ec-textbox', template: "<div class=\"control control-label-{{labelPosition}}\"\r\n [ngClass]=\"{'is-readonly': readonly}\">\r\n\r\n <label *ngIf=\"label\">\r\n <span>{{label | translate}}</span>\r\n <span *ngIf=\"validationErrors.length > 0 && formModel.touched && formModel.invalid\">&nbsp;{{validationErrors |\r\n translate}}</span>\r\n <ec-help-popover id=\"{{id}}_helpPopover\"\r\n *ngIf=\"helpPopover\"\r\n class=\"d-inline-block my-n3 mx-n1\"\r\n text=\"{{helpPopover | translate}}\"\r\n contentPosition=\"{{helpPopoverPosition}}\">\r\n </ec-help-popover>\r\n </label>\r\n\r\n <div class=\"input-wrapper control-input\">\r\n <input *ngIf=\"type !== 'multi_line'\"\r\n #textboxInput\r\n email=\"{{type === 'email' ? true : false}}\"\r\n pattern=\"{{validationPattern}}\"\r\n type=\"{{type}}\"\r\n tabindex=\"{{tabindex}}\"\r\n title=\"{{tooltip}}\"\r\n [attr.id]=\"inputId\"\r\n [attr.autocomplete]=\"autocomplete\"\r\n [attr.placeholder]=\"placeholder\"\r\n [attr.maxlength]=\"maxlength\"\r\n [attr.minlength]=\"minlength\"\r\n [attr.required]=\"required ? required : null\"\r\n [formControl]=\"formModel\"\r\n [ngClass]=\"{'is-empty': !formModel?.value, 'is-pending': pending, 'is-uppercase': upperCase}\"\r\n (focusout)=\"focusOutEvent()\"\r\n [attr.cdkFocusInitial]=\"autofocus || null\">\r\n\r\n <textarea *ngIf=\"type === 'multi_line'\"\r\n [attr.rows]=\"rows\"\r\n #textboxInput\r\n tabindex=\"{{tabindex}}\"\r\n [attr.id]=\"inputId\"\r\n [attr.placeholder]=\"placeholder\"\r\n [attr.maxlength]=\"maxlength\"\r\n [attr.minlength]=\"minlength\"\r\n [attr.required]=\"required ? required : null\"\r\n [formControl]=\"formModel\"\r\n [ngClass]=\"{'is-empty': formModel?.value === '', 'is-pending': pending}\"\r\n [attr.cdkFocusInitial]=\"autofocus || null\">\r\n </textarea>\r\n\r\n <i class=\"ec-icon icon-required\"></i>\r\n <i class=\"ec-icon icon-invalid\"></i>\r\n <i class=\"ec-icon icon-loading\"></i>\r\n </div>\r\n</div>", styles: [":host{color:var(--ec-form-control-color);font-size:var(--ec-form-control-font-size);display:block;margin-bottom:1rem;width:100%}:host :host-context(.form-condensed){margin-bottom:.5rem}:host .control{width:100%;display:flex;flex-direction:column}:host .control.control-label-bottom{flex-direction:column-reverse}:host .control.control-label-left{flex-direction:row}:host .control.control-label-left label{margin-right:.25rem}:host .control.control-label-right{flex-direction:row-reverse}:host .control.control-label-right label{margin-left:.25rem}:host .control.control-label-left,:host .control.control-label-right{align-items:center}:host .control.control-label-left label,:host .control.control-label-right label{flex:1 1;margin-top:0;margin-bottom:0}:host .control.control-label-left .control-input,:host .control.control-label-right .control-input{flex:2 2}:host .control.is-readonly input,:host .control.is-readonly select,:host .control.is-readonly textarea{border-color:var(--ec-form-control-border-color-readonly);background-color:var(--ec-form-control-background-color-readonly);background-clip:border-box;background-image:none;color:var(--ec-form-control-color-readonly);opacity:1;-webkit-user-select:none;user-select:none;pointer-events:none;overflow:hidden;white-space:nowrap}:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-invalid,:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-valid{background-color:var(--ec-form-control-background-color-invalid);border-color:var(--ec-form-control-border-color-invalid);background-repeat:no-repeat;background-position:.5rem center;background-size:1rem,1rem;padding-left:1.75rem;background-image:none}:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-invalid:focus,:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-valid:focus{border-color:var(--ec-form-control-background-color-invalid)}:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-invalid~.icon-invalid,:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-valid~.icon-invalid{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-invalid~.icon-required,:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-valid~.icon-required{display:none}:host .control.invalid:not(.open) .textbox-group-btn-right ::ng-deep button{background-color:var(--ec-form-control-background-color-invalid)}:host .control.invalid:not(.open) .textbox-group-btn-right ::ng-deep button:not(:focus){border-color:var(--ec-form-control-border-color-invalid)}:host .textbox-group{display:flex;position:relative}:host textarea:focus,:host input:focus,:host select:focus{outline:none}:host label{color:var(--ec-form-control-label-color, var(--ec-color-secondary-dark));display:block;font-size:var(--ec-font-size-label);line-height:1;margin:calc(var(--ec-font-size-label) / 2) 0}:host input{background-color:var(--ec-form-control-background-color);border:1px solid var(--ec-form-control-border-color);border-radius:var(--ec-border-radius);background-image:none;background-clip:padding-box;width:100%;line-height:1.25rem;padding:.3125rem .5rem;height:2rem}:host input::selection{background-color:var(--ec-form-control-background-color-selection);color:var(--ec-form-control-color-selection)}:host input::-webkit-input-placeholder{color:var(--ec-form-control-color-placeholder)}:host input::-moz-placeholder{color:var(--ec-form-control-color-placeholder);opacity:1}:host input:-ms-input-placeholder{color:var(--ec-form-control-color-placeholder)}:host input:-moz-placeholder{color:var(--ec-form-control-color-placeholder);opacity:1}:host input~.icon-required,:host input~.icon-invalid{color:var(--ec-form-control-border-color-invalid)}:host input:required.is-empty{background-repeat:no-repeat;background-position:.5rem center;background-size:1rem,1rem;padding-left:1.75rem;background-image:none}:host input:required.is-empty~.icon-required{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host input.ng-invalid.ng-touched{background-color:var(--ec-form-control-background-color-invalid);border-color:var(--ec-form-control-border-color-invalid);background-repeat:no-repeat;background-position:.5rem center;background-size:1rem,1rem;padding-left:1.75rem;background-image:none}:host input.ng-invalid.ng-touched:focus{border-color:var(--ec-form-control-background-color-invalid)}:host input.ng-invalid.ng-touched~.icon-invalid{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host input.ng-invalid.ng-touched~.icon-required{display:none}:host input.is-pending.ng-valid,:host input.is-pending.ng-invalid,:host input.is-pending.ng-pending{background-image:\"\";background-repeat:no-repeat;background-position:.5rem center;background-size:1rem,1rem;padding-left:1.75rem}:host input.is-pending.ng-valid~.icon-loading,:host input.is-pending.ng-invalid~.icon-loading,:host input.is-pending.ng-pending~.icon-loading{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host input.is-pending.ng-valid~.icon-required,:host input.is-pending.ng-valid~.icon-invalid,:host input.is-pending.ng-invalid~.icon-required,:host input.is-pending.ng-invalid~.icon-invalid,:host input.is-pending.ng-pending~.icon-required,:host input.is-pending.ng-pending~.icon-invalid{display:none}:host input:focus,:host input:focus.is-empty{border-color:var(--ec-form-control-border-color-focus);box-shadow:var(--ec-form-control-box-shadow-focus);position:relative;z-index:1}:host input:disabled{background-color:var(--ec-form-control-background-color-disabled);border-color:var(--ec-form-control-border-color-disabled);color:var(--ec-form-control-color-disabled);opacity:var(--ec-form-control-opacity-disabled)}:host input:disabled:required,:host input:disabled:required.is-empty{background-image:none;padding-left:.5rem;background-color:var(--ec-form-control-background-color-disabled);border-color:var(--ec-form-control-border-color-disabled)}:host input:disabled:required+.icon-required,:host input:disabled:required.is-empty+.icon-required{display:none}:host input.is-uppercase:not(.is-empty){text-transform:uppercase}:host textarea{background-color:var(--ec-form-control-background-color);border:1px solid var(--ec-form-control-border-color);border-radius:var(--ec-border-radius);background-image:none;background-clip:padding-box;width:100%;line-height:1.25rem;padding:.3125rem .5rem;height:auto;resize:none;display:block}:host textarea::selection{background-color:var(--ec-form-control-background-color-selection);color:var(--ec-form-control-color-selection)}:host textarea::-webkit-input-placeholder{color:var(--ec-form-control-color-placeholder)}:host textarea::-moz-placeholder{color:var(--ec-form-control-color-placeholder);opacity:1}:host textarea:-ms-input-placeholder{color:var(--ec-form-control-color-placeholder)}:host textarea:-moz-placeholder{color:var(--ec-form-control-color-placeholder);opacity:1}:host textarea~.icon-required,:host textarea~.icon-invalid{color:var(--ec-form-control-border-color-invalid)}:host textarea:required.is-empty{background-repeat:no-repeat;background-position:.5rem .5rem;background-size:1rem,1rem;padding-left:1.75rem;background-image:none}:host textarea:required.is-empty~.icon-required{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host textarea.ng-invalid.ng-touched{background-color:var(--ec-form-control-background-color-invalid);border-color:var(--ec-form-control-border-color-invalid);background-repeat:no-repeat;background-position:.5rem .5rem;background-size:1rem,1rem;padding-left:1.75rem;background-image:none}:host textarea.ng-invalid.ng-touched:focus{border-color:var(--ec-form-control-background-color-invalid)}:host textarea.ng-invalid.ng-touched~.icon-invalid{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host textarea.ng-invalid.ng-touched~.icon-required{display:none}:host textarea.is-pending.ng-valid,:host textarea.is-pending.ng-invalid,:host textarea.is-pending.ng-pending{background-image:\"\";background-repeat:no-repeat;background-position:.5rem .5rem;background-size:1rem,1rem;padding-left:1.75rem}:host textarea.is-pending.ng-valid~.icon-loading,:host textarea.is-pending.ng-invalid~.icon-loading,:host textarea.is-pending.ng-pending~.icon-loading{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host textarea.is-pending.ng-valid~.icon-required,:host textarea.is-pending.ng-valid~.icon-invalid,:host textarea.is-pending.ng-invalid~.icon-required,:host textarea.is-pending.ng-invalid~.icon-invalid,:host textarea.is-pending.ng-pending~.icon-required,:host textarea.is-pending.ng-pending~.icon-invalid{display:none}:host textarea:focus,:host textarea:focus.is-empty{border-color:var(--ec-form-control-border-color-focus);box-shadow:var(--ec-form-control-box-shadow-focus);position:relative;z-index:1}:host textarea:disabled{background-color:var(--ec-form-control-background-color-disabled);border-color:var(--ec-form-control-border-color-disabled);color:var(--ec-form-control-color-disabled);opacity:var(--ec-form-control-opacity-disabled)}:host textarea:disabled:required,:host textarea:disabled:required.is-empty{background-image:none;padding-left:.5rem;background-color:var(--ec-form-control-background-color-disabled);border-color:var(--ec-form-control-border-color-disabled)}:host textarea:disabled:required+.icon-required,:host textarea:disabled:required.is-empty+.icon-required{display:none}:host textarea.is-uppercase:not(.is-empty){text-transform:uppercase}.input-wrapper{position:relative}.input-wrapper>.ec-icon{display:none}:host(.textbox-group-input:not(:last-child)){flex:1 1 0%;width:1px}:host(.textbox-group-input:not(:last-child)) .control{margin-bottom:0}:host(.textbox-group-input:not(:last-child)) .control.is-readonly input{border-right-width:1px}:host(.textbox-group-input:not(:last-child)) input{border-top-right-radius:0;border-bottom-right-radius:0;border-right-width:0}:host(.textbox-group-input:not(:last-child)) input:focus{position:relative;z-index:1;border-right-width:1px}:host(.text-truncate) input{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host(.is-monospace) input,:host(.is-monospace) textarea,:host-context(.is-monospace) input,:host-context(.is-monospace) textarea{font-family:var(--ec-font-family-monospace)}\n"] }]
1850
+ }], ctorParameters: function () { return [{ type: ValidationMessageService }, { type: FormGroupHelper }, { type: i2.TranslateService }]; }, propDecorators: { autocomplete: [{
1851
+ type: Input
1852
+ }], type: [{
1853
+ type: Input
1854
+ }], placeholder: [{
1855
+ type: Input
1856
+ }], maxlength: [{
1857
+ type: Input
1858
+ }], minlength: [{
1859
+ type: Input
1860
+ }], rows: [{
1861
+ type: Input
1862
+ }], selectOnAutofocus: [{
1863
+ type: Input
1864
+ }], upperCase: [{
1865
+ type: Input
1866
+ }], inputElement: [{
1867
+ type: ViewChild,
1868
+ args: ['textboxInput']
1834
1869
  }] } });
1835
1870
 
1836
- ;
1837
- const menuAnimationSpeed = 350;
1871
+ /** Exposes the markup and styles that represent the spinner. No inputs or outputs defined because it is just a visual component*/
1872
+ class SpinnerComponent {
1873
+ }
1874
+ SpinnerComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: SpinnerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1875
+ SpinnerComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: SpinnerComponent, selector: "ec-spinner", ngImport: i0, template: "<div class=\"spinner\">\r\n <span class=\"spinner-dot\"></span>\r\n <span class=\"spinner-dot\"></span>\r\n <span class=\"spinner-dot\"></span>\r\n <span class=\"spinner-dot\"></span>\r\n</div>", styles: ["@keyframes sk-bouncedelay{0%,80%,to{opacity:0}40%{opacity:1}}.spinner{display:flex}.spinner-dot{width:.75rem;height:.75rem;background-color:var(--ec-color-interactive);animation:sk-bouncedelay 1.7s infinite ease-in-out both;margin-right:.25rem}.spinner-dot:nth-child(1){animation-delay:-.6s}.spinner-dot:nth-child(2){animation-delay:-.4s}.spinner-dot:nth-child(3){animation-delay:-.2s}:host(.spinner-small) .spinner-dot{width:.5rem;height:.5rem;background-color:var(--ec-color-interactive);animation:sk-bouncedelay 1.7s infinite ease-in-out both;margin-right:.1666666667rem}:host(.spinner-small) .spinner-dot:nth-child(1){animation-delay:-.6s}:host(.spinner-small) .spinner-dot:nth-child(2){animation-delay:-.4s}:host(.spinner-small) .spinner-dot:nth-child(3){animation-delay:-.2s}\n"] });
1876
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: SpinnerComponent, decorators: [{
1877
+ type: Component,
1878
+ args: [{ selector: 'ec-spinner', template: "<div class=\"spinner\">\r\n <span class=\"spinner-dot\"></span>\r\n <span class=\"spinner-dot\"></span>\r\n <span class=\"spinner-dot\"></span>\r\n <span class=\"spinner-dot\"></span>\r\n</div>", styles: ["@keyframes sk-bouncedelay{0%,80%,to{opacity:0}40%{opacity:1}}.spinner{display:flex}.spinner-dot{width:.75rem;height:.75rem;background-color:var(--ec-color-interactive);animation:sk-bouncedelay 1.7s infinite ease-in-out both;margin-right:.25rem}.spinner-dot:nth-child(1){animation-delay:-.6s}.spinner-dot:nth-child(2){animation-delay:-.4s}.spinner-dot:nth-child(3){animation-delay:-.2s}:host(.spinner-small) .spinner-dot{width:.5rem;height:.5rem;background-color:var(--ec-color-interactive);animation:sk-bouncedelay 1.7s infinite ease-in-out both;margin-right:.1666666667rem}:host(.spinner-small) .spinner-dot:nth-child(1){animation-delay:-.6s}:host(.spinner-small) .spinner-dot:nth-child(2){animation-delay:-.4s}:host(.spinner-small) .spinner-dot:nth-child(3){animation-delay:-.2s}\n"] }]
1879
+ }] });
1880
+
1881
+ class Overlay {
1882
+ constructor(status, message) {
1883
+ this.status = 'hasData';
1884
+ this.message = '';
1885
+ this.setStatus(status, message);
1886
+ }
1887
+ setStatus(status, message, action, noDataTemplate, overlayClassList) {
1888
+ this.status = status;
1889
+ this.message = message || '';
1890
+ this.action = action || undefined;
1891
+ this.noDataTemplate = noDataTemplate || undefined;
1892
+ this.overlayClassList = overlayClassList || '';
1893
+ }
1894
+ }
1838
1895
  /**
1839
- * Primitive Menu component that encapsulates known templates
1840
- *
1841
- * @export
1896
+ * Wraps content in order to show pending, error, and no data states with an optional message/noDataTemplate
1842
1897
  */
1843
- class MenuComponent {
1898
+ class ViewOverlayComponent {
1899
+ constructor() {
1900
+ this.status = 'hasData';
1901
+ }
1902
+ setStatus(status, message, action, noDataTemplate) {
1903
+ this.status = status;
1904
+ this.message = message || '';
1905
+ this.action = action || undefined;
1906
+ this.noDataTemplate = noDataTemplate || undefined;
1907
+ }
1908
+ actionClicked(event) {
1909
+ if (this.action && this.action.onClick) {
1910
+ this.action.onClick(event);
1911
+ }
1912
+ }
1913
+ }
1914
+ ViewOverlayComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ViewOverlayComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1915
+ ViewOverlayComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: ViewOverlayComponent, selector: "[ecOverlay]", inputs: { status: "status", message: "message", action: "action", noDataTemplate: "noDataTemplate", displayAsMask: "displayAsMask", overlayClassList: "overlayClassList" }, ngImport: i0, template: "<!-- Transcluded Content -->\r\n<ng-content *ngIf=\"displayAsMask || (!displayAsMask && status === 'hasData')\"></ng-content>\r\n<!--Used by GI tests to know the overlay status whether we use ngIf or mask version. No visual impact-->\r\n<span [hidden]=\"true\"\r\n\t class=\"overlay-status-{{status}}\"></span>\r\n<!-- Overlay goes last so it is rendered on top of preceding content due to source order -->\r\n<div *ngIf=\"status !== 'hasData'\"\r\n\t class=\"overlay flex-grow {{overlayClassList}}\"\r\n\t [ngClass]=\"{'not-mask': !displayAsMask,\r\n\t\t\t\t'overlay-error': status === 'error',\r\n\t\t\t\t'overlay-nodata': status === 'noData',\r\n\t\t\t\t'overlay-pending': status === 'pending'}\">\r\n\r\n\t<!--Pending Spinner-->\r\n\t<ec-spinner [hidden]=\"status !== 'pending'\"></ec-spinner>\r\n\r\n\t<ng-template [ngIf]=\"status === 'noData' && noDataTemplate\">\r\n\t\t<ng-container *ngTemplateOutlet=\"noDataTemplate\"></ng-container>\r\n\t</ng-template>\r\n\r\n\t<ng-container *ngIf=\"(status === 'noData' && !noDataTemplate) || status !== 'noData'\">\r\n\t\t<!--Status Message-->\r\n\t\t<div id=\"statusMessage\"\r\n\t\t\t class=\"message\"\r\n\t\t\t *ngIf=\"message\"\r\n\t\t\t [ngClass]=\"{'error': status === 'error', 'mt-1': status === 'pending'}\"\r\n\t\t\t [innerHtml]=\"message | translate\">\r\n\t\t</div>\r\n\r\n\t\t<!-- Action -->\r\n\t\t<ec-button type=\"common\"\r\n\t\t\t\t class=\"mt-3\"\r\n\t\t\t\t *ngIf=\"action?.onClick\"\r\n\t\t\t\t [icon]=\"action?.icon\"\r\n\t\t\t\t (clicked)=\"actionClicked($event)\"\r\n\t\t\t\t [label]=\"action?.label\"\r\n\t\t\t\t [hidden]=\"status === 'pending'\">\r\n\t\t</ec-button>\r\n\t</ng-container>\r\n\r\n</div>", styles: [":host{position:relative}:host(.bg-body)>.overlay{background-color:var(--ec-background-color-body)}:host(.bg-body).is-translucent>.overlay{background-color:var(--ec-background-color-overlay)}:host(.bg-content)>.overlay{background-color:var(--ec-background-color)}:host(.bg-content).is-translucent>.overlay{background-color:var(--ec-background-color-overlay)}.overlay{align-items:center;background-color:var(--ec-overlay-background-color, var(--ec-background-color));display:flex;flex-direction:column;justify-content:center;padding:3rem 4rem;z-index:var(--ec-z-index-overlay);position:absolute;inset:0}.overlay.not-mask{position:relative;min-height:100%}.message{color:var(--ec-color-secondary-dark);font-size:var(--ec-font-size-title)}.message.error{color:var(--ec-color-danger);font-size:var(--ec-font-size-title)}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ButtonComponent, selector: "ec-button", inputs: ["id", "disabled", "icon", "label", "badge", "tabindex", "type", "pending", "pendingIcon", "customTemplate", "isSubmit", "autofocus"], outputs: ["clicked"] }, { kind: "component", type: SpinnerComponent, selector: "ec-spinner" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }] });
1916
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ViewOverlayComponent, decorators: [{
1917
+ type: Component,
1918
+ args: [{ selector: '[ecOverlay]', template: "<!-- Transcluded Content -->\r\n<ng-content *ngIf=\"displayAsMask || (!displayAsMask && status === 'hasData')\"></ng-content>\r\n<!--Used by GI tests to know the overlay status whether we use ngIf or mask version. No visual impact-->\r\n<span [hidden]=\"true\"\r\n\t class=\"overlay-status-{{status}}\"></span>\r\n<!-- Overlay goes last so it is rendered on top of preceding content due to source order -->\r\n<div *ngIf=\"status !== 'hasData'\"\r\n\t class=\"overlay flex-grow {{overlayClassList}}\"\r\n\t [ngClass]=\"{'not-mask': !displayAsMask,\r\n\t\t\t\t'overlay-error': status === 'error',\r\n\t\t\t\t'overlay-nodata': status === 'noData',\r\n\t\t\t\t'overlay-pending': status === 'pending'}\">\r\n\r\n\t<!--Pending Spinner-->\r\n\t<ec-spinner [hidden]=\"status !== 'pending'\"></ec-spinner>\r\n\r\n\t<ng-template [ngIf]=\"status === 'noData' && noDataTemplate\">\r\n\t\t<ng-container *ngTemplateOutlet=\"noDataTemplate\"></ng-container>\r\n\t</ng-template>\r\n\r\n\t<ng-container *ngIf=\"(status === 'noData' && !noDataTemplate) || status !== 'noData'\">\r\n\t\t<!--Status Message-->\r\n\t\t<div id=\"statusMessage\"\r\n\t\t\t class=\"message\"\r\n\t\t\t *ngIf=\"message\"\r\n\t\t\t [ngClass]=\"{'error': status === 'error', 'mt-1': status === 'pending'}\"\r\n\t\t\t [innerHtml]=\"message | translate\">\r\n\t\t</div>\r\n\r\n\t\t<!-- Action -->\r\n\t\t<ec-button type=\"common\"\r\n\t\t\t\t class=\"mt-3\"\r\n\t\t\t\t *ngIf=\"action?.onClick\"\r\n\t\t\t\t [icon]=\"action?.icon\"\r\n\t\t\t\t (clicked)=\"actionClicked($event)\"\r\n\t\t\t\t [label]=\"action?.label\"\r\n\t\t\t\t [hidden]=\"status === 'pending'\">\r\n\t\t</ec-button>\r\n\t</ng-container>\r\n\r\n</div>", styles: [":host{position:relative}:host(.bg-body)>.overlay{background-color:var(--ec-background-color-body)}:host(.bg-body).is-translucent>.overlay{background-color:var(--ec-background-color-overlay)}:host(.bg-content)>.overlay{background-color:var(--ec-background-color)}:host(.bg-content).is-translucent>.overlay{background-color:var(--ec-background-color-overlay)}.overlay{align-items:center;background-color:var(--ec-overlay-background-color, var(--ec-background-color));display:flex;flex-direction:column;justify-content:center;padding:3rem 4rem;z-index:var(--ec-z-index-overlay);position:absolute;inset:0}.overlay.not-mask{position:relative;min-height:100%}.message{color:var(--ec-color-secondary-dark);font-size:var(--ec-font-size-title)}.message.error{color:var(--ec-color-danger);font-size:var(--ec-font-size-title)}\n"] }]
1919
+ }], propDecorators: { status: [{
1920
+ type: Input
1921
+ }], message: [{
1922
+ type: Input
1923
+ }], action: [{
1924
+ type: Input
1925
+ }], noDataTemplate: [{
1926
+ type: Input
1927
+ }], displayAsMask: [{
1928
+ type: Input
1929
+ }], overlayClassList: [{
1930
+ type: Input
1931
+ }] } });
1932
+
1933
+ /**
1934
+ * Service to help with interfacing with the window object
1935
+ * and navigating around the application (going outside of the Angular 2+ router)
1936
+ */
1937
+ class WindowService {
1844
1938
  /**
1845
- * Helper function to return a flat list of all selectable items in the provided menu items. Filters out headings and divided-sections.
1846
- * This makes it much easier to keep track of currently highlighted items for keyboard navigation.
1939
+ * Tracks if there are any unsaved changes that the user could lose.
1940
+ *
1941
+ * It is set up as `get` only because it is set with `addNavigateAwayPrompt`.
1942
+ *
1943
+ * This also includes adding a prompt to the window itself (in addition to
1944
+ * working with the `CanDeactivateUnsavedChanges` guard) to cover page reloads
1945
+ * which do not trigger router events.
1847
1946
  */
1848
- static getSelectableItems(items) {
1849
- return items.reduce((selectableItems, item) => {
1850
- if (item.display !== 'heading' && item.display !== 'divided-section') {
1851
- selectableItems.push(item);
1852
- }
1853
- else if (item.items) {
1854
- selectableItems.push(...item.items.filter(childItem => childItem.display !== 'heading' && childItem.display !== 'divided-section'));
1855
- }
1856
- return selectableItems;
1857
- }, []);
1947
+ get hasUnsavedChanges() {
1948
+ return this._hasUnsavedChanges;
1858
1949
  }
1859
1950
  /**
1860
- * Returns an ID for the provided item based on its index in the provided items array. This mimics the behavior of the MenuComponent's
1861
- * generated IDs for items that don't have provided IDs. This is used in MenuComponent and ComboboxComponent to scroll to specific items.
1862
- * NOTE: If the items array does not match what is displayed in the menu, this function will not return the correct ID.
1863
- *
1864
- * Returns null if the not found
1865
- * @param items The MenuItems array to search through.
1866
- * @param item The item to generate the ID for.
1867
- * @param menuComponentId Used to prefix the generated ID. This should be the ID of the menu component the item is present in.
1868
- * @memberof MenuComponent
1951
+ * Expose the innerWidth on the window global. Protects against errors when code
1952
+ * is running on a non-browser platform.
1869
1953
  */
1870
- static getIndexedItemId(items, item, menuComponentId) {
1871
- if (item) {
1872
- for (let i = 0; i < items.length; i++) {
1873
- const itemInList = items[i];
1874
- if (itemInList.label === item.label) {
1875
- return `${menuComponentId}_item${i}`;
1876
- }
1877
- // If the item is a heading or divided section, check its children
1878
- if (itemInList.items && (itemInList.display === 'heading' || itemInList.display === 'divided-section')) {
1879
- for (let j = 0; j < itemInList.items.length; j++) {
1880
- const childItem = itemInList.items[j];
1881
- // Fall back to checking only the label if the item doesn't have an id
1882
- if (childItem.label === item.label) {
1883
- return `${menuComponentId}_item${i}-${j}`;
1884
- }
1885
- }
1886
- }
1887
- }
1888
- }
1889
- return null;
1954
+ get innerWidth() {
1955
+ return window ? window.innerWidth : undefined;
1890
1956
  }
1891
- constructor(el, renderer, windowService, scrollService) {
1892
- this.el = el;
1893
- this.renderer = renderer;
1894
- this.windowService = windowService;
1895
- this.scrollService = scrollService;
1896
- /**
1897
- * Array of items to display
1898
- *
1899
- * @memberof MenuComponent
1900
- */
1901
- this.items = [];
1902
- /**
1903
- * Selected item; annotates the item
1904
- * when displayed with 'selected' class
1905
- *
1906
- * @memberof MenuComponent
1907
- */
1908
- this.selected = null;
1957
+ constructor(router, activatedRoute) {
1958
+ this.router = router;
1959
+ this.activatedRoute = activatedRoute;
1960
+ this._hasUnsavedChanges = false;
1909
1961
  /**
1910
- * Display template
1962
+ * Function called when the window `beforeunload` event is fired.
1911
1963
  *
1912
- * @memberof MenuComponent
1913
- */
1914
- this.templateType = 'label';
1915
- /**
1916
- * Show message when there are no items
1917
- */
1918
- this.showNoItems = false;
1919
- /**
1920
- * Text to show when menu is empty and showNoItems is true
1921
- */
1922
- this.noDataText = 'NoItems_TC';
1923
- /**
1924
- * Controls whether keyboard navigation is enabled
1925
- */
1926
- this.enableKeyNav = false;
1927
- /**
1928
- * Item currently highlighted by keyboard navigation
1929
- */
1930
- this.highlightedItem = null;
1931
- /**
1932
- * Tells the menu to maintain the selected/lastSelected item. Turning this off is useful for
1933
- * action type menus that are displayed on the screen at all times and you do not
1934
- * want the item to be selected when clicked.
1935
- */
1936
- this.maintainSelectedItem = true;
1937
- /**
1938
- * Will prevent text-wrapping of menu items and truncate instead. Also turns on a tooltip for the menu item. Default: false;
1939
- */
1940
- this.truncateItems = false;
1941
- /**
1942
- * When true, the space for the icon is preserved for menu items that do not have icons.
1943
- * Only applicable for iconAndLabel menus.
1944
- */
1945
- this.preserveIconSpace = false;
1946
- /**
1947
- * Emitted when `selected` is changed. Emits the referenced object.
1964
+ * A reference to the function that was passed to `window.addEventListener`
1965
+ * must be retained for `window.removeEventListener` to function properly.
1948
1966
  *
1949
- * @memberof MenuComponent
1950
- */
1951
- this.selectedChanged = new EventEmitter();
1952
- /**
1953
- * Emitted when the menu has a parent and back is clicked
1954
- * @memberof MenuComponent
1955
- */
1956
- this.menuClosed = new EventEmitter();
1957
- /**
1958
- * Index of the item currently highlighted using keyboard nav
1959
- */
1960
- this.highlightedItemIndex = -1;
1961
- /**
1962
- * Last item this.selected was set to via selectItem().
1963
- * This isn't necessarily the same as this.selected, because this.selected is an input property
1964
- * and could have been changed by a consumer through some means other than selectItem().
1965
- * This allows us to prevent double-calls to selectItem() with the same input.
1966
- */
1967
- this.lastSelected = null;
1968
- /**
1969
- * Flattened array of all selectable items in the menu. Makes it easier to keep track of the currently highlighted item for keyboard navigation.
1967
+ * Some browsers require the event's `returnValue` to be set to show the confirmation
1968
+ * dialog.
1969
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event
1970
1970
  */
1971
- this.selectableItems = [];
1972
- }
1973
- ngOnChanges(changes) {
1974
- if (changes.items && this.items) {
1975
- this.selectableItems = MenuComponent.getSelectableItems(this.items);
1971
+ this.beforeUnloadFunction = (event) => {
1972
+ // Cancel the event as stated by the standard.
1973
+ event.preventDefault();
1974
+ // Chrome requires returnValue to be set.
1975
+ event.returnValue = '';
1976
+ };
1977
+ if (window) {
1978
+ this.resized = fromEvent(window, 'resize');
1976
1979
  }
1977
1980
  }
1978
1981
  /**
1979
- * Sets & displays the interalized template based on
1980
- * the set template.
1981
- * @see { @link https://angular.io/guide/lifecycle-hooks|Angular Lifecycle Hooks}
1982
- *
1983
- * @memberof MenuComponent
1982
+ * Navigates to the previous page the user had visited
1984
1983
  */
1985
- ngAfterContentInit() {
1986
- switch (this.templateType) {
1987
- case ("label"):
1988
- this.internalizedTemplate = this.iconAndLabelTemplate;
1989
- break;
1990
- case ("iconAndLabel"):
1991
- this.internalizedTemplate = this.iconAndLabelTemplate;
1992
- break;
1993
- case ("checkAndLabel"):
1994
- this.internalizedTemplate = this.checkAndLabelTemplate;
1995
- break;
1996
- case ("iconLabelCaption"):
1997
- this.internalizedTemplate = this.iconLabelCaptionTemplate;
1998
- break;
1999
- default:
2000
- throw new Error(`Invalid templateType for MenuComponent. Please use either: 'label', 'iconAndLabel' or 'checkAndLabel'`);
2001
- }
2002
- //if the consumer provided a menuItemTemplate, override the internalizedTemplate with that.
2003
- if (this.customMenuTemplate) {
2004
- this.internalizedTemplate = this.customMenuTemplate;
2005
- }
2006
- if (this.id) {
2007
- this.attrId = this.id;
2008
- }
2009
- this.setItemIds();
2010
- if (this.highlightedItem && this.selectableItems.length) {
2011
- this.highlightedItemIndex = this.selectableItems.findIndex(item => { return this.highlightedItem === item; });
2012
- }
2013
- this.addKeydownListener();
1984
+ goBack() {
1985
+ window.history.back();
2014
1986
  }
2015
- ngOnDestroy() {
2016
- // Remove the listener when the component is destroyed
2017
- if (this.removeKeydownListener) {
2018
- this.removeKeydownListener();
2019
- }
1987
+ /**An abstraction around the browsers window history length.
1988
+ * Returns zero if unable to access or running outside a browser context
1989
+ */
1990
+ getHistoryLength() {
1991
+ return window?.history?.length || 0;
2020
1992
  }
2021
1993
  /**
2022
- * When a menu item is selected, open a child menu if the item has items, call
2023
- * the item's click method if defined, or emit the selected item.
2024
- *
2025
- * @param item The selected item
2026
- * @memberof MenuComponent
2027
- */
2028
- selectItem(event, item, isKeyEvent) {
2029
- event.stopPropagation();
2030
- //In the case that the user clicks an item, selectItem() will be called from the click handler
2031
- //and through onRouterLinkActivated. Only one of these will make it through this if statement
2032
- //because the first one will set this.lastSelected = item.
2033
- if (!item.disabled && !item.readonly && this.lastSelected !== item) {
2034
- if (!item.url) {
2035
- if (item.onClick) {
2036
- item.onClick(item, false);
2037
- }
2038
- if (item.items && item.display !== 'heading' && item.display !== 'divided-section') {
2039
- this.toggleChildMenu(true);
2040
- }
2041
- else {
2042
- this.onSelection(item);
2043
- }
2044
- // We need to manually handle the url navigation if the keyboard was used
2045
- }
2046
- else if (isKeyEvent || event.target?.tagName === 'LI') {
2047
- if (item.target) {
2048
- window.open(item.url, item.target);
2049
- }
2050
- else {
2051
- this.windowService.navigateToUrl(item.url);
2052
- }
2053
- // Emit so upstream components know an item was selected
2054
- this.onSelection(item);
2055
- }
2056
- else {
2057
- this.onSelection(item);
2058
- }
2059
- if (this.maintainSelectedItem) {
2060
- this.selected = item;
2061
- this.lastSelected = item;
1994
+ * Navigate to any url you know the path to
1995
+ * @param url The URL to navigate to
1996
+ *
1997
+ * @deprecated For legacy support only; use `router.navigateByUrl` instead
1998
+ */
1999
+ async navigateToUrl(url) {
2000
+ try {
2001
+ if (url.indexOf('/app/') === 0) {
2002
+ await this.router.navigateByUrl(url.substring(5));
2062
2003
  }
2063
2004
  else {
2064
- this.selected = null;
2065
- this.lastSelected = null;
2005
+ await this.router.navigateByUrl(url);
2066
2006
  }
2067
2007
  }
2008
+ catch (e) {
2009
+ // If the router throws we will try to navigate to the fully qualified url as a last ditch effort.
2010
+ // This can happen if we missed a link that needs to be converted to ng5 or our ng1Href directive
2011
+ // didn't handle a link correctly
2012
+ window.location.href = url;
2013
+ }
2068
2014
  }
2069
2015
  /**
2070
- * Close the current menu and open the parent menu
2071
- * @memberof MenuComponent
2016
+ * Adds a `beforeunload` function to the window to alert the user that there are about to leave
2017
+ * the current page and ask if they'd like to leave or stay
2072
2018
  */
2073
- back(event) {
2074
- event.stopPropagation();
2075
- if (this.parent && this.parent.onClick) {
2076
- this.parent.onClick(null, true);
2077
- }
2078
- this.menuClosed.emit();
2019
+ addNavigateAwayPrompt() {
2020
+ this._hasUnsavedChanges = true;
2021
+ window.addEventListener("beforeunload", this.beforeUnloadFunction);
2079
2022
  }
2080
2023
  /**
2081
- * Emit the selected item
2082
- * @param item The selected item
2024
+ * Removes the `beforeunload` function added to the window
2083
2025
  */
2084
- onSelection(item) {
2085
- if (item.display !== 'heading' && item.display !== 'divided-section') {
2086
- this.selectedChanged.emit(item);
2087
- }
2026
+ removeNavigateAwayPrompt() {
2027
+ this._hasUnsavedChanges = false;
2028
+ window.removeEventListener("beforeunload", this.beforeUnloadFunction);
2088
2029
  }
2089
2030
  /**
2090
- * Open or close the child menu. When the child menu closes, the selected
2091
- * item is reset.
2092
- * @memberof MenuComponent
2031
+ * Send data to another window.
2032
+ *
2033
+ * __SECURITY RISK__ - Always use a specific target origin. Failing to provide a specific target origin can allow
2034
+ * malicious sites to receive the message.
2035
+ *
2036
+ * @param targetWindow - Window to send the message to
2037
+ * @param message - Data to send
2038
+ * @param targetOrigin - What the URI of the target window must be for the message to be sent.
2039
+ * If sending data to another EnergyCAP window, this should always be `window.location.origin` to ensure
2040
+ * that only instances of EnergyCAP app receive the message.
2093
2041
  */
2094
- toggleChildMenu(open) {
2095
- let navEl = this.el.nativeElement.querySelector('nav');
2096
- if (open) {
2097
- // Remove the listener on the parent menu when a child menu is opened
2098
- // This is to avoid interference between the parent and child menus
2099
- if (this.removeKeydownListener) {
2100
- this.removeKeydownListener();
2101
- }
2102
- let height = navEl.offsetHeight;
2103
- let width = navEl.offsetWidth;
2104
- // In order to animate the child menu, we need to set height on the nav element
2105
- // so we can absolutely position the two menus and maintain the current menu's height
2106
- this.renderer.setStyle(navEl, 'height', `${height}px`);
2107
- this.renderer.setStyle(navEl, 'width', `${width}px`);
2108
- this.renderer.addClass(this.el.nativeElement, 'open');
2109
- setTimeout(() => {
2110
- this.renderer.addClass(this.el.nativeElement, 'open-active');
2111
- });
2112
- }
2113
- else {
2114
- // Re-add the listener once the child menu closes
2115
- this.addKeydownListener();
2116
- this.renderer.removeClass(this.el.nativeElement, 'open-active');
2117
- setTimeout(() => {
2118
- this.renderer.removeClass(this.el.nativeElement, 'open');
2119
- // Reset the nav element's height to auto
2120
- this.renderer.setStyle(navEl, 'height', '100%');
2121
- this.selected = null;
2122
- }, menuAnimationSpeed);
2123
- }
2042
+ postMessage(targetWindow, message, targetOrigin) {
2043
+ targetWindow.postMessage(message, targetOrigin);
2124
2044
  }
2125
2045
  /**
2126
- * Handle key presses to navigate the menu
2046
+ * Open a new window
2047
+ * @param url - The URL of the resource to be loaded
2127
2048
  */
2128
- keyNavigate(event) {
2129
- if (this.enableKeyNav && event.target === this.dropdownToggleButton?.nativeElement) {
2130
- switch (event.key) {
2131
- case 'ArrowUp':
2132
- case 'Up':
2133
- case 'ArrowDown':
2134
- case 'Down':
2135
- event.stopPropagation();
2136
- event.preventDefault();
2137
- this.moveHighlightedUpOrDown(event);
2138
- break;
2139
- case 'ArrowRight':
2140
- case 'Right':
2141
- event.stopPropagation();
2142
- event.preventDefault();
2143
- // Select the item if it has child items
2144
- if (this.highlightedItem && this.highlightedItem.items) {
2145
- this.selectItem(event, this.highlightedItem, true);
2146
- }
2147
- break;
2148
- case 'ArrowLeft':
2149
- case 'Left':
2150
- event.stopPropagation();
2151
- event.preventDefault();
2152
- // Close the menu if it is a child menu
2153
- if (this.parent) {
2154
- this.back(event);
2155
- }
2156
- break;
2157
- case ' ':
2158
- case 'Spacebar':
2159
- case 'Enter':
2160
- // Prevent 'enter' from doing whatever it does on the currently focused element
2161
- event.preventDefault();
2162
- if (this.highlightedItemIndex > -1 && this.highlightedItem) {
2163
- this.selectItem(event, this.highlightedItem, true);
2164
- // If the header is highlighted
2165
- }
2166
- else if (this.highlightedItemIndex === -1) {
2167
- // Close the menu if it's a child
2168
- if (this.parent) {
2169
- this.back(event);
2170
- }
2171
- }
2172
- break;
2173
- default:
2174
- return;
2175
- }
2176
- }
2049
+ openNew(url) {
2050
+ window.open(url, '_blank');
2177
2051
  }
2178
2052
  /**
2179
- * Scroll to the item currently marked as 'is-selected'. Wait a tick for the
2180
- * NgClassDirecitve or NavItemActiveDirective to respond to the model changes
2181
- * and update the view.
2053
+ * A wrapper around the router for changing the query params for the current url
2054
+ * without creating a new history entry or removing any existing query parameters.
2055
+ * The provided params are updated if they already exist or added to the url if they don't
2056
+ *
2057
+ * @returns a promise that resolves to true if the navigation succeeds, false if something (like a guard) blocks it.
2058
+ * In normal use, the navigation should succeed unless we use query params to block access to a route the user is already on
2182
2059
  */
2183
- scrollToSelectedItem() {
2184
- window.setTimeout(() => {
2185
- const linkSelector = `li.is-selected`;
2186
- this.scrollService.scrollToItem(`#${this.id}_list`, linkSelector);
2060
+ async modifyHistoryQueryParamsSubset(queryParams) {
2061
+ return this.router.navigate([], {
2062
+ relativeTo: this.activatedRoute,
2063
+ replaceUrl: true,
2064
+ queryParams: queryParams,
2065
+ queryParamsHandling: 'merge',
2187
2066
  });
2188
2067
  }
2189
- moveHighlightedUpOrDown(event) {
2190
- switch (event.key) {
2191
- case 'ArrowUp':
2192
- case 'Up':
2193
- if (this.highlightedItemIndex > -1) {
2194
- this.highlightedItemIndex--;
2195
- }
2196
- break;
2197
- case 'ArrowDown':
2198
- case 'Down':
2199
- if (this.highlightedItemIndex < this.selectableItems.length - 1) {
2200
- this.highlightedItemIndex++;
2201
- }
2202
- break;
2203
- default:
2204
- return;
2068
+ /**A wrapper around the default javascript confirm dialog to allow us to unit test dependent code.
2069
+ * Of course eventually we'd like to have pretty confirmations for everything, but in some cases it wasn't worth the extra time
2070
+ * so we're using this instead.
2071
+ */
2072
+ confirm(prompt) {
2073
+ return Promise.resolve(confirm(prompt));
2074
+ }
2075
+ /**
2076
+ * Close the current window or a window instance if one is provided
2077
+ * @param windowInstance - Window to close (optional)
2078
+ */
2079
+ closeWindow(windowInstance) {
2080
+ if (windowInstance) {
2081
+ windowInstance.close();
2205
2082
  }
2206
- if (this.highlightedItemIndex > -1) {
2207
- // Store the item at the current highlight index
2208
- this.highlightedItem = this.selectableItems[this.highlightedItemIndex];
2083
+ else {
2084
+ window.close();
2085
+ }
2086
+ }
2087
+ getLocation() {
2088
+ return window.location.pathname + window.location.hash;
2089
+ }
2090
+ /** Get the current value of the full url, including protocol, host and path */
2091
+ getFullUrl() {
2092
+ return window.location.href;
2093
+ }
2094
+ /** Get the current value of the base url, including protocol, domain and port (if explicitly specified) */
2095
+ getBaseUrl() {
2096
+ return window.location.origin;
2097
+ }
2098
+ /**
2099
+ * Reloads the browser window.
2100
+ * NOT RECOMMENDED. Seek other options for reloading content within Angular before resorting to this.
2101
+ */
2102
+ reloadWindow() {
2103
+ window.location.reload();
2104
+ }
2105
+ }
2106
+ WindowService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: WindowService, deps: [{ token: i1$2.Router }, { token: i1$2.ActivatedRoute }], target: i0.ɵɵFactoryTarget.Injectable });
2107
+ WindowService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: WindowService, providedIn: 'root' });
2108
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: WindowService, decorators: [{
2109
+ type: Injectable,
2110
+ args: [{
2111
+ providedIn: 'root'
2112
+ }]
2113
+ }], ctorParameters: function () { return [{ type: i1$2.Router }, { type: i1$2.ActivatedRoute }]; } });
2114
+
2115
+ class NavItemActiveDirective {
2116
+ /**
2117
+ * Determines whether the directive will try to make an exact match on the url or not
2118
+ * If false, the directive will add the active class if the first part of the url matches
2119
+ * the active route.
2120
+ * see: https://angular.io/api/router/Router#isactive
2121
+ */
2122
+ set exact(value) {
2123
+ if (value === undefined) {
2124
+ this._exact = true;
2209
2125
  }
2210
2126
  else {
2211
- this.highlightedItem = null;
2127
+ this._exact = value;
2212
2128
  }
2213
- this.scrollToHighlightedItem();
2129
+ this.update();
2214
2130
  }
2215
2131
  /**
2216
- * Scroll to the specified menu item.
2217
- * If no item is provided, it will scroll to the first item.
2218
- *
2219
- * @param item The menu item to scroll to.
2220
- * @memberof MenuComponent
2132
+ * The url of the NavItem to check for activeness. Convert the item url into a
2133
+ * UrlTree relative to the ActivatedRoute so router#isActive works even with relative urls.
2134
+ * See Angular's [routerLink](https://github.com/angular/angular/blob/8282e15c2becbe42a49befa07d6407247e8243d8/packages/router/src/directives/router_link.ts#L249)
2135
+ * and [routerLinkActive](https://github.com/angular/angular/blob/8282e15c2becbe42a49befa07d6407247e8243d8/packages/router/src/directives/router_link_active.ts#L139)
2136
+ * for a similiar implementation.
2221
2137
  */
2222
- scrollMenu(item) {
2223
- if (this.items.length > 0 && this.id) {
2224
- item = item ? item : this.items[0];
2225
- let itemId = item.id ? item.id : MenuComponent.getIndexedItemId(this.items, item, this.id);
2226
- this.scrollService.scrollItemCentered(`#${this.id}_list`, `#${itemId}`);
2138
+ set url(value) {
2139
+ if (value !== null && value !== undefined) {
2140
+ this._url = this.router.createUrlTree([value], { relativeTo: this.route, queryParams: this.queryParams });
2141
+ this.update();
2227
2142
  }
2228
2143
  }
2229
- scrollToHighlightedItem() {
2230
- this.scrollMenu(this.highlightedItem);
2144
+ constructor(router, el, renderer, route) {
2145
+ this.router = router;
2146
+ this.el = el;
2147
+ this.renderer = renderer;
2148
+ this.route = route;
2149
+ this._exact = true;
2150
+ /** Emits when the url becomes active */
2151
+ this.routerLinkActivated = new EventEmitter();
2152
+ /** Subject that emits when component is destroyed to unsubscribe from any subscriptions */
2153
+ this.destroyed = new Subject();
2154
+ }
2155
+ /** Check if url is active on NavigationEnd events */
2156
+ ngOnInit() {
2157
+ this.router.events.pipe(takeUntil(this.destroyed), filter(e => e instanceof NavigationEnd)).subscribe(() => {
2158
+ this.update();
2159
+ });
2231
2160
  }
2232
- addKeydownListener() {
2233
- // Only attempt to add the listener if keyboard nav is enabled
2234
- if (this.enableKeyNav) {
2235
- // renderer.listen adds the listener and returns a function to remove it from the renderer.
2236
- // The listener remains active until this function is called.
2237
- this.removeKeydownListener = this.renderer.listen('document', 'keydown', (event) => this.keyNavigate(event));
2238
- }
2161
+ ngOnDestroy() {
2162
+ this.destroyed.next();
2163
+ this.destroyed.unsubscribe();
2239
2164
  }
2240
- /**
2241
- * Sets the menu item ids using its index if item doesn't already have one
2242
- */
2243
- setItemIds() {
2244
- if (this.items) {
2245
- this.items.forEach((item, index) => {
2246
- item.id = item.id ? item.id : this.id + '_item' + index;
2247
- if (item.items) {
2248
- item.items.forEach((childItem, childIndex) => {
2249
- childItem.id = childItem.id ? childItem.id : this.id + '_item' + index + '-' + childIndex;
2250
- });
2251
- }
2252
- });
2165
+ /** If url is active apply the defined class to the element, otherwise remove it */
2166
+ update() {
2167
+ if (this._url && this.classValue) {
2168
+ if (this.router.isActive(this._url, { matrixParams: 'ignored', queryParams: this._exact ? 'exact' : 'subset', paths: this._exact ? 'exact' : 'subset', fragment: 'ignored' })) {
2169
+ this.renderer.addClass(this.el.nativeElement, this.classValue);
2170
+ this.routerLinkActivated.emit(new Event('routerLinkActivated'));
2171
+ }
2172
+ else {
2173
+ this.renderer.removeClass(this.el.nativeElement, this.classValue);
2174
+ }
2253
2175
  }
2254
2176
  }
2255
2177
  }
2256
- MenuComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MenuComponent, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: WindowService }, { token: ScrollService }], target: i0.ɵɵFactoryTarget.Component });
2257
- MenuComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: MenuComponent, selector: "ec-menu", inputs: { id: "id", items: "items", selected: "selected", parent: "parent", templateType: "templateType", customMenuTemplate: "customMenuTemplate", title: "title", showNoItems: "showNoItems", noDataText: "noDataText", enableKeyNav: "enableKeyNav", highlightedItem: "highlightedItem", maintainSelectedItem: "maintainSelectedItem", truncateItems: "truncateItems", preserveIconSpace: "preserveIconSpace", dropdownToggleButton: "dropdownToggleButton" }, outputs: { selectedChanged: "selectedChanged", menuClosed: "menuClosed" }, host: { properties: { "attr.id": "this.attrId" } }, viewQueries: [{ propertyName: "labelTemplate", first: true, predicate: ["label"], descendants: true, static: true }, { propertyName: "iconAndLabelTemplate", first: true, predicate: ["iconAndLabel"], descendants: true, static: true }, { propertyName: "checkAndLabelTemplate", first: true, predicate: ["checkAndLabel"], descendants: true, static: true }, { propertyName: "iconLabelCaptionTemplate", first: true, predicate: ["iconLabelCaption"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<nav>\r\n <div class=\"parent\"\r\n [class.no-data]=\"showNoItems && (!items || items.length === 0)\">\r\n <header id=\"{{id}}_header\"\r\n class=\"text-heading-3 p-1\"\r\n [class.is-selected]=\"highlightedItemIndex === -1\"\r\n *ngIf=\"parent\"\r\n (click)=\"back($event)\">\r\n <div class=\"item-wrapper\">\r\n <i class=\"ec-icon icon-angle-down rotate-90 flex-shrink\"></i>\r\n <span class=\"label text-truncate flex-grow\">{{parent?.label}}</span>\r\n </div>\r\n </header>\r\n\r\n <ul id=\"{{id}}_list\"\r\n class=\"py-1\">\r\n <ng-container *ngFor=\"let item of items; index as i\">\r\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: {$implicit: item, index: i}\"></ng-container>\r\n\r\n <!-- Show child items under parent item if the item is a heading or divided-section -->\r\n <ng-container *ngIf=\"item.items?.length && (item.display === 'heading' || item.display === 'divided-section')\">\r\n <ng-container *ngFor=\"let childItem of item.items; index as j; first as isFirst; last as isLast\"\r\n [ngTemplateOutlet]=\"itemTemplate\"\r\n [ngTemplateOutletContext]=\"{$implicit: childItem, index: i + '-' + j, isDividedSectionChild: item.display === 'divided-section', isFirst: isFirst, isLast: isLast}\">\r\n </ng-container>\r\n </ng-container>\r\n </ng-container>\r\n </ul>\r\n\r\n <p class=\"no-data-message\">{{noDataText | translate}}</p>\r\n </div>\r\n\r\n <!-- Child menu (Rendered to the right) -->\r\n <ec-menu *ngIf=\"selected?.items && selected?.display !== 'heading' && selected?.display !== 'divided-section'\"\r\n id=\"{{id}}_child\"\r\n class=\"child\"\r\n [parent]=\"selected\"\r\n [items]=\"selected?.items\"\r\n [showNoItems]=\"true\"\r\n [templateType]=\"templateType\"\r\n [enableKeyNav]=\"true\"\r\n [truncateItems]=\"truncateItems\"\r\n (selectedChanged)=\"onSelection($event)\"\r\n (menuClosed)=\"toggleChildMenu(false)\">\r\n </ec-menu>\r\n</nav>\r\n\r\n<ng-template #itemTemplate\r\n let-item\r\n let-i=\"index\"\r\n let-isDividedSectionChild=\"isDividedSectionChild\"\r\n let-isFirst=\"isFirst\"\r\n let-isLast=\"isLast\">\r\n <li *ngIf=\"!(item.hideIfNoItems && !item.items?.length) && item.display !== 'divided-section'\"\r\n id=\"{{item.id || id + '_item' + i}}\"\r\n class=\"{{item.display || 'item'}} {{item.classList}}\"\r\n [class.divider-top]=\"item.display === 'divider-top' || (isDividedSectionChild && isFirst)\"\r\n [class.divider]=\"item.display === 'divider' || (isDividedSectionChild && isLast)\"\r\n [attr.disabled]=\"item.disabled\"\r\n [hidden]=\"item.hidden\"\r\n ecNavItemActive=\"is-selected\"\r\n [ecNavItemActiveQueryParams]=\"item.queryParams\"\r\n [ecNavItemActiveUrl]=\"item.url\"\r\n [ecNavItemActiveExactMatch]='item.isActiveExactMatch'\r\n (routerLinkActivated)=\"selectItem($event, item)\"\r\n [ngClass]=\"{'is-highlighted':(selected === item && item?.display !== 'heading') || highlightedItem === item, 'is-link': item.url, 'is-disabled': item.disabled, 'is-readonly': item.readonly, 'is-checked': item.checked, 'text-heading-3': item?.display === 'heading'}\"\r\n (click)=\"selectItem($event, item)\">\r\n\r\n <a *ngIf=\"item.url && !item.externalLink\"\r\n id=\"{{item.id}}_link\"\r\n title=\"{{truncateItems ? item.label : ''}}\"\r\n class=\"item-wrapper\"\r\n [routerLink]=\"item.url\"\r\n [queryParams]=\"item.queryParams || null\"\r\n target=\"{{item.target || '_self'}}\">\r\n <ng-container *ngTemplateOutlet=\"internalizedTemplate; context: {$implicit: item}\"></ng-container>\r\n </a>\r\n\r\n <a *ngIf=\"item.url && item.externalLink\"\r\n id=\"{{item.id}}_link\"\r\n title=\"{{truncateItems ? item.label : ''}}\"\r\n class=\"item-wrapper\"\r\n href=\"{{item.url}}\"\r\n target=\"{{item.target || '_self'}}\">\r\n <ng-container *ngTemplateOutlet=\"internalizedTemplate; context: {$implicit: item}\"></ng-container>\r\n </a>\r\n\r\n <div *ngIf=\"!item.url\"\r\n title=\"{{truncateItems ? item.label : ''}}\"\r\n class=\"item-wrapper\">\r\n <ng-container *ngTemplateOutlet=\"internalizedTemplate; context: {$implicit: item}\"></ng-container>\r\n </div>\r\n </li>\r\n</ng-template>\r\n\r\n<!-- 'label' Item Template -->\r\n<ng-template #label\r\n let-item>\r\n <span id=\"{{item.id}}_label\"\r\n class=\"label\"\r\n [class.text-truncate]=\"truncateItems\">{{item.label}}</span>\r\n\r\n <i class=\"ec-icon icon-angle-down rotate-270\"\r\n *ngIf=\"item?.items && item.display !== 'heading' && item.display !== 'divided-section'\"></i>\r\n</ng-template>\r\n\r\n<!-- 'checkAndLabel' Item Template -->\r\n<ng-template #checkAndLabel\r\n let-item>\r\n\r\n <i class=\"ec-icon icon-check ec-icon-sm\"\r\n *ngIf=\"item.display !== 'heading'\"></i>\r\n\r\n <i class=\"ec-icon {{item.icon}} ml-2\"\r\n *ngIf=\"item.icon\"></i>\r\n\r\n <span id=\"{{item.id}}_label\"\r\n class=\"label\"\r\n [class.text-truncate]=\"truncateItems\">{{item.label}}</span>\r\n\r\n <i class=\"ec-icon icon-angle-down rotate-270\"\r\n *ngIf=\"item?.items && item.display !== 'heading' && item.display !== 'divided-section'\"></i>\r\n</ng-template>\r\n\r\n<!-- 'iconAndLabel' Item Template -->\r\n<ng-template #iconAndLabel\r\n let-item>\r\n <!-- If menuItem.icon exists and is not blank, show the icon in the menu -->\r\n <i class=\"ec-icon {{item.icon}}\"\r\n *ngIf=\"(item.icon && item.icon !== '') || preserveIconSpace\"></i>\r\n\r\n <span id=\"{{item.id}}_label\"\r\n class=\"label\"\r\n [class.text-truncate]=\"truncateItems\">{{item.label}}</span>\r\n\r\n <i class=\"ec-icon icon-angle-down rotate-270\"\r\n *ngIf=\"item?.items && item.display !== 'heading' && item.display !== 'divided-section'\"></i>\r\n</ng-template>\r\n\r\n<ng-template #iconLabelCaption\r\n let-item>\r\n <i class=\"ec-icon {{item.icon}}\"\r\n *ngIf=\"(item.icon && item.icon !== '') || preserveIconSpace\"></i>\r\n <div *ngIf=\"item.display !== 'heading'\"\r\n class=\"label flex-grow\">\r\n <div id=\"{{item.id}}_label\"\r\n class=\"text-body-1\"\r\n [class.text-truncate]=\"truncateItems\">{{item.label}}</div>\r\n <div id=\"{{item.id}}_caption\"\r\n *ngIf=\"item.caption\"\r\n class=\"text-caption-1\"\r\n [class.text-truncate]=\"truncateItems\">{{item.caption}}</div>\r\n </div>\r\n <h3 *ngIf=\"item.display === 'heading'\"\r\n class=\"flex-grow text-heading-3 align-self-center\"\r\n [class.text-truncate]=\"truncateItems\">{{item.label}}</h3>\r\n <i class=\"ec-icon icon-angle-down rotate-270 align-self-center\"\r\n *ngIf=\"item?.items && item.display !== 'heading' && item.display !== 'divided-section'\"></i>\r\n</ng-template>", styles: ["@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(1turn)}}:host{display:block;font-size:var(--ec-menu-font-size, var(--ec-font-size-action));font-weight:400;background-color:var(--ec-menu-background-color, var(--ec-background-color))}:host.open>nav>.parent,:host.open>nav>.child{position:absolute;left:0;top:0;right:0;height:100%;transition:transform .25s ease}:host.open>nav>.parent{transform:translate(0)}:host.open>nav>.child{transform:translate(100%)}:host.open-active>nav>.parent{transform:translate(-100%)}:host.open-active>nav>.child{transform:translate(0)}:host(.bg-transparent){background-color:transparent}:host-context(.is-always-open){height:100%}:host-context(.is-always-open) .item-wrapper{padding-left:1rem;padding-right:1rem}nav{display:flex;position:relative;height:100%;overflow:hidden}.parent{display:flex;flex-direction:column;flex:auto;position:relative;max-width:100%}.parent>header{cursor:pointer}.parent>header.is-selected .item-wrapper,.parent>header.is-highlighted .item-wrapper{background-color:var(--ec-background-color-selected)}.parent>header:hover .item-wrapper{background-color:var(--ec-background-color-hover)}.parent.no-data ul{display:none}.parent.no-data .no-data-message{display:block}ul{padding:0;margin:0;list-style:none;flex:auto;height:100%;overflow-y:auto}ul li{cursor:pointer;padding:0 .25rem}ul li a{color:inherit;border-bottom:0;text-decoration:none}ul li.is-selected .item-wrapper,ul li.is-highlighted .item-wrapper{background-color:var(--ec-background-color-selected)}ul li:hover .item-wrapper{background-color:var(--ec-background-color-hover)}ul li:focus .item-wrapper{outline:none;background-color:var(--ec-color-disabled-dark);position:relative;z-index:1}ul li.is-disabled .item-wrapper{color:var(--ec-color-disabled-dark);opacity:var(--ec-form-control-opacity-disabled)}ul li.is-disabled,ul li.is-readonly{cursor:default}ul li.is-disabled .item-wrapper,ul li.is-readonly .item-wrapper{background-color:transparent;color:inherit}ul li.is-checked .icon-check{opacity:1}ul li.heading{cursor:default}ul li.heading .item-wrapper{background-color:transparent}ul li.heading:not(:first-child){margin-top:.5rem}ul li.divider:not(:last-child){border-bottom:1px solid var(--ec-border-color);padding-bottom:.25rem;margin-bottom:.25rem}ul li.divider-top:not(:first-child):not(.divider + .divider-top){border-top:1px solid var(--ec-border-color);padding-top:.25rem;margin-top:.25rem}ul li.indent-1 .item-wrapper{padding-left:1.5rem}ul li.indent-2 .item-wrapper{padding-left:2.5rem}ul li.indent-3 .item-wrapper{padding-left:3.5rem}.item-wrapper{cursor:inherit;line-height:1.25rem;min-height:1.75rem;padding:.25rem .5rem;border-radius:var(--ec-border-radius);display:flex;color:inherit}.item-wrapper .label{margin-right:auto}.item-wrapper .label+.ec-icon{margin-left:.5rem}.item-wrapper .ec-icon{margin-top:calc((1.25rem - var(--ec-font-size-icon)) / 2);flex:none}.item-wrapper .ec-icon+.label{margin-left:.5rem}.item-wrapper .ec-icon-sm{margin-top:calc((1.25rem - calc(var(--ec-font-size-icon) * .75)) / 2)}.item-wrapper .icon-check{opacity:0}.no-data-message{display:none;text-align:center;padding:1rem;color:var(--ec-color-hint-dark);margin-bottom:0;font-size:var(--ec-font-size-body)}:host-context(ec-tree) ul{overflow-x:hidden}:host-context(ec-tree) li.is-selected,:host-context(ec-tree) li.is-highlighted{font-weight:700;color:var(--ec-menu-color-highlighted, inherit)}:host-context(ec-tree) li.is-selected:not(:hover),:host-context(ec-tree) li.is-highlighted:not(:hover){background-color:transparent}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: NavItemActiveDirective, selector: "[ecNavItemActive]", inputs: ["ecNavItemActive", "ecNavItemActiveExactMatch", "ecNavItemActiveQueryParams", "ecNavItemActiveUrl"], outputs: ["routerLinkActivated"] }, { kind: "component", type: MenuComponent, selector: "ec-menu", inputs: ["id", "items", "selected", "parent", "templateType", "customMenuTemplate", "title", "showNoItems", "noDataText", "enableKeyNav", "highlightedItem", "maintainSelectedItem", "truncateItems", "preserveIconSpace", "dropdownToggleButton"], outputs: ["selectedChanged", "menuClosed"] }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }] });
2258
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MenuComponent, decorators: [{
2259
- type: Component,
2260
- args: [{ selector: 'ec-menu', template: "<nav>\r\n <div class=\"parent\"\r\n [class.no-data]=\"showNoItems && (!items || items.length === 0)\">\r\n <header id=\"{{id}}_header\"\r\n class=\"text-heading-3 p-1\"\r\n [class.is-selected]=\"highlightedItemIndex === -1\"\r\n *ngIf=\"parent\"\r\n (click)=\"back($event)\">\r\n <div class=\"item-wrapper\">\r\n <i class=\"ec-icon icon-angle-down rotate-90 flex-shrink\"></i>\r\n <span class=\"label text-truncate flex-grow\">{{parent?.label}}</span>\r\n </div>\r\n </header>\r\n\r\n <ul id=\"{{id}}_list\"\r\n class=\"py-1\">\r\n <ng-container *ngFor=\"let item of items; index as i\">\r\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: {$implicit: item, index: i}\"></ng-container>\r\n\r\n <!-- Show child items under parent item if the item is a heading or divided-section -->\r\n <ng-container *ngIf=\"item.items?.length && (item.display === 'heading' || item.display === 'divided-section')\">\r\n <ng-container *ngFor=\"let childItem of item.items; index as j; first as isFirst; last as isLast\"\r\n [ngTemplateOutlet]=\"itemTemplate\"\r\n [ngTemplateOutletContext]=\"{$implicit: childItem, index: i + '-' + j, isDividedSectionChild: item.display === 'divided-section', isFirst: isFirst, isLast: isLast}\">\r\n </ng-container>\r\n </ng-container>\r\n </ng-container>\r\n </ul>\r\n\r\n <p class=\"no-data-message\">{{noDataText | translate}}</p>\r\n </div>\r\n\r\n <!-- Child menu (Rendered to the right) -->\r\n <ec-menu *ngIf=\"selected?.items && selected?.display !== 'heading' && selected?.display !== 'divided-section'\"\r\n id=\"{{id}}_child\"\r\n class=\"child\"\r\n [parent]=\"selected\"\r\n [items]=\"selected?.items\"\r\n [showNoItems]=\"true\"\r\n [templateType]=\"templateType\"\r\n [enableKeyNav]=\"true\"\r\n [truncateItems]=\"truncateItems\"\r\n (selectedChanged)=\"onSelection($event)\"\r\n (menuClosed)=\"toggleChildMenu(false)\">\r\n </ec-menu>\r\n</nav>\r\n\r\n<ng-template #itemTemplate\r\n let-item\r\n let-i=\"index\"\r\n let-isDividedSectionChild=\"isDividedSectionChild\"\r\n let-isFirst=\"isFirst\"\r\n let-isLast=\"isLast\">\r\n <li *ngIf=\"!(item.hideIfNoItems && !item.items?.length) && item.display !== 'divided-section'\"\r\n id=\"{{item.id || id + '_item' + i}}\"\r\n class=\"{{item.display || 'item'}} {{item.classList}}\"\r\n [class.divider-top]=\"item.display === 'divider-top' || (isDividedSectionChild && isFirst)\"\r\n [class.divider]=\"item.display === 'divider' || (isDividedSectionChild && isLast)\"\r\n [attr.disabled]=\"item.disabled\"\r\n [hidden]=\"item.hidden\"\r\n ecNavItemActive=\"is-selected\"\r\n [ecNavItemActiveQueryParams]=\"item.queryParams\"\r\n [ecNavItemActiveUrl]=\"item.url\"\r\n [ecNavItemActiveExactMatch]='item.isActiveExactMatch'\r\n (routerLinkActivated)=\"selectItem($event, item)\"\r\n [ngClass]=\"{'is-highlighted':(selected === item && item?.display !== 'heading') || highlightedItem === item, 'is-link': item.url, 'is-disabled': item.disabled, 'is-readonly': item.readonly, 'is-checked': item.checked, 'text-heading-3': item?.display === 'heading'}\"\r\n (click)=\"selectItem($event, item)\">\r\n\r\n <a *ngIf=\"item.url && !item.externalLink\"\r\n id=\"{{item.id}}_link\"\r\n title=\"{{truncateItems ? item.label : ''}}\"\r\n class=\"item-wrapper\"\r\n [routerLink]=\"item.url\"\r\n [queryParams]=\"item.queryParams || null\"\r\n target=\"{{item.target || '_self'}}\">\r\n <ng-container *ngTemplateOutlet=\"internalizedTemplate; context: {$implicit: item}\"></ng-container>\r\n </a>\r\n\r\n <a *ngIf=\"item.url && item.externalLink\"\r\n id=\"{{item.id}}_link\"\r\n title=\"{{truncateItems ? item.label : ''}}\"\r\n class=\"item-wrapper\"\r\n href=\"{{item.url}}\"\r\n target=\"{{item.target || '_self'}}\">\r\n <ng-container *ngTemplateOutlet=\"internalizedTemplate; context: {$implicit: item}\"></ng-container>\r\n </a>\r\n\r\n <div *ngIf=\"!item.url\"\r\n title=\"{{truncateItems ? item.label : ''}}\"\r\n class=\"item-wrapper\">\r\n <ng-container *ngTemplateOutlet=\"internalizedTemplate; context: {$implicit: item}\"></ng-container>\r\n </div>\r\n </li>\r\n</ng-template>\r\n\r\n<!-- 'label' Item Template -->\r\n<ng-template #label\r\n let-item>\r\n <span id=\"{{item.id}}_label\"\r\n class=\"label\"\r\n [class.text-truncate]=\"truncateItems\">{{item.label}}</span>\r\n\r\n <i class=\"ec-icon icon-angle-down rotate-270\"\r\n *ngIf=\"item?.items && item.display !== 'heading' && item.display !== 'divided-section'\"></i>\r\n</ng-template>\r\n\r\n<!-- 'checkAndLabel' Item Template -->\r\n<ng-template #checkAndLabel\r\n let-item>\r\n\r\n <i class=\"ec-icon icon-check ec-icon-sm\"\r\n *ngIf=\"item.display !== 'heading'\"></i>\r\n\r\n <i class=\"ec-icon {{item.icon}} ml-2\"\r\n *ngIf=\"item.icon\"></i>\r\n\r\n <span id=\"{{item.id}}_label\"\r\n class=\"label\"\r\n [class.text-truncate]=\"truncateItems\">{{item.label}}</span>\r\n\r\n <i class=\"ec-icon icon-angle-down rotate-270\"\r\n *ngIf=\"item?.items && item.display !== 'heading' && item.display !== 'divided-section'\"></i>\r\n</ng-template>\r\n\r\n<!-- 'iconAndLabel' Item Template -->\r\n<ng-template #iconAndLabel\r\n let-item>\r\n <!-- If menuItem.icon exists and is not blank, show the icon in the menu -->\r\n <i class=\"ec-icon {{item.icon}}\"\r\n *ngIf=\"(item.icon && item.icon !== '') || preserveIconSpace\"></i>\r\n\r\n <span id=\"{{item.id}}_label\"\r\n class=\"label\"\r\n [class.text-truncate]=\"truncateItems\">{{item.label}}</span>\r\n\r\n <i class=\"ec-icon icon-angle-down rotate-270\"\r\n *ngIf=\"item?.items && item.display !== 'heading' && item.display !== 'divided-section'\"></i>\r\n</ng-template>\r\n\r\n<ng-template #iconLabelCaption\r\n let-item>\r\n <i class=\"ec-icon {{item.icon}}\"\r\n *ngIf=\"(item.icon && item.icon !== '') || preserveIconSpace\"></i>\r\n <div *ngIf=\"item.display !== 'heading'\"\r\n class=\"label flex-grow\">\r\n <div id=\"{{item.id}}_label\"\r\n class=\"text-body-1\"\r\n [class.text-truncate]=\"truncateItems\">{{item.label}}</div>\r\n <div id=\"{{item.id}}_caption\"\r\n *ngIf=\"item.caption\"\r\n class=\"text-caption-1\"\r\n [class.text-truncate]=\"truncateItems\">{{item.caption}}</div>\r\n </div>\r\n <h3 *ngIf=\"item.display === 'heading'\"\r\n class=\"flex-grow text-heading-3 align-self-center\"\r\n [class.text-truncate]=\"truncateItems\">{{item.label}}</h3>\r\n <i class=\"ec-icon icon-angle-down rotate-270 align-self-center\"\r\n *ngIf=\"item?.items && item.display !== 'heading' && item.display !== 'divided-section'\"></i>\r\n</ng-template>", styles: ["@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(1turn)}}:host{display:block;font-size:var(--ec-menu-font-size, var(--ec-font-size-action));font-weight:400;background-color:var(--ec-menu-background-color, var(--ec-background-color))}:host.open>nav>.parent,:host.open>nav>.child{position:absolute;left:0;top:0;right:0;height:100%;transition:transform .25s ease}:host.open>nav>.parent{transform:translate(0)}:host.open>nav>.child{transform:translate(100%)}:host.open-active>nav>.parent{transform:translate(-100%)}:host.open-active>nav>.child{transform:translate(0)}:host(.bg-transparent){background-color:transparent}:host-context(.is-always-open){height:100%}:host-context(.is-always-open) .item-wrapper{padding-left:1rem;padding-right:1rem}nav{display:flex;position:relative;height:100%;overflow:hidden}.parent{display:flex;flex-direction:column;flex:auto;position:relative;max-width:100%}.parent>header{cursor:pointer}.parent>header.is-selected .item-wrapper,.parent>header.is-highlighted .item-wrapper{background-color:var(--ec-background-color-selected)}.parent>header:hover .item-wrapper{background-color:var(--ec-background-color-hover)}.parent.no-data ul{display:none}.parent.no-data .no-data-message{display:block}ul{padding:0;margin:0;list-style:none;flex:auto;height:100%;overflow-y:auto}ul li{cursor:pointer;padding:0 .25rem}ul li a{color:inherit;border-bottom:0;text-decoration:none}ul li.is-selected .item-wrapper,ul li.is-highlighted .item-wrapper{background-color:var(--ec-background-color-selected)}ul li:hover .item-wrapper{background-color:var(--ec-background-color-hover)}ul li:focus .item-wrapper{outline:none;background-color:var(--ec-color-disabled-dark);position:relative;z-index:1}ul li.is-disabled .item-wrapper{color:var(--ec-color-disabled-dark);opacity:var(--ec-form-control-opacity-disabled)}ul li.is-disabled,ul li.is-readonly{cursor:default}ul li.is-disabled .item-wrapper,ul li.is-readonly .item-wrapper{background-color:transparent;color:inherit}ul li.is-checked .icon-check{opacity:1}ul li.heading{cursor:default}ul li.heading .item-wrapper{background-color:transparent}ul li.heading:not(:first-child){margin-top:.5rem}ul li.divider:not(:last-child){border-bottom:1px solid var(--ec-border-color);padding-bottom:.25rem;margin-bottom:.25rem}ul li.divider-top:not(:first-child):not(.divider + .divider-top){border-top:1px solid var(--ec-border-color);padding-top:.25rem;margin-top:.25rem}ul li.indent-1 .item-wrapper{padding-left:1.5rem}ul li.indent-2 .item-wrapper{padding-left:2.5rem}ul li.indent-3 .item-wrapper{padding-left:3.5rem}.item-wrapper{cursor:inherit;line-height:1.25rem;min-height:1.75rem;padding:.25rem .5rem;border-radius:var(--ec-border-radius);display:flex;color:inherit}.item-wrapper .label{margin-right:auto}.item-wrapper .label+.ec-icon{margin-left:.5rem}.item-wrapper .ec-icon{margin-top:calc((1.25rem - var(--ec-font-size-icon)) / 2);flex:none}.item-wrapper .ec-icon+.label{margin-left:.5rem}.item-wrapper .ec-icon-sm{margin-top:calc((1.25rem - calc(var(--ec-font-size-icon) * .75)) / 2)}.item-wrapper .icon-check{opacity:0}.no-data-message{display:none;text-align:center;padding:1rem;color:var(--ec-color-hint-dark);margin-bottom:0;font-size:var(--ec-font-size-body)}:host-context(ec-tree) ul{overflow-x:hidden}:host-context(ec-tree) li.is-selected,:host-context(ec-tree) li.is-highlighted{font-weight:700;color:var(--ec-menu-color-highlighted, inherit)}:host-context(ec-tree) li.is-selected:not(:hover),:host-context(ec-tree) li.is-highlighted:not(:hover){background-color:transparent}\n"] }]
2261
- }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: WindowService }, { type: ScrollService }]; }, propDecorators: { id: [{
2262
- type: Input
2263
- }], attrId: [{
2264
- type: HostBinding,
2265
- args: ['attr.id']
2266
- }], items: [{
2267
- type: Input
2268
- }], selected: [{
2269
- type: Input
2270
- }], parent: [{
2271
- type: Input
2272
- }], templateType: [{
2273
- type: Input
2274
- }], customMenuTemplate: [{
2275
- type: Input
2276
- }], title: [{
2277
- type: Input
2278
- }], showNoItems: [{
2279
- type: Input
2280
- }], noDataText: [{
2281
- type: Input
2282
- }], enableKeyNav: [{
2283
- type: Input
2284
- }], highlightedItem: [{
2285
- type: Input
2286
- }], maintainSelectedItem: [{
2287
- type: Input
2288
- }], truncateItems: [{
2289
- type: Input
2290
- }], preserveIconSpace: [{
2291
- type: Input
2292
- }], dropdownToggleButton: [{
2293
- type: Input
2294
- }], selectedChanged: [{
2295
- type: Output
2296
- }], menuClosed: [{
2178
+ NavItemActiveDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NavItemActiveDirective, deps: [{ token: i1$2.Router }, { token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i1$2.ActivatedRoute }], target: i0.ɵɵFactoryTarget.Directive });
2179
+ NavItemActiveDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: NavItemActiveDirective, selector: "[ecNavItemActive]", inputs: { classValue: ["ecNavItemActive", "classValue"], exact: ["ecNavItemActiveExactMatch", "exact"], queryParams: ["ecNavItemActiveQueryParams", "queryParams"], url: ["ecNavItemActiveUrl", "url"] }, outputs: { routerLinkActivated: "routerLinkActivated" }, ngImport: i0 });
2180
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NavItemActiveDirective, decorators: [{
2181
+ type: Directive,
2182
+ args: [{
2183
+ selector: '[ecNavItemActive]'
2184
+ }]
2185
+ }], ctorParameters: function () { return [{ type: i1$2.Router }, { type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i1$2.ActivatedRoute }]; }, propDecorators: { classValue: [{
2186
+ type: Input,
2187
+ args: ['ecNavItemActive']
2188
+ }], exact: [{
2189
+ type: Input,
2190
+ args: ['ecNavItemActiveExactMatch']
2191
+ }], queryParams: [{
2192
+ type: Input,
2193
+ args: ['ecNavItemActiveQueryParams']
2194
+ }], url: [{
2195
+ type: Input,
2196
+ args: ['ecNavItemActiveUrl']
2197
+ }], routerLinkActivated: [{
2297
2198
  type: Output
2298
- }], labelTemplate: [{
2299
- type: ViewChild,
2300
- args: ['label', { static: true }]
2301
- }], iconAndLabelTemplate: [{
2302
- type: ViewChild,
2303
- args: ['iconAndLabel', { static: true }]
2304
- }], checkAndLabelTemplate: [{
2305
- type: ViewChild,
2306
- args: ['checkAndLabel', { static: true }]
2307
- }], iconLabelCaptionTemplate: [{
2308
- type: ViewChild,
2309
- args: ['iconLabelCaption', { static: true }]
2310
2199
  }] } });
2311
2200
 
2201
+ ;
2202
+ const menuAnimationSpeed = 350;
2312
2203
  /**
2313
- * Primitive directive that popups a container using PopperJS
2204
+ * Primitive Menu component that encapsulates known templates
2314
2205
  *
2315
2206
  * @export
2316
2207
  */
2317
- class PopupContainerDirective {
2318
- /**
2319
- * Creates an instance of PopupContainerDirective.
2320
- * @param templateRef Reference to the popup template
2321
- * @param viewContainer Reference to the view container
2322
- * @param document Reference to Document
2323
- * @memberof PopupContainerDirective
2324
- */
2325
- constructor(templateRef, viewContainer, document, renderer) {
2326
- this.templateRef = templateRef;
2327
- this.viewContainer = viewContainer;
2328
- this.document = document;
2208
+ class MenuComponent {
2209
+ constructor(el, renderer, windowService, scrollService) {
2210
+ this.el = el;
2329
2211
  this.renderer = renderer;
2212
+ this.windowService = windowService;
2213
+ this.scrollService = scrollService;
2330
2214
  /**
2331
- * Emit the {@link PopupStatus} when it changes
2215
+ * Array of items to display
2216
+ *
2217
+ * @memberof MenuComponent
2332
2218
  */
2333
- this.popperStatusChange = new EventEmitter();
2334
- }
2335
- /**
2336
- * Angular onInit lifecycle hook
2337
- * @see https://angular.io/guide/lifecycle-hooks
2338
- */
2339
- ngOnInit() {
2340
- this.templateViewRef = this.viewContainer.createEmbeddedView(this.templateRef);
2341
- }
2342
- /**
2343
- * Angular onDestroy lifecycle hook. Close and delete references. Unsubscribe observables
2344
- * @see https://angular.io/guide/lifecycle-hooks
2345
- */
2346
- ngOnDestroy() {
2347
- this.hide();
2219
+ this.items = [];
2220
+ /**
2221
+ * Selected item; annotates the item
2222
+ * when displayed with 'selected' class
2223
+ *
2224
+ * @memberof MenuComponent
2225
+ */
2226
+ this.selected = null;
2227
+ /**
2228
+ * Display template
2229
+ *
2230
+ * @memberof MenuComponent
2231
+ */
2232
+ this.templateType = 'label';
2233
+ /**
2234
+ * Show message when there are no items
2235
+ */
2236
+ this.showNoItems = false;
2237
+ /**
2238
+ * Text to show when menu is empty and showNoItems is true
2239
+ */
2240
+ this.noDataText = 'NoItems_TC';
2241
+ /**
2242
+ * Controls whether keyboard navigation is enabled
2243
+ */
2244
+ this.enableKeyNav = false;
2245
+ /**
2246
+ * Item currently highlighted by keyboard navigation
2247
+ */
2248
+ this.highlightedItem = null;
2249
+ /**
2250
+ * Tells the menu to maintain the selected/lastSelected item. Turning this off is useful for
2251
+ * action type menus that are displayed on the screen at all times and you do not
2252
+ * want the item to be selected when clicked.
2253
+ */
2254
+ this.maintainSelectedItem = true;
2255
+ /**
2256
+ * Will prevent text-wrapping of menu items and truncate instead. Also turns on a tooltip for the menu item. Default: false;
2257
+ */
2258
+ this.truncateItems = false;
2259
+ /**
2260
+ * When true, the space for the icon is preserved for menu items that do not have icons.
2261
+ * Only applicable for iconAndLabel menus.
2262
+ */
2263
+ this.preserveIconSpace = false;
2264
+ /**
2265
+ * Emitted when `selected` is changed. Emits the referenced object.
2266
+ *
2267
+ * @memberof MenuComponent
2268
+ */
2269
+ this.selectedChanged = new EventEmitter();
2270
+ /**
2271
+ * Emitted when the menu has a parent and back is clicked
2272
+ * @memberof MenuComponent
2273
+ */
2274
+ this.menuClosed = new EventEmitter();
2275
+ /**
2276
+ * Index of the item currently highlighted using keyboard nav
2277
+ */
2278
+ this.highlightedItemIndex = -1;
2279
+ /**
2280
+ * Last item this.selected was set to via selectItem().
2281
+ * This isn't necessarily the same as this.selected, because this.selected is an input property
2282
+ * and could have been changed by a consumer through some means other than selectItem().
2283
+ * This allows us to prevent double-calls to selectItem() with the same input.
2284
+ */
2285
+ this.lastSelected = null;
2348
2286
  }
2349
2287
  /**
2350
- * Displays the templateRef as a popup
2288
+ * Sets & displays the interalized template based on
2289
+ * the set template.
2290
+ * @see { @link https://angular.io/guide/lifecycle-hooks|Angular Lifecycle Hooks}
2351
2291
  *
2352
- * @memberof PopupContainerDirective
2292
+ * @memberof MenuComponent
2353
2293
  */
2354
- show() {
2355
- if (PopupContainerDirective.GlobalPopupRef) {
2356
- if (PopupContainerDirective.GlobalPopupRef != this) {
2357
- PopupContainerDirective.GlobalPopupRef.hide();
2358
- PopupContainerDirective.GlobalPopupRef = undefined;
2359
- }
2294
+ ngAfterContentInit() {
2295
+ switch (this.templateType) {
2296
+ case ("label"):
2297
+ this.internalizedTemplate = this.iconAndLabelTemplate;
2298
+ break;
2299
+ case ("iconAndLabel"):
2300
+ this.internalizedTemplate = this.iconAndLabelTemplate;
2301
+ break;
2302
+ case ("checkAndLabel"):
2303
+ this.internalizedTemplate = this.checkAndLabelTemplate;
2304
+ break;
2305
+ case ("iconLabelCaption"):
2306
+ this.internalizedTemplate = this.iconLabelCaptionTemplate;
2307
+ break;
2308
+ default:
2309
+ throw new Error(`Invalid templateType for MenuComponent. Please use either: 'label', 'iconAndLabel' or 'checkAndLabel'`);
2360
2310
  }
2361
- if (!this.globalCloseSubscription) {
2362
- this.globalCloseSubscription = fromEvent(this.document.body, "click").subscribe((event) => {
2363
- this.hide();
2364
- });
2311
+ //if the consumer provided a menuItemTemplate, override the internalizedTemplate with that.
2312
+ if (this.customMenuTemplate) {
2313
+ this.internalizedTemplate = this.customMenuTemplate;
2314
+ }
2315
+ if (this.id) {
2316
+ this.attrId = this.id;
2365
2317
  }
2366
- if (!this.popperRef) {
2367
- // Add the popper template as an embedded view since PopperJS
2368
- // manipulates DOM elements.
2369
- this.popupViewRef = this.viewContainer.createEmbeddedView(this.popup);
2370
- // Since popper needs real DOM elements, grab the first non-comment
2371
- // DOM element to use as our anchor.
2372
- let anchorElement = this.popupViewRef.rootNodes.find(elem => { return elem.nodeName !== "#text"; });
2373
- // Use the parents elements as our DOM elements to Popper
2374
- this.popperRef = new Popper(this.templateViewRef.rootNodes[0], anchorElement, this.popperOptions);
2375
- PopupContainerDirective.GlobalPopupRef = this;
2376
- this.popperStatusChange.emit('visible');
2318
+ this.setItemIds();
2319
+ if (this.highlightedItem && this.items.length) {
2320
+ this.highlightedItemIndex = this.items.findIndex(item => { return this.highlightedItem === item; });
2377
2321
  }
2322
+ this.addKeydownListener();
2378
2323
  }
2379
- /**
2380
- * Hides the templateRef
2381
- *
2382
- * @memberof PopupContainerDirective
2383
- */
2384
- hide() {
2385
- if (this.globalCloseSubscription) {
2386
- this.globalCloseSubscription.unsubscribe();
2387
- this.globalCloseSubscription = undefined;
2388
- }
2389
- if (this.popperRef && this.popupViewRef) {
2390
- this.popupViewRef.destroy();
2391
- this.popperRef.destroy();
2392
- this.popperRef = undefined;
2393
- this.popperStatusChange.emit('hidden');
2324
+ ngOnDestroy() {
2325
+ // Remove the listener when the component is destroyed
2326
+ if (this.removeKeydownListener) {
2327
+ this.removeKeydownListener();
2394
2328
  }
2395
2329
  }
2396
2330
  /**
2397
- * Updates the popup container position
2331
+ * When a menu item is selected, open a child menu if the item has items, call
2332
+ * the item's click method if defined, or emit the selected item.
2333
+ *
2334
+ * @param item The selected item
2335
+ * @memberof MenuComponent
2398
2336
  */
2399
- update() {
2400
- if (this.popperRef) {
2401
- this.popperRef.update();
2402
- }
2403
- }
2404
- fixPosition(minWidthNone, appendToBody = false) {
2405
- if (this.popperRef && this.popperRef['reference'] && this.popperRef['popper']) {
2406
- let popupEl = this.popperRef['popper'];
2407
- // Reset width style previously assigned because the content may have
2408
- // changed and the auto width would be different
2409
- this.renderer.removeStyle(popupEl, 'width');
2410
- this.renderer.setStyle(popupEl, 'position', 'fixed');
2411
- if (appendToBody) {
2412
- const bodyEl = this.document.querySelector('body');
2413
- const popupParent = this.renderer.parentNode(popupEl);
2414
- if (popupParent !== bodyEl) {
2415
- this.renderer.appendChild(bodyEl, popupEl);
2337
+ selectItem(event, item, isKeyEvent) {
2338
+ event.stopPropagation();
2339
+ //In the case that the user clicks an item, selectItem() will be called from the click handler
2340
+ //and through onRouterLinkActivated. Only one of these will make it through this if statement
2341
+ //because the first one will set this.lastSelected = item.
2342
+ if (!item.disabled && !item.readonly && this.lastSelected !== item) {
2343
+ if (!item.url) {
2344
+ if (item.onClick) {
2345
+ item.onClick(item, false);
2346
+ }
2347
+ if (item.items) {
2348
+ this.toggleChildMenu(true);
2349
+ }
2350
+ else {
2351
+ this.onSelection(item);
2416
2352
  }
2353
+ // We need to manually handle the url navigation if the keyboard was used
2417
2354
  }
2418
- let toggleEl = this.popperRef['reference'];
2419
- let width = popupEl.offsetWidth;
2420
- let boundaries = popupEl.getBoundingClientRect();
2421
- let left = boundaries.left;
2422
- let coords = toggleEl.getBoundingClientRect();
2423
- // Set the top of our menu to the bottom of the toggle element
2424
- let top = coords.bottom;
2425
- if (this.popperOptions && this.popperOptions.placement) {
2426
- if (this.popperOptions.placement === 'bottom-start' || this.popperOptions.placement === 'top-start') {
2427
- left = coords.left;
2355
+ else if (isKeyEvent || event.target?.tagName === 'LI') {
2356
+ if (item.target) {
2357
+ window.open(item.url, item.target);
2428
2358
  }
2429
2359
  else {
2430
- left = coords.right - ((minWidthNone || width > coords.width) ? width : coords.width);
2360
+ this.windowService.navigateToUrl(item.url);
2431
2361
  }
2362
+ // Emit so upstream components know an item was selected
2363
+ this.onSelection(item);
2432
2364
  }
2433
- // if it won't fit (with 10px space before hitting the window edge), flip it
2434
- if (boundaries.height + top + 10 > window.innerHeight) {
2435
- top = coords.top - boundaries.height;
2365
+ else {
2366
+ this.onSelection(item);
2436
2367
  }
2437
- this.renderer.setStyle(popupEl, 'transform', 'none');
2438
- this.renderer.setStyle(popupEl, 'left', left + 'px');
2439
- this.renderer.setStyle(popupEl, 'top', top + 'px');
2440
- this.renderer.setStyle(popupEl, 'width', width + 'px');
2441
- if (!minWidthNone) {
2442
- this.renderer.setStyle(popupEl, 'min-width', coords.width + 'px');
2368
+ if (this.maintainSelectedItem) {
2369
+ this.selected = item;
2370
+ this.lastSelected = item;
2371
+ }
2372
+ else {
2373
+ this.selected = null;
2374
+ this.lastSelected = null;
2443
2375
  }
2444
2376
  }
2445
2377
  }
2446
- }
2447
- /**
2448
- * Global reference to the currently displayed popup; only
2449
- * one popup directive can be in `show` state at a given time.
2450
- *
2451
- * @memberof PopupContainerDirective
2452
- */
2453
- PopupContainerDirective.GlobalPopupRef = undefined;
2454
- PopupContainerDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: PopupContainerDirective, deps: [{ token: i0.TemplateRef }, { token: i0.ViewContainerRef }, { token: DOCUMENT }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive });
2455
- PopupContainerDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: PopupContainerDirective, selector: "[ecPopup]", inputs: { popup: ["ecPopup", "popup"], popperOptions: ["options", "popperOptions"] }, ngImport: i0 });
2456
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: PopupContainerDirective, decorators: [{
2457
- type: Directive,
2458
- args: [{ selector: '[ecPopup]' }]
2459
- }], ctorParameters: function () { return [{ type: i0.TemplateRef }, { type: i0.ViewContainerRef }, { type: undefined, decorators: [{
2460
- type: Inject,
2461
- args: [DOCUMENT]
2462
- }] }, { type: i0.Renderer2 }]; }, propDecorators: { popup: [{
2463
- type: Input,
2464
- args: ['ecPopup']
2465
- }], popperOptions: [{
2466
- type: Input,
2467
- args: ['options']
2468
- }] } });
2469
-
2470
- /** Advanced validation for textbox form controls */
2471
- const textboxValidation = (validatorParams) => {
2472
- return (control) => {
2473
- let validators = [];
2474
- // Innocent until proven guilty
2475
- validatorParams.valid = true;
2476
- if (validatorParams.required) {
2477
- validators.push(Validators.required);
2478
- }
2479
- if (validatorParams.minLength !== undefined) {
2480
- validators.push(Validators.minLength(validatorParams.minLength));
2481
- }
2482
- if (validatorParams.maxLength !== undefined) {
2483
- validators.push(Validators.maxLength(validatorParams.maxLength));
2378
+ /**
2379
+ * Close the current menu and open the parent menu
2380
+ * @memberof MenuComponent
2381
+ */
2382
+ back(event) {
2383
+ event.stopPropagation();
2384
+ if (this.parent && this.parent.onClick) {
2385
+ this.parent.onClick(null, true);
2484
2386
  }
2485
- if (validatorParams.pattern !== undefined) {
2486
- validators.push(Validators.pattern(validatorParams.pattern));
2387
+ this.menuClosed.emit();
2388
+ }
2389
+ /**
2390
+ * Emit the selected item
2391
+ * @param item The selected item
2392
+ */
2393
+ onSelection(item) {
2394
+ if (item.display !== 'heading') {
2395
+ this.selectedChanged.emit(item);
2487
2396
  }
2488
- validators.forEach(validator => {
2489
- let validationResult = validator(control);
2490
- if (validationResult) {
2491
- validatorParams.valid = false;
2397
+ }
2398
+ /**
2399
+ * Open or close the child menu. When the child menu closes, the selected
2400
+ * item is reset.
2401
+ * @memberof MenuComponent
2402
+ */
2403
+ toggleChildMenu(open) {
2404
+ let navEl = this.el.nativeElement.querySelector('nav');
2405
+ if (open) {
2406
+ // Remove the listener on the parent menu when a child menu is opened
2407
+ // This is to avoid interference between the parent and child menus
2408
+ if (this.removeKeydownListener) {
2409
+ this.removeKeydownListener();
2492
2410
  }
2493
- });
2494
- if (validatorParams.valid) {
2495
- return null;
2411
+ let height = navEl.offsetHeight;
2412
+ let width = navEl.offsetWidth;
2413
+ // In order to animate the child menu, we need to set height on the nav element
2414
+ // so we can absolutely position the two menus and maintain the current menu's height
2415
+ this.renderer.setStyle(navEl, 'height', `${height}px`);
2416
+ this.renderer.setStyle(navEl, 'width', `${width}px`);
2417
+ this.renderer.addClass(this.el.nativeElement, 'open');
2418
+ setTimeout(() => {
2419
+ this.renderer.addClass(this.el.nativeElement, 'open-active');
2420
+ });
2496
2421
  }
2497
2422
  else {
2498
- return {
2499
- textbox: validatorParams
2500
- };
2423
+ // Re-add the listener once the child menu closes
2424
+ this.addKeydownListener();
2425
+ this.renderer.removeClass(this.el.nativeElement, 'open-active');
2426
+ setTimeout(() => {
2427
+ this.renderer.removeClass(this.el.nativeElement, 'open');
2428
+ // Reset the nav element's height to auto
2429
+ this.renderer.setStyle(navEl, 'height', '100%');
2430
+ this.selected = null;
2431
+ }, menuAnimationSpeed);
2432
+ }
2433
+ }
2434
+ /**
2435
+ * Handle key presses to navigate the menu
2436
+ */
2437
+ keyNavigate(event) {
2438
+ if (this.enableKeyNav && event.target === this.dropdownToggleButton?.nativeElement) {
2439
+ switch (event.key) {
2440
+ case 'ArrowUp':
2441
+ case 'Up':
2442
+ case 'ArrowDown':
2443
+ case 'Down':
2444
+ event.stopPropagation();
2445
+ event.preventDefault();
2446
+ this.moveHighlightedUpOrDown(event);
2447
+ break;
2448
+ case 'ArrowRight':
2449
+ case 'Right':
2450
+ event.stopPropagation();
2451
+ event.preventDefault();
2452
+ // Select the item if it has child items
2453
+ if (this.highlightedItem && this.highlightedItem.items) {
2454
+ this.selectItem(event, this.highlightedItem, true);
2455
+ }
2456
+ break;
2457
+ case 'ArrowLeft':
2458
+ case 'Left':
2459
+ event.stopPropagation();
2460
+ event.preventDefault();
2461
+ // Close the menu if it is a child menu
2462
+ if (this.parent) {
2463
+ this.back(event);
2464
+ }
2465
+ break;
2466
+ case ' ':
2467
+ case 'Spacebar':
2468
+ case 'Enter':
2469
+ // Prevent 'enter' from doing whatever it does on the currently focused element
2470
+ event.preventDefault();
2471
+ if (this.highlightedItemIndex > -1 && this.highlightedItem) {
2472
+ this.selectItem(event, this.highlightedItem, true);
2473
+ // If the header is highlighted
2474
+ }
2475
+ else if (this.highlightedItemIndex === -1) {
2476
+ // Close the menu if it's a child
2477
+ if (this.parent) {
2478
+ this.back(event);
2479
+ }
2480
+ }
2481
+ break;
2482
+ default:
2483
+ return;
2484
+ }
2501
2485
  }
2502
- };
2503
- };
2504
- const phoneNumberValidationPattern = '^\\s*(?:\\+?(\\d{1,3}))?[-. (]*(\\d{3})[-. )]*(\\d{3})[-. ]*(\\d{4})(?: *x(\\d+))?\\s*$';
2505
- const urlValidationPattern = '([A-Za-z])+(:\/\/)+[^\\s]*';
2506
- class TextboxComponent extends FormControlBase {
2507
- constructor(validationMessageService, formGroupHelper, translate) {
2508
- super(validationMessageService, formGroupHelper);
2509
- this.validationMessageService = validationMessageService;
2510
- this.formGroupHelper = formGroupHelper;
2511
- this.translate = translate;
2512
- /**
2513
- * Set the value of the input's autocomplete attribute
2514
- */
2515
- this.autocomplete = 'off';
2516
- /**
2517
- * The textbox type
2518
- */
2519
- this.type = "text";
2520
- /**
2521
- * The value of the rows attribute for a textarea. Only applies to multi-line type
2522
- */
2523
- this.rows = 3;
2524
- /**
2525
- * If set to true, we will select all text within the input if
2526
- * autofocus is also set to true
2527
- */
2528
- this.selectOnAutofocus = false;
2529
- /**
2530
- * If set to true, we will upper case on focus out
2531
- */
2532
- this.upperCase = false;
2533
- /**
2534
- * Validation pattern for the input. This is determined on the input type specified
2535
- */
2536
- this.validationPattern = '';
2537
- }
2538
- ngOnChanges(changes) {
2539
- super.ngOnChanges(changes);
2540
2486
  }
2541
2487
  /**
2542
- * The angular onInit lifecycle hook
2488
+ * Scroll to the item currently marked as 'is-selected'. Wait a tick for the
2489
+ * NgClassDirecitve or NavItemActiveDirective to respond to the model changes
2490
+ * and update the view.
2543
2491
  */
2544
- ngOnInit() {
2545
- super.ngOnInit();
2546
- this.validationPattern = '';
2547
- if (this.type === 'tel') {
2548
- this.validationPattern = phoneNumberValidationPattern;
2492
+ scrollToSelectedItem() {
2493
+ window.setTimeout(() => {
2494
+ const linkSelector = `li.is-selected`;
2495
+ this.scrollService.scrollToItem(`#${this.id}_list`, linkSelector);
2496
+ });
2497
+ }
2498
+ moveHighlightedUpOrDown(event) {
2499
+ switch (event.key) {
2500
+ case 'ArrowUp':
2501
+ case 'Up':
2502
+ if (this.highlightedItemIndex > -1) {
2503
+ this.highlightedItemIndex--;
2504
+ // Skip any in-menu heading items
2505
+ if (this.highlightedItemIndex > -1 && this.items[this.highlightedItemIndex].display === 'heading') {
2506
+ this.highlightedItemIndex--;
2507
+ }
2508
+ }
2509
+ break;
2510
+ case 'ArrowDown':
2511
+ case 'Down':
2512
+ if (this.highlightedItemIndex < this.items.length - 1) {
2513
+ this.highlightedItemIndex++;
2514
+ // Skip any in-menu heading items
2515
+ if (this.highlightedItemIndex < this.items.length - 1 && this.items[this.highlightedItemIndex].display === 'heading') {
2516
+ this.highlightedItemIndex++;
2517
+ }
2518
+ }
2519
+ break;
2520
+ default:
2521
+ return;
2549
2522
  }
2550
- else if (this.type === 'url') {
2551
- this.validationPattern = urlValidationPattern;
2523
+ if (this.highlightedItemIndex > -1) {
2524
+ // Store the item at the current highlight index
2525
+ this.highlightedItem = this.items[this.highlightedItemIndex];
2552
2526
  }
2553
- if (this.placeholder) {
2554
- this.translate.get(this.placeholder)
2555
- .subscribe((translated) => {
2556
- this.placeholder = translated;
2557
- });
2527
+ else {
2528
+ this.highlightedItem = null;
2558
2529
  }
2530
+ this.scrollToHighlightedItem();
2559
2531
  }
2560
2532
  /**
2561
- * The angular afterViewInit lifecycle hook
2533
+ * Scroll to the specified menu item.
2534
+ * If no item is provided, it will scroll to the first item.
2535
+ *
2536
+ * @param item The menu item to scroll to.
2537
+ * @memberof MenuComponent
2562
2538
  */
2563
- ngAfterViewInit() {
2564
- if (this.autofocus) {
2565
- this.setFocus(this.selectOnAutofocus);
2539
+ scrollMenu(item) {
2540
+ if (this.items.length > 0) {
2541
+ item = item ? item : this.items[0];
2542
+ let itemIndex = this.findItemIndex(item);
2543
+ if (this.id) {
2544
+ let itemSelector = item.id ? `#${item.id}` : `#${this.id}_item${itemIndex}`;
2545
+ this.scrollService.scrollItemCentered(`#${this.id}_list`, itemSelector);
2546
+ }
2566
2547
  }
2567
2548
  }
2549
+ scrollToHighlightedItem() {
2550
+ this.scrollMenu(this.highlightedItem);
2551
+ }
2568
2552
  /**
2569
- * Function to set focus on an input programmatically after the page
2570
- * had been rendered. The highlight text flag will select the text
2571
- * within the input if passed in and true
2553
+ * Find a given item's index in the filtered items array.
2554
+ *
2555
+ * Returns -1 if not found
2556
+ * @param itemToFind The matching item to find in the items array.
2557
+ * @memberof MenuComponent
2572
2558
  */
2573
- setFocus(highlightText) {
2574
- this.inputElement.nativeElement.focus();
2575
- if (highlightText) {
2576
- this.inputElement.nativeElement.select();
2559
+ findItemIndex(itemToFind) {
2560
+ if (itemToFind) {
2561
+ return this.items.findIndex(item => {
2562
+ return item.label === itemToFind.label;
2563
+ });
2564
+ }
2565
+ else {
2566
+ return -1;
2567
+ }
2568
+ }
2569
+ addKeydownListener() {
2570
+ // Only attempt to add the listener if keyboard nav is enabled
2571
+ if (this.enableKeyNav) {
2572
+ // renderer.listen adds the listener and returns a function to remove it from the renderer.
2573
+ // The listener remains active until this function is called.
2574
+ this.removeKeydownListener = this.renderer.listen('document', 'keydown', (event) => this.keyNavigate(event));
2577
2575
  }
2578
2576
  }
2579
2577
  /**
2580
- * Focus out event handler
2581
- * will upper case and trim value if upperCase is true (this is what we do on the apis)
2582
- */
2583
- focusOutEvent() {
2584
- if (this.upperCase && this.formModel.value) {
2585
- this.formModel.setValue(this.formModel.value.toUpperCase().trim());
2578
+ * Sets the menu item ids using its index if item doesn't already have one
2579
+ */
2580
+ setItemIds() {
2581
+ if (this.items) {
2582
+ this.items.forEach((item, index) => {
2583
+ item.id = item.id ? item.id : this.id + '_item' + index;
2584
+ });
2586
2585
  }
2587
2586
  }
2588
2587
  }
2589
- TextboxComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TextboxComponent, deps: [{ token: ValidationMessageService }, { token: FormGroupHelper }, { token: i2.TranslateService }], target: i0.ɵɵFactoryTarget.Component });
2590
- TextboxComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: TextboxComponent, selector: "ec-textbox", inputs: { autocomplete: "autocomplete", type: "type", placeholder: "placeholder", maxlength: "maxlength", minlength: "minlength", rows: "rows", selectOnAutofocus: "selectOnAutofocus", upperCase: "upperCase" }, viewQueries: [{ propertyName: "inputElement", first: true, predicate: ["textboxInput"], descendants: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"control control-label-{{labelPosition}}\"\r\n [ngClass]=\"{'is-readonly': readonly}\">\r\n\r\n <label *ngIf=\"label\">\r\n <span>{{label | translate}}</span>\r\n <span *ngIf=\"validationErrors.length > 0 && formModel.touched && formModel.invalid\">&nbsp;{{validationErrors |\r\n translate}}</span>\r\n <ec-help-popover id=\"{{id}}_helpPopover\"\r\n *ngIf=\"helpPopover\"\r\n class=\"d-inline-block my-n3 mx-n1\"\r\n text=\"{{helpPopover | translate}}\"\r\n contentPosition=\"{{helpPopoverPosition}}\">\r\n </ec-help-popover>\r\n </label>\r\n\r\n <div class=\"input-wrapper control-input\">\r\n <input *ngIf=\"type !== 'multi_line'\"\r\n #textboxInput\r\n email=\"{{type === 'email' ? true : false}}\"\r\n pattern=\"{{validationPattern}}\"\r\n type=\"{{type}}\"\r\n tabindex=\"{{tabindex}}\"\r\n title=\"{{tooltip}}\"\r\n [attr.id]=\"inputId\"\r\n [attr.autocomplete]=\"autocomplete\"\r\n [attr.placeholder]=\"placeholder\"\r\n [attr.maxlength]=\"maxlength\"\r\n [attr.minlength]=\"minlength\"\r\n [attr.required]=\"required ? required : null\"\r\n [formControl]=\"formModel\"\r\n [ngClass]=\"{'is-empty': !formModel?.value, 'is-pending': pending, 'is-uppercase': upperCase}\"\r\n (focusout)=\"focusOutEvent()\"\r\n [attr.cdkFocusInitial]=\"autofocus || null\">\r\n\r\n <textarea *ngIf=\"type === 'multi_line'\"\r\n [attr.rows]=\"rows\"\r\n #textboxInput\r\n tabindex=\"{{tabindex}}\"\r\n [attr.id]=\"inputId\"\r\n [attr.placeholder]=\"placeholder\"\r\n [attr.maxlength]=\"maxlength\"\r\n [attr.minlength]=\"minlength\"\r\n [attr.required]=\"required ? required : null\"\r\n [formControl]=\"formModel\"\r\n [ngClass]=\"{'is-empty': formModel?.value === '', 'is-pending': pending}\"\r\n [attr.cdkFocusInitial]=\"autofocus || null\">\r\n </textarea>\r\n\r\n <i class=\"ec-icon icon-required\"></i>\r\n <i class=\"ec-icon icon-invalid\"></i>\r\n <i class=\"ec-icon icon-loading\"></i>\r\n </div>\r\n</div>", styles: [":host{color:var(--ec-form-control-color);font-size:var(--ec-form-control-font-size);display:block;margin-bottom:1rem;width:100%}:host :host-context(.form-condensed){margin-bottom:.5rem}:host .control{width:100%;display:flex;flex-direction:column}:host .control.control-label-bottom{flex-direction:column-reverse}:host .control.control-label-left{flex-direction:row}:host .control.control-label-left label{margin-right:.25rem}:host .control.control-label-right{flex-direction:row-reverse}:host .control.control-label-right label{margin-left:.25rem}:host .control.control-label-left,:host .control.control-label-right{align-items:center}:host .control.control-label-left label,:host .control.control-label-right label{flex:1 1;margin-top:0;margin-bottom:0}:host .control.control-label-left .control-input,:host .control.control-label-right .control-input{flex:2 2}:host .control.is-readonly input,:host .control.is-readonly select,:host .control.is-readonly textarea{border-color:var(--ec-form-control-border-color-readonly);background-color:var(--ec-form-control-background-color-readonly);background-clip:border-box;background-image:none;color:var(--ec-form-control-color-readonly);opacity:1;-webkit-user-select:none;user-select:none;pointer-events:none;overflow:hidden;white-space:nowrap}:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-invalid,:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-valid{background-color:var(--ec-form-control-background-color-invalid);border-color:var(--ec-form-control-border-color-invalid);background-repeat:no-repeat;background-position:.5rem center;background-size:1rem,1rem;padding-left:1.75rem;background-image:none}:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-invalid:focus,:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-valid:focus{border-color:var(--ec-form-control-background-color-invalid)}:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-invalid~.icon-invalid,:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-valid~.icon-invalid{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-invalid~.icon-required,:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-valid~.icon-required{display:none}:host .control.invalid:not(.open) .textbox-group-btn-right ::ng-deep button{background-color:var(--ec-form-control-background-color-invalid)}:host .control.invalid:not(.open) .textbox-group-btn-right ::ng-deep button:not(:focus){border-color:var(--ec-form-control-border-color-invalid)}:host .textbox-group{display:flex;position:relative}:host textarea:focus,:host input:focus,:host select:focus{outline:none}:host label{color:var(--ec-form-control-label-color, var(--ec-color-secondary-dark));display:block;font-size:var(--ec-font-size-label);line-height:1;margin:calc(var(--ec-font-size-label) / 2) 0}:host input{background-color:var(--ec-form-control-background-color);border:1px solid var(--ec-form-control-border-color);border-radius:var(--ec-border-radius);background-image:none;background-clip:padding-box;width:100%;line-height:1.25rem;padding:.3125rem .5rem;height:2rem}:host input::selection{background-color:var(--ec-form-control-background-color-selection);color:var(--ec-form-control-color-selection)}:host input::-webkit-input-placeholder{color:var(--ec-form-control-color-placeholder)}:host input::-moz-placeholder{color:var(--ec-form-control-color-placeholder);opacity:1}:host input:-ms-input-placeholder{color:var(--ec-form-control-color-placeholder)}:host input:-moz-placeholder{color:var(--ec-form-control-color-placeholder);opacity:1}:host input~.icon-required,:host input~.icon-invalid{color:var(--ec-form-control-border-color-invalid)}:host input:required.is-empty{background-repeat:no-repeat;background-position:.5rem center;background-size:1rem,1rem;padding-left:1.75rem;background-image:none}:host input:required.is-empty~.icon-required{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host input.ng-invalid.ng-touched{background-color:var(--ec-form-control-background-color-invalid);border-color:var(--ec-form-control-border-color-invalid);background-repeat:no-repeat;background-position:.5rem center;background-size:1rem,1rem;padding-left:1.75rem;background-image:none}:host input.ng-invalid.ng-touched:focus{border-color:var(--ec-form-control-background-color-invalid)}:host input.ng-invalid.ng-touched~.icon-invalid{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host input.ng-invalid.ng-touched~.icon-required{display:none}:host input.is-pending.ng-valid,:host input.is-pending.ng-invalid,:host input.is-pending.ng-pending{background-image:\"\";background-repeat:no-repeat;background-position:.5rem center;background-size:1rem,1rem;padding-left:1.75rem}:host input.is-pending.ng-valid~.icon-loading,:host input.is-pending.ng-invalid~.icon-loading,:host input.is-pending.ng-pending~.icon-loading{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host input.is-pending.ng-valid~.icon-required,:host input.is-pending.ng-valid~.icon-invalid,:host input.is-pending.ng-invalid~.icon-required,:host input.is-pending.ng-invalid~.icon-invalid,:host input.is-pending.ng-pending~.icon-required,:host input.is-pending.ng-pending~.icon-invalid{display:none}:host input:focus,:host input:focus.is-empty{border-color:var(--ec-form-control-border-color-focus);box-shadow:var(--ec-form-control-box-shadow-focus);position:relative;z-index:1}:host input:disabled{background-color:var(--ec-form-control-background-color-disabled);border-color:var(--ec-form-control-border-color-disabled);color:var(--ec-form-control-color-disabled);opacity:var(--ec-form-control-opacity-disabled)}:host input:disabled:required,:host input:disabled:required.is-empty{background-image:none;padding-left:.5rem;background-color:var(--ec-form-control-background-color-disabled);border-color:var(--ec-form-control-border-color-disabled)}:host input:disabled:required+.icon-required,:host input:disabled:required.is-empty+.icon-required{display:none}:host input.is-uppercase:not(.is-empty){text-transform:uppercase}:host textarea{background-color:var(--ec-form-control-background-color);border:1px solid var(--ec-form-control-border-color);border-radius:var(--ec-border-radius);background-image:none;background-clip:padding-box;width:100%;line-height:1.25rem;padding:.3125rem .5rem;height:auto;resize:none;display:block}:host textarea::selection{background-color:var(--ec-form-control-background-color-selection);color:var(--ec-form-control-color-selection)}:host textarea::-webkit-input-placeholder{color:var(--ec-form-control-color-placeholder)}:host textarea::-moz-placeholder{color:var(--ec-form-control-color-placeholder);opacity:1}:host textarea:-ms-input-placeholder{color:var(--ec-form-control-color-placeholder)}:host textarea:-moz-placeholder{color:var(--ec-form-control-color-placeholder);opacity:1}:host textarea~.icon-required,:host textarea~.icon-invalid{color:var(--ec-form-control-border-color-invalid)}:host textarea:required.is-empty{background-repeat:no-repeat;background-position:.5rem .5rem;background-size:1rem,1rem;padding-left:1.75rem;background-image:none}:host textarea:required.is-empty~.icon-required{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host textarea.ng-invalid.ng-touched{background-color:var(--ec-form-control-background-color-invalid);border-color:var(--ec-form-control-border-color-invalid);background-repeat:no-repeat;background-position:.5rem .5rem;background-size:1rem,1rem;padding-left:1.75rem;background-image:none}:host textarea.ng-invalid.ng-touched:focus{border-color:var(--ec-form-control-background-color-invalid)}:host textarea.ng-invalid.ng-touched~.icon-invalid{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host textarea.ng-invalid.ng-touched~.icon-required{display:none}:host textarea.is-pending.ng-valid,:host textarea.is-pending.ng-invalid,:host textarea.is-pending.ng-pending{background-image:\"\";background-repeat:no-repeat;background-position:.5rem .5rem;background-size:1rem,1rem;padding-left:1.75rem}:host textarea.is-pending.ng-valid~.icon-loading,:host textarea.is-pending.ng-invalid~.icon-loading,:host textarea.is-pending.ng-pending~.icon-loading{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host textarea.is-pending.ng-valid~.icon-required,:host textarea.is-pending.ng-valid~.icon-invalid,:host textarea.is-pending.ng-invalid~.icon-required,:host textarea.is-pending.ng-invalid~.icon-invalid,:host textarea.is-pending.ng-pending~.icon-required,:host textarea.is-pending.ng-pending~.icon-invalid{display:none}:host textarea:focus,:host textarea:focus.is-empty{border-color:var(--ec-form-control-border-color-focus);box-shadow:var(--ec-form-control-box-shadow-focus);position:relative;z-index:1}:host textarea:disabled{background-color:var(--ec-form-control-background-color-disabled);border-color:var(--ec-form-control-border-color-disabled);color:var(--ec-form-control-color-disabled);opacity:var(--ec-form-control-opacity-disabled)}:host textarea:disabled:required,:host textarea:disabled:required.is-empty{background-image:none;padding-left:.5rem;background-color:var(--ec-form-control-background-color-disabled);border-color:var(--ec-form-control-border-color-disabled)}:host textarea:disabled:required+.icon-required,:host textarea:disabled:required.is-empty+.icon-required{display:none}:host textarea.is-uppercase:not(.is-empty){text-transform:uppercase}.input-wrapper{position:relative}.input-wrapper>.ec-icon{display:none}:host(.textbox-group-input:not(:last-child)){flex:1 1 0%;width:1px}:host(.textbox-group-input:not(:last-child)) .control{margin-bottom:0}:host(.textbox-group-input:not(:last-child)) .control.is-readonly input{border-right-width:1px}:host(.textbox-group-input:not(:last-child)) input{border-top-right-radius:0;border-bottom-right-radius:0;border-right-width:0}:host(.textbox-group-input:not(:last-child)) input:focus{position:relative;z-index:1;border-right-width:1px}:host(.text-truncate) input{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host(.is-monospace) input,:host(.is-monospace) textarea,:host-context(.is-monospace) input,:host-context(.is-monospace) textarea{font-family:var(--ec-font-family-monospace)}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i4.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i4.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i4.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i4.EmailValidator, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: ["email"] }, { kind: "directive", type: i4.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: HelpPopoverComponent, selector: "ec-help-popover", inputs: ["id", "text", "contentPosition", "maxWidth"] }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }] });
2591
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TextboxComponent, decorators: [{
2588
+ MenuComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MenuComponent, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: WindowService }, { token: ScrollService }], target: i0.ɵɵFactoryTarget.Component });
2589
+ MenuComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: MenuComponent, selector: "ec-menu", inputs: { id: "id", items: "items", selected: "selected", parent: "parent", templateType: "templateType", customMenuTemplate: "customMenuTemplate", title: "title", showNoItems: "showNoItems", noDataText: "noDataText", enableKeyNav: "enableKeyNav", highlightedItem: "highlightedItem", maintainSelectedItem: "maintainSelectedItem", truncateItems: "truncateItems", preserveIconSpace: "preserveIconSpace", dropdownToggleButton: "dropdownToggleButton" }, outputs: { selectedChanged: "selectedChanged", menuClosed: "menuClosed" }, host: { properties: { "attr.id": "this.attrId" } }, viewQueries: [{ propertyName: "labelTemplate", first: true, predicate: ["label"], descendants: true, static: true }, { propertyName: "iconAndLabelTemplate", first: true, predicate: ["iconAndLabel"], descendants: true, static: true }, { propertyName: "checkAndLabelTemplate", first: true, predicate: ["checkAndLabel"], descendants: true, static: true }, { propertyName: "iconLabelCaptionTemplate", first: true, predicate: ["iconLabelCaption"], descendants: true, static: true }], ngImport: i0, template: "<nav>\r\n <div class=\"parent\"\r\n [class.no-data]=\"showNoItems && (!items || items.length === 0)\">\r\n <header id=\"{{id}}_header\"\r\n class=\"text-heading-3 p-1\"\r\n [class.is-selected]=\"highlightedItemIndex === -1\"\r\n *ngIf=\"parent\"\r\n (click)=\"back($event)\">\r\n <div class=\"item-wrapper\">\r\n <i class=\"ec-icon icon-angle-down rotate-90 flex-shrink\"></i>\r\n <span class=\"label text-truncate flex-grow\">{{parent?.label}}</span>\r\n </div>\r\n </header>\r\n\r\n <ul id=\"{{id}}_list\"\r\n class=\"py-1\">\r\n <li *ngFor=\"let item of items; index as i\"\r\n id=\"{{item.id || id + '_item' + i}}\"\r\n class=\"{{item.display || 'item'}} {{item.classList}}\"\r\n [attr.disabled]=\"item.disabled\"\r\n [hidden]=\"item.hidden\"\r\n ecNavItemActive=\"is-selected\"\r\n [ecNavItemActiveQueryParams]=\"item.queryParams\"\r\n [ecNavItemActiveUrl]=\"item.url\"\r\n [ecNavItemActiveExactMatch]='item.isActiveExactMatch'\r\n (routerLinkActivated)=\"selectItem($event, item)\"\r\n [ngClass]=\"{'is-highlighted':(selected === item && item?.display !== 'heading') || highlightedItem === item, 'is-link': item.url, 'is-disabled': item.disabled, 'is-readonly': item.readonly, 'is-checked': item.checked, 'text-heading-3': item?.display === 'heading'}\"\r\n (click)=\"selectItem($event, item)\">\r\n\r\n <a *ngIf=\"item.url && !item.externalLink\"\r\n id=\"{{item.id}}_link\"\r\n title=\"{{truncateItems ? item.label : ''}}\"\r\n class=\"item-wrapper\"\r\n [routerLink]=\"item.url\"\r\n [queryParams]=\"item.queryParams || null\"\r\n target=\"{{item.target || '_self'}}\">\r\n\r\n <ng-container *ngTemplateOutlet=\"internalizedTemplate; context: {$implicit: item}\"></ng-container>\r\n </a>\r\n\r\n <a *ngIf=\"item.url && item.externalLink\"\r\n id=\"{{item.id}}_link\"\r\n title=\"{{truncateItems ? item.label : ''}}\"\r\n class=\"item-wrapper\"\r\n href=\"{{item.url}}\"\r\n target=\"{{item.target || '_self'}}\">\r\n\r\n <ng-container *ngTemplateOutlet=\"internalizedTemplate; context: {$implicit: item}\"></ng-container>\r\n </a>\r\n\r\n <div *ngIf=\"!item.url\"\r\n title=\"{{truncateItems ? item.label : ''}}\"\r\n class=\"item-wrapper\">\r\n <ng-container *ngTemplateOutlet=\"internalizedTemplate; context: {$implicit: item}\"></ng-container>\r\n </div>\r\n </li>\r\n </ul>\r\n\r\n <p class=\"no-data-message\">{{noDataText | translate}}</p>\r\n </div>\r\n\r\n <!-- Child menu (Rendered to the right) -->\r\n <ec-menu *ngIf=\"selected?.items\"\r\n id=\"{{id}}_child\"\r\n class=\"child\"\r\n [parent]=\"selected\"\r\n [items]=\"selected?.items\"\r\n [showNoItems]=\"true\"\r\n [templateType]=\"templateType\"\r\n [enableKeyNav]=\"true\"\r\n [truncateItems]=\"truncateItems\"\r\n (selectedChanged)=\"onSelection($event)\"\r\n (menuClosed)=\"toggleChildMenu(false)\">\r\n </ec-menu>\r\n</nav>\r\n\r\n<!-- 'label' Item Template -->\r\n<ng-template #label\r\n let-item>\r\n <span id=\"{{item.id}}_label\"\r\n class=\"label\"\r\n [class.text-truncate]=\"truncateItems\">{{item.label}}</span>\r\n\r\n <i class=\"ec-icon icon-angle-down rotate-270\"\r\n *ngIf=\"item?.items\"></i>\r\n</ng-template>\r\n\r\n<!-- 'checkAndLabel' Item Template -->\r\n<ng-template #checkAndLabel\r\n let-item>\r\n\r\n <i class=\"ec-icon icon-check ec-icon-sm\"\r\n *ngIf=\"item.display !== 'heading'\"></i>\r\n\r\n <i class=\"ec-icon {{item.icon}} ml-2\"\r\n *ngIf=\"item.icon\"></i>\r\n\r\n <span id=\"{{item.id}}_label\"\r\n class=\"label\"\r\n [class.text-truncate]=\"truncateItems\">{{item.label}}</span>\r\n\r\n <i class=\"ec-icon icon-angle-down rotate-270\"\r\n *ngIf=\"item?.items\"></i>\r\n</ng-template>\r\n\r\n<!-- 'iconAndLabel' Item Template -->\r\n<ng-template #iconAndLabel\r\n let-item>\r\n <!-- If menuItem.icon exists and is not blank, show the icon in the menu -->\r\n <i class=\"ec-icon {{item.icon}}\"\r\n *ngIf=\"(item.icon && item.icon !== '') || preserveIconSpace\"></i>\r\n\r\n <span id=\"{{item.id}}_label\"\r\n class=\"label\"\r\n [class.text-truncate]=\"truncateItems\">{{item.label}}</span>\r\n\r\n <i class=\"ec-icon icon-angle-down rotate-270\"\r\n *ngIf=\"item?.items\"></i>\r\n</ng-template>\r\n\r\n<ng-template #iconLabelCaption\r\n let-item>\r\n <i class=\"ec-icon {{item.icon}}\"\r\n *ngIf=\"(item.icon && item.icon !== '') || preserveIconSpace\"></i>\r\n <div *ngIf=\"item.display !== 'heading'\"\r\n class=\"label flex-grow\">\r\n <div id=\"{{item.id}}_label\"\r\n class=\"text-body-1\"\r\n [class.text-truncate]=\"truncateItems\">{{item.label}}</div>\r\n <div id=\"{{item.id}}_caption\"\r\n *ngIf=\"item.caption\"\r\n class=\"text-caption-1\"\r\n [class.text-truncate]=\"truncateItems\">{{item.caption}}</div>\r\n </div>\r\n <h3 *ngIf=\"item.display === 'heading'\"\r\n class=\"flex-grow text-heading-3 align-self-center\"\r\n [class.text-truncate]=\"truncateItems\">{{item.label}}</h3>\r\n <i class=\"ec-icon icon-angle-down rotate-270 align-self-center\"\r\n *ngIf=\"item?.items\"></i>\r\n</ng-template>", styles: ["@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(1turn)}}:host{display:block;font-size:var(--ec-menu-font-size, var(--ec-font-size-action));font-weight:400;background-color:var(--ec-menu-background-color, var(--ec-background-color))}:host.open>nav>.parent,:host.open>nav>.child{position:absolute;left:0;top:0;right:0;height:100%;transition:transform .25s ease}:host.open>nav>.parent{transform:translate(0)}:host.open>nav>.child{transform:translate(100%)}:host.open-active>nav>.parent{transform:translate(-100%)}:host.open-active>nav>.child{transform:translate(0)}:host(.bg-transparent){background-color:transparent}:host-context(.is-always-open){height:100%}:host-context(.is-always-open) .item-wrapper{padding-left:1rem;padding-right:1rem}nav{display:flex;position:relative;height:100%;overflow:hidden}.parent{display:flex;flex-direction:column;flex:auto;position:relative;max-width:100%}.parent>header{cursor:pointer}.parent>header.is-selected .item-wrapper,.parent>header.is-highlighted .item-wrapper{background-color:var(--ec-background-color-selected)}.parent>header:hover .item-wrapper{background-color:var(--ec-background-color-hover)}.parent.no-data ul{display:none}.parent.no-data .no-data-message{display:block}ul{padding:0;margin:0;list-style:none;flex:auto;height:100%;overflow-y:auto}ul li{cursor:pointer;padding:0 .25rem}ul li a{color:inherit;border-bottom:0;text-decoration:none}ul li.is-selected .item-wrapper,ul li.is-highlighted .item-wrapper{background-color:var(--ec-background-color-selected)}ul li:hover .item-wrapper{background-color:var(--ec-background-color-hover)}ul li:focus .item-wrapper{outline:none;background-color:var(--ec-color-disabled-dark);position:relative;z-index:1}ul li.is-disabled .item-wrapper{color:var(--ec-color-disabled-dark);opacity:var(--ec-form-control-opacity-disabled)}ul li.is-disabled,ul li.is-readonly{cursor:default}ul li.is-disabled .item-wrapper,ul li.is-readonly .item-wrapper{background-color:transparent;color:inherit}ul li.is-checked .icon-check{opacity:1}ul li.heading{cursor:default}ul li.heading .item-wrapper{background-color:transparent}ul li.heading:not(:first-child){margin-top:.5rem}ul li.divider{border-bottom:1px solid var(--ec-border-color);padding-bottom:.25rem;margin-bottom:.25rem}ul li.indent-1 .item-wrapper{padding-left:1.5rem}ul li.indent-2 .item-wrapper{padding-left:2.5rem}ul li.indent-3 .item-wrapper{padding-left:3.5rem}.item-wrapper{cursor:inherit;line-height:1.25rem;min-height:1.75rem;padding:.25rem .5rem;border-radius:var(--ec-border-radius);display:flex;color:inherit}.item-wrapper .label{margin-right:auto}.item-wrapper .label+.ec-icon{margin-left:.5rem}.item-wrapper .ec-icon{margin-top:calc((1.25rem - var(--ec-font-size-icon)) / 2);flex:none}.item-wrapper .ec-icon+.label{margin-left:.5rem}.item-wrapper .ec-icon-sm{margin-top:calc((1.25rem - calc(var(--ec-font-size-icon) * .75)) / 2)}.item-wrapper .icon-check{opacity:0}.no-data-message{display:none;text-align:center;padding:1rem;color:var(--ec-color-hint-dark);margin-bottom:0;font-size:var(--ec-font-size-body)}:host-context(ec-tree) ul{overflow-x:hidden}:host-context(ec-tree) li.is-selected,:host-context(ec-tree) li.is-highlighted{font-weight:700;color:var(--ec-menu-color-highlighted, inherit)}:host-context(ec-tree) li.is-selected:not(:hover),:host-context(ec-tree) li.is-highlighted:not(:hover){background-color:transparent}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: NavItemActiveDirective, selector: "[ecNavItemActive]", inputs: ["ecNavItemActive", "ecNavItemActiveExactMatch", "ecNavItemActiveQueryParams", "ecNavItemActiveUrl"], outputs: ["routerLinkActivated"] }, { kind: "component", type: MenuComponent, selector: "ec-menu", inputs: ["id", "items", "selected", "parent", "templateType", "customMenuTemplate", "title", "showNoItems", "noDataText", "enableKeyNav", "highlightedItem", "maintainSelectedItem", "truncateItems", "preserveIconSpace", "dropdownToggleButton"], outputs: ["selectedChanged", "menuClosed"] }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }] });
2590
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MenuComponent, decorators: [{
2592
2591
  type: Component,
2593
- args: [{ selector: 'ec-textbox', template: "<div class=\"control control-label-{{labelPosition}}\"\r\n [ngClass]=\"{'is-readonly': readonly}\">\r\n\r\n <label *ngIf=\"label\">\r\n <span>{{label | translate}}</span>\r\n <span *ngIf=\"validationErrors.length > 0 && formModel.touched && formModel.invalid\">&nbsp;{{validationErrors |\r\n translate}}</span>\r\n <ec-help-popover id=\"{{id}}_helpPopover\"\r\n *ngIf=\"helpPopover\"\r\n class=\"d-inline-block my-n3 mx-n1\"\r\n text=\"{{helpPopover | translate}}\"\r\n contentPosition=\"{{helpPopoverPosition}}\">\r\n </ec-help-popover>\r\n </label>\r\n\r\n <div class=\"input-wrapper control-input\">\r\n <input *ngIf=\"type !== 'multi_line'\"\r\n #textboxInput\r\n email=\"{{type === 'email' ? true : false}}\"\r\n pattern=\"{{validationPattern}}\"\r\n type=\"{{type}}\"\r\n tabindex=\"{{tabindex}}\"\r\n title=\"{{tooltip}}\"\r\n [attr.id]=\"inputId\"\r\n [attr.autocomplete]=\"autocomplete\"\r\n [attr.placeholder]=\"placeholder\"\r\n [attr.maxlength]=\"maxlength\"\r\n [attr.minlength]=\"minlength\"\r\n [attr.required]=\"required ? required : null\"\r\n [formControl]=\"formModel\"\r\n [ngClass]=\"{'is-empty': !formModel?.value, 'is-pending': pending, 'is-uppercase': upperCase}\"\r\n (focusout)=\"focusOutEvent()\"\r\n [attr.cdkFocusInitial]=\"autofocus || null\">\r\n\r\n <textarea *ngIf=\"type === 'multi_line'\"\r\n [attr.rows]=\"rows\"\r\n #textboxInput\r\n tabindex=\"{{tabindex}}\"\r\n [attr.id]=\"inputId\"\r\n [attr.placeholder]=\"placeholder\"\r\n [attr.maxlength]=\"maxlength\"\r\n [attr.minlength]=\"minlength\"\r\n [attr.required]=\"required ? required : null\"\r\n [formControl]=\"formModel\"\r\n [ngClass]=\"{'is-empty': formModel?.value === '', 'is-pending': pending}\"\r\n [attr.cdkFocusInitial]=\"autofocus || null\">\r\n </textarea>\r\n\r\n <i class=\"ec-icon icon-required\"></i>\r\n <i class=\"ec-icon icon-invalid\"></i>\r\n <i class=\"ec-icon icon-loading\"></i>\r\n </div>\r\n</div>", styles: [":host{color:var(--ec-form-control-color);font-size:var(--ec-form-control-font-size);display:block;margin-bottom:1rem;width:100%}:host :host-context(.form-condensed){margin-bottom:.5rem}:host .control{width:100%;display:flex;flex-direction:column}:host .control.control-label-bottom{flex-direction:column-reverse}:host .control.control-label-left{flex-direction:row}:host .control.control-label-left label{margin-right:.25rem}:host .control.control-label-right{flex-direction:row-reverse}:host .control.control-label-right label{margin-left:.25rem}:host .control.control-label-left,:host .control.control-label-right{align-items:center}:host .control.control-label-left label,:host .control.control-label-right label{flex:1 1;margin-top:0;margin-bottom:0}:host .control.control-label-left .control-input,:host .control.control-label-right .control-input{flex:2 2}:host .control.is-readonly input,:host .control.is-readonly select,:host .control.is-readonly textarea{border-color:var(--ec-form-control-border-color-readonly);background-color:var(--ec-form-control-background-color-readonly);background-clip:border-box;background-image:none;color:var(--ec-form-control-color-readonly);opacity:1;-webkit-user-select:none;user-select:none;pointer-events:none;overflow:hidden;white-space:nowrap}:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-invalid,:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-valid{background-color:var(--ec-form-control-background-color-invalid);border-color:var(--ec-form-control-border-color-invalid);background-repeat:no-repeat;background-position:.5rem center;background-size:1rem,1rem;padding-left:1.75rem;background-image:none}:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-invalid:focus,:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-valid:focus{border-color:var(--ec-form-control-background-color-invalid)}:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-invalid~.icon-invalid,:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-valid~.icon-invalid{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-invalid~.icon-required,:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-valid~.icon-required{display:none}:host .control.invalid:not(.open) .textbox-group-btn-right ::ng-deep button{background-color:var(--ec-form-control-background-color-invalid)}:host .control.invalid:not(.open) .textbox-group-btn-right ::ng-deep button:not(:focus){border-color:var(--ec-form-control-border-color-invalid)}:host .textbox-group{display:flex;position:relative}:host textarea:focus,:host input:focus,:host select:focus{outline:none}:host label{color:var(--ec-form-control-label-color, var(--ec-color-secondary-dark));display:block;font-size:var(--ec-font-size-label);line-height:1;margin:calc(var(--ec-font-size-label) / 2) 0}:host input{background-color:var(--ec-form-control-background-color);border:1px solid var(--ec-form-control-border-color);border-radius:var(--ec-border-radius);background-image:none;background-clip:padding-box;width:100%;line-height:1.25rem;padding:.3125rem .5rem;height:2rem}:host input::selection{background-color:var(--ec-form-control-background-color-selection);color:var(--ec-form-control-color-selection)}:host input::-webkit-input-placeholder{color:var(--ec-form-control-color-placeholder)}:host input::-moz-placeholder{color:var(--ec-form-control-color-placeholder);opacity:1}:host input:-ms-input-placeholder{color:var(--ec-form-control-color-placeholder)}:host input:-moz-placeholder{color:var(--ec-form-control-color-placeholder);opacity:1}:host input~.icon-required,:host input~.icon-invalid{color:var(--ec-form-control-border-color-invalid)}:host input:required.is-empty{background-repeat:no-repeat;background-position:.5rem center;background-size:1rem,1rem;padding-left:1.75rem;background-image:none}:host input:required.is-empty~.icon-required{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host input.ng-invalid.ng-touched{background-color:var(--ec-form-control-background-color-invalid);border-color:var(--ec-form-control-border-color-invalid);background-repeat:no-repeat;background-position:.5rem center;background-size:1rem,1rem;padding-left:1.75rem;background-image:none}:host input.ng-invalid.ng-touched:focus{border-color:var(--ec-form-control-background-color-invalid)}:host input.ng-invalid.ng-touched~.icon-invalid{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host input.ng-invalid.ng-touched~.icon-required{display:none}:host input.is-pending.ng-valid,:host input.is-pending.ng-invalid,:host input.is-pending.ng-pending{background-image:\"\";background-repeat:no-repeat;background-position:.5rem center;background-size:1rem,1rem;padding-left:1.75rem}:host input.is-pending.ng-valid~.icon-loading,:host input.is-pending.ng-invalid~.icon-loading,:host input.is-pending.ng-pending~.icon-loading{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host input.is-pending.ng-valid~.icon-required,:host input.is-pending.ng-valid~.icon-invalid,:host input.is-pending.ng-invalid~.icon-required,:host input.is-pending.ng-invalid~.icon-invalid,:host input.is-pending.ng-pending~.icon-required,:host input.is-pending.ng-pending~.icon-invalid{display:none}:host input:focus,:host input:focus.is-empty{border-color:var(--ec-form-control-border-color-focus);box-shadow:var(--ec-form-control-box-shadow-focus);position:relative;z-index:1}:host input:disabled{background-color:var(--ec-form-control-background-color-disabled);border-color:var(--ec-form-control-border-color-disabled);color:var(--ec-form-control-color-disabled);opacity:var(--ec-form-control-opacity-disabled)}:host input:disabled:required,:host input:disabled:required.is-empty{background-image:none;padding-left:.5rem;background-color:var(--ec-form-control-background-color-disabled);border-color:var(--ec-form-control-border-color-disabled)}:host input:disabled:required+.icon-required,:host input:disabled:required.is-empty+.icon-required{display:none}:host input.is-uppercase:not(.is-empty){text-transform:uppercase}:host textarea{background-color:var(--ec-form-control-background-color);border:1px solid var(--ec-form-control-border-color);border-radius:var(--ec-border-radius);background-image:none;background-clip:padding-box;width:100%;line-height:1.25rem;padding:.3125rem .5rem;height:auto;resize:none;display:block}:host textarea::selection{background-color:var(--ec-form-control-background-color-selection);color:var(--ec-form-control-color-selection)}:host textarea::-webkit-input-placeholder{color:var(--ec-form-control-color-placeholder)}:host textarea::-moz-placeholder{color:var(--ec-form-control-color-placeholder);opacity:1}:host textarea:-ms-input-placeholder{color:var(--ec-form-control-color-placeholder)}:host textarea:-moz-placeholder{color:var(--ec-form-control-color-placeholder);opacity:1}:host textarea~.icon-required,:host textarea~.icon-invalid{color:var(--ec-form-control-border-color-invalid)}:host textarea:required.is-empty{background-repeat:no-repeat;background-position:.5rem .5rem;background-size:1rem,1rem;padding-left:1.75rem;background-image:none}:host textarea:required.is-empty~.icon-required{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host textarea.ng-invalid.ng-touched{background-color:var(--ec-form-control-background-color-invalid);border-color:var(--ec-form-control-border-color-invalid);background-repeat:no-repeat;background-position:.5rem .5rem;background-size:1rem,1rem;padding-left:1.75rem;background-image:none}:host textarea.ng-invalid.ng-touched:focus{border-color:var(--ec-form-control-background-color-invalid)}:host textarea.ng-invalid.ng-touched~.icon-invalid{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host textarea.ng-invalid.ng-touched~.icon-required{display:none}:host textarea.is-pending.ng-valid,:host textarea.is-pending.ng-invalid,:host textarea.is-pending.ng-pending{background-image:\"\";background-repeat:no-repeat;background-position:.5rem .5rem;background-size:1rem,1rem;padding-left:1.75rem}:host textarea.is-pending.ng-valid~.icon-loading,:host textarea.is-pending.ng-invalid~.icon-loading,:host textarea.is-pending.ng-pending~.icon-loading{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host textarea.is-pending.ng-valid~.icon-required,:host textarea.is-pending.ng-valid~.icon-invalid,:host textarea.is-pending.ng-invalid~.icon-required,:host textarea.is-pending.ng-invalid~.icon-invalid,:host textarea.is-pending.ng-pending~.icon-required,:host textarea.is-pending.ng-pending~.icon-invalid{display:none}:host textarea:focus,:host textarea:focus.is-empty{border-color:var(--ec-form-control-border-color-focus);box-shadow:var(--ec-form-control-box-shadow-focus);position:relative;z-index:1}:host textarea:disabled{background-color:var(--ec-form-control-background-color-disabled);border-color:var(--ec-form-control-border-color-disabled);color:var(--ec-form-control-color-disabled);opacity:var(--ec-form-control-opacity-disabled)}:host textarea:disabled:required,:host textarea:disabled:required.is-empty{background-image:none;padding-left:.5rem;background-color:var(--ec-form-control-background-color-disabled);border-color:var(--ec-form-control-border-color-disabled)}:host textarea:disabled:required+.icon-required,:host textarea:disabled:required.is-empty+.icon-required{display:none}:host textarea.is-uppercase:not(.is-empty){text-transform:uppercase}.input-wrapper{position:relative}.input-wrapper>.ec-icon{display:none}:host(.textbox-group-input:not(:last-child)){flex:1 1 0%;width:1px}:host(.textbox-group-input:not(:last-child)) .control{margin-bottom:0}:host(.textbox-group-input:not(:last-child)) .control.is-readonly input{border-right-width:1px}:host(.textbox-group-input:not(:last-child)) input{border-top-right-radius:0;border-bottom-right-radius:0;border-right-width:0}:host(.textbox-group-input:not(:last-child)) input:focus{position:relative;z-index:1;border-right-width:1px}:host(.text-truncate) input{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host(.is-monospace) input,:host(.is-monospace) textarea,:host-context(.is-monospace) input,:host-context(.is-monospace) textarea{font-family:var(--ec-font-family-monospace)}\n"] }]
2594
- }], ctorParameters: function () { return [{ type: ValidationMessageService }, { type: FormGroupHelper }, { type: i2.TranslateService }]; }, propDecorators: { autocomplete: [{
2592
+ args: [{ selector: 'ec-menu', template: "<nav>\r\n <div class=\"parent\"\r\n [class.no-data]=\"showNoItems && (!items || items.length === 0)\">\r\n <header id=\"{{id}}_header\"\r\n class=\"text-heading-3 p-1\"\r\n [class.is-selected]=\"highlightedItemIndex === -1\"\r\n *ngIf=\"parent\"\r\n (click)=\"back($event)\">\r\n <div class=\"item-wrapper\">\r\n <i class=\"ec-icon icon-angle-down rotate-90 flex-shrink\"></i>\r\n <span class=\"label text-truncate flex-grow\">{{parent?.label}}</span>\r\n </div>\r\n </header>\r\n\r\n <ul id=\"{{id}}_list\"\r\n class=\"py-1\">\r\n <li *ngFor=\"let item of items; index as i\"\r\n id=\"{{item.id || id + '_item' + i}}\"\r\n class=\"{{item.display || 'item'}} {{item.classList}}\"\r\n [attr.disabled]=\"item.disabled\"\r\n [hidden]=\"item.hidden\"\r\n ecNavItemActive=\"is-selected\"\r\n [ecNavItemActiveQueryParams]=\"item.queryParams\"\r\n [ecNavItemActiveUrl]=\"item.url\"\r\n [ecNavItemActiveExactMatch]='item.isActiveExactMatch'\r\n (routerLinkActivated)=\"selectItem($event, item)\"\r\n [ngClass]=\"{'is-highlighted':(selected === item && item?.display !== 'heading') || highlightedItem === item, 'is-link': item.url, 'is-disabled': item.disabled, 'is-readonly': item.readonly, 'is-checked': item.checked, 'text-heading-3': item?.display === 'heading'}\"\r\n (click)=\"selectItem($event, item)\">\r\n\r\n <a *ngIf=\"item.url && !item.externalLink\"\r\n id=\"{{item.id}}_link\"\r\n title=\"{{truncateItems ? item.label : ''}}\"\r\n class=\"item-wrapper\"\r\n [routerLink]=\"item.url\"\r\n [queryParams]=\"item.queryParams || null\"\r\n target=\"{{item.target || '_self'}}\">\r\n\r\n <ng-container *ngTemplateOutlet=\"internalizedTemplate; context: {$implicit: item}\"></ng-container>\r\n </a>\r\n\r\n <a *ngIf=\"item.url && item.externalLink\"\r\n id=\"{{item.id}}_link\"\r\n title=\"{{truncateItems ? item.label : ''}}\"\r\n class=\"item-wrapper\"\r\n href=\"{{item.url}}\"\r\n target=\"{{item.target || '_self'}}\">\r\n\r\n <ng-container *ngTemplateOutlet=\"internalizedTemplate; context: {$implicit: item}\"></ng-container>\r\n </a>\r\n\r\n <div *ngIf=\"!item.url\"\r\n title=\"{{truncateItems ? item.label : ''}}\"\r\n class=\"item-wrapper\">\r\n <ng-container *ngTemplateOutlet=\"internalizedTemplate; context: {$implicit: item}\"></ng-container>\r\n </div>\r\n </li>\r\n </ul>\r\n\r\n <p class=\"no-data-message\">{{noDataText | translate}}</p>\r\n </div>\r\n\r\n <!-- Child menu (Rendered to the right) -->\r\n <ec-menu *ngIf=\"selected?.items\"\r\n id=\"{{id}}_child\"\r\n class=\"child\"\r\n [parent]=\"selected\"\r\n [items]=\"selected?.items\"\r\n [showNoItems]=\"true\"\r\n [templateType]=\"templateType\"\r\n [enableKeyNav]=\"true\"\r\n [truncateItems]=\"truncateItems\"\r\n (selectedChanged)=\"onSelection($event)\"\r\n (menuClosed)=\"toggleChildMenu(false)\">\r\n </ec-menu>\r\n</nav>\r\n\r\n<!-- 'label' Item Template -->\r\n<ng-template #label\r\n let-item>\r\n <span id=\"{{item.id}}_label\"\r\n class=\"label\"\r\n [class.text-truncate]=\"truncateItems\">{{item.label}}</span>\r\n\r\n <i class=\"ec-icon icon-angle-down rotate-270\"\r\n *ngIf=\"item?.items\"></i>\r\n</ng-template>\r\n\r\n<!-- 'checkAndLabel' Item Template -->\r\n<ng-template #checkAndLabel\r\n let-item>\r\n\r\n <i class=\"ec-icon icon-check ec-icon-sm\"\r\n *ngIf=\"item.display !== 'heading'\"></i>\r\n\r\n <i class=\"ec-icon {{item.icon}} ml-2\"\r\n *ngIf=\"item.icon\"></i>\r\n\r\n <span id=\"{{item.id}}_label\"\r\n class=\"label\"\r\n [class.text-truncate]=\"truncateItems\">{{item.label}}</span>\r\n\r\n <i class=\"ec-icon icon-angle-down rotate-270\"\r\n *ngIf=\"item?.items\"></i>\r\n</ng-template>\r\n\r\n<!-- 'iconAndLabel' Item Template -->\r\n<ng-template #iconAndLabel\r\n let-item>\r\n <!-- If menuItem.icon exists and is not blank, show the icon in the menu -->\r\n <i class=\"ec-icon {{item.icon}}\"\r\n *ngIf=\"(item.icon && item.icon !== '') || preserveIconSpace\"></i>\r\n\r\n <span id=\"{{item.id}}_label\"\r\n class=\"label\"\r\n [class.text-truncate]=\"truncateItems\">{{item.label}}</span>\r\n\r\n <i class=\"ec-icon icon-angle-down rotate-270\"\r\n *ngIf=\"item?.items\"></i>\r\n</ng-template>\r\n\r\n<ng-template #iconLabelCaption\r\n let-item>\r\n <i class=\"ec-icon {{item.icon}}\"\r\n *ngIf=\"(item.icon && item.icon !== '') || preserveIconSpace\"></i>\r\n <div *ngIf=\"item.display !== 'heading'\"\r\n class=\"label flex-grow\">\r\n <div id=\"{{item.id}}_label\"\r\n class=\"text-body-1\"\r\n [class.text-truncate]=\"truncateItems\">{{item.label}}</div>\r\n <div id=\"{{item.id}}_caption\"\r\n *ngIf=\"item.caption\"\r\n class=\"text-caption-1\"\r\n [class.text-truncate]=\"truncateItems\">{{item.caption}}</div>\r\n </div>\r\n <h3 *ngIf=\"item.display === 'heading'\"\r\n class=\"flex-grow text-heading-3 align-self-center\"\r\n [class.text-truncate]=\"truncateItems\">{{item.label}}</h3>\r\n <i class=\"ec-icon icon-angle-down rotate-270 align-self-center\"\r\n *ngIf=\"item?.items\"></i>\r\n</ng-template>", styles: ["@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(1turn)}}:host{display:block;font-size:var(--ec-menu-font-size, var(--ec-font-size-action));font-weight:400;background-color:var(--ec-menu-background-color, var(--ec-background-color))}:host.open>nav>.parent,:host.open>nav>.child{position:absolute;left:0;top:0;right:0;height:100%;transition:transform .25s ease}:host.open>nav>.parent{transform:translate(0)}:host.open>nav>.child{transform:translate(100%)}:host.open-active>nav>.parent{transform:translate(-100%)}:host.open-active>nav>.child{transform:translate(0)}:host(.bg-transparent){background-color:transparent}:host-context(.is-always-open){height:100%}:host-context(.is-always-open) .item-wrapper{padding-left:1rem;padding-right:1rem}nav{display:flex;position:relative;height:100%;overflow:hidden}.parent{display:flex;flex-direction:column;flex:auto;position:relative;max-width:100%}.parent>header{cursor:pointer}.parent>header.is-selected .item-wrapper,.parent>header.is-highlighted .item-wrapper{background-color:var(--ec-background-color-selected)}.parent>header:hover .item-wrapper{background-color:var(--ec-background-color-hover)}.parent.no-data ul{display:none}.parent.no-data .no-data-message{display:block}ul{padding:0;margin:0;list-style:none;flex:auto;height:100%;overflow-y:auto}ul li{cursor:pointer;padding:0 .25rem}ul li a{color:inherit;border-bottom:0;text-decoration:none}ul li.is-selected .item-wrapper,ul li.is-highlighted .item-wrapper{background-color:var(--ec-background-color-selected)}ul li:hover .item-wrapper{background-color:var(--ec-background-color-hover)}ul li:focus .item-wrapper{outline:none;background-color:var(--ec-color-disabled-dark);position:relative;z-index:1}ul li.is-disabled .item-wrapper{color:var(--ec-color-disabled-dark);opacity:var(--ec-form-control-opacity-disabled)}ul li.is-disabled,ul li.is-readonly{cursor:default}ul li.is-disabled .item-wrapper,ul li.is-readonly .item-wrapper{background-color:transparent;color:inherit}ul li.is-checked .icon-check{opacity:1}ul li.heading{cursor:default}ul li.heading .item-wrapper{background-color:transparent}ul li.heading:not(:first-child){margin-top:.5rem}ul li.divider{border-bottom:1px solid var(--ec-border-color);padding-bottom:.25rem;margin-bottom:.25rem}ul li.indent-1 .item-wrapper{padding-left:1.5rem}ul li.indent-2 .item-wrapper{padding-left:2.5rem}ul li.indent-3 .item-wrapper{padding-left:3.5rem}.item-wrapper{cursor:inherit;line-height:1.25rem;min-height:1.75rem;padding:.25rem .5rem;border-radius:var(--ec-border-radius);display:flex;color:inherit}.item-wrapper .label{margin-right:auto}.item-wrapper .label+.ec-icon{margin-left:.5rem}.item-wrapper .ec-icon{margin-top:calc((1.25rem - var(--ec-font-size-icon)) / 2);flex:none}.item-wrapper .ec-icon+.label{margin-left:.5rem}.item-wrapper .ec-icon-sm{margin-top:calc((1.25rem - calc(var(--ec-font-size-icon) * .75)) / 2)}.item-wrapper .icon-check{opacity:0}.no-data-message{display:none;text-align:center;padding:1rem;color:var(--ec-color-hint-dark);margin-bottom:0;font-size:var(--ec-font-size-body)}:host-context(ec-tree) ul{overflow-x:hidden}:host-context(ec-tree) li.is-selected,:host-context(ec-tree) li.is-highlighted{font-weight:700;color:var(--ec-menu-color-highlighted, inherit)}:host-context(ec-tree) li.is-selected:not(:hover),:host-context(ec-tree) li.is-highlighted:not(:hover){background-color:transparent}\n"] }]
2593
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: WindowService }, { type: ScrollService }]; }, propDecorators: { id: [{
2595
2594
  type: Input
2596
- }], type: [{
2595
+ }], attrId: [{
2596
+ type: HostBinding,
2597
+ args: ['attr.id']
2598
+ }], items: [{
2597
2599
  type: Input
2598
- }], placeholder: [{
2600
+ }], selected: [{
2599
2601
  type: Input
2600
- }], maxlength: [{
2602
+ }], parent: [{
2601
2603
  type: Input
2602
- }], minlength: [{
2604
+ }], templateType: [{
2603
2605
  type: Input
2604
- }], rows: [{
2606
+ }], customMenuTemplate: [{
2605
2607
  type: Input
2606
- }], selectOnAutofocus: [{
2608
+ }], title: [{
2607
2609
  type: Input
2608
- }], upperCase: [{
2610
+ }], showNoItems: [{
2609
2611
  type: Input
2610
- }], inputElement: [{
2611
- type: ViewChild,
2612
- args: ['textboxInput']
2613
- }] } });
2614
-
2615
- /** Exposes the markup and styles that represent the spinner. No inputs or outputs defined because it is just a visual component*/
2616
- class SpinnerComponent {
2617
- }
2618
- SpinnerComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: SpinnerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2619
- SpinnerComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: SpinnerComponent, selector: "ec-spinner", ngImport: i0, template: "<div class=\"spinner\">\r\n <span class=\"spinner-dot\"></span>\r\n <span class=\"spinner-dot\"></span>\r\n <span class=\"spinner-dot\"></span>\r\n <span class=\"spinner-dot\"></span>\r\n</div>", styles: ["@keyframes sk-bouncedelay{0%,80%,to{opacity:0}40%{opacity:1}}.spinner{display:flex}.spinner-dot{width:.75rem;height:.75rem;background-color:var(--ec-color-interactive);animation:sk-bouncedelay 1.7s infinite ease-in-out both;margin-right:.25rem}.spinner-dot:nth-child(1){animation-delay:-.6s}.spinner-dot:nth-child(2){animation-delay:-.4s}.spinner-dot:nth-child(3){animation-delay:-.2s}:host(.spinner-small) .spinner-dot{width:.5rem;height:.5rem;background-color:var(--ec-color-interactive);animation:sk-bouncedelay 1.7s infinite ease-in-out both;margin-right:.1666666667rem}:host(.spinner-small) .spinner-dot:nth-child(1){animation-delay:-.6s}:host(.spinner-small) .spinner-dot:nth-child(2){animation-delay:-.4s}:host(.spinner-small) .spinner-dot:nth-child(3){animation-delay:-.2s}\n"] });
2620
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: SpinnerComponent, decorators: [{
2621
- type: Component,
2622
- args: [{ selector: 'ec-spinner', template: "<div class=\"spinner\">\r\n <span class=\"spinner-dot\"></span>\r\n <span class=\"spinner-dot\"></span>\r\n <span class=\"spinner-dot\"></span>\r\n <span class=\"spinner-dot\"></span>\r\n</div>", styles: ["@keyframes sk-bouncedelay{0%,80%,to{opacity:0}40%{opacity:1}}.spinner{display:flex}.spinner-dot{width:.75rem;height:.75rem;background-color:var(--ec-color-interactive);animation:sk-bouncedelay 1.7s infinite ease-in-out both;margin-right:.25rem}.spinner-dot:nth-child(1){animation-delay:-.6s}.spinner-dot:nth-child(2){animation-delay:-.4s}.spinner-dot:nth-child(3){animation-delay:-.2s}:host(.spinner-small) .spinner-dot{width:.5rem;height:.5rem;background-color:var(--ec-color-interactive);animation:sk-bouncedelay 1.7s infinite ease-in-out both;margin-right:.1666666667rem}:host(.spinner-small) .spinner-dot:nth-child(1){animation-delay:-.6s}:host(.spinner-small) .spinner-dot:nth-child(2){animation-delay:-.4s}:host(.spinner-small) .spinner-dot:nth-child(3){animation-delay:-.2s}\n"] }]
2623
- }] });
2624
-
2625
- class Overlay {
2626
- constructor(status, message) {
2627
- this.status = 'hasData';
2628
- this.message = '';
2629
- this.setStatus(status, message);
2630
- }
2631
- setStatus(status, message, action, noDataTemplate, overlayClassList) {
2632
- this.status = status;
2633
- this.message = message || '';
2634
- this.action = action || undefined;
2635
- this.noDataTemplate = noDataTemplate || undefined;
2636
- this.overlayClassList = overlayClassList || '';
2637
- }
2638
- }
2639
- /**
2640
- * Wraps content in order to show pending, error, and no data states with an optional message/noDataTemplate
2641
- */
2642
- class ViewOverlayComponent {
2643
- constructor() {
2644
- this.status = 'hasData';
2645
- }
2646
- setStatus(status, message, action, noDataTemplate) {
2647
- this.status = status;
2648
- this.message = message || '';
2649
- this.action = action || undefined;
2650
- this.noDataTemplate = noDataTemplate || undefined;
2651
- }
2652
- actionClicked(event) {
2653
- if (this.action && this.action.onClick) {
2654
- this.action.onClick(event);
2655
- }
2656
- }
2657
- }
2658
- ViewOverlayComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ViewOverlayComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2659
- ViewOverlayComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: ViewOverlayComponent, selector: "[ecOverlay]", inputs: { status: "status", message: "message", action: "action", noDataTemplate: "noDataTemplate", displayAsMask: "displayAsMask", overlayClassList: "overlayClassList" }, ngImport: i0, template: "<!-- Transcluded Content -->\r\n<ng-content *ngIf=\"displayAsMask || (!displayAsMask && status === 'hasData')\"></ng-content>\r\n<!--Used by GI tests to know the overlay status whether we use ngIf or mask version. No visual impact-->\r\n<span [hidden]=\"true\"\r\n\t class=\"overlay-status-{{status}}\"></span>\r\n<!-- Overlay goes last so it is rendered on top of preceding content due to source order -->\r\n<div *ngIf=\"status !== 'hasData'\"\r\n\t class=\"overlay flex-grow {{overlayClassList}}\"\r\n\t [ngClass]=\"{'not-mask': !displayAsMask,\r\n\t\t\t\t'overlay-error': status === 'error',\r\n\t\t\t\t'overlay-nodata': status === 'noData',\r\n\t\t\t\t'overlay-pending': status === 'pending'}\">\r\n\r\n\t<!--Pending Spinner-->\r\n\t<ec-spinner [hidden]=\"status !== 'pending'\"></ec-spinner>\r\n\r\n\t<ng-template [ngIf]=\"status === 'noData' && noDataTemplate\">\r\n\t\t<ng-container *ngTemplateOutlet=\"noDataTemplate\"></ng-container>\r\n\t</ng-template>\r\n\r\n\t<ng-container *ngIf=\"(status === 'noData' && !noDataTemplate) || status !== 'noData'\">\r\n\t\t<!--Status Message-->\r\n\t\t<div id=\"statusMessage\"\r\n\t\t\t class=\"message\"\r\n\t\t\t *ngIf=\"message\"\r\n\t\t\t [ngClass]=\"{'error': status === 'error', 'mt-1': status === 'pending'}\"\r\n\t\t\t [innerHtml]=\"message | translate\">\r\n\t\t</div>\r\n\r\n\t\t<!-- Action -->\r\n\t\t<ec-button type=\"common\"\r\n\t\t\t\t class=\"mt-3\"\r\n\t\t\t\t *ngIf=\"action?.onClick\"\r\n\t\t\t\t [icon]=\"action?.icon\"\r\n\t\t\t\t (clicked)=\"actionClicked($event)\"\r\n\t\t\t\t [label]=\"action?.label\"\r\n\t\t\t\t [hidden]=\"status === 'pending'\">\r\n\t\t</ec-button>\r\n\t</ng-container>\r\n\r\n</div>", styles: [":host{position:relative}:host(.bg-body)>.overlay{background-color:var(--ec-background-color-body)}:host(.bg-body).is-translucent>.overlay{background-color:var(--ec-background-color-overlay)}:host(.bg-content)>.overlay{background-color:var(--ec-background-color)}:host(.bg-content).is-translucent>.overlay{background-color:var(--ec-background-color-overlay)}.overlay{align-items:center;background-color:var(--ec-overlay-background-color, var(--ec-background-color));display:flex;flex-direction:column;justify-content:center;padding:3rem 4rem;z-index:var(--ec-z-index-overlay);position:absolute;inset:0}.overlay.not-mask{position:relative;min-height:100%}.message{color:var(--ec-color-secondary-dark);font-size:var(--ec-font-size-title)}.message.error{color:var(--ec-color-danger);font-size:var(--ec-font-size-title)}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ButtonComponent, selector: "ec-button", inputs: ["id", "disabled", "icon", "label", "badge", "tabindex", "type", "pending", "pendingIcon", "customTemplate", "isSubmit", "autofocus"], outputs: ["clicked"] }, { kind: "component", type: SpinnerComponent, selector: "ec-spinner" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }] });
2660
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ViewOverlayComponent, decorators: [{
2661
- type: Component,
2662
- args: [{ selector: '[ecOverlay]', template: "<!-- Transcluded Content -->\r\n<ng-content *ngIf=\"displayAsMask || (!displayAsMask && status === 'hasData')\"></ng-content>\r\n<!--Used by GI tests to know the overlay status whether we use ngIf or mask version. No visual impact-->\r\n<span [hidden]=\"true\"\r\n\t class=\"overlay-status-{{status}}\"></span>\r\n<!-- Overlay goes last so it is rendered on top of preceding content due to source order -->\r\n<div *ngIf=\"status !== 'hasData'\"\r\n\t class=\"overlay flex-grow {{overlayClassList}}\"\r\n\t [ngClass]=\"{'not-mask': !displayAsMask,\r\n\t\t\t\t'overlay-error': status === 'error',\r\n\t\t\t\t'overlay-nodata': status === 'noData',\r\n\t\t\t\t'overlay-pending': status === 'pending'}\">\r\n\r\n\t<!--Pending Spinner-->\r\n\t<ec-spinner [hidden]=\"status !== 'pending'\"></ec-spinner>\r\n\r\n\t<ng-template [ngIf]=\"status === 'noData' && noDataTemplate\">\r\n\t\t<ng-container *ngTemplateOutlet=\"noDataTemplate\"></ng-container>\r\n\t</ng-template>\r\n\r\n\t<ng-container *ngIf=\"(status === 'noData' && !noDataTemplate) || status !== 'noData'\">\r\n\t\t<!--Status Message-->\r\n\t\t<div id=\"statusMessage\"\r\n\t\t\t class=\"message\"\r\n\t\t\t *ngIf=\"message\"\r\n\t\t\t [ngClass]=\"{'error': status === 'error', 'mt-1': status === 'pending'}\"\r\n\t\t\t [innerHtml]=\"message | translate\">\r\n\t\t</div>\r\n\r\n\t\t<!-- Action -->\r\n\t\t<ec-button type=\"common\"\r\n\t\t\t\t class=\"mt-3\"\r\n\t\t\t\t *ngIf=\"action?.onClick\"\r\n\t\t\t\t [icon]=\"action?.icon\"\r\n\t\t\t\t (clicked)=\"actionClicked($event)\"\r\n\t\t\t\t [label]=\"action?.label\"\r\n\t\t\t\t [hidden]=\"status === 'pending'\">\r\n\t\t</ec-button>\r\n\t</ng-container>\r\n\r\n</div>", styles: [":host{position:relative}:host(.bg-body)>.overlay{background-color:var(--ec-background-color-body)}:host(.bg-body).is-translucent>.overlay{background-color:var(--ec-background-color-overlay)}:host(.bg-content)>.overlay{background-color:var(--ec-background-color)}:host(.bg-content).is-translucent>.overlay{background-color:var(--ec-background-color-overlay)}.overlay{align-items:center;background-color:var(--ec-overlay-background-color, var(--ec-background-color));display:flex;flex-direction:column;justify-content:center;padding:3rem 4rem;z-index:var(--ec-z-index-overlay);position:absolute;inset:0}.overlay.not-mask{position:relative;min-height:100%}.message{color:var(--ec-color-secondary-dark);font-size:var(--ec-font-size-title)}.message.error{color:var(--ec-color-danger);font-size:var(--ec-font-size-title)}\n"] }]
2663
- }], propDecorators: { status: [{
2612
+ }], noDataText: [{
2664
2613
  type: Input
2665
- }], message: [{
2614
+ }], enableKeyNav: [{
2666
2615
  type: Input
2667
- }], action: [{
2616
+ }], highlightedItem: [{
2668
2617
  type: Input
2669
- }], noDataTemplate: [{
2618
+ }], maintainSelectedItem: [{
2670
2619
  type: Input
2671
- }], displayAsMask: [{
2620
+ }], truncateItems: [{
2672
2621
  type: Input
2673
- }], overlayClassList: [{
2622
+ }], preserveIconSpace: [{
2623
+ type: Input
2624
+ }], dropdownToggleButton: [{
2674
2625
  type: Input
2626
+ }], selectedChanged: [{
2627
+ type: Output
2628
+ }], menuClosed: [{
2629
+ type: Output
2630
+ }], labelTemplate: [{
2631
+ type: ViewChild,
2632
+ args: ['label', { static: true }]
2633
+ }], iconAndLabelTemplate: [{
2634
+ type: ViewChild,
2635
+ args: ['iconAndLabel', { static: true }]
2636
+ }], checkAndLabelTemplate: [{
2637
+ type: ViewChild,
2638
+ args: ['checkAndLabel', { static: true }]
2639
+ }], iconLabelCaptionTemplate: [{
2640
+ type: ViewChild,
2641
+ args: ['iconLabelCaption', { static: true }]
2675
2642
  }] } });
2676
2643
 
2677
2644
  /**
@@ -2827,11 +2794,6 @@ class ComboboxComponent extends FormControlBase {
2827
2794
  * Number of filtered options to display in the footer. Excludes headings.
2828
2795
  */
2829
2796
  this.filteredOptionCount = 0;
2830
- /**
2831
- * Flat list of selectable items in the combobox.
2832
- * Does not include headings or divider-section items.
2833
- */
2834
- this.selectableItems = [];
2835
2797
  /**
2836
2798
  * Index of the currently-selected options
2837
2799
  */
@@ -2968,14 +2930,13 @@ class ComboboxComponent extends FormControlBase {
2968
2930
  */
2969
2931
  resetOptions(options) {
2970
2932
  this.filteredOptions = options || this.options;
2971
- this.selectableItems = MenuComponent.getSelectableItems(this.filteredOptions);
2972
2933
  // do not count headings
2973
- this.filteredOptionCount = this.filteredOptions?.filter(o => o.display !== 'heading' && o.display !== 'divided-section').length || 0;
2934
+ this.filteredOptionCount = this.filteredOptions?.filter(o => o.display !== 'heading').length || 0;
2974
2935
  //if they have no search term, don't try to select anything so they can clear the box out by hitting enter
2975
2936
  //if they have a search term and the options changed, select an option from the menu so they will get it automatically if they hit enter
2976
2937
  if (this.textboxFormModel.value !== '') {
2977
- this.selectedItemIndex = this.findDefaultSelectionIndex(this.selectableItems, this.textboxFormModel.value);
2978
- this.selectedItem = this.selectableItems[this.selectedItemIndex] || null;
2938
+ this.selectedItemIndex = this.findDefaultSelectionIndex(this.filteredOptions, this.textboxFormModel.value);
2939
+ this.selectedItem = this.filteredOptions[this.selectedItemIndex] || null;
2979
2940
  this.scrollMenu(this.selectedItemIndex);
2980
2941
  }
2981
2942
  else {
@@ -3169,11 +3130,18 @@ class ComboboxComponent extends FormControlBase {
3169
3130
  if (this.selectedItemIndex === -1 && this.addNewButton && !this.addNewButton.nativeElement.hidden && !this.addNewSelected) {
3170
3131
  this.addNewSelected = true;
3171
3132
  }
3172
- else if (this.selectedItemIndex < this.selectableItems.length - 1) {
3173
- this.selectedItemIndex++;
3133
+ else if (this.selectedItemIndex < this.filteredOptions.length - 1) {
3174
3134
  if (this.addNewSelected) {
3175
3135
  this.addNewSelected = false;
3176
3136
  }
3137
+ //if the last item is a heading and we are on the second last item, we shouldn't increment the selectedItemIndex because that would select the heading
3138
+ if (!(this.selectedItemIndex == this.filteredOptions.length - 2 && this.filteredOptions[this.selectedItemIndex + 1].display === 'heading')) {
3139
+ this.selectedItemIndex++;
3140
+ // Skip any in-menu heading items
3141
+ if (this.selectedItemIndex < this.filteredOptions.length - 1 && this.filteredOptions[this.selectedItemIndex].display === 'heading') {
3142
+ this.selectedItemIndex++;
3143
+ }
3144
+ }
3177
3145
  }
3178
3146
  break;
3179
3147
  case "ArrowUp":
@@ -3182,16 +3150,30 @@ class ComboboxComponent extends FormControlBase {
3182
3150
  this.addNewSelected = false;
3183
3151
  }
3184
3152
  else if (this.selectedItemIndex > -1) {
3185
- this.selectedItemIndex--;
3186
- if (this.selectedItemIndex === -1 && this.addNewButton && !this.addNewButton.nativeElement.hidden) {
3153
+ if (this.selectedItemIndex === 0 && this.addNewButton && !this.addNewButton.nativeElement.hidden) {
3187
3154
  this.addNewSelected = true;
3188
3155
  }
3156
+ this.selectedItemIndex--;
3157
+ // Skip any in-menu heading items
3158
+ if (this.selectedItemIndex > -1 && this.filteredOptions[this.selectedItemIndex].display === 'heading') {
3159
+ this.selectedItemIndex--;
3160
+ //if the selectedItemIndex is -1, that means the very first item was a heading and we just skipped over it.
3161
+ if (this.selectedItemIndex == -1) {
3162
+ //if there is an add new button we should select it. otherwise we should lock them at index 1 (right before the heading)
3163
+ if (this.addNewButton && !this.addNewButton.nativeElement.hidden) {
3164
+ this.addNewSelected = true;
3165
+ }
3166
+ else {
3167
+ this.selectedItemIndex = 1;
3168
+ }
3169
+ }
3170
+ }
3189
3171
  }
3190
3172
  break;
3191
3173
  default:
3192
3174
  return;
3193
3175
  }
3194
- this.selectedItem = this.selectableItems[this.selectedItemIndex];
3176
+ this.selectedItem = this.filteredOptions[this.selectedItemIndex];
3195
3177
  if (this.menuStatus === 'hidden') {
3196
3178
  this.setFormModelValue(this.selectedItem);
3197
3179
  }
@@ -3210,22 +3192,10 @@ class ComboboxComponent extends FormControlBase {
3210
3192
  filterOptionsArray(filterText) {
3211
3193
  let searchText = filterText.toLowerCase();
3212
3194
  if (filterText && filterText !== '') {
3213
- const matchesSearch = (item) => item.label.toLowerCase().indexOf(searchText) >= 0 || (item.caption && item.caption.toLowerCase().indexOf(searchText) >= 0);
3214
- return this.options.reduce((filteredItems, item) => {
3215
- // Match the item itself if it doesn't have any children
3216
- if (!item.items?.length && matchesSearch(item)) {
3217
- filteredItems.push(item);
3218
- // If we have children, filter them and add the parent if it has any children that match
3219
- }
3220
- else if (item.items?.length && (item.display === 'heading' || item.display === 'divided-section')) {
3221
- const filteredChildItems = item.items.filter(matchesSearch);
3222
- if (filteredChildItems.length) {
3223
- // Need to clone the parent item with the filtered children so we don't modify the original
3224
- filteredItems.push({ ...item, items: filteredChildItems });
3225
- }
3226
- }
3227
- return filteredItems;
3228
- }, []);
3195
+ return this.options.filter(item => {
3196
+ return item.label.toLowerCase().indexOf(searchText) >= 0 ||
3197
+ (item.caption && item.caption.toLowerCase().indexOf(searchText) >= 0);
3198
+ });
3229
3199
  }
3230
3200
  else {
3231
3201
  return this.options;
@@ -3242,7 +3212,7 @@ class ComboboxComponent extends FormControlBase {
3242
3212
  findSelectedItemIndex(item) {
3243
3213
  let itemToFind = item ? item : this.selectedItem;
3244
3214
  if (itemToFind) {
3245
- return this.selectableItems.findIndex(item => {
3215
+ return this.filteredOptions.findIndex(item => {
3246
3216
  if (item.id && itemToFind.id) {
3247
3217
  return item.label === itemToFind.label && item.id === itemToFind.id;
3248
3218
  }
@@ -3268,7 +3238,7 @@ class ComboboxComponent extends FormControlBase {
3268
3238
  this.textboxFormModel.setValue(data.selectedLabel || data.label);
3269
3239
  this.selectedItemIndex = this.findSelectedItemIndex(data);
3270
3240
  if (this.selectedItemIndex >= 0) {
3271
- this.selectedItem = this.selectableItems[this.selectedItemIndex];
3241
+ this.selectedItem = this.filteredOptions[this.selectedItemIndex];
3272
3242
  }
3273
3243
  }
3274
3244
  else {
@@ -3289,10 +3259,10 @@ class ComboboxComponent extends FormControlBase {
3289
3259
  if (this.selectedItemIndex > -1 && this.selectedItem) {
3290
3260
  // The menu component will automatically generate ids for the menu items if they dont have them,
3291
3261
  // so they need to be accounted for here since the combobox doesn't know about those ids
3292
- let itemId = this.selectedItem.id ? this.selectedItem.id : MenuComponent.getIndexedItemId(this.filteredOptions, this.selectedItem, `${this.id}_menu`);
3262
+ let itemSelector = this.selectedItem.id ? `#${this.selectedItem.id}` : `#${this.id}_menu_item${this.selectedItemIndex}`;
3293
3263
  //trigger the scrolling after a tick to allow the menu to be up to date (and pending mask cleared) before measuring
3294
3264
  setTimeout(() => {
3295
- this.scrollService.scrollItemCentered(`#${this.id}_menu_list`, `#${itemId}`);
3265
+ this.scrollService.scrollItemCentered(`#${this.id}_menu_list`, itemSelector);
3296
3266
  }, 0);
3297
3267
  }
3298
3268
  else {
@@ -3458,10 +3428,10 @@ class ComboboxComponent extends FormControlBase {
3458
3428
  findDefaultSelectionIndex(options, searchText) {
3459
3429
  let index = -1;
3460
3430
  if (options && options.length) {
3461
- index = options.findIndex(option => option != null);
3431
+ index = options.findIndex(option => option != null && option.display !== 'heading');
3462
3432
  if (searchText) {
3463
3433
  searchText = searchText.toLowerCase().trim();
3464
- let exactMatchIndex = options.findIndex(option => option.label.toLowerCase() == searchText);
3434
+ let exactMatchIndex = options.findIndex(option => option.label.toLowerCase() == searchText && option.display !== 'heading');
3465
3435
  if (exactMatchIndex >= 0) {
3466
3436
  index = exactMatchIndex;
3467
3437
  }
@@ -3800,10 +3770,10 @@ class FormControlComponent {
3800
3770
  }
3801
3771
  }
3802
3772
  FormControlComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: FormControlComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
3803
- FormControlComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: FormControlComponent, selector: "ec-form-control", inputs: { id: "id", icon: "icon", actionIcon: "actionIcon", showClear: "showClear", pending: "pending", required: "required", readonly: "readonly" }, outputs: { actionClicked: "actionClicked" }, host: { listeners: { "click": "onClick()" }, properties: { "class.is-pending": "this.pending", "class.is-required": "this.required", "class.is-readonly": "this.readonly", "class.is-empty": "this.empty", "class.is-invalid": "this.invalid", "class.is-disabled": "this.disabled" } }, queries: [{ propertyName: "formModel", first: true, predicate: FormControlDirective, descendants: true }], ngImport: i0, template: "<ng-content></ng-content>\r\n<i id=\"{{id + '_icon'}}\" class=\"ec-form-control-icon ec-icon {{icon}}\"></i>\r\n<i id=\"{{id + '_loading'}}\" class=\"ec-form-control-icon ec-icon icon-loading\"></i>\r\n<i id=\"{{id + '_required'}}\" class=\"ec-form-control-icon ec-icon icon-required\"></i>\r\n<i id=\"{{id + '_invalid'}}\" class=\"ec-form-control-icon ec-icon icon-invalid\"></i>\r\n<i id=\"{{id + '_clear'}}\" *ngIf=\"showClear\" class=\"ec-form-control-clear ec-icon icon-cancel\" (click)=\"clear()\"></i>\r\n<i id=\"{{id + '_action'}}\" *ngIf=\"actionIcon\" class=\"ec-form-control-action ec-icon {{actionIcon}}\" (click)=\"actionClicked.emit()\"></i>\r\n<div class=\"ec-focus-ring\"></div>", styles: ["ec-form-control{background-color:var(--ec-form-control-background-color);background-clip:padding-box;border:1px solid var(--ec-form-control-border-color);border-radius:var(--ec-border-radius);padding:0 .5rem;position:relative;color:var(--ec-form-control-color);display:flex;font-size:var(--ec-form-control-font-size);min-height:2rem;width:100%}ec-form-control>input,ec-form-control>select,ec-form-control>textarea{color:inherit;flex:1 1;min-width:0;border:0;background-color:transparent;order:2}ec-form-control>input::selection,ec-form-control>select::selection,ec-form-control>textarea::selection{background-color:var(--ec-form-control-background-color-selection);color:var(--ec-form-control-color-selection)}ec-form-control>input::-webkit-input-placeholder,ec-form-control>select::-webkit-input-placeholder,ec-form-control>textarea::-webkit-input-placeholder{color:var(--ec-form-control-color-placeholder)}ec-form-control>input::-moz-placeholder,ec-form-control>select::-moz-placeholder,ec-form-control>textarea::-moz-placeholder{color:var(--ec-form-control-color-placeholder);opacity:1}ec-form-control>input:-ms-input-placeholder,ec-form-control>select:-ms-input-placeholder,ec-form-control>textarea:-ms-input-placeholder{color:var(--ec-form-control-color-placeholder)}ec-form-control>input:-moz-placeholder,ec-form-control>select:-moz-placeholder,ec-form-control>textarea:-moz-placeholder{color:var(--ec-form-control-color-placeholder);opacity:1}ec-form-control>input:focus,ec-form-control>select:focus,ec-form-control>textarea:focus{outline:none}ec-form-control>input:focus~.ec-focus-ring,ec-form-control>select:focus~.ec-focus-ring,ec-form-control>textarea:focus~.ec-focus-ring{display:block}ec-form-control>input,ec-form-control>textarea,ec-form-control>select,ec-form-control .ec-form-control-prefix,ec-form-control .ec-form-control-suffix{padding:calc((1.875rem - (var(--ec-font-size-body) * 1.25)) / 2) 0;line-height:1.25;font-size:inherit}ec-form-control>textarea{resize:none;padding:.3125rem 0}ec-form-control .ec-form-control-icon{margin-top:.4375rem;margin-right:.25rem;order:1}ec-form-control .ec-form-control-icon:not([class*=icon-]){display:none}ec-form-control .icon-required,ec-form-control .icon-invalid{color:var(--ec-form-control-border-color-invalid)}ec-form-control .ec-form-control-clear,ec-form-control .ec-form-control-action{cursor:pointer;flex:none;height:1.875rem;width:2rem;order:3}ec-form-control .ec-form-control-clear:last-of-type,ec-form-control .ec-form-control-action:last-of-type{margin-right:-.5rem}ec-form-control .ec-form-control-clear:hover,ec-form-control .ec-form-control-action:hover{background-color:var(--ec-background-color-hover)}ec-form-control .ec-form-control-clear{width:1.5rem}ec-form-control .ec-form-control-prefix,ec-form-control .ec-form-control-suffix{color:var(--ec-color-secondary-dark);flex:none;cursor:default}ec-form-control .ec-form-control-prefix{margin-right:.125rem;order:2}ec-form-control .ec-form-control-suffix{margin-left:.125rem;order:3}ec-form-control .ec-focus-ring{position:absolute;inset:-1px;border:.125rem solid var(--ec-form-control-border-color-focus);pointer-events:none;display:none;border-radius:var(--ec-form-control-border-radius);z-index:1}ec-form-control.is-pending .icon-invalid,ec-form-control.is-pending .icon-required{display:none}ec-form-control.is-invalid{border-color:var(--ec-form-control-border-color-invalid);background-color:var(--ec-form-control-background-color-invalid)}ec-form-control.is-invalid .icon-required{display:none}ec-form-control.is-empty .ec-form-control-clear{display:none}ec-form-control.is-required.is-empty .ec-form-control-icon:first-of-type{display:none}ec-form-control:not(.is-pending) .icon-loading{display:none}ec-form-control:not(.is-required) .icon-required,ec-form-control:not(.is-empty) .icon-required{display:none}ec-form-control:not(.is-invalid) .icon-invalid{display:none}ec-form-control.is-readonly,ec-form-control.is-disabled{background-color:var(--ec-form-control-background-color-disabled)}ec-form-control.is-readonly .icon-loading,ec-form-control.is-readonly .icon-invalid,ec-form-control.is-readonly .icon-required,ec-form-control.is-readonly .ec-form-control-clear,ec-form-control.is-disabled .icon-loading,ec-form-control.is-disabled .icon-invalid,ec-form-control.is-disabled .icon-required,ec-form-control.is-disabled .ec-form-control-clear{display:none}ec-form-control.is-readonly{border-color:var(--ec-form-control-border-color-readonly);color:var(--ec-form-control-color-readonly)}ec-form-control.is-readonly .ec-form-control-action{display:none}ec-form-control.is-disabled:not(.is-readonly){color:var(--ec-form-control-color-disabled);opacity:.6}ec-form-control.is-disabled:not(.is-readonly) .ec-form-control-prefix,ec-form-control.is-disabled:not(.is-readonly) .ec-form-control-suffix{color:inherit}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], encapsulation: i0.ViewEncapsulation.None });
3773
+ FormControlComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: FormControlComponent, selector: "ec-form-control", inputs: { id: "id", icon: "icon", actionIcon: "actionIcon", showClear: "showClear", pending: "pending", required: "required", readonly: "readonly" }, outputs: { actionClicked: "actionClicked" }, host: { listeners: { "click": "onClick()" }, properties: { "class.is-pending": "this.pending", "class.is-required": "this.required", "class.is-readonly": "this.readonly", "class.is-empty": "this.empty", "class.is-invalid": "this.invalid", "class.is-disabled": "this.disabled" } }, queries: [{ propertyName: "formModel", first: true, predicate: FormControlDirective, descendants: true }], ngImport: i0, template: "<ng-content></ng-content>\r\n<i id=\"{{id + '_icon'}}\" class=\"ec-form-control-icon ec-icon {{icon}}\"></i>\r\n<i id=\"{{id + '_loading'}}\" class=\"ec-form-control-icon ec-icon icon-loading\"></i>\r\n<i id=\"{{id + '_required'}}\" class=\"ec-form-control-icon ec-icon icon-required\"></i>\r\n<i id=\"{{id + '_invalid'}}\" class=\"ec-form-control-icon ec-icon icon-invalid\"></i>\r\n<i id=\"{{id + '_clear'}}\" *ngIf=\"showClear\" class=\"ec-form-control-clear ec-icon icon-cancel\" (click)=\"clear()\"></i>\r\n<i id=\"{{id + '_action'}}\" *ngIf=\"actionIcon\" class=\"ec-form-control-action ec-icon {{actionIcon}}\" (click)=\"actionClicked.emit()\"></i>\r\n<div class=\"ec-focus-ring\"></div>", styles: ["ec-form-control{background-color:var(--ec-form-control-background-color);background-clip:padding-box;border:1px solid var(--ec-form-control-border-color);border-radius:var(--ec-border-radius);padding:0 .5rem;position:relative;color:var(--ec-form-control-color);display:flex;font-size:var(--ec-form-control-font-size);min-height:2rem;width:100%}ec-form-control>input,ec-form-control>select,ec-form-control>textarea,ec-form-control>div[contenteditable=true]{color:inherit;flex:1 1;min-width:0;border:0;background-color:transparent;order:2}ec-form-control>input::selection,ec-form-control>select::selection,ec-form-control>textarea::selection,ec-form-control>div[contenteditable=true]::selection{background-color:var(--ec-form-control-background-color-selection);color:var(--ec-form-control-color-selection)}ec-form-control>input::-webkit-input-placeholder,ec-form-control>select::-webkit-input-placeholder,ec-form-control>textarea::-webkit-input-placeholder,ec-form-control>div[contenteditable=true]::-webkit-input-placeholder{color:var(--ec-form-control-color-placeholder)}ec-form-control>input::-moz-placeholder,ec-form-control>select::-moz-placeholder,ec-form-control>textarea::-moz-placeholder,ec-form-control>div[contenteditable=true]::-moz-placeholder{color:var(--ec-form-control-color-placeholder);opacity:1}ec-form-control>input:-ms-input-placeholder,ec-form-control>select:-ms-input-placeholder,ec-form-control>textarea:-ms-input-placeholder,ec-form-control>div[contenteditable=true]:-ms-input-placeholder{color:var(--ec-form-control-color-placeholder)}ec-form-control>input:-moz-placeholder,ec-form-control>select:-moz-placeholder,ec-form-control>textarea:-moz-placeholder,ec-form-control>div[contenteditable=true]:-moz-placeholder{color:var(--ec-form-control-color-placeholder);opacity:1}ec-form-control>input:focus,ec-form-control>select:focus,ec-form-control>textarea:focus,ec-form-control>div[contenteditable=true]:focus{outline:none}ec-form-control>input:focus~.ec-focus-ring,ec-form-control>select:focus~.ec-focus-ring,ec-form-control>textarea:focus~.ec-focus-ring,ec-form-control>div[contenteditable=true]:focus~.ec-focus-ring{display:block}ec-form-control>input,ec-form-control>textarea,ec-form-control>select,ec-form-control>div[contenteditable=true],ec-form-control .ec-form-control-prefix,ec-form-control .ec-form-control-suffix{padding:calc((1.875rem - (var(--ec-font-size-body) * 1.25)) / 2) 0;line-height:1.25;font-size:inherit}ec-form-control>textarea{resize:none;padding:.3125rem 0}ec-form-control .ec-form-control-icon{margin-top:.4375rem;margin-right:.25rem;order:1}ec-form-control .ec-form-control-icon:not([class*=icon-]){display:none}ec-form-control .icon-required,ec-form-control .icon-invalid{color:var(--ec-form-control-border-color-invalid)}ec-form-control .ec-form-control-clear,ec-form-control .ec-form-control-action{cursor:pointer;flex:none;height:1.875rem;width:2rem;order:3}ec-form-control .ec-form-control-clear:last-of-type,ec-form-control .ec-form-control-action:last-of-type{margin-right:-.5rem}ec-form-control .ec-form-control-clear:hover,ec-form-control .ec-form-control-action:hover{background-color:var(--ec-background-color-hover)}ec-form-control .ec-form-control-clear{width:1.5rem}ec-form-control .ec-form-control-prefix,ec-form-control .ec-form-control-suffix{color:var(--ec-color-secondary-dark);flex:none;cursor:default}ec-form-control .ec-form-control-prefix{margin-right:.125rem;order:2}ec-form-control .ec-form-control-suffix{margin-left:.125rem;order:3}ec-form-control .ec-focus-ring{position:absolute;inset:-1px;border:.125rem solid var(--ec-form-control-border-color-focus);pointer-events:none;display:none;border-radius:var(--ec-form-control-border-radius);z-index:1}ec-form-control.is-pending .icon-invalid,ec-form-control.is-pending .icon-required{display:none}ec-form-control.is-invalid{border-color:var(--ec-form-control-border-color-invalid);background-color:var(--ec-form-control-background-color-invalid)}ec-form-control.is-invalid .icon-required{display:none}ec-form-control.is-empty .ec-form-control-clear{display:none}ec-form-control.is-required.is-empty .ec-form-control-icon:first-of-type{display:none}ec-form-control:not(.is-pending) .icon-loading{display:none}ec-form-control:not(.is-required) .icon-required,ec-form-control:not(.is-empty) .icon-required{display:none}ec-form-control:not(.is-invalid) .icon-invalid{display:none}ec-form-control.is-readonly,ec-form-control.is-disabled{background-color:var(--ec-form-control-background-color-disabled)}ec-form-control.is-readonly .icon-loading,ec-form-control.is-readonly .icon-invalid,ec-form-control.is-readonly .icon-required,ec-form-control.is-readonly .ec-form-control-clear,ec-form-control.is-disabled .icon-loading,ec-form-control.is-disabled .icon-invalid,ec-form-control.is-disabled .icon-required,ec-form-control.is-disabled .ec-form-control-clear{display:none}ec-form-control.is-readonly{border-color:var(--ec-form-control-border-color-readonly);color:var(--ec-form-control-color-readonly)}ec-form-control.is-readonly .ec-form-control-action{display:none}ec-form-control.is-disabled:not(.is-readonly){color:var(--ec-form-control-color-disabled);opacity:.6}ec-form-control.is-disabled:not(.is-readonly) .ec-form-control-prefix,ec-form-control.is-disabled:not(.is-readonly) .ec-form-control-suffix{color:inherit}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], encapsulation: i0.ViewEncapsulation.None });
3804
3774
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: FormControlComponent, decorators: [{
3805
3775
  type: Component,
3806
- args: [{ selector: 'ec-form-control', encapsulation: ViewEncapsulation.None, template: "<ng-content></ng-content>\r\n<i id=\"{{id + '_icon'}}\" class=\"ec-form-control-icon ec-icon {{icon}}\"></i>\r\n<i id=\"{{id + '_loading'}}\" class=\"ec-form-control-icon ec-icon icon-loading\"></i>\r\n<i id=\"{{id + '_required'}}\" class=\"ec-form-control-icon ec-icon icon-required\"></i>\r\n<i id=\"{{id + '_invalid'}}\" class=\"ec-form-control-icon ec-icon icon-invalid\"></i>\r\n<i id=\"{{id + '_clear'}}\" *ngIf=\"showClear\" class=\"ec-form-control-clear ec-icon icon-cancel\" (click)=\"clear()\"></i>\r\n<i id=\"{{id + '_action'}}\" *ngIf=\"actionIcon\" class=\"ec-form-control-action ec-icon {{actionIcon}}\" (click)=\"actionClicked.emit()\"></i>\r\n<div class=\"ec-focus-ring\"></div>", styles: ["ec-form-control{background-color:var(--ec-form-control-background-color);background-clip:padding-box;border:1px solid var(--ec-form-control-border-color);border-radius:var(--ec-border-radius);padding:0 .5rem;position:relative;color:var(--ec-form-control-color);display:flex;font-size:var(--ec-form-control-font-size);min-height:2rem;width:100%}ec-form-control>input,ec-form-control>select,ec-form-control>textarea{color:inherit;flex:1 1;min-width:0;border:0;background-color:transparent;order:2}ec-form-control>input::selection,ec-form-control>select::selection,ec-form-control>textarea::selection{background-color:var(--ec-form-control-background-color-selection);color:var(--ec-form-control-color-selection)}ec-form-control>input::-webkit-input-placeholder,ec-form-control>select::-webkit-input-placeholder,ec-form-control>textarea::-webkit-input-placeholder{color:var(--ec-form-control-color-placeholder)}ec-form-control>input::-moz-placeholder,ec-form-control>select::-moz-placeholder,ec-form-control>textarea::-moz-placeholder{color:var(--ec-form-control-color-placeholder);opacity:1}ec-form-control>input:-ms-input-placeholder,ec-form-control>select:-ms-input-placeholder,ec-form-control>textarea:-ms-input-placeholder{color:var(--ec-form-control-color-placeholder)}ec-form-control>input:-moz-placeholder,ec-form-control>select:-moz-placeholder,ec-form-control>textarea:-moz-placeholder{color:var(--ec-form-control-color-placeholder);opacity:1}ec-form-control>input:focus,ec-form-control>select:focus,ec-form-control>textarea:focus{outline:none}ec-form-control>input:focus~.ec-focus-ring,ec-form-control>select:focus~.ec-focus-ring,ec-form-control>textarea:focus~.ec-focus-ring{display:block}ec-form-control>input,ec-form-control>textarea,ec-form-control>select,ec-form-control .ec-form-control-prefix,ec-form-control .ec-form-control-suffix{padding:calc((1.875rem - (var(--ec-font-size-body) * 1.25)) / 2) 0;line-height:1.25;font-size:inherit}ec-form-control>textarea{resize:none;padding:.3125rem 0}ec-form-control .ec-form-control-icon{margin-top:.4375rem;margin-right:.25rem;order:1}ec-form-control .ec-form-control-icon:not([class*=icon-]){display:none}ec-form-control .icon-required,ec-form-control .icon-invalid{color:var(--ec-form-control-border-color-invalid)}ec-form-control .ec-form-control-clear,ec-form-control .ec-form-control-action{cursor:pointer;flex:none;height:1.875rem;width:2rem;order:3}ec-form-control .ec-form-control-clear:last-of-type,ec-form-control .ec-form-control-action:last-of-type{margin-right:-.5rem}ec-form-control .ec-form-control-clear:hover,ec-form-control .ec-form-control-action:hover{background-color:var(--ec-background-color-hover)}ec-form-control .ec-form-control-clear{width:1.5rem}ec-form-control .ec-form-control-prefix,ec-form-control .ec-form-control-suffix{color:var(--ec-color-secondary-dark);flex:none;cursor:default}ec-form-control .ec-form-control-prefix{margin-right:.125rem;order:2}ec-form-control .ec-form-control-suffix{margin-left:.125rem;order:3}ec-form-control .ec-focus-ring{position:absolute;inset:-1px;border:.125rem solid var(--ec-form-control-border-color-focus);pointer-events:none;display:none;border-radius:var(--ec-form-control-border-radius);z-index:1}ec-form-control.is-pending .icon-invalid,ec-form-control.is-pending .icon-required{display:none}ec-form-control.is-invalid{border-color:var(--ec-form-control-border-color-invalid);background-color:var(--ec-form-control-background-color-invalid)}ec-form-control.is-invalid .icon-required{display:none}ec-form-control.is-empty .ec-form-control-clear{display:none}ec-form-control.is-required.is-empty .ec-form-control-icon:first-of-type{display:none}ec-form-control:not(.is-pending) .icon-loading{display:none}ec-form-control:not(.is-required) .icon-required,ec-form-control:not(.is-empty) .icon-required{display:none}ec-form-control:not(.is-invalid) .icon-invalid{display:none}ec-form-control.is-readonly,ec-form-control.is-disabled{background-color:var(--ec-form-control-background-color-disabled)}ec-form-control.is-readonly .icon-loading,ec-form-control.is-readonly .icon-invalid,ec-form-control.is-readonly .icon-required,ec-form-control.is-readonly .ec-form-control-clear,ec-form-control.is-disabled .icon-loading,ec-form-control.is-disabled .icon-invalid,ec-form-control.is-disabled .icon-required,ec-form-control.is-disabled .ec-form-control-clear{display:none}ec-form-control.is-readonly{border-color:var(--ec-form-control-border-color-readonly);color:var(--ec-form-control-color-readonly)}ec-form-control.is-readonly .ec-form-control-action{display:none}ec-form-control.is-disabled:not(.is-readonly){color:var(--ec-form-control-color-disabled);opacity:.6}ec-form-control.is-disabled:not(.is-readonly) .ec-form-control-prefix,ec-form-control.is-disabled:not(.is-readonly) .ec-form-control-suffix{color:inherit}\n"] }]
3776
+ args: [{ selector: 'ec-form-control', encapsulation: ViewEncapsulation.None, template: "<ng-content></ng-content>\r\n<i id=\"{{id + '_icon'}}\" class=\"ec-form-control-icon ec-icon {{icon}}\"></i>\r\n<i id=\"{{id + '_loading'}}\" class=\"ec-form-control-icon ec-icon icon-loading\"></i>\r\n<i id=\"{{id + '_required'}}\" class=\"ec-form-control-icon ec-icon icon-required\"></i>\r\n<i id=\"{{id + '_invalid'}}\" class=\"ec-form-control-icon ec-icon icon-invalid\"></i>\r\n<i id=\"{{id + '_clear'}}\" *ngIf=\"showClear\" class=\"ec-form-control-clear ec-icon icon-cancel\" (click)=\"clear()\"></i>\r\n<i id=\"{{id + '_action'}}\" *ngIf=\"actionIcon\" class=\"ec-form-control-action ec-icon {{actionIcon}}\" (click)=\"actionClicked.emit()\"></i>\r\n<div class=\"ec-focus-ring\"></div>", styles: ["ec-form-control{background-color:var(--ec-form-control-background-color);background-clip:padding-box;border:1px solid var(--ec-form-control-border-color);border-radius:var(--ec-border-radius);padding:0 .5rem;position:relative;color:var(--ec-form-control-color);display:flex;font-size:var(--ec-form-control-font-size);min-height:2rem;width:100%}ec-form-control>input,ec-form-control>select,ec-form-control>textarea,ec-form-control>div[contenteditable=true]{color:inherit;flex:1 1;min-width:0;border:0;background-color:transparent;order:2}ec-form-control>input::selection,ec-form-control>select::selection,ec-form-control>textarea::selection,ec-form-control>div[contenteditable=true]::selection{background-color:var(--ec-form-control-background-color-selection);color:var(--ec-form-control-color-selection)}ec-form-control>input::-webkit-input-placeholder,ec-form-control>select::-webkit-input-placeholder,ec-form-control>textarea::-webkit-input-placeholder,ec-form-control>div[contenteditable=true]::-webkit-input-placeholder{color:var(--ec-form-control-color-placeholder)}ec-form-control>input::-moz-placeholder,ec-form-control>select::-moz-placeholder,ec-form-control>textarea::-moz-placeholder,ec-form-control>div[contenteditable=true]::-moz-placeholder{color:var(--ec-form-control-color-placeholder);opacity:1}ec-form-control>input:-ms-input-placeholder,ec-form-control>select:-ms-input-placeholder,ec-form-control>textarea:-ms-input-placeholder,ec-form-control>div[contenteditable=true]:-ms-input-placeholder{color:var(--ec-form-control-color-placeholder)}ec-form-control>input:-moz-placeholder,ec-form-control>select:-moz-placeholder,ec-form-control>textarea:-moz-placeholder,ec-form-control>div[contenteditable=true]:-moz-placeholder{color:var(--ec-form-control-color-placeholder);opacity:1}ec-form-control>input:focus,ec-form-control>select:focus,ec-form-control>textarea:focus,ec-form-control>div[contenteditable=true]:focus{outline:none}ec-form-control>input:focus~.ec-focus-ring,ec-form-control>select:focus~.ec-focus-ring,ec-form-control>textarea:focus~.ec-focus-ring,ec-form-control>div[contenteditable=true]:focus~.ec-focus-ring{display:block}ec-form-control>input,ec-form-control>textarea,ec-form-control>select,ec-form-control>div[contenteditable=true],ec-form-control .ec-form-control-prefix,ec-form-control .ec-form-control-suffix{padding:calc((1.875rem - (var(--ec-font-size-body) * 1.25)) / 2) 0;line-height:1.25;font-size:inherit}ec-form-control>textarea{resize:none;padding:.3125rem 0}ec-form-control .ec-form-control-icon{margin-top:.4375rem;margin-right:.25rem;order:1}ec-form-control .ec-form-control-icon:not([class*=icon-]){display:none}ec-form-control .icon-required,ec-form-control .icon-invalid{color:var(--ec-form-control-border-color-invalid)}ec-form-control .ec-form-control-clear,ec-form-control .ec-form-control-action{cursor:pointer;flex:none;height:1.875rem;width:2rem;order:3}ec-form-control .ec-form-control-clear:last-of-type,ec-form-control .ec-form-control-action:last-of-type{margin-right:-.5rem}ec-form-control .ec-form-control-clear:hover,ec-form-control .ec-form-control-action:hover{background-color:var(--ec-background-color-hover)}ec-form-control .ec-form-control-clear{width:1.5rem}ec-form-control .ec-form-control-prefix,ec-form-control .ec-form-control-suffix{color:var(--ec-color-secondary-dark);flex:none;cursor:default}ec-form-control .ec-form-control-prefix{margin-right:.125rem;order:2}ec-form-control .ec-form-control-suffix{margin-left:.125rem;order:3}ec-form-control .ec-focus-ring{position:absolute;inset:-1px;border:.125rem solid var(--ec-form-control-border-color-focus);pointer-events:none;display:none;border-radius:var(--ec-form-control-border-radius);z-index:1}ec-form-control.is-pending .icon-invalid,ec-form-control.is-pending .icon-required{display:none}ec-form-control.is-invalid{border-color:var(--ec-form-control-border-color-invalid);background-color:var(--ec-form-control-background-color-invalid)}ec-form-control.is-invalid .icon-required{display:none}ec-form-control.is-empty .ec-form-control-clear{display:none}ec-form-control.is-required.is-empty .ec-form-control-icon:first-of-type{display:none}ec-form-control:not(.is-pending) .icon-loading{display:none}ec-form-control:not(.is-required) .icon-required,ec-form-control:not(.is-empty) .icon-required{display:none}ec-form-control:not(.is-invalid) .icon-invalid{display:none}ec-form-control.is-readonly,ec-form-control.is-disabled{background-color:var(--ec-form-control-background-color-disabled)}ec-form-control.is-readonly .icon-loading,ec-form-control.is-readonly .icon-invalid,ec-form-control.is-readonly .icon-required,ec-form-control.is-readonly .ec-form-control-clear,ec-form-control.is-disabled .icon-loading,ec-form-control.is-disabled .icon-invalid,ec-form-control.is-disabled .icon-required,ec-form-control.is-disabled .ec-form-control-clear{display:none}ec-form-control.is-readonly{border-color:var(--ec-form-control-border-color-readonly);color:var(--ec-form-control-color-readonly)}ec-form-control.is-readonly .ec-form-control-action{display:none}ec-form-control.is-disabled:not(.is-readonly){color:var(--ec-form-control-color-disabled);opacity:.6}ec-form-control.is-disabled:not(.is-readonly) .ec-form-control-prefix,ec-form-control.is-disabled:not(.is-readonly) .ec-form-control-suffix{color:inherit}\n"] }]
3807
3777
  }], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { formModel: [{
3808
3778
  type: ContentChild,
3809
3779
  args: [FormControlDirective]