@cute-widgets/base 20.0.5 → 21.0.0
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.
- package/CHANGELOG.md +7 -0
- package/fesm2022/cute-widgets-base-abstract.mjs +15 -15
- package/fesm2022/cute-widgets-base-abstract.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-alert.mjs +10 -10
- package/fesm2022/cute-widgets-base-alert.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-autocomplete.mjs +14 -14
- package/fesm2022/cute-widgets-base-autocomplete.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-badge.mjs +46 -14
- package/fesm2022/cute-widgets-base-badge.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-bottom-sheet.mjs +11 -11
- package/fesm2022/cute-widgets-base-bottom-sheet.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-button-toggle.mjs +29 -12
- package/fesm2022/cute-widgets-base-button-toggle.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-button.mjs +20 -20
- package/fesm2022/cute-widgets-base-button.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-card.mjs +40 -40
- package/fesm2022/cute-widgets-base-card.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-checkbox.mjs +28 -28
- package/fesm2022/cute-widgets-base-checkbox.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-chips.mjs +49 -49
- package/fesm2022/cute-widgets-base-chips.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-collapse.mjs +14 -14
- package/fesm2022/cute-widgets-base-collapse.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-core-datetime.mjs +11 -11
- package/fesm2022/cute-widgets-base-core-datetime.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-core-directives.mjs +21 -21
- package/fesm2022/cute-widgets-base-core-directives.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-core-error.mjs +6 -6
- package/fesm2022/cute-widgets-base-core-error.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-core-line.mjs +7 -7
- package/fesm2022/cute-widgets-base-core-line.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-core-nav.mjs +30 -28
- package/fesm2022/cute-widgets-base-core-nav.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-core-observers.mjs +16 -16
- package/fesm2022/cute-widgets-base-core-observers.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-core-option.mjs +10 -10
- package/fesm2022/cute-widgets-base-core-option.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-core-pipes.mjs +6 -6
- package/fesm2022/cute-widgets-base-core-pipes.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-core-ripple.mjs +3 -3
- package/fesm2022/cute-widgets-base-core-ripple.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-core-theming.mjs +6 -6
- package/fesm2022/cute-widgets-base-core-theming.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-core-utils.mjs +3 -3
- package/fesm2022/cute-widgets-base-core-utils.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-core.mjs +36 -36
- package/fesm2022/cute-widgets-base-core.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-datepicker.mjs +100 -99
- package/fesm2022/cute-widgets-base-datepicker.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-dialog.mjs +31 -31
- package/fesm2022/cute-widgets-base-dialog.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-divider.mjs +7 -7
- package/fesm2022/cute-widgets-base-divider.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-expansion.mjs +27 -27
- package/fesm2022/cute-widgets-base-expansion.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-form-field.mjs +28 -28
- package/fesm2022/cute-widgets-base-form-field.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-grid-list.mjs +22 -22
- package/fesm2022/cute-widgets-base-grid-list.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-icon.mjs +10 -10
- package/fesm2022/cute-widgets-base-icon.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-input.mjs +7 -7
- package/fesm2022/cute-widgets-base-input.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-layout-container.mjs +10 -10
- package/fesm2022/cute-widgets-base-layout-container.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-layout-stack.mjs +13 -13
- package/fesm2022/cute-widgets-base-layout-stack.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-layout.mjs +23 -23
- package/fesm2022/cute-widgets-base-layout.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-list.mjs +56 -56
- package/fesm2022/cute-widgets-base-list.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-menu.mjs +579 -364
- package/fesm2022/cute-widgets-base-menu.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-navbar.mjs +27 -27
- package/fesm2022/cute-widgets-base-navbar.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-paginator.mjs +11 -11
- package/fesm2022/cute-widgets-base-paginator.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-progress.mjs +10 -10
- package/fesm2022/cute-widgets-base-progress.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-radio.mjs +11 -11
- package/fesm2022/cute-widgets-base-radio.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-select.mjs +11 -11
- package/fesm2022/cute-widgets-base-select.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-sidenav.mjs +26 -26
- package/fesm2022/cute-widgets-base-sidenav.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-slider.mjs +10 -10
- package/fesm2022/cute-widgets-base-slider.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-snack-bar.mjs +28 -28
- package/fesm2022/cute-widgets-base-snack-bar.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-sort.mjs +13 -13
- package/fesm2022/cute-widgets-base-sort.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-spinner.mjs +8 -8
- package/fesm2022/cute-widgets-base-spinner.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-stepper.mjs +40 -40
- package/fesm2022/cute-widgets-base-stepper.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-table.mjs +58 -66
- package/fesm2022/cute-widgets-base-table.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-tabs.mjs +16 -16
- package/fesm2022/cute-widgets-base-tabs.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-timepicker.mjs +34 -63
- package/fesm2022/cute-widgets-base-timepicker.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-toolbar.mjs +13 -13
- package/fesm2022/cute-widgets-base-toolbar.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-tooltip.mjs +10 -10
- package/fesm2022/cute-widgets-base-tooltip.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-tree.mjs +25 -25
- package/fesm2022/cute-widgets-base-tree.mjs.map +1 -1
- package/fesm2022/cute-widgets-base.mjs +4 -4
- package/package.json +118 -118
- package/{abstract/index.d.ts → types/cute-widgets-base-abstract.d.ts} +1 -1
- package/{autocomplete/index.d.ts → types/cute-widgets-base-autocomplete.d.ts} +2 -2
- package/{badge/index.d.ts → types/cute-widgets-base-badge.d.ts} +6 -5
- package/{bottom-sheet/index.d.ts → types/cute-widgets-base-bottom-sheet.d.ts} +1 -1
- package/{button-toggle/index.d.ts → types/cute-widgets-base-button-toggle.d.ts} +7 -1
- package/{button/index.d.ts → types/cute-widgets-base-button.d.ts} +1 -1
- package/{checkbox/index.d.ts → types/cute-widgets-base-checkbox.d.ts} +1 -1
- package/{chips/index.d.ts → types/cute-widgets-base-chips.d.ts} +1 -1
- package/{core/nav/index.d.ts → types/cute-widgets-base-core-nav.d.ts} +2 -1
- package/{core/observers/index.d.ts → types/cute-widgets-base-core-observers.d.ts} +1 -1
- package/{core/option/index.d.ts → types/cute-widgets-base-core-option.d.ts} +1 -1
- package/{datepicker/index.d.ts → types/cute-widgets-base-datepicker.d.ts} +2 -2
- package/{expansion/index.d.ts → types/cute-widgets-base-expansion.d.ts} +4 -14
- package/{form-field/index.d.ts → types/cute-widgets-base-form-field.d.ts} +1 -1
- package/{icon/index.d.ts → types/cute-widgets-base-icon.d.ts} +1 -1
- package/{input/index.d.ts → types/cute-widgets-base-input.d.ts} +1 -1
- package/{menu/index.d.ts → types/cute-widgets-base-menu.d.ts} +202 -121
- package/{progress/index.d.ts → types/cute-widgets-base-progress.d.ts} +1 -1
- package/{radio/index.d.ts → types/cute-widgets-base-radio.d.ts} +2 -2
- package/{sidenav/index.d.ts → types/cute-widgets-base-sidenav.d.ts} +1 -1
- package/{snack-bar/index.d.ts → types/cute-widgets-base-snack-bar.d.ts} +1 -1
- package/{sort/index.d.ts → types/cute-widgets-base-sort.d.ts} +1 -1
- package/{table/index.d.ts → types/cute-widgets-base-table.d.ts} +1 -1
- package/{tree/index.d.ts → types/cute-widgets-base-tree.d.ts} +2 -2
- /package/{alert/index.d.ts → types/cute-widgets-base-alert.d.ts} +0 -0
- /package/{card/index.d.ts → types/cute-widgets-base-card.d.ts} +0 -0
- /package/{collapse/index.d.ts → types/cute-widgets-base-collapse.d.ts} +0 -0
- /package/{core/animation/index.d.ts → types/cute-widgets-base-core-animation.d.ts} +0 -0
- /package/{core/datetime/index.d.ts → types/cute-widgets-base-core-datetime.d.ts} +0 -0
- /package/{core/directives/index.d.ts → types/cute-widgets-base-core-directives.d.ts} +0 -0
- /package/{core/error/index.d.ts → types/cute-widgets-base-core-error.d.ts} +0 -0
- /package/{core/interfaces/index.d.ts → types/cute-widgets-base-core-interfaces.d.ts} +0 -0
- /package/{core/layout/index.d.ts → types/cute-widgets-base-core-layout.d.ts} +0 -0
- /package/{core/line/index.d.ts → types/cute-widgets-base-core-line.d.ts} +0 -0
- /package/{core/pipes/index.d.ts → types/cute-widgets-base-core-pipes.d.ts} +0 -0
- /package/{core/ripple/index.d.ts → types/cute-widgets-base-core-ripple.d.ts} +0 -0
- /package/{core/testing/index.d.ts → types/cute-widgets-base-core-testing.d.ts} +0 -0
- /package/{core/theming/index.d.ts → types/cute-widgets-base-core-theming.d.ts} +0 -0
- /package/{core/types/index.d.ts → types/cute-widgets-base-core-types.d.ts} +0 -0
- /package/{core/utils/index.d.ts → types/cute-widgets-base-core-utils.d.ts} +0 -0
- /package/{core/index.d.ts → types/cute-widgets-base-core.d.ts} +0 -0
- /package/{dialog/index.d.ts → types/cute-widgets-base-dialog.d.ts} +0 -0
- /package/{divider/index.d.ts → types/cute-widgets-base-divider.d.ts} +0 -0
- /package/{grid-list/index.d.ts → types/cute-widgets-base-grid-list.d.ts} +0 -0
- /package/{layout/container/index.d.ts → types/cute-widgets-base-layout-container.d.ts} +0 -0
- /package/{layout/stack/index.d.ts → types/cute-widgets-base-layout-stack.d.ts} +0 -0
- /package/{layout/index.d.ts → types/cute-widgets-base-layout.d.ts} +0 -0
- /package/{list/index.d.ts → types/cute-widgets-base-list.d.ts} +0 -0
- /package/{navbar/index.d.ts → types/cute-widgets-base-navbar.d.ts} +0 -0
- /package/{paginator/index.d.ts → types/cute-widgets-base-paginator.d.ts} +0 -0
- /package/{select/index.d.ts → types/cute-widgets-base-select.d.ts} +0 -0
- /package/{slider/index.d.ts → types/cute-widgets-base-slider.d.ts} +0 -0
- /package/{spinner/index.d.ts → types/cute-widgets-base-spinner.d.ts} +0 -0
- /package/{stepper/index.d.ts → types/cute-widgets-base-stepper.d.ts} +0 -0
- /package/{tabs/index.d.ts → types/cute-widgets-base-tabs.d.ts} +0 -0
- /package/{timepicker/index.d.ts → types/cute-widgets-base-timepicker.d.ts} +0 -0
- /package/{toolbar/index.d.ts → types/cute-widgets-base-toolbar.d.ts} +0 -0
- /package/{tooltip/index.d.ts → types/cute-widgets-base-tooltip.d.ts} +0 -0
- /package/{index.d.ts → types/cute-widgets-base.d.ts} +0 -0
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, inject, ElementRef, DOCUMENT, ChangeDetectorRef, booleanAttribute, Input, ViewEncapsulation, ChangeDetectionStrategy, Component, TemplateRef, ApplicationRef, Injector, ViewContainerRef, Directive, isDevMode, QueryList, EventEmitter, afterNextRender, Output, ContentChild, ContentChildren, ViewChild,
|
|
3
|
-
import * as i4 from '@angular/cdk/a11y';
|
|
2
|
+
import { InjectionToken, inject, ElementRef, DOCUMENT, ChangeDetectorRef, booleanAttribute, Input, ViewEncapsulation, ChangeDetectionStrategy, Component, TemplateRef, ApplicationRef, Injector, ViewContainerRef, Directive, isDevMode, QueryList, signal, EventEmitter, afterNextRender, Output, ContentChild, ContentChildren, ViewChild, NgZone, Renderer2, NgModule } from '@angular/core';
|
|
4
3
|
import { FocusMonitor, FocusKeyManager, isFakeTouchstartFromScreenReader, isFakeMousedownFromScreenReader } from '@angular/cdk/a11y';
|
|
5
4
|
import { UP_ARROW, DOWN_ARROW, RIGHT_ARROW, LEFT_ARROW, ESCAPE, hasModifierKey, ENTER, SPACE } from '@angular/cdk/keycodes';
|
|
6
|
-
import { Subject, merge, Subscription, of
|
|
7
|
-
import { startWith, switchMap, takeUntil,
|
|
5
|
+
import { Subject, merge, Subscription, of } from 'rxjs';
|
|
6
|
+
import { startWith, switchMap, takeUntil, take, filter, skipWhile } from 'rxjs/operators';
|
|
8
7
|
import { TemplatePortal, DomPortalOutlet } from '@angular/cdk/portal';
|
|
9
|
-
import {
|
|
8
|
+
import { _animationsDisabled } from '@cute-widgets/base/core';
|
|
10
9
|
import * as i1 from '@angular/cdk/menu';
|
|
11
10
|
import { CdkMenu } from '@angular/cdk/menu';
|
|
12
|
-
import
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import
|
|
16
|
-
import { CommonModule } from '@angular/common';
|
|
11
|
+
import { Directionality } from '@angular/cdk/bidi';
|
|
12
|
+
import { createRepositionScrollStrategy, createOverlayRef, OverlayConfig, createFlexibleConnectedPositionStrategy, ViewportRuler, ScrollDispatcher, OverlayModule } from '@angular/cdk/overlay';
|
|
13
|
+
import { _getEventTarget, _getShadowRoot } from '@angular/cdk/platform';
|
|
14
|
+
import { CdkScrollableModule } from '@angular/cdk/scrolling';
|
|
17
15
|
|
|
18
16
|
/**
|
|
19
17
|
* @license Apache-2.0
|
|
@@ -135,10 +133,10 @@ class CuteMenuItem {
|
|
|
135
133
|
_hasFocus() {
|
|
136
134
|
return this._document && this._document.activeElement === this._getHostElement();
|
|
137
135
|
}
|
|
138
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
139
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
136
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CuteMenuItem, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
137
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: CuteMenuItem, isStandalone: true, selector: "[cute-menu-item]", inputs: { role: "role", disabled: ["disabled", "disabled", booleanAttribute], disableRipple: ["disableRipple", "disableRipple", booleanAttribute] }, host: { listeners: { "click": "_checkDisabled($event)", "mouseenter": "_handleMouseEnter()" }, properties: { "attr.role": "role", "class.active": "_highlighted", "class.cute-menu-item-submenu-trigger": "_triggersSubmenu", "attr.tabindex": "_getTabIndex()", "attr.aria-disabled": "disabled", "attr.disabled": "disabled || null" }, classAttribute: "cute-menu-item" }, exportAs: ["cuteMenuItem"], ngImport: i0, template: "<ng-content select=\"cute-icon, i[class], [cuteMenuItemIcon]\"></ng-content>\r\n<span class=\"cute-menu-item-text\"><ng-content></ng-content></span>\r\n\r\n@if (_triggersSubmenu) {\r\n <svg class=\"cute-menu-item-submenu-icon\"\r\n viewBox=\"0 0 5 10\"\r\n focusable=\"false\"\r\n aria-hidden=\"true\">\r\n <polygon points=\"0,0 5,5 0,10\"/>\r\n </svg>\r\n}\r\n", styles: [".cute-menu-item{display:flex;position:relative;justify-content:flex-start;cursor:pointer;width:100%;text-align:start;text-overflow:ellipsis;text-decoration:none;box-sizing:border-box;font-size:inherit;margin:0;padding:.25rem 1rem;align-items:center;white-space:nowrap;overflow:hidden;-webkit-user-select:none;user-select:none;border:none;outline:none;background:none;pointer-events:auto}.cute-menu-item:hover,.cute-menu-item.active{outline:none;background-color:rgba(var(--bs-body-color-rgb),.04)}.cute-menu-item[disabled]{opacity:.38;color:var(--bs-body-color);cursor:default}.cute-menu-item[disabled]:hover,.cute-menu-item[disabled]:focus{outline:none;background-color:var(--bs-body-bg)}.cute-menu-item[disabled]:after{display:block;position:absolute;content:\"\";inset:0}.cute-menu-item:not([disabled]).cdk-keyboard-focused{background-color:rgba(var(--bs-body-color-rgb),.04)}.cute-menu-item>[role=img],.cute-menu-item i[class]{position:absolute;left:4px;width:24px;height:24px;line-height:24px;background-repeat:no-repeat;display:inline-block;fill:currentColor;text-align:center;overflow:hidden;-webkit-user-select:none;user-select:none;-webkit-font-smoothing:antialiased}[dir=rtl] .cute-menu-item>[role=img],[dir=rtl] .cute-menu-item i[class]{right:4px;left:auto}.cute-menu-item .cute-menu-item-text{margin-left:1rem}[dir=rtl] .cute-menu-item .cute-menu-item-text{margin-right:1rem;margin-left:auto}.cute-menu-item .cute-menu-item-submenu-icon{position:absolute;top:50%;right:12px;transform:translateY(-50%);width:5px;height:10px;fill:currentColor;opacity:.75}[dir=rtl] .cute-menu-item .cute-menu-item-submenu-icon{left:12px;right:auto;transform:translateY(-50%) rotate(180deg)}.cute-menu-item-submenu-trigger{padding-right:2rem}[dir=rtl] .cute-menu-item-submenu-trigger{padding-right:1rem;padding-left:2rem}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
140
138
|
}
|
|
141
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
139
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CuteMenuItem, decorators: [{
|
|
142
140
|
type: Component,
|
|
143
141
|
args: [{ selector: '[cute-menu-item]', exportAs: 'cuteMenuItem', host: {
|
|
144
142
|
'[attr.role]': 'role',
|
|
@@ -148,7 +146,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
148
146
|
//'[class.disabled]': 'disabled',
|
|
149
147
|
'[attr.tabindex]': '_getTabIndex()',
|
|
150
148
|
'[attr.aria-disabled]': 'disabled',
|
|
151
|
-
'[disabled]': 'disabled || null',
|
|
149
|
+
'[attr.disabled]': 'disabled || null',
|
|
152
150
|
'(click)': '_checkDisabled($event)',
|
|
153
151
|
'(mouseenter)': '_handleMouseEnter()',
|
|
154
152
|
}, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<ng-content select=\"cute-icon, i[class], [cuteMenuItemIcon]\"></ng-content>\r\n<span class=\"cute-menu-item-text\"><ng-content></ng-content></span>\r\n\r\n@if (_triggersSubmenu) {\r\n <svg class=\"cute-menu-item-submenu-icon\"\r\n viewBox=\"0 0 5 10\"\r\n focusable=\"false\"\r\n aria-hidden=\"true\">\r\n <polygon points=\"0,0 5,5 0,10\"/>\r\n </svg>\r\n}\r\n", styles: [".cute-menu-item{display:flex;position:relative;justify-content:flex-start;cursor:pointer;width:100%;text-align:start;text-overflow:ellipsis;text-decoration:none;box-sizing:border-box;font-size:inherit;margin:0;padding:.25rem 1rem;align-items:center;white-space:nowrap;overflow:hidden;-webkit-user-select:none;user-select:none;border:none;outline:none;background:none;pointer-events:auto}.cute-menu-item:hover,.cute-menu-item.active{outline:none;background-color:rgba(var(--bs-body-color-rgb),.04)}.cute-menu-item[disabled]{opacity:.38;color:var(--bs-body-color);cursor:default}.cute-menu-item[disabled]:hover,.cute-menu-item[disabled]:focus{outline:none;background-color:var(--bs-body-bg)}.cute-menu-item[disabled]:after{display:block;position:absolute;content:\"\";inset:0}.cute-menu-item:not([disabled]).cdk-keyboard-focused{background-color:rgba(var(--bs-body-color-rgb),.04)}.cute-menu-item>[role=img],.cute-menu-item i[class]{position:absolute;left:4px;width:24px;height:24px;line-height:24px;background-repeat:no-repeat;display:inline-block;fill:currentColor;text-align:center;overflow:hidden;-webkit-user-select:none;user-select:none;-webkit-font-smoothing:antialiased}[dir=rtl] .cute-menu-item>[role=img],[dir=rtl] .cute-menu-item i[class]{right:4px;left:auto}.cute-menu-item .cute-menu-item-text{margin-left:1rem}[dir=rtl] .cute-menu-item .cute-menu-item-text{margin-right:1rem;margin-left:auto}.cute-menu-item .cute-menu-item-submenu-icon{position:absolute;top:50%;right:12px;transform:translateY(-50%);width:5px;height:10px;fill:currentColor;opacity:.75}[dir=rtl] .cute-menu-item .cute-menu-item-submenu-icon{left:12px;right:auto;transform:translateY(-50%) rotate(180deg)}.cute-menu-item-submenu-trigger{padding-right:2rem}[dir=rtl] .cute-menu-item-submenu-trigger{padding-right:1rem;padding-left:2rem}\n"] }]
|
|
@@ -265,10 +263,10 @@ class CuteMenuContent {
|
|
|
265
263
|
this.detach();
|
|
266
264
|
this._outlet?.dispose();
|
|
267
265
|
}
|
|
268
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
269
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "
|
|
266
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CuteMenuContent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
267
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.0", type: CuteMenuContent, isStandalone: true, selector: "ng-template[cuteMenuContent]", providers: [{ provide: CUTE_MENU_CONTENT, useExisting: CuteMenuContent }], ngImport: i0 }); }
|
|
270
268
|
}
|
|
271
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
269
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CuteMenuContent, decorators: [{
|
|
272
270
|
type: Directive,
|
|
273
271
|
args: [{
|
|
274
272
|
selector: 'ng-template[cuteMenuContent]',
|
|
@@ -277,70 +275,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
277
275
|
}]
|
|
278
276
|
}], ctorParameters: () => [] });
|
|
279
277
|
|
|
280
|
-
/**
|
|
281
|
-
* @license Apache-2.0
|
|
282
|
-
*
|
|
283
|
-
* Copyright (c) 2025 CuteWidgets Team. All Rights Reserved.
|
|
284
|
-
*
|
|
285
|
-
* You may not use this file except in compliance with the License
|
|
286
|
-
* that can be found at http://www.apache.org/licenses/LICENSE-2.0
|
|
287
|
-
*
|
|
288
|
-
* This code is a modification of the `@angular/material` original
|
|
289
|
-
* code licensed under MIT-style License (https://angular.dev/license).
|
|
290
|
-
*/
|
|
291
|
-
/**
|
|
292
|
-
* Animations used by the cute-menu component.
|
|
293
|
-
* Animation duration and timing values are based on:
|
|
294
|
-
* https://material.io/guidelines/components/menus.html#menus-usage
|
|
295
|
-
* @docs-private
|
|
296
|
-
*/
|
|
297
|
-
const cuteMenuAnimations = {
|
|
298
|
-
/**
|
|
299
|
-
* This animation controls the menu panel's entry and exit from the page.
|
|
300
|
-
*
|
|
301
|
-
* When the menu panel is added to the DOM, it scales in and fades in its border.
|
|
302
|
-
*
|
|
303
|
-
* When the menu panel is removed from the DOM, it simply fades out after a brief
|
|
304
|
-
* delay to display the ripple.
|
|
305
|
-
*/
|
|
306
|
-
transformMenu: trigger('transformMenu', [
|
|
307
|
-
state('void', style({
|
|
308
|
-
opacity: 0,
|
|
309
|
-
transform: 'scale(0.8)',
|
|
310
|
-
})),
|
|
311
|
-
transition('void => enter', animate('120ms cubic-bezier(0, 0, 0.2, 1)', style({
|
|
312
|
-
opacity: 1,
|
|
313
|
-
transform: 'scale(1)',
|
|
314
|
-
}))),
|
|
315
|
-
transition('* => void', animate('100ms 25ms linear', style({ opacity: 0 }))),
|
|
316
|
-
]),
|
|
317
|
-
/**
|
|
318
|
-
* This animation fades in the background color and content of the menu panel
|
|
319
|
-
* after its containing element is scaled in.
|
|
320
|
-
*/
|
|
321
|
-
fadeInItems: trigger('fadeInItems', [
|
|
322
|
-
// TODO(crisbeto): this is inside the `transformMenu`
|
|
323
|
-
// now. Remove next time we do breaking changes.
|
|
324
|
-
state('showing', style({ opacity: 1 })),
|
|
325
|
-
transition('void => *', [
|
|
326
|
-
style({ opacity: 0 }),
|
|
327
|
-
animate('400ms 100ms cubic-bezier(0.55, 0, 0.55, 0.2)'),
|
|
328
|
-
]),
|
|
329
|
-
]),
|
|
330
|
-
};
|
|
331
|
-
/**
|
|
332
|
-
* @deprecated
|
|
333
|
-
* @breaking-change 8.0.0
|
|
334
|
-
* @docs-private
|
|
335
|
-
*/
|
|
336
|
-
//export const fadeInItems = cuteMenuAnimations.fadeInItems;
|
|
337
|
-
/**
|
|
338
|
-
* @deprecated
|
|
339
|
-
* @breaking-change 8.0.0
|
|
340
|
-
* @docs-private
|
|
341
|
-
*/
|
|
342
|
-
//export const transformMenu = cuteMenuAnimations.transformMenu;
|
|
343
|
-
|
|
344
278
|
/**
|
|
345
279
|
* @license Apache-2.0
|
|
346
280
|
*
|
|
@@ -356,21 +290,17 @@ let menuPanelUid = 0;
|
|
|
356
290
|
/** Injection token to be used to override the default options for `mat-menu`. */
|
|
357
291
|
const CUTE_MENU_DEFAULT_OPTIONS = new InjectionToken('cute-menu-default-options', {
|
|
358
292
|
providedIn: 'root',
|
|
359
|
-
factory:
|
|
360
|
-
});
|
|
361
|
-
/**
|
|
362
|
-
* @docs-private
|
|
363
|
-
* @deprecated No longer used, will be removed.
|
|
364
|
-
* @breaking-change 21.0.0
|
|
365
|
-
*/
|
|
366
|
-
function CUTE_MENU_DEFAULT_OPTIONS_FACTORY() {
|
|
367
|
-
return {
|
|
293
|
+
factory: () => ({
|
|
368
294
|
overlapTrigger: false,
|
|
369
295
|
xPosition: 'after',
|
|
370
296
|
yPosition: 'below',
|
|
371
297
|
backdropClass: 'cdk-overlay-transparent-backdrop',
|
|
372
|
-
}
|
|
373
|
-
}
|
|
298
|
+
}),
|
|
299
|
+
});
|
|
300
|
+
/** Name of the enter animation `@keyframes`. */
|
|
301
|
+
const ENTER_ANIMATION = '_cute-menu-enter';
|
|
302
|
+
/** Name of the exit animation `@keyframes`. */
|
|
303
|
+
const EXIT_ANIMATION = '_cute-menu-exit';
|
|
374
304
|
class CuteMenu {
|
|
375
305
|
/** Position of the menu in the X axis. */
|
|
376
306
|
get xPosition() { return this._xPosition; }
|
|
@@ -393,7 +323,7 @@ class CuteMenu {
|
|
|
393
323
|
this.setPositionClasses();
|
|
394
324
|
}
|
|
395
325
|
/**
|
|
396
|
-
* This method takes classes set on the host
|
|
326
|
+
* This method takes classes set on the host cute-menu element and applies them on the
|
|
397
327
|
* menu template that displays in the overlay container. Otherwise, it's difficult
|
|
398
328
|
* to style the containing menu from outside the component.
|
|
399
329
|
* @param classes list of class names
|
|
@@ -421,14 +351,17 @@ class CuteMenu {
|
|
|
421
351
|
this._injector = inject(Injector);
|
|
422
352
|
this._elevationPrefix = 'shadow';
|
|
423
353
|
this._baseElevation = 1;
|
|
354
|
+
this._animationsDisabled = _animationsDisabled();
|
|
355
|
+
/** Only the direct descendant menu items. */
|
|
356
|
+
this._directDescendantItems = new QueryList();
|
|
424
357
|
/** Config object to be passed into the menu's ngClass */
|
|
425
358
|
this._classList = {};
|
|
426
359
|
/** Current state of the panel animation. */
|
|
427
360
|
this._panelAnimationState = 'void';
|
|
428
|
-
/** Only the direct descendant menu items. */
|
|
429
|
-
this._directDescendantItems = new QueryList();
|
|
430
361
|
/** Emits whenever an animation on the menu completes. */
|
|
431
362
|
this._animationDone = new Subject();
|
|
363
|
+
/** Whether the menu is animating. */
|
|
364
|
+
this._isAnimating = signal(false, ...(ngDevMode ? [{ debugName: "_isAnimating" }] : []));
|
|
432
365
|
/** Event emitted when the menu is closed. */
|
|
433
366
|
this.closed = new EventEmitter();
|
|
434
367
|
this.panelId = `cute-menu-panel-${menuPanelUid++}`;
|
|
@@ -597,8 +530,8 @@ class CuteMenu {
|
|
|
597
530
|
/**
|
|
598
531
|
* Adds classes to the menu panel based on its position. Can be used by
|
|
599
532
|
* consumers to add specific styling based on the position.
|
|
600
|
-
* @param posX Position of the menu along the x
|
|
601
|
-
* @param posY Position of the menu along the y
|
|
533
|
+
* @param posX Position of the menu along the x-axis.
|
|
534
|
+
* @param posY Position of the menu along the y-axis.
|
|
602
535
|
*/
|
|
603
536
|
setPositionClasses(posX = this.xPosition, posY = this.yPosition) {
|
|
604
537
|
const classes = this._classList;
|
|
@@ -608,32 +541,52 @@ class CuteMenu {
|
|
|
608
541
|
classes['cute-menu-below'] = posY === 'below';
|
|
609
542
|
this._changeDetectorRef.markForCheck();
|
|
610
543
|
}
|
|
611
|
-
/**
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
544
|
+
/** Callback that is invoked when the panel animation completes. */
|
|
545
|
+
_onAnimationDone(state) {
|
|
546
|
+
const isExit = state === EXIT_ANIMATION;
|
|
547
|
+
if (isExit || state === ENTER_ANIMATION) {
|
|
548
|
+
if (isExit) {
|
|
549
|
+
clearTimeout(this._exitFallbackTimeout);
|
|
550
|
+
this._exitFallbackTimeout = undefined;
|
|
551
|
+
}
|
|
552
|
+
this._animationDone.next(isExit ? 'void' : 'enter');
|
|
553
|
+
this._isAnimating.set(false);
|
|
554
|
+
}
|
|
615
555
|
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
556
|
+
_onAnimationStart(state) {
|
|
557
|
+
if (state === ENTER_ANIMATION || state === EXIT_ANIMATION) {
|
|
558
|
+
this._isAnimating.set(true);
|
|
559
|
+
}
|
|
620
560
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
561
|
+
_setIsOpen(isOpen) {
|
|
562
|
+
this._panelAnimationState = isOpen ? 'enter' : 'void';
|
|
563
|
+
if (isOpen) {
|
|
564
|
+
if (this._keyManager?.activeItemIndex === 0) {
|
|
565
|
+
// Scroll the content element to the top as soon as the animation starts. This is necessary,
|
|
566
|
+
// because we move focus to the first item while it's still being animated, which can throw
|
|
567
|
+
// the browser off when it determines the scroll position. Alternatively we can move focus
|
|
568
|
+
// when the animation is done, however moving focus asynchronously will interrupt screen
|
|
569
|
+
// readers which are in the process of reading out the menu already. We take the `element`
|
|
570
|
+
// from the `event` since we can't use a `ViewChild` to access the pane.
|
|
571
|
+
const menuPanel = this._resolvePanel();
|
|
572
|
+
if (menuPanel) {
|
|
573
|
+
menuPanel.scrollTop = 0;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
636
576
|
}
|
|
577
|
+
else if (!this._animationsDisabled) {
|
|
578
|
+
// Some apps do `* { animation: none !important; }` in tests which will prevent the
|
|
579
|
+
// `animationend` event from firing. Since the exit animation is loading-bearing for
|
|
580
|
+
// removing the content from the DOM, add a fallback timer.
|
|
581
|
+
this._exitFallbackTimeout = setTimeout(() => this._onAnimationDone(EXIT_ANIMATION), 200);
|
|
582
|
+
}
|
|
583
|
+
// Animation events won't fire when animations are disabled so we simulate them.
|
|
584
|
+
if (this._animationsDisabled) {
|
|
585
|
+
setTimeout(() => {
|
|
586
|
+
this._onAnimationDone(isOpen ? ENTER_ANIMATION : EXIT_ANIMATION);
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
this._changeDetectorRef.markForCheck();
|
|
637
590
|
}
|
|
638
591
|
/**
|
|
639
592
|
* Sets up a stream that will keep track of any newly-added menu items and will update the list
|
|
@@ -653,7 +606,7 @@ class CuteMenu {
|
|
|
653
606
|
_resolvePanel() {
|
|
654
607
|
let menuPanel = null;
|
|
655
608
|
if (this._directDescendantItems.length) {
|
|
656
|
-
// Because the `
|
|
609
|
+
// Because the `cute-menuPanel` is at the DOM insertion point, not inside the overlay, we don't
|
|
657
610
|
// have a nice way of getting a hold of the menuPanel panel. We can't use a `ViewChild` either
|
|
658
611
|
// because the panel is inside an `ng-template`. We work around it by starting from one of
|
|
659
612
|
// the items and walking up the DOM.
|
|
@@ -661,18 +614,21 @@ class CuteMenu {
|
|
|
661
614
|
}
|
|
662
615
|
return menuPanel;
|
|
663
616
|
}
|
|
664
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
665
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "
|
|
617
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CuteMenu, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
618
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "21.2.0", type: CuteMenu, isStandalone: true, selector: "cute-menu", inputs: { backdropClass: "backdropClass", ariaLabel: ["aria-label", "ariaLabel"], ariaLabelledby: ["aria-labelledby", "ariaLabelledby"], ariaDescribedby: ["aria-describedby", "ariaDescribedby"], xPosition: "xPosition", yPosition: "yPosition", overlapTrigger: ["overlapTrigger", "overlapTrigger", booleanAttribute], hasBackdrop: ["hasBackdrop", "hasBackdrop", (value) => (value == null ? null : booleanAttribute(value))], panelClass: ["class", "panelClass"] }, outputs: { closed: "closed" }, host: { properties: { "attr.aria-label": "null", "attr.aria-labelledby": "null", "attr.aria-describedby": "null" }, classAttribute: "cute-menu" }, providers: [{ provide: CUTE_MENU_PANEL, useExisting: CuteMenu }], queries: [{ propertyName: "lazyContent", first: true, predicate: CUTE_MENU_CONTENT, descendants: true }, { propertyName: "_allItems", predicate: CuteMenuItem, descendants: true }, { propertyName: "items", predicate: CuteMenuItem }], viewQueries: [{ propertyName: "templateRef", first: true, predicate: TemplateRef, descendants: true }], exportAs: ["cuteMenu"], hostDirectives: [{ directive: i1.CdkMenu }], ngImport: i0, template: "<ng-template>\r\n <div\r\n class=\"cute-menu-panel shadow\"\r\n [id]=\"panelId\"\r\n [class]=\"_classList\"\r\n [class.cute-menu-panel-animations-disabled]=\"_animationsDisabled\"\r\n [class.cute-menu-panel-exit-animation]=\"_panelAnimationState === 'void'\"\r\n [class.cute-menu-panel-animating]=\"_isAnimating()\"\r\n (click)=\"onClick($event)\"\r\n (animationstart)=\"_onAnimationStart($event.animationName)\"\r\n (animationend)=\"_onAnimationDone($event.animationName)\"\r\n (animationcancel)=\"_onAnimationDone($event.animationName)\"\r\n tabindex=\"-1\"\r\n role=\"menu\"\r\n [attr.aria-label]=\"ariaLabel || null\"\r\n [attr.aria-labelledby]=\"ariaLabelledby || null\"\r\n [attr.aria-describedby]=\"ariaDescribedby || null\">\r\n <div class=\"cute-menu-content\">\r\n <ng-content></ng-content>\r\n </div>\r\n </div>\r\n</ng-template>\r\n", styles: [".cute-menu{display:none}.cute-menu-panel{min-width:112px;max-width:280px;overflow:hidden;box-sizing:border-box;outline:0;animation:_cute-menu-enter .12s cubic-bezier(0,0,.2,1);will-change:transform,opacity}.cute-menu-panel.cute-menu-panel-exit-animation{animation:_cute-menu-exit .1s 25ms linear forwards}.cute-menu-panel.cute-menu-panel-animations-disabled{animation:none}.cute-menu-panel.cute-menu-panel-animating{pointer-events:none}.cute-menu-panel.cute-menu-panel-animating:has(.cute-menu-content:empty){display:none}@media(forced-colors:active){.cute-menu-panel{outline:solid 1px}}.cute-menu-panel .cute-menu-content{display:block;width:100%;padding:.5rem 0;font-size:1rem;color:var(--bs-body-color);text-align:start;list-style:none;background-color:var(--bs-body-bg);border:var(--bs-border-width) solid var(--bs-border-color-translucent);border-radius:var(--bs-border-radius)}.cute-menu-panel .cute-menu-content [role=separator]{margin:.5rem 0!important;max-height:1px}@keyframes _cute-menu-enter{0%{opacity:0;transform:scale(.8)}to{opacity:1;transform:none}}@keyframes _cute-menu-exit{0%{opacity:1}to{opacity:0}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
666
619
|
}
|
|
667
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
620
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CuteMenu, decorators: [{
|
|
668
621
|
type: Component,
|
|
669
622
|
args: [{ selector: 'cute-menu', exportAs: 'cuteMenu', host: {
|
|
670
623
|
'class': 'cute-menu',
|
|
671
624
|
'[attr.aria-label]': 'null',
|
|
672
625
|
'[attr.aria-labelledby]': 'null',
|
|
673
626
|
'[attr.aria-describedby]': 'null',
|
|
674
|
-
},
|
|
675
|
-
}], ctorParameters: () => [], propDecorators: {
|
|
627
|
+
}, providers: [{ provide: CUTE_MENU_PANEL, useExisting: CuteMenu }], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, hostDirectives: [CdkMenu], template: "<ng-template>\r\n <div\r\n class=\"cute-menu-panel shadow\"\r\n [id]=\"panelId\"\r\n [class]=\"_classList\"\r\n [class.cute-menu-panel-animations-disabled]=\"_animationsDisabled\"\r\n [class.cute-menu-panel-exit-animation]=\"_panelAnimationState === 'void'\"\r\n [class.cute-menu-panel-animating]=\"_isAnimating()\"\r\n (click)=\"onClick($event)\"\r\n (animationstart)=\"_onAnimationStart($event.animationName)\"\r\n (animationend)=\"_onAnimationDone($event.animationName)\"\r\n (animationcancel)=\"_onAnimationDone($event.animationName)\"\r\n tabindex=\"-1\"\r\n role=\"menu\"\r\n [attr.aria-label]=\"ariaLabel || null\"\r\n [attr.aria-labelledby]=\"ariaLabelledby || null\"\r\n [attr.aria-describedby]=\"ariaDescribedby || null\">\r\n <div class=\"cute-menu-content\">\r\n <ng-content></ng-content>\r\n </div>\r\n </div>\r\n</ng-template>\r\n", styles: [".cute-menu{display:none}.cute-menu-panel{min-width:112px;max-width:280px;overflow:hidden;box-sizing:border-box;outline:0;animation:_cute-menu-enter .12s cubic-bezier(0,0,.2,1);will-change:transform,opacity}.cute-menu-panel.cute-menu-panel-exit-animation{animation:_cute-menu-exit .1s 25ms linear forwards}.cute-menu-panel.cute-menu-panel-animations-disabled{animation:none}.cute-menu-panel.cute-menu-panel-animating{pointer-events:none}.cute-menu-panel.cute-menu-panel-animating:has(.cute-menu-content:empty){display:none}@media(forced-colors:active){.cute-menu-panel{outline:solid 1px}}.cute-menu-panel .cute-menu-content{display:block;width:100%;padding:.5rem 0;font-size:1rem;color:var(--bs-body-color);text-align:start;list-style:none;background-color:var(--bs-body-bg);border:var(--bs-border-width) solid var(--bs-border-color-translucent);border-radius:var(--bs-border-radius)}.cute-menu-panel .cute-menu-content [role=separator]{margin:.5rem 0!important;max-height:1px}@keyframes _cute-menu-enter{0%{opacity:0;transform:scale(.8)}to{opacity:1;transform:none}}@keyframes _cute-menu-exit{0%{opacity:1}to{opacity:0}}\n"] }]
|
|
628
|
+
}], ctorParameters: () => [], propDecorators: { _allItems: [{
|
|
629
|
+
type: ContentChildren,
|
|
630
|
+
args: [CuteMenuItem, { descendants: true }]
|
|
631
|
+
}], backdropClass: [{
|
|
676
632
|
type: Input
|
|
677
633
|
}], ariaLabel: [{
|
|
678
634
|
type: Input,
|
|
@@ -696,9 +652,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
696
652
|
}], templateRef: [{
|
|
697
653
|
type: ViewChild,
|
|
698
654
|
args: [TemplateRef]
|
|
699
|
-
}], _allItems: [{
|
|
700
|
-
type: ContentChildren,
|
|
701
|
-
args: [CuteMenuItem, { descendants: true }]
|
|
702
655
|
}], items: [{
|
|
703
656
|
type: ContentChildren,
|
|
704
657
|
args: [CuteMenuItem, { descendants: false }]
|
|
@@ -736,31 +689,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
736
689
|
* code licensed under MIT-style License (https://angular.dev/license).
|
|
737
690
|
*/
|
|
738
691
|
/** Injection token that determines the scroll handling while the menu is open. */
|
|
739
|
-
const CUTE_MENU_SCROLL_STRATEGY = new InjectionToken('cute-menu-scroll-strategy'
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
const
|
|
692
|
+
const CUTE_MENU_SCROLL_STRATEGY = new InjectionToken('cute-menu-scroll-strategy', {
|
|
693
|
+
providedIn: 'root',
|
|
694
|
+
factory: () => {
|
|
695
|
+
const injector = inject(Injector);
|
|
696
|
+
return () => createRepositionScrollStrategy(injector);
|
|
697
|
+
},
|
|
698
|
+
});
|
|
699
|
+
/**
|
|
700
|
+
* Default top padding of the menu panel.
|
|
701
|
+
* @deprecated No longer being used. Will be removed.
|
|
702
|
+
* @breaking-change 15.0.0
|
|
703
|
+
*/
|
|
704
|
+
const MENU_PANEL_TOP_PADDING = 8;
|
|
705
|
+
/** Mapping between menu panels and the last trigger that opened them. */
|
|
706
|
+
const PANELS_TO_TRIGGERS = new WeakMap();
|
|
752
707
|
/** Directive applied to an element that should trigger a `cute-menu`. */
|
|
753
|
-
class
|
|
754
|
-
/**
|
|
755
|
-
get
|
|
756
|
-
|
|
757
|
-
|
|
708
|
+
class CuteMenuTriggerBase {
|
|
709
|
+
/** Element reference that can be used in class compositions. */
|
|
710
|
+
get elementRef() { return this._element; }
|
|
711
|
+
/** Menu currently assigned to the trigger. */
|
|
712
|
+
get _menu() {
|
|
713
|
+
return this._menuInternal;
|
|
714
|
+
}
|
|
715
|
+
set _menu(menu) {
|
|
716
|
+
if (menu === this._menuInternal) {
|
|
758
717
|
return;
|
|
759
718
|
}
|
|
760
|
-
this.
|
|
719
|
+
this._menuInternal = menu;
|
|
761
720
|
this._menuCloseSubscription.unsubscribe();
|
|
762
721
|
if (menu) {
|
|
763
|
-
if (menu === this._parentCuteMenu &&
|
|
722
|
+
if (menu === this._parentCuteMenu && (typeof ngDevMode === 'undefined' || ngDevMode)) {
|
|
764
723
|
throwCuteMenuRecursiveError();
|
|
765
724
|
}
|
|
766
725
|
this._menuCloseSubscription = menu.closed.subscribe((reason) => {
|
|
@@ -771,65 +730,42 @@ class CuteMenuTrigger {
|
|
|
771
730
|
}
|
|
772
731
|
});
|
|
773
732
|
}
|
|
774
|
-
this._menuItemInstance?._setTriggersSubmenu(this.
|
|
775
|
-
}
|
|
776
|
-
constructor(
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
this.
|
|
781
|
-
this.
|
|
782
|
-
this.
|
|
783
|
-
this.
|
|
784
|
-
this.
|
|
785
|
-
this.
|
|
786
|
-
this.
|
|
787
|
-
this.
|
|
733
|
+
this._menuItemInstance?._setTriggersSubmenu(this._triggersSubmenu());
|
|
734
|
+
}
|
|
735
|
+
constructor(_canHaveBackdrop) {
|
|
736
|
+
this._canHaveBackdrop = _canHaveBackdrop;
|
|
737
|
+
this._element = inject(ElementRef);
|
|
738
|
+
this._viewContainerRef = inject(ViewContainerRef);
|
|
739
|
+
this._menuItemInstance = inject(CuteMenuItem, { optional: true, self: true });
|
|
740
|
+
this._dir = inject(Directionality, { optional: true });
|
|
741
|
+
this._focusMonitor = inject(FocusMonitor);
|
|
742
|
+
this._ngZone = inject(NgZone);
|
|
743
|
+
this._injector = inject(Injector);
|
|
744
|
+
this._scrollStrategy = inject(CUTE_MENU_SCROLL_STRATEGY);
|
|
745
|
+
this._changeDetectorRef = inject(ChangeDetectorRef);
|
|
746
|
+
this._animationsDisabled = _animationsDisabled();
|
|
788
747
|
this._overlayRef = null;
|
|
789
748
|
this._menuOpen = false;
|
|
790
749
|
this._closingActionsSubscription = Subscription.EMPTY;
|
|
791
|
-
this._hoverSubscription = Subscription.EMPTY;
|
|
792
750
|
this._menuCloseSubscription = Subscription.EMPTY;
|
|
793
|
-
|
|
794
|
-
/**
|
|
795
|
-
* Handles touch start events on the trigger.
|
|
796
|
-
* Needs to be an arrow function, so we can easily use addEventListener and removeEventListener.
|
|
797
|
-
*/
|
|
798
|
-
this._handleTouchStart = (event) => {
|
|
799
|
-
if (!isFakeTouchstartFromScreenReader(event)) {
|
|
800
|
-
this._openedBy = 'touch';
|
|
801
|
-
}
|
|
802
|
-
};
|
|
803
|
-
// Tracking input type is necessary, so it's possible to only autofocus
|
|
751
|
+
// Tracking input type is necessary so it's possible to only auto-focus
|
|
804
752
|
// the first item of the list when the menu is opened via the keyboard
|
|
805
753
|
this._openedBy = undefined;
|
|
806
|
-
this.
|
|
807
|
-
|
|
808
|
-
* Whether focus should be restored when the menu is closed.
|
|
809
|
-
* Note that disabling this option can have accessibility implications, and
|
|
810
|
-
* it's up to you to manage focus if you decide to turn it off.
|
|
811
|
-
*/
|
|
812
|
-
this.restoreFocus = true;
|
|
813
|
-
/** Event emitted when the associated menu is opened. */
|
|
814
|
-
this.menuOpened = new EventEmitter();
|
|
815
|
-
/** Event emitted when the associated menu is closed. */
|
|
816
|
-
this.menuClosed = new EventEmitter();
|
|
817
|
-
this._scrollStrategy = scrollStrategy;
|
|
754
|
+
this._menuInternal = null;
|
|
755
|
+
const parentMenu = inject(CUTE_MENU_PANEL, { optional: true });
|
|
818
756
|
this._parentCuteMenu = parentMenu instanceof CuteMenu ? parentMenu : undefined;
|
|
819
|
-
_element.nativeElement.addEventListener('touchstart', this._handleTouchStart, passiveEventListenerOptions);
|
|
820
|
-
}
|
|
821
|
-
ngAfterContentInit() {
|
|
822
|
-
this._handleHover();
|
|
823
757
|
}
|
|
824
758
|
ngOnDestroy() {
|
|
759
|
+
if (this._menu && this._ownsMenu(this._menu)) {
|
|
760
|
+
PANELS_TO_TRIGGERS.delete(this._menu);
|
|
761
|
+
}
|
|
762
|
+
this._pendingRemoval?.unsubscribe();
|
|
763
|
+
this._menuCloseSubscription.unsubscribe();
|
|
764
|
+
this._closingActionsSubscription.unsubscribe();
|
|
825
765
|
if (this._overlayRef) {
|
|
826
766
|
this._overlayRef.dispose();
|
|
827
767
|
this._overlayRef = null;
|
|
828
768
|
}
|
|
829
|
-
this._element.nativeElement.removeEventListener('touchstart', this._handleTouchStart, passiveEventListenerOptions);
|
|
830
|
-
this._menuCloseSubscription.unsubscribe();
|
|
831
|
-
this._closingActionsSubscription.unsubscribe();
|
|
832
|
-
this._hoverSubscription.unsubscribe();
|
|
833
769
|
}
|
|
834
770
|
/** Whether the menu is open. */
|
|
835
771
|
get menuOpen() {
|
|
@@ -839,53 +775,69 @@ class CuteMenuTrigger {
|
|
|
839
775
|
get dir() {
|
|
840
776
|
return this._dir && this._dir.value === 'rtl' ? 'rtl' : 'ltr';
|
|
841
777
|
}
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
/** Whether the menu triggers a submenu or a top-level one. */
|
|
846
|
-
triggersSubmenu() {
|
|
847
|
-
return !!(this._menuItemInstance && this._parentCuteMenu && this.menu);
|
|
778
|
+
/** Whether the menu triggers a sub-menu or a top-level one. */
|
|
779
|
+
_triggersSubmenu() {
|
|
780
|
+
return !!(this._menuItemInstance && this._parentCuteMenu && this._menu);
|
|
848
781
|
}
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
return this._menuOpen ? this.closeMenu() : this.openMenu();
|
|
782
|
+
_closeMenu() {
|
|
783
|
+
this._menu?.closed.emit();
|
|
852
784
|
}
|
|
853
|
-
/**
|
|
854
|
-
|
|
855
|
-
|
|
785
|
+
/** Internal method to open menu providing option to auto focus on first item. */
|
|
786
|
+
_openMenu(autoFocus) {
|
|
787
|
+
if (this._triggerIsAriaDisabled()) {
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
const menu = this._menu;
|
|
856
791
|
if (this._menuOpen || !menu) {
|
|
857
792
|
return;
|
|
858
793
|
}
|
|
794
|
+
this._pendingRemoval?.unsubscribe();
|
|
795
|
+
const previousTrigger = PANELS_TO_TRIGGERS.get(menu);
|
|
796
|
+
PANELS_TO_TRIGGERS.set(menu, this);
|
|
797
|
+
// If the same menu is currently attached to another trigger,
|
|
798
|
+
// we need to close it so it doesn't end up in a broken state.
|
|
799
|
+
if (previousTrigger && previousTrigger !== this) {
|
|
800
|
+
previousTrigger._closeMenu();
|
|
801
|
+
}
|
|
859
802
|
const overlayRef = this._createOverlay(menu);
|
|
860
803
|
const overlayConfig = overlayRef.getConfig();
|
|
861
804
|
const positionStrategy = overlayConfig.positionStrategy;
|
|
862
805
|
this._setPosition(menu, positionStrategy);
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
806
|
+
if (this._canHaveBackdrop) {
|
|
807
|
+
overlayConfig.hasBackdrop =
|
|
808
|
+
menu.hasBackdrop == null ? !this._triggersSubmenu() : menu.hasBackdrop;
|
|
809
|
+
}
|
|
810
|
+
else {
|
|
811
|
+
overlayConfig.hasBackdrop = false;
|
|
812
|
+
}
|
|
813
|
+
// We need the `hasAttached` check for the case where the user kicked off a removal animation,
|
|
814
|
+
// but re-entered the menu. Re-attaching the same portal will trigger an error otherwise.
|
|
815
|
+
if (!overlayRef.hasAttached()) {
|
|
816
|
+
overlayRef.attach(this._getPortal(menu));
|
|
817
|
+
menu.lazyContent?.attach(this.menuData);
|
|
868
818
|
}
|
|
869
|
-
this._closingActionsSubscription = this._menuClosingActions().subscribe(() => this.
|
|
870
|
-
this.
|
|
819
|
+
this._closingActionsSubscription = this._menuClosingActions().subscribe(() => this._closeMenu());
|
|
820
|
+
menu.parentMenu = this._triggersSubmenu() ? this._parentCuteMenu : undefined;
|
|
821
|
+
menu.direction = this.dir;
|
|
822
|
+
//this._setMenuElevation(menu);
|
|
823
|
+
if (autoFocus) {
|
|
824
|
+
menu.focusFirstItem(this._openedBy || 'program');
|
|
825
|
+
}
|
|
826
|
+
this._setIsMenuOpen(true);
|
|
871
827
|
if (menu instanceof CuteMenu) {
|
|
872
|
-
menu.
|
|
828
|
+
menu._setIsOpen(true);
|
|
873
829
|
menu._directDescendantItems.changes.pipe(takeUntil(menu.closed)).subscribe(() => {
|
|
874
|
-
// Re-adjust the position without locking when the
|
|
830
|
+
// Re-adjust the position without locking when the amount of items
|
|
875
831
|
// changes so that the overlay is allowed to pick a new optimal position.
|
|
876
832
|
positionStrategy.withLockedPosition(false).reapplyLastPosition();
|
|
877
833
|
positionStrategy.withLockedPosition(true);
|
|
878
834
|
});
|
|
879
835
|
}
|
|
880
836
|
}
|
|
881
|
-
/** Closes the menu. */
|
|
882
|
-
closeMenu() {
|
|
883
|
-
this.menu?.closed.emit();
|
|
884
|
-
}
|
|
885
837
|
/**
|
|
886
838
|
* Focuses the menu trigger.
|
|
887
839
|
* @param origin Source of the menu trigger's focus.
|
|
888
|
-
* @param options
|
|
840
|
+
* @param options Optional focus options.
|
|
889
841
|
*/
|
|
890
842
|
focus(origin, options) {
|
|
891
843
|
if (this._focusMonitor && origin) {
|
|
@@ -895,61 +847,47 @@ class CuteMenuTrigger {
|
|
|
895
847
|
this._element.nativeElement.focus(options);
|
|
896
848
|
}
|
|
897
849
|
}
|
|
898
|
-
/**
|
|
899
|
-
* Updates the position of the menu to ensure that it fits all options within the viewport.
|
|
900
|
-
*/
|
|
901
|
-
updatePosition() {
|
|
902
|
-
this._overlayRef?.updatePosition();
|
|
903
|
-
}
|
|
904
850
|
/** Closes the menu and does the necessary cleanup. */
|
|
905
851
|
_destroyMenu(reason) {
|
|
906
|
-
|
|
852
|
+
const overlayRef = this._overlayRef;
|
|
853
|
+
const menu = this._menu;
|
|
854
|
+
if (!overlayRef || !this.menuOpen) {
|
|
907
855
|
return;
|
|
908
856
|
}
|
|
909
|
-
const menu = this.menu;
|
|
910
857
|
this._closingActionsSubscription.unsubscribe();
|
|
911
|
-
this.
|
|
858
|
+
this._pendingRemoval?.unsubscribe();
|
|
859
|
+
// Note that we don't wait for the animation to finish if another trigger took
|
|
860
|
+
// over the menu, because the panel will end up empty which looks glitchy.
|
|
861
|
+
if (menu instanceof CuteMenu && this._ownsMenu(menu)) {
|
|
862
|
+
this._pendingRemoval = menu._animationDone.pipe(take(1)).subscribe(() => {
|
|
863
|
+
overlayRef.detach();
|
|
864
|
+
// Only detach the lazy content if no other trigger took over the menu, otherwise we may
|
|
865
|
+
// detach something we no longer own. Note that we don't use `this._ownsMenu` here,
|
|
866
|
+
// because the current trigger relinquishes ownership as soon as the closing sequence
|
|
867
|
+
// is kicked off whereas the animation takes some time to play out.
|
|
868
|
+
if (!PANELS_TO_TRIGGERS.has(menu)) {
|
|
869
|
+
menu.lazyContent?.detach();
|
|
870
|
+
}
|
|
871
|
+
});
|
|
872
|
+
menu._setIsOpen(false);
|
|
873
|
+
}
|
|
874
|
+
else {
|
|
875
|
+
overlayRef.detach();
|
|
876
|
+
menu?.lazyContent?.detach();
|
|
877
|
+
}
|
|
878
|
+
if (menu && this._ownsMenu(menu)) {
|
|
879
|
+
PANELS_TO_TRIGGERS.delete(menu);
|
|
880
|
+
}
|
|
912
881
|
// Always restore focus if the user is navigating using the keyboard or the menu was opened
|
|
913
882
|
// programmatically. We don't restore for non-root triggers, because it can prevent focus
|
|
914
883
|
// from making it back to the root trigger when closing a long chain of menus by clicking
|
|
915
884
|
// on the backdrop.
|
|
916
|
-
if (this.restoreFocus &&
|
|
885
|
+
if (this.restoreFocus &&
|
|
886
|
+
(reason === 'keydown' || !this._openedBy || !this._triggersSubmenu())) {
|
|
917
887
|
this.focus(this._openedBy);
|
|
918
888
|
}
|
|
919
889
|
this._openedBy = undefined;
|
|
920
|
-
|
|
921
|
-
menu._resetAnimation();
|
|
922
|
-
if (menu.lazyContent) {
|
|
923
|
-
// Wait for the exit animation to finish before detaching the content.
|
|
924
|
-
menu._animationDone
|
|
925
|
-
.pipe(filter(event => event.toState === 'void'), take(1),
|
|
926
|
-
// Interrupt if the content got re-attached.
|
|
927
|
-
takeUntil(menu.lazyContent._attached))
|
|
928
|
-
.subscribe({
|
|
929
|
-
next: () => menu.lazyContent.detach(),
|
|
930
|
-
// No matter whether the content got re-attached, reset the menu.
|
|
931
|
-
complete: () => this._setIsMenuOpen(false),
|
|
932
|
-
});
|
|
933
|
-
}
|
|
934
|
-
else {
|
|
935
|
-
this._setIsMenuOpen(false);
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
else {
|
|
939
|
-
this._setIsMenuOpen(false);
|
|
940
|
-
menu?.lazyContent?.detach();
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
/**
|
|
944
|
-
* This method sets the menu state to open and focuses the first item if
|
|
945
|
-
* the menu was opened via the keyboard.
|
|
946
|
-
*/
|
|
947
|
-
_initMenu(menu) {
|
|
948
|
-
menu.parentMenu = this.triggersSubmenu() ? this._parentCuteMenu : undefined;
|
|
949
|
-
menu.direction = this.dir;
|
|
950
|
-
this._setMenuElevation(menu);
|
|
951
|
-
menu.focusFirstItem(this._openedBy || 'program');
|
|
952
|
-
this._setIsMenuOpen(true);
|
|
890
|
+
this._setIsMenuOpen(false);
|
|
953
891
|
}
|
|
954
892
|
/** Updates the menu elevation based on the amount of parent menus that it has. */
|
|
955
893
|
_setMenuElevation(menu) {
|
|
@@ -968,7 +906,7 @@ class CuteMenuTrigger {
|
|
|
968
906
|
if (isOpen !== this._menuOpen) {
|
|
969
907
|
this._menuOpen = isOpen;
|
|
970
908
|
this._menuOpen ? this.menuOpened.emit() : this.menuClosed.emit();
|
|
971
|
-
if (this.
|
|
909
|
+
if (this._triggersSubmenu()) {
|
|
972
910
|
this._menuItemInstance._setHighlighted(isOpen);
|
|
973
911
|
}
|
|
974
912
|
this._changeDetectorRef.markForCheck();
|
|
@@ -982,11 +920,12 @@ class CuteMenuTrigger {
|
|
|
982
920
|
if (!this._overlayRef) {
|
|
983
921
|
const config = this._getOverlayConfig(menu);
|
|
984
922
|
this._subscribeToPositions(menu, config.positionStrategy);
|
|
985
|
-
this._overlayRef = this.
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
923
|
+
this._overlayRef = createOverlayRef(this._injector, config);
|
|
924
|
+
this._overlayRef.keydownEvents().subscribe(event => {
|
|
925
|
+
if (this._menu instanceof CuteMenu) {
|
|
926
|
+
this._menu._handleKeydown(event);
|
|
927
|
+
}
|
|
928
|
+
});
|
|
990
929
|
}
|
|
991
930
|
return this._overlayRef;
|
|
992
931
|
}
|
|
@@ -996,16 +935,15 @@ class CuteMenuTrigger {
|
|
|
996
935
|
*/
|
|
997
936
|
_getOverlayConfig(menu) {
|
|
998
937
|
return new OverlayConfig({
|
|
999
|
-
positionStrategy: this.
|
|
1000
|
-
.position()
|
|
1001
|
-
.flexibleConnectedTo(this._element)
|
|
938
|
+
positionStrategy: createFlexibleConnectedPositionStrategy(this._injector, this._getOverlayOrigin())
|
|
1002
939
|
.withLockedPosition()
|
|
1003
940
|
.withGrowAfterOpen()
|
|
1004
941
|
.withTransformOriginOn('.cute-menu-panel'),
|
|
1005
942
|
backdropClass: menu.backdropClass || 'cdk-overlay-transparent-backdrop',
|
|
1006
943
|
panelClass: menu.overlayPanelClass,
|
|
1007
944
|
scrollStrategy: this._scrollStrategy(),
|
|
1008
|
-
direction: this._dir,
|
|
945
|
+
direction: this._dir || 'ltr',
|
|
946
|
+
disableAnimations: this._animationsDisabled,
|
|
1009
947
|
});
|
|
1010
948
|
}
|
|
1011
949
|
/**
|
|
@@ -1016,34 +954,28 @@ class CuteMenuTrigger {
|
|
|
1016
954
|
_subscribeToPositions(menu, position) {
|
|
1017
955
|
if (menu.setPositionClasses) {
|
|
1018
956
|
position.positionChanges.subscribe(change => {
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
// `positionChanges` fires outside of the `ngZone` and `setPositionClasses` might be
|
|
1023
|
-
// updating something in the view, so we need to bring it back in.
|
|
1024
|
-
if (this._ngZone) {
|
|
1025
|
-
this._ngZone.run(() => menu.setPositionClasses(posX, posY));
|
|
1026
|
-
}
|
|
1027
|
-
else {
|
|
957
|
+
this._ngZone.run(() => {
|
|
958
|
+
const posX = change.connectionPair.overlayX === 'start' ? 'after' : 'before';
|
|
959
|
+
const posY = change.connectionPair.overlayY === 'top' ? 'below' : 'above';
|
|
1028
960
|
menu.setPositionClasses(posX, posY);
|
|
1029
|
-
}
|
|
961
|
+
});
|
|
1030
962
|
});
|
|
1031
963
|
}
|
|
1032
964
|
}
|
|
1033
965
|
/**
|
|
1034
966
|
* Sets the appropriate positions on a position strategy
|
|
1035
967
|
* so the overlay connects with the trigger correctly.
|
|
1036
|
-
* @param menu Menu panel
|
|
1037
|
-
* @param positionStrategy Strategy whose position
|
|
968
|
+
* @param menu Menu panel.
|
|
969
|
+
* @param positionStrategy Strategy whose position to update.
|
|
1038
970
|
*/
|
|
1039
971
|
_setPosition(menu, positionStrategy) {
|
|
1040
972
|
let [originX, originFallbackX] = menu.xPosition === 'before' ? ['end', 'start'] : ['start', 'end'];
|
|
1041
973
|
let [overlayY, overlayFallbackY] = menu.yPosition === 'above' ? ['bottom', 'top'] : ['top', 'bottom'];
|
|
1042
974
|
let [originY, originFallbackY] = [overlayY, overlayFallbackY];
|
|
1043
975
|
let [overlayX, overlayFallbackX] = [originX, originFallbackX];
|
|
1044
|
-
let offsetY = 0;
|
|
1045
|
-
if (this.
|
|
1046
|
-
// When the menu is a
|
|
976
|
+
let offsetY = 4; //0;
|
|
977
|
+
if (this._triggersSubmenu()) {
|
|
978
|
+
// When the menu is a sub-menu, it should always align itself
|
|
1047
979
|
// to the edges of the trigger, instead of overlapping it.
|
|
1048
980
|
overlayFallbackX = originX = menu.xPosition === 'before' ? 'start' : 'end';
|
|
1049
981
|
originFallbackX = overlayX = originX === 'end' ? 'start' : 'end';
|
|
@@ -1056,7 +988,6 @@ class CuteMenuTrigger {
|
|
|
1056
988
|
}
|
|
1057
989
|
}
|
|
1058
990
|
else if (!menu.overlapTrigger) {
|
|
1059
|
-
offsetY = 4;
|
|
1060
991
|
originY = overlayY === 'top' ? 'bottom' : 'top';
|
|
1061
992
|
originFallbackY = overlayFallbackY === 'top' ? 'bottom' : 'top';
|
|
1062
993
|
}
|
|
@@ -1081,13 +1012,147 @@ class CuteMenuTrigger {
|
|
|
1081
1012
|
}
|
|
1082
1013
|
/** Returns a stream that emits whenever an action that should close the menu occurs. */
|
|
1083
1014
|
_menuClosingActions() {
|
|
1084
|
-
const
|
|
1015
|
+
const outsideClicks = this._getOutsideClickStream(this._overlayRef);
|
|
1085
1016
|
const detachments = this._overlayRef.detachments();
|
|
1086
1017
|
const parentClose = this._parentCuteMenu ? this._parentCuteMenu.closed : of();
|
|
1087
1018
|
const hover = this._parentCuteMenu
|
|
1088
|
-
? this._parentCuteMenu
|
|
1019
|
+
? this._parentCuteMenu
|
|
1020
|
+
._hovered()
|
|
1021
|
+
.pipe(filter(active => this._menuOpen && active !== this._menuItemInstance))
|
|
1089
1022
|
: of();
|
|
1090
|
-
return merge(
|
|
1023
|
+
return merge(outsideClicks, parentClose, hover, detachments);
|
|
1024
|
+
}
|
|
1025
|
+
/** Gets the portal that should be attached to the overlay. */
|
|
1026
|
+
_getPortal(menu) {
|
|
1027
|
+
// Note that we can avoid this check by keeping the portal on the menu panel.
|
|
1028
|
+
// While it would be cleaner, we'd have to introduce another required method on
|
|
1029
|
+
// `CuteMenuPanel`, making it harder to consume.
|
|
1030
|
+
if (!this._portal || this._portal.templateRef !== menu.templateRef) {
|
|
1031
|
+
this._portal = new TemplatePortal(menu.templateRef, this._viewContainerRef);
|
|
1032
|
+
}
|
|
1033
|
+
return this._portal;
|
|
1034
|
+
}
|
|
1035
|
+
/**
|
|
1036
|
+
* Determines whether the trigger owns a specific menu panel, at the current point in time.
|
|
1037
|
+
* This allows us to distinguish the case where the same panel is passed into multiple triggers
|
|
1038
|
+
* and multiple are open at a time.
|
|
1039
|
+
*/
|
|
1040
|
+
_ownsMenu(menu) {
|
|
1041
|
+
return PANELS_TO_TRIGGERS.get(menu) === this;
|
|
1042
|
+
}
|
|
1043
|
+
/**
|
|
1044
|
+
* Detect if the trigger element is aria-disabled, indicating it should behave as
|
|
1045
|
+
* disabled and not open the menu.
|
|
1046
|
+
*/
|
|
1047
|
+
_triggerIsAriaDisabled() {
|
|
1048
|
+
return booleanAttribute(this._element.nativeElement.getAttribute('aria-disabled'));
|
|
1049
|
+
}
|
|
1050
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CuteMenuTriggerBase, deps: "invalid", target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1051
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.0", type: CuteMenuTriggerBase, isStandalone: true, ngImport: i0 }); }
|
|
1052
|
+
}
|
|
1053
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CuteMenuTriggerBase, decorators: [{
|
|
1054
|
+
type: Directive
|
|
1055
|
+
}], ctorParameters: () => [{ type: undefined }] });
|
|
1056
|
+
|
|
1057
|
+
/**
|
|
1058
|
+
* @license Apache-2.0
|
|
1059
|
+
*
|
|
1060
|
+
* Copyright (c) 2025 CuteWidgets Team. All Rights Reserved.
|
|
1061
|
+
*
|
|
1062
|
+
* You may not use this file except in compliance with the License
|
|
1063
|
+
* that can be found at http://www.apache.org/licenses/LICENSE-2.0
|
|
1064
|
+
*
|
|
1065
|
+
* This code is a modification of the `@angular/material` original
|
|
1066
|
+
* code licensed under MIT-style License (https://angular.dev/license).
|
|
1067
|
+
*/
|
|
1068
|
+
/** Directive applied to an element that should trigger a `mat-menu`. */
|
|
1069
|
+
class CuteMenuTrigger extends CuteMenuTriggerBase {
|
|
1070
|
+
/**
|
|
1071
|
+
* @deprecated
|
|
1072
|
+
* @breaking-change 8.0.0
|
|
1073
|
+
*/
|
|
1074
|
+
get _deprecatedCuteMenuTriggerFor() {
|
|
1075
|
+
return this.menu;
|
|
1076
|
+
}
|
|
1077
|
+
set _deprecatedCuteMenuTriggerFor(v) {
|
|
1078
|
+
this.menu = v;
|
|
1079
|
+
}
|
|
1080
|
+
/** References the menu instance that the trigger is associated with. */
|
|
1081
|
+
get menu() {
|
|
1082
|
+
return this._menu;
|
|
1083
|
+
}
|
|
1084
|
+
set menu(menu) {
|
|
1085
|
+
this._menu = menu;
|
|
1086
|
+
}
|
|
1087
|
+
constructor() {
|
|
1088
|
+
super(true);
|
|
1089
|
+
this._hoverSubscription = Subscription.EMPTY;
|
|
1090
|
+
/**
|
|
1091
|
+
* Whether focus should be restored when the menu is closed.
|
|
1092
|
+
* Note that disabling this option can have accessibility implications
|
|
1093
|
+
* and it's up to you to manage focus, if you decide to turn it off.
|
|
1094
|
+
*/
|
|
1095
|
+
this.restoreFocus = true;
|
|
1096
|
+
/** Event emitted when the associated menu is opened. */
|
|
1097
|
+
this.menuOpened = new EventEmitter();
|
|
1098
|
+
/**
|
|
1099
|
+
* Event emitted when the associated menu is opened.
|
|
1100
|
+
* @deprecated Switch to `menuOpened` instead
|
|
1101
|
+
* @breaking-change 8.0.0
|
|
1102
|
+
*/
|
|
1103
|
+
// tslint:disable-next-line:no-output-on-prefix
|
|
1104
|
+
this.onMenuOpen = this.menuOpened;
|
|
1105
|
+
/** Event emitted when the associated menu is closed. */
|
|
1106
|
+
this.menuClosed = new EventEmitter();
|
|
1107
|
+
/**
|
|
1108
|
+
* Event emitted when the associated menu is closed.
|
|
1109
|
+
* @deprecated Switch to `menuClosed` instead
|
|
1110
|
+
* @breaking-change 8.0.0
|
|
1111
|
+
*/
|
|
1112
|
+
// tslint:disable-next-line:no-output-on-prefix
|
|
1113
|
+
this.onMenuClose = this.menuClosed;
|
|
1114
|
+
const renderer = inject(Renderer2);
|
|
1115
|
+
this._cleanupTouchstart = renderer.listen(this._element.nativeElement, 'touchstart', (event) => {
|
|
1116
|
+
if (!isFakeTouchstartFromScreenReader(event)) {
|
|
1117
|
+
this._openedBy = 'touch';
|
|
1118
|
+
}
|
|
1119
|
+
}, { passive: true });
|
|
1120
|
+
}
|
|
1121
|
+
/** Whether the menu triggers a sub-menu or a top-level one. */
|
|
1122
|
+
triggersSubmenu() {
|
|
1123
|
+
return super._triggersSubmenu();
|
|
1124
|
+
}
|
|
1125
|
+
/** Toggles the menu between the open and closed states. */
|
|
1126
|
+
toggleMenu() {
|
|
1127
|
+
return this.menuOpen ? this.closeMenu() : this.openMenu();
|
|
1128
|
+
}
|
|
1129
|
+
/** Opens the menu. */
|
|
1130
|
+
openMenu() {
|
|
1131
|
+
this._openMenu(true);
|
|
1132
|
+
}
|
|
1133
|
+
/** Closes the menu. */
|
|
1134
|
+
closeMenu() {
|
|
1135
|
+
this._closeMenu();
|
|
1136
|
+
}
|
|
1137
|
+
/**
|
|
1138
|
+
* Updates the position of the menu to ensure that it fits all options within the viewport.
|
|
1139
|
+
*/
|
|
1140
|
+
updatePosition() {
|
|
1141
|
+
this._overlayRef?.updatePosition();
|
|
1142
|
+
}
|
|
1143
|
+
ngAfterContentInit() {
|
|
1144
|
+
this._handleHover();
|
|
1145
|
+
}
|
|
1146
|
+
ngOnDestroy() {
|
|
1147
|
+
super.ngOnDestroy();
|
|
1148
|
+
this._cleanupTouchstart();
|
|
1149
|
+
this._hoverSubscription.unsubscribe();
|
|
1150
|
+
}
|
|
1151
|
+
_getOverlayOrigin() {
|
|
1152
|
+
return this._element;
|
|
1153
|
+
}
|
|
1154
|
+
_getOutsideClickStream(overlayRef) {
|
|
1155
|
+
return overlayRef.backdropClick();
|
|
1091
1156
|
}
|
|
1092
1157
|
/** Handles mouse presses on the trigger. */
|
|
1093
1158
|
_handleMousedown(event) {
|
|
@@ -1095,7 +1160,7 @@ class CuteMenuTrigger {
|
|
|
1095
1160
|
// Since right or middle button clicks won't trigger the `click` event,
|
|
1096
1161
|
// we shouldn't consider the menu as opened by mouse in those cases.
|
|
1097
1162
|
this._openedBy = event.button === 0 ? 'mouse' : undefined;
|
|
1098
|
-
// Since clicking on the trigger won't close the menu if it opens a
|
|
1163
|
+
// Since clicking on the trigger won't close the menu if it opens a sub-menu,
|
|
1099
1164
|
// we should prevent focus from moving onto it via click to avoid the
|
|
1100
1165
|
// highlight from lingering on the menu item.
|
|
1101
1166
|
if (this.triggersSubmenu()) {
|
|
@@ -1131,76 +1196,43 @@ class CuteMenuTrigger {
|
|
|
1131
1196
|
/** Handles the cases where the user hovers over the trigger. */
|
|
1132
1197
|
_handleHover() {
|
|
1133
1198
|
// Subscribe to changes in the hovered item in order to toggle the panel.
|
|
1134
|
-
if (
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
// before doing so. Also interrupt if the user moves to another item.
|
|
1148
|
-
if (this.menu instanceof CuteMenu && this.menu._isAnimating) {
|
|
1149
|
-
// We need the `delay(0)` here in order to avoid
|
|
1150
|
-
// 'changed after checked' errors in some cases. See #12194.
|
|
1151
|
-
this.menu._animationDone
|
|
1152
|
-
.pipe(take(1), delay(0, asapScheduler), takeUntil(this._parentCuteMenu._hovered()))
|
|
1153
|
-
.subscribe(() => this.openMenu());
|
|
1154
|
-
}
|
|
1155
|
-
else {
|
|
1156
|
-
this.openMenu();
|
|
1157
|
-
}
|
|
1158
|
-
});
|
|
1159
|
-
}
|
|
1160
|
-
/** Gets the portal that should be attached to the overlay. */
|
|
1161
|
-
_getPortal(menu) {
|
|
1162
|
-
// Note that we can avoid this check by keeping the portal on the menu panel.
|
|
1163
|
-
// While it would be cleaner, we'd have to introduce another required method on
|
|
1164
|
-
// `CuteMenuPanel`, making it harder to consume.
|
|
1165
|
-
if (!this._portal || this._portal.templateRef !== menu.templateRef) {
|
|
1166
|
-
this._portal = new TemplatePortal(menu.templateRef, this._viewContainerRef);
|
|
1199
|
+
if (this.triggersSubmenu() && this._parentCuteMenu) {
|
|
1200
|
+
this._hoverSubscription = this._parentCuteMenu._hovered().subscribe(active => {
|
|
1201
|
+
if (active === this._menuItemInstance &&
|
|
1202
|
+
!active.disabled &&
|
|
1203
|
+
// Ignore hover events if the parent menu is in the process of being closed (see #31956).
|
|
1204
|
+
this._parentCuteMenu?._panelAnimationState !== 'void') {
|
|
1205
|
+
this._openedBy = 'mouse';
|
|
1206
|
+
// Open the menu, but do NOT auto-focus on first item when just hovering.
|
|
1207
|
+
// When VoiceOver is enabled, this is particularly confusing as the focus will
|
|
1208
|
+
// cause another hover event, and continue opening sub-menus without interaction.
|
|
1209
|
+
this._openMenu(false);
|
|
1210
|
+
}
|
|
1211
|
+
});
|
|
1167
1212
|
}
|
|
1168
|
-
return this._portal;
|
|
1169
1213
|
}
|
|
1170
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1171
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "
|
|
1214
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CuteMenuTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1215
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.0", type: CuteMenuTrigger, isStandalone: true, selector: "[cute-menu-trigger-for], [cuteMenuTriggerFor]", inputs: { _deprecatedCuteMenuTriggerFor: ["cute-menu-trigger-for", "_deprecatedCuteMenuTriggerFor"], menu: ["cuteMenuTriggerFor", "menu"], menuData: ["cuteMenuTriggerData", "menuData"], restoreFocus: ["cuteMenuTriggerRestoreFocus", "restoreFocus"] }, outputs: { menuOpened: "menuOpened", onMenuOpen: "onMenuOpen", menuClosed: "menuClosed", onMenuClose: "onMenuClose" }, host: { listeners: { "click": "_handleClick($event)", "mousedown": "_handleMousedown($event)", "keydown": "_handleKeydown($event)" }, properties: { "attr.aria-haspopup": "menu ? \"menu\" : null", "attr.aria-expanded": "menuOpen", "attr.aria-controls": "menuOpen ? menu?.panelId : null" }, classAttribute: "cute-menu-trigger" }, exportAs: ["cuteMenuTrigger"], usesInheritance: true, ngImport: i0 }); }
|
|
1172
1216
|
}
|
|
1173
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1217
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CuteMenuTrigger, decorators: [{
|
|
1174
1218
|
type: Directive,
|
|
1175
1219
|
args: [{
|
|
1176
|
-
selector:
|
|
1177
|
-
exportAs: 'cuteMenuTrigger',
|
|
1220
|
+
selector: '[cute-menu-trigger-for], [cuteMenuTriggerFor]',
|
|
1178
1221
|
host: {
|
|
1179
1222
|
'class': 'cute-menu-trigger',
|
|
1180
1223
|
'[attr.aria-haspopup]': 'menu ? "menu" : null',
|
|
1181
1224
|
'[attr.aria-expanded]': 'menuOpen',
|
|
1182
|
-
'[attr.aria-controls]': 'menuOpen ? menu
|
|
1225
|
+
'[attr.aria-controls]': 'menuOpen ? menu?.panelId : null',
|
|
1183
1226
|
'(click)': '_handleClick($event)',
|
|
1184
1227
|
'(mousedown)': '_handleMousedown($event)',
|
|
1185
1228
|
'(keydown)': '_handleKeydown($event)',
|
|
1186
1229
|
},
|
|
1187
|
-
|
|
1230
|
+
exportAs: 'cuteMenuTrigger',
|
|
1188
1231
|
}]
|
|
1189
|
-
}], ctorParameters: () => [
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
type: Inject,
|
|
1194
|
-
args: [CUTE_MENU_PANEL]
|
|
1195
|
-
}, {
|
|
1196
|
-
type: Optional
|
|
1197
|
-
}] }, { type: CuteMenuItem, decorators: [{
|
|
1198
|
-
type: Optional
|
|
1199
|
-
}, {
|
|
1200
|
-
type: Self
|
|
1201
|
-
}] }, { type: i3.Directionality, decorators: [{
|
|
1202
|
-
type: Optional
|
|
1203
|
-
}] }, { type: i4.FocusMonitor }, { type: i0.NgZone }], propDecorators: { menu: [{
|
|
1232
|
+
}], ctorParameters: () => [], propDecorators: { _deprecatedCuteMenuTriggerFor: [{
|
|
1233
|
+
type: Input,
|
|
1234
|
+
args: ['cute-menu-trigger-for']
|
|
1235
|
+
}], menu: [{
|
|
1204
1236
|
type: Input,
|
|
1205
1237
|
args: ['cuteMenuTriggerFor']
|
|
1206
1238
|
}], menuData: [{
|
|
@@ -1211,6 +1243,191 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
1211
1243
|
args: ['cuteMenuTriggerRestoreFocus']
|
|
1212
1244
|
}], menuOpened: [{
|
|
1213
1245
|
type: Output
|
|
1246
|
+
}], onMenuOpen: [{
|
|
1247
|
+
type: Output
|
|
1248
|
+
}], menuClosed: [{
|
|
1249
|
+
type: Output
|
|
1250
|
+
}], onMenuClose: [{
|
|
1251
|
+
type: Output
|
|
1252
|
+
}] } });
|
|
1253
|
+
|
|
1254
|
+
/**
|
|
1255
|
+
* @license Apache-2.0
|
|
1256
|
+
*
|
|
1257
|
+
* Copyright (c) 2025 CuteWidgets Team. All Rights Reserved.
|
|
1258
|
+
*
|
|
1259
|
+
* You may not use this file except in compliance with the License
|
|
1260
|
+
* that can be found at http://www.apache.org/licenses/LICENSE-2.0
|
|
1261
|
+
*
|
|
1262
|
+
* This code is a modification of the `@angular/material` original
|
|
1263
|
+
* code licensed under MIT-style License (https://angular.dev/license).
|
|
1264
|
+
*/
|
|
1265
|
+
/**
|
|
1266
|
+
* Trigger that opens a menu whenever the user right-clicks within its host element.
|
|
1267
|
+
*/
|
|
1268
|
+
class CuteContextMenuTrigger extends CuteMenuTriggerBase {
|
|
1269
|
+
/** References the menu instance that the trigger is associated with. */
|
|
1270
|
+
get menu() {
|
|
1271
|
+
return this._menu;
|
|
1272
|
+
}
|
|
1273
|
+
set menu(menu) {
|
|
1274
|
+
this._menu = menu;
|
|
1275
|
+
}
|
|
1276
|
+
constructor() {
|
|
1277
|
+
super(false);
|
|
1278
|
+
this._point = { x: 0, y: 0, initialX: 0, initialY: 0, initialScrollX: 0, initialScrollY: 0 };
|
|
1279
|
+
this._triggerPressedControl = false;
|
|
1280
|
+
this._document = inject(DOCUMENT);
|
|
1281
|
+
this._viewportRuler = inject(ViewportRuler);
|
|
1282
|
+
this._scrollDispatcher = inject(ScrollDispatcher);
|
|
1283
|
+
/**
|
|
1284
|
+
* Whether focus should be restored when the menu is closed.
|
|
1285
|
+
* Note that disabling this option can have accessibility implications
|
|
1286
|
+
* and it's up to you to manage focus, if you decide to turn it off.
|
|
1287
|
+
*/
|
|
1288
|
+
this.restoreFocus = true;
|
|
1289
|
+
/** Whether the context menu is disabled. */
|
|
1290
|
+
this.disabled = false;
|
|
1291
|
+
/** Event emitted when the associated menu is opened. */
|
|
1292
|
+
this.menuOpened = new EventEmitter();
|
|
1293
|
+
/** Event emitted when the associated menu is closed. */
|
|
1294
|
+
this.menuClosed = new EventEmitter();
|
|
1295
|
+
}
|
|
1296
|
+
ngOnDestroy() {
|
|
1297
|
+
super.ngOnDestroy();
|
|
1298
|
+
this._scrollSubscription?.unsubscribe();
|
|
1299
|
+
}
|
|
1300
|
+
/** Handler for `contextmenu` events. */
|
|
1301
|
+
_handleContextMenuEvent(event) {
|
|
1302
|
+
if (!this.disabled) {
|
|
1303
|
+
event.preventDefault();
|
|
1304
|
+
// If the menu is already open, only update its position.
|
|
1305
|
+
if (this.menuOpen) {
|
|
1306
|
+
this._initializePoint(event.clientX, event.clientY);
|
|
1307
|
+
this._updatePosition();
|
|
1308
|
+
}
|
|
1309
|
+
else {
|
|
1310
|
+
this._openContextMenu(event);
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
_destroyMenu(reason) {
|
|
1315
|
+
super._destroyMenu(reason);
|
|
1316
|
+
this._scrollSubscription?.unsubscribe();
|
|
1317
|
+
}
|
|
1318
|
+
_getOverlayOrigin() {
|
|
1319
|
+
return this._point;
|
|
1320
|
+
}
|
|
1321
|
+
_getOutsideClickStream(overlayRef) {
|
|
1322
|
+
return overlayRef.outsidePointerEvents().pipe(skipWhile((event, index) => {
|
|
1323
|
+
if (event.type === 'contextmenu') {
|
|
1324
|
+
// Do not close when attempting to open a context menu within the trigger.
|
|
1325
|
+
return this._isWithinMenuOrTrigger(_getEventTarget(event));
|
|
1326
|
+
}
|
|
1327
|
+
else if (event.type === 'auxclick') {
|
|
1328
|
+
// Skip the first `auxclick` since it happens at
|
|
1329
|
+
// the same time as the event that opens the menu.
|
|
1330
|
+
if (index === 0) {
|
|
1331
|
+
return true;
|
|
1332
|
+
}
|
|
1333
|
+
// Do not close on `auxclick` within the menu since we want to reposition the menu
|
|
1334
|
+
// instead. Note that we have to resolve the clicked element using its position,
|
|
1335
|
+
// rather than `event.target`, because the `target` is set to the `body`.
|
|
1336
|
+
this._rootNode ??= _getShadowRoot(this._element.nativeElement) || this._document;
|
|
1337
|
+
return this._isWithinMenuOrTrigger(this._rootNode.elementFromPoint(event.clientX, event.clientY));
|
|
1338
|
+
}
|
|
1339
|
+
// Using a mouse, the `contextmenu` event can fire either when pressing the right button
|
|
1340
|
+
// or left button + control. Most browsers won't dispatch a `click` event right after
|
|
1341
|
+
// a `contextmenu` event triggered by left button + control, but Safari will (see #27832).
|
|
1342
|
+
// This closes the menu immediately. To work around it, we check that both the triggering
|
|
1343
|
+
// event and the current outside click event both had the control key pressed, and that
|
|
1344
|
+
// that this is the first outside click event.
|
|
1345
|
+
return this._triggerPressedControl && index === 0 && event.ctrlKey;
|
|
1346
|
+
}));
|
|
1347
|
+
}
|
|
1348
|
+
/** Checks whether an element is within the trigger or the opened overlay. */
|
|
1349
|
+
_isWithinMenuOrTrigger(target) {
|
|
1350
|
+
if (!target) {
|
|
1351
|
+
return false;
|
|
1352
|
+
}
|
|
1353
|
+
const element = this._element.nativeElement;
|
|
1354
|
+
if (target === element || element.contains(target)) {
|
|
1355
|
+
return true;
|
|
1356
|
+
}
|
|
1357
|
+
const overlay = this._overlayRef?.hostElement;
|
|
1358
|
+
return overlay === target || !!overlay?.contains(target);
|
|
1359
|
+
}
|
|
1360
|
+
/** Opens the context menu. */
|
|
1361
|
+
_openContextMenu(event) {
|
|
1362
|
+
// A context menu can be triggered via a mouse right click or a keyboard shortcut.
|
|
1363
|
+
if (event.button === 2) {
|
|
1364
|
+
this._openedBy = 'mouse';
|
|
1365
|
+
}
|
|
1366
|
+
else {
|
|
1367
|
+
this._openedBy = event.button === 0 ? 'keyboard' : undefined;
|
|
1368
|
+
}
|
|
1369
|
+
this._initializePoint(event.clientX, event.clientY);
|
|
1370
|
+
this._triggerPressedControl = event.ctrlKey;
|
|
1371
|
+
super._openMenu(true);
|
|
1372
|
+
this._scrollSubscription?.unsubscribe();
|
|
1373
|
+
this._scrollSubscription = this._scrollDispatcher.scrolled(0).subscribe(() => {
|
|
1374
|
+
// When passing a point to the connected position strategy, the position
|
|
1375
|
+
// won't update as the user is scrolling so we have to do it manually.
|
|
1376
|
+
const position = this._viewportRuler.getViewportScrollPosition();
|
|
1377
|
+
const point = this._point;
|
|
1378
|
+
point.y = point.initialY + (point.initialScrollY - position.top);
|
|
1379
|
+
point.x = point.initialX + (point.initialScrollX - position.left);
|
|
1380
|
+
this._updatePosition();
|
|
1381
|
+
});
|
|
1382
|
+
}
|
|
1383
|
+
/** Initializes the point representing the origin relative to which the menu will be rendered. */
|
|
1384
|
+
_initializePoint(x, y) {
|
|
1385
|
+
const scrollPosition = this._viewportRuler.getViewportScrollPosition();
|
|
1386
|
+
const point = this._point;
|
|
1387
|
+
point.x = point.initialX = x;
|
|
1388
|
+
point.y = point.initialY = y;
|
|
1389
|
+
point.initialScrollX = scrollPosition.left;
|
|
1390
|
+
point.initialScrollY = scrollPosition.top;
|
|
1391
|
+
}
|
|
1392
|
+
/** Refreshes the position of the overlay. */
|
|
1393
|
+
_updatePosition() {
|
|
1394
|
+
const overlayRef = this._overlayRef;
|
|
1395
|
+
if (overlayRef) {
|
|
1396
|
+
const positionStrategy = overlayRef.getConfig()
|
|
1397
|
+
.positionStrategy;
|
|
1398
|
+
positionStrategy.setOrigin(this._point);
|
|
1399
|
+
overlayRef.updatePosition();
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CuteContextMenuTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1403
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "21.2.0", type: CuteContextMenuTrigger, isStandalone: true, selector: "[cuteContextMenuTriggerFor]", inputs: { menu: ["cuteContextMenuTriggerFor", "menu"], menuData: ["cuteContextMenuTriggerData", "menuData"], restoreFocus: ["cuteContextMenuTriggerRestoreFocus", "restoreFocus"], disabled: ["cuteContextMenuTriggerDisabled", "disabled", booleanAttribute] }, outputs: { menuOpened: "menuOpened", menuClosed: "menuClosed" }, host: { listeners: { "contextmenu": "_handleContextMenuEvent($event)" }, properties: { "class.cute-context-menu-trigger-disabled": "disabled", "attr.aria-controls": "menuOpen ? menu?.panelId : null" }, classAttribute: "cute-context-menu-trigger" }, exportAs: ["cuteContextMenuTrigger"], usesInheritance: true, ngImport: i0 }); }
|
|
1404
|
+
}
|
|
1405
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CuteContextMenuTrigger, decorators: [{
|
|
1406
|
+
type: Directive,
|
|
1407
|
+
args: [{
|
|
1408
|
+
selector: '[cuteContextMenuTriggerFor]',
|
|
1409
|
+
host: {
|
|
1410
|
+
'class': 'cute-context-menu-trigger',
|
|
1411
|
+
'[class.cute-context-menu-trigger-disabled]': 'disabled',
|
|
1412
|
+
'[attr.aria-controls]': 'menuOpen ? menu?.panelId : null',
|
|
1413
|
+
'(contextmenu)': '_handleContextMenuEvent($event)',
|
|
1414
|
+
},
|
|
1415
|
+
exportAs: 'cuteContextMenuTrigger',
|
|
1416
|
+
}]
|
|
1417
|
+
}], ctorParameters: () => [], propDecorators: { menu: [{
|
|
1418
|
+
type: Input,
|
|
1419
|
+
args: [{ alias: 'cuteContextMenuTriggerFor', required: true }]
|
|
1420
|
+
}], menuData: [{
|
|
1421
|
+
type: Input,
|
|
1422
|
+
args: ['cuteContextMenuTriggerData']
|
|
1423
|
+
}], restoreFocus: [{
|
|
1424
|
+
type: Input,
|
|
1425
|
+
args: ['cuteContextMenuTriggerRestoreFocus']
|
|
1426
|
+
}], disabled: [{
|
|
1427
|
+
type: Input,
|
|
1428
|
+
args: [{ alias: 'cuteContextMenuTriggerDisabled', transform: booleanAttribute }]
|
|
1429
|
+
}], menuOpened: [{
|
|
1430
|
+
type: Output
|
|
1214
1431
|
}], menuClosed: [{
|
|
1215
1432
|
type: Output
|
|
1216
1433
|
}] } });
|
|
@@ -1224,32 +1441,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
1224
1441
|
* that can be found at http://www.apache.org/licenses/LICENSE-2.0
|
|
1225
1442
|
*/
|
|
1226
1443
|
const TYPES = [
|
|
1227
|
-
CommonModule,
|
|
1228
1444
|
CuteMenu,
|
|
1229
1445
|
CuteMenuItem,
|
|
1230
1446
|
CuteMenuContent,
|
|
1231
1447
|
CuteMenuTrigger,
|
|
1448
|
+
CuteContextMenuTrigger,
|
|
1232
1449
|
];
|
|
1233
1450
|
class CuteMenuModule {
|
|
1234
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1235
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "
|
|
1236
|
-
CuteMenu,
|
|
1451
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CuteMenuModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
1452
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.0", ngImport: i0, type: CuteMenuModule, imports: [OverlayModule, CuteMenu,
|
|
1237
1453
|
CuteMenuItem,
|
|
1238
1454
|
CuteMenuContent,
|
|
1239
|
-
CuteMenuTrigger
|
|
1240
|
-
CuteMenu,
|
|
1455
|
+
CuteMenuTrigger,
|
|
1456
|
+
CuteContextMenuTrigger], exports: [CdkScrollableModule, CuteMenu,
|
|
1241
1457
|
CuteMenuItem,
|
|
1242
1458
|
CuteMenuContent,
|
|
1243
|
-
CuteMenuTrigger
|
|
1244
|
-
|
|
1459
|
+
CuteMenuTrigger,
|
|
1460
|
+
CuteContextMenuTrigger] }); }
|
|
1461
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CuteMenuModule, imports: [OverlayModule, CdkScrollableModule] }); }
|
|
1245
1462
|
}
|
|
1246
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1463
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CuteMenuModule, decorators: [{
|
|
1247
1464
|
type: NgModule,
|
|
1248
1465
|
args: [{
|
|
1249
|
-
imports: [
|
|
1250
|
-
exports: TYPES,
|
|
1251
|
-
declarations: [],
|
|
1252
|
-
providers: [CUTE_MENU_SCROLL_STRATEGY_FACTORY_PROVIDER],
|
|
1466
|
+
imports: [OverlayModule, ...TYPES],
|
|
1467
|
+
exports: [CdkScrollableModule, ...TYPES],
|
|
1253
1468
|
}]
|
|
1254
1469
|
}] });
|
|
1255
1470
|
|
|
@@ -1257,5 +1472,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
1257
1472
|
* Generated bundle index. Do not edit.
|
|
1258
1473
|
*/
|
|
1259
1474
|
|
|
1260
|
-
export { CUTE_MENU_CONTENT, CUTE_MENU_DEFAULT_OPTIONS,
|
|
1475
|
+
export { CUTE_MENU_CONTENT, CUTE_MENU_DEFAULT_OPTIONS, CUTE_MENU_PANEL, CUTE_MENU_SCROLL_STRATEGY, CuteContextMenuTrigger, CuteMenu, CuteMenuContent, CuteMenuItem, CuteMenuModule, CuteMenuTrigger, MENU_PANEL_TOP_PADDING };
|
|
1261
1476
|
//# sourceMappingURL=cute-widgets-base-menu.mjs.map
|