@energycap/components 0.39.17-ECAP-25250-eclipse-formula-builder-proto.20240530-1020 → 0.39.17

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