@ifsworld/granite-components 18.0.2-beta.1 → 18.1.0-beta.2
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.
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, ElementRef, ChangeDetectionStrategy, Component, Input, ContentChildren, HostBinding, NgModule, Renderer2, InjectionToken, HostAttributeToken, HostListener, Directive, EventEmitter, ChangeDetectorRef, QueryList, TemplateRef, ContentChild, Output, ViewChild, ViewContainerRef, NgZone,
|
|
2
|
+
import { inject, ElementRef, ChangeDetectionStrategy, Component, Input, ContentChildren, HostBinding, NgModule, Renderer2, InjectionToken, Injectable, HostAttributeToken, HostListener, Directive, EventEmitter, ChangeDetectorRef, QueryList, TemplateRef, ContentChild, Output, ViewChild, ViewContainerRef, NgZone, Optional, Pipe } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/common';
|
|
4
4
|
import { CommonModule, DOCUMENT } from '@angular/common';
|
|
5
5
|
import { coerceNumberProperty, coerceBooleanProperty } from '@angular/cdk/coercion';
|
|
@@ -665,55 +665,51 @@ const graniteMenuTouchAnimations = {
|
|
|
665
665
|
const GRANITE_MENU_PANEL = new InjectionToken('GRANITE_MENU_PANEL');
|
|
666
666
|
|
|
667
667
|
/**
|
|
668
|
-
*
|
|
669
|
-
*
|
|
668
|
+
* Stack of open touch menus so outside-click / close affects only the top
|
|
669
|
+
* panel.
|
|
670
670
|
*/
|
|
671
|
-
class
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
const index = this.stack.
|
|
671
|
+
class GraniteMenuStackService {
|
|
672
|
+
constructor() {
|
|
673
|
+
this.stack = [];
|
|
674
|
+
}
|
|
675
|
+
/** Push a menu onto the stack when it opens */
|
|
676
|
+
push(menu) {
|
|
677
|
+
const index = this.stack.findIndex((m) => m.panelId === menu.panelId);
|
|
678
678
|
if (index === -1) {
|
|
679
679
|
this.stack.push(menu);
|
|
680
680
|
}
|
|
681
681
|
}
|
|
682
|
-
/**
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
static pop(menu) {
|
|
686
|
-
const index = this.stack.indexOf(menu);
|
|
682
|
+
/** Remove a menu from the stack when it closes */
|
|
683
|
+
pop(menu) {
|
|
684
|
+
const index = this.stack.findIndex((m) => m.panelId === menu.panelId);
|
|
687
685
|
if (index !== -1) {
|
|
688
686
|
this.stack.splice(index, 1);
|
|
689
687
|
}
|
|
690
688
|
}
|
|
691
|
-
/**
|
|
692
|
-
|
|
693
|
-
*/
|
|
694
|
-
static getTop() {
|
|
689
|
+
/** Topmost menu in the stack */
|
|
690
|
+
getTop() {
|
|
695
691
|
return this.stack.length > 0 ? this.stack[this.stack.length - 1] : null;
|
|
696
692
|
}
|
|
697
|
-
/**
|
|
698
|
-
|
|
699
|
-
*/
|
|
700
|
-
static isTop(menu) {
|
|
693
|
+
/** Whether the menu is the topmost one */
|
|
694
|
+
isTop(menu) {
|
|
701
695
|
const top = this.getTop();
|
|
702
696
|
return top !== null && top.panelId === menu.panelId;
|
|
703
697
|
}
|
|
704
|
-
/**
|
|
705
|
-
|
|
706
|
-
*/
|
|
707
|
-
static clear() {
|
|
698
|
+
/** Clear the stack (e.g. tests) */
|
|
699
|
+
clear() {
|
|
708
700
|
this.stack = [];
|
|
709
701
|
}
|
|
710
|
-
/**
|
|
711
|
-
|
|
712
|
-
*/
|
|
713
|
-
static size() {
|
|
702
|
+
/** Current stack depth */
|
|
703
|
+
size() {
|
|
714
704
|
return this.stack.length;
|
|
715
705
|
}
|
|
706
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: GraniteMenuStackService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
707
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: GraniteMenuStackService, providedIn: 'root' }); }
|
|
716
708
|
}
|
|
709
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: GraniteMenuStackService, decorators: [{
|
|
710
|
+
type: Injectable,
|
|
711
|
+
args: [{ providedIn: 'root' }]
|
|
712
|
+
}] });
|
|
717
713
|
|
|
718
714
|
/**
|
|
719
715
|
* @license
|
|
@@ -966,7 +962,10 @@ class _MenuBaseComponent {
|
|
|
966
962
|
this._transformMenu = new BehaviorSubject(transformMenuDefault);
|
|
967
963
|
/** Emits whenever an animation on the menu completes. */
|
|
968
964
|
this._animationDone = new Subject();
|
|
965
|
+
/** Whether this menu participates in the menu stack. */
|
|
966
|
+
this._usesMenuStack = false;
|
|
969
967
|
this._changeDetectorRef = inject(ChangeDetectorRef);
|
|
968
|
+
this._menuStack = inject(GraniteMenuStackService);
|
|
970
969
|
this._menuEmpty$ = new BehaviorSubject(false);
|
|
971
970
|
// eslint-disable-next-line @typescript-eslint/member-ordering
|
|
972
971
|
this._isMenuEmpty$ = combineLatest([
|
|
@@ -1211,7 +1210,7 @@ class _MenuBaseComponent {
|
|
|
1211
1210
|
//#region --- Touch device customizations ---
|
|
1212
1211
|
if (this._clientOutput?.device === 'touch') {
|
|
1213
1212
|
event?.stopPropagation();
|
|
1214
|
-
if (!
|
|
1213
|
+
if (this._usesMenuStack && !this._menuStack.isTop(this)) {
|
|
1215
1214
|
return;
|
|
1216
1215
|
}
|
|
1217
1216
|
}
|
|
@@ -1404,6 +1403,7 @@ class GraniteMenuTriggerForDirective {
|
|
|
1404
1403
|
// Tracking input type is necessary so it's possible to only auto-focus
|
|
1405
1404
|
// the first item of the list when the menu is opened via the keyboard
|
|
1406
1405
|
this.openedBy = null;
|
|
1406
|
+
this._isInCustomTemplate = false;
|
|
1407
1407
|
this._hoverSubscription = Subscription.EMPTY;
|
|
1408
1408
|
this._menuCloseSubscription = Subscription.EMPTY;
|
|
1409
1409
|
this._closingActionsSubscription = Subscription.EMPTY;
|
|
@@ -1428,6 +1428,7 @@ class GraniteMenuTriggerForDirective {
|
|
|
1428
1428
|
});
|
|
1429
1429
|
this._dir = inject(Directionality, { optional: true });
|
|
1430
1430
|
this._focusMonitor = inject(FocusMonitor);
|
|
1431
|
+
this._menuStack = inject(GraniteMenuStackService);
|
|
1431
1432
|
this._touchTouchingElement = false;
|
|
1432
1433
|
/**
|
|
1433
1434
|
* Handles touch start events on the trigger.
|
|
@@ -1544,9 +1545,11 @@ class GraniteMenuTriggerForDirective {
|
|
|
1544
1545
|
if (this._overlayRef) {
|
|
1545
1546
|
//#region --- Touch device customizations ---
|
|
1546
1547
|
this.removeOverlayListeners();
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1548
|
+
if (this.menu &&
|
|
1549
|
+
this._clientOutput?.device === 'touch' &&
|
|
1550
|
+
this._isInCustomTemplate) {
|
|
1551
|
+
this._menuStack.pop(this.menu);
|
|
1552
|
+
this.menu._usesMenuStack = false;
|
|
1550
1553
|
}
|
|
1551
1554
|
//#endregion --- Touch device customizations ---
|
|
1552
1555
|
this._overlayRef.dispose();
|
|
@@ -1571,8 +1574,10 @@ class GraniteMenuTriggerForDirective {
|
|
|
1571
1574
|
this.menu._isClosing = true;
|
|
1572
1575
|
// Get rid of the menu and tell any parent to restore its position
|
|
1573
1576
|
if (this._clientOutput.device === 'touch') {
|
|
1574
|
-
|
|
1575
|
-
|
|
1577
|
+
if (this._isInCustomTemplate) {
|
|
1578
|
+
this._menuStack.pop(this.menu);
|
|
1579
|
+
this.menu._usesMenuStack = false;
|
|
1580
|
+
}
|
|
1576
1581
|
// First we wait for any running animation to complete
|
|
1577
1582
|
const runningAnimationDone = this.menu._isAnimating
|
|
1578
1583
|
? this.menu._animationDone
|
|
@@ -1590,11 +1595,11 @@ class GraniteMenuTriggerForDirective {
|
|
|
1590
1595
|
//#endregion --- Touch device customizations ---
|
|
1591
1596
|
this._destroyMenu();
|
|
1592
1597
|
}
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
+
const isTouch = this._clientOutput.device === 'touch';
|
|
1599
|
+
const shouldPropagate = this._parentMenu &&
|
|
1600
|
+
(isTouch
|
|
1601
|
+
? !this._isInCustomTemplate && reason !== 'keydown'
|
|
1602
|
+
: reason === 'click' || reason === 'tab');
|
|
1598
1603
|
if (shouldPropagate) {
|
|
1599
1604
|
if (!this.menu.preventParentClose) {
|
|
1600
1605
|
this._parentMenu.closed.emit(reason);
|
|
@@ -1616,12 +1621,11 @@ class GraniteMenuTriggerForDirective {
|
|
|
1616
1621
|
this.menu.parentMenu = this.triggersSubmenu()
|
|
1617
1622
|
? this._parentMenu
|
|
1618
1623
|
: undefined;
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
}
|
|
1624
|
+
const isInCustomTemplate = !!(this._parentMenu &&
|
|
1625
|
+
this._viewContainerRef?.element?.nativeElement?.closest('.granite-menu-custom-template'));
|
|
1626
|
+
this._isInCustomTemplate = isInCustomTemplate;
|
|
1627
|
+
if (isInCustomTemplate) {
|
|
1628
|
+
this.menu.preventParentClose = true;
|
|
1625
1629
|
}
|
|
1626
1630
|
this.menu.direction = this._dir.value === 'rtl' ? 'rtl' : 'ltr';
|
|
1627
1631
|
if (this._parentMenu) {
|
|
@@ -1647,6 +1651,12 @@ class GraniteMenuTriggerForDirective {
|
|
|
1647
1651
|
if (this._clientOutput.device === 'touch') {
|
|
1648
1652
|
panelClass.push('granite-overlay-pane-fill-width-bottom');
|
|
1649
1653
|
}
|
|
1654
|
+
if (isInCustomTemplate) {
|
|
1655
|
+
panelClass.push('from-custom-template');
|
|
1656
|
+
}
|
|
1657
|
+
if (this.menu.customTemplate) {
|
|
1658
|
+
panelClass.push('has-custom-template');
|
|
1659
|
+
}
|
|
1650
1660
|
// setting scrollStrategy options to overlay
|
|
1651
1661
|
const scrollStrategy = this.setScrollStrategyToOverylay(this._clientOutput.device, this.menu.scrollStrategy);
|
|
1652
1662
|
const hasBackdrop = this._clientOutput.device === 'touch' && !this.triggersSubmenu();
|
|
@@ -1674,6 +1684,18 @@ class GraniteMenuTriggerForDirective {
|
|
|
1674
1684
|
if (this._clientOutput.device === 'touch') {
|
|
1675
1685
|
this.menu._panelAnimationState = 'void';
|
|
1676
1686
|
}
|
|
1687
|
+
if (isInCustomTemplate) {
|
|
1688
|
+
this._overlayRef.addPanelClass('from-custom-template');
|
|
1689
|
+
}
|
|
1690
|
+
else {
|
|
1691
|
+
this._overlayRef.removePanelClass('from-custom-template');
|
|
1692
|
+
}
|
|
1693
|
+
if (this.menu.customTemplate) {
|
|
1694
|
+
this._overlayRef.addPanelClass('has-custom-template');
|
|
1695
|
+
}
|
|
1696
|
+
else {
|
|
1697
|
+
this._overlayRef.removePanelClass('has-custom-template');
|
|
1698
|
+
}
|
|
1677
1699
|
}
|
|
1678
1700
|
// Create portal from associated menu's template
|
|
1679
1701
|
if (!this._portal || this._portal.templateRef !== this.menu.templateRef) {
|
|
@@ -1685,9 +1707,10 @@ class GraniteMenuTriggerForDirective {
|
|
|
1685
1707
|
this._closingActionsSubscription = this._menuClosingActions().subscribe(() => this.closeMenu());
|
|
1686
1708
|
this.animateOpenMenu();
|
|
1687
1709
|
//#region --- Touch device customizations ---
|
|
1688
|
-
// Add menu to stack when opening on touch devices
|
|
1689
|
-
if (this._clientOutput.device === 'touch') {
|
|
1690
|
-
|
|
1710
|
+
// Add menu to stack when opening on touch devices from custom template
|
|
1711
|
+
if (this._clientOutput.device === 'touch' && isInCustomTemplate) {
|
|
1712
|
+
this._menuStack.push(this.menu);
|
|
1713
|
+
this.menu._usesMenuStack = true;
|
|
1691
1714
|
}
|
|
1692
1715
|
//#endregion --- Touch device customizations ---
|
|
1693
1716
|
this._setIsMenuOpen(true);
|
|
@@ -1951,13 +1974,20 @@ class GraniteMenuTriggerForDirective {
|
|
|
1951
1974
|
// root menu only.
|
|
1952
1975
|
// For touch devices, handle outside clicks for all menus (not just root)
|
|
1953
1976
|
// but only close the topmost menu in the stack
|
|
1954
|
-
const
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1977
|
+
const isTouchCustom = this._clientOutput.device === 'touch' && this._isInCustomTemplate;
|
|
1978
|
+
const outsideClick = isTouchCustom
|
|
1979
|
+
? fromEvent(this._document, 'click').pipe(filter((e) => {
|
|
1980
|
+
const el = e.target;
|
|
1981
|
+
return (el !== this._element.nativeElement &&
|
|
1982
|
+
el.closest('.granite-menu') === null &&
|
|
1983
|
+
this._menuStack.isTop(this.menu));
|
|
1984
|
+
}), filter(() => !this.menu._isAnimating))
|
|
1958
1985
|
: !this._parentMenu
|
|
1959
|
-
? fromEvent(this._document, 'click').pipe(filter((e) =>
|
|
1960
|
-
|
|
1986
|
+
? fromEvent(this._document, 'click').pipe(filter((e) => {
|
|
1987
|
+
const el = e.target;
|
|
1988
|
+
return (el !== this._element.nativeElement &&
|
|
1989
|
+
el.closest('.granite-menu') === null);
|
|
1990
|
+
}), filter(() => !this.menu._isAnimating), filter(() => !this.menu.customTemplate || this._menuStack.size() === 0))
|
|
1961
1991
|
: of(null);
|
|
1962
1992
|
return merge(detachments, hover, parentClose, outsideClick).pipe(filter((event) => event !== null));
|
|
1963
1993
|
}
|