@cute-widgets/base 20.0.4 → 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 +18 -6
- package/fesm2022/cute-widgets-base-abstract.mjs +19 -29
- 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 +61 -37
- package/fesm2022/cute-widgets-base-button-toggle.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-button.mjs +176 -112
- 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 -61
- package/fesm2022/cute-widgets-base-chips.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-collapse.mjs +120 -132
- 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-layout.mjs +13 -1
- package/fesm2022/cute-widgets-base-core-layout.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 +7 -7
- 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 +50 -38
- 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 +170 -135
- 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 +581 -388
- package/fesm2022/cute-widgets-base-menu.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-navbar.mjs +101 -61
- package/fesm2022/cute-widgets-base-navbar.mjs.map +1 -1
- package/fesm2022/cute-widgets-base-paginator.mjs +13 -13
- 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 +100 -75
- 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 +121 -121
- package/{abstract/index.d.ts → types/cute-widgets-base-abstract.d.ts} +9 -3
- 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} +15 -5
- package/{button/index.d.ts → types/cute-widgets-base-button.d.ts} +44 -22
- 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/types/cute-widgets-base-collapse.d.ts +98 -0
- package/{core/layout/index.d.ts → types/cute-widgets-base-core-layout.d.ts} +7 -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/{core/index.d.ts → types/cute-widgets-base-core.d.ts} +7 -1
- package/{datepicker/index.d.ts → types/cute-widgets-base-datepicker.d.ts} +2 -2
- package/{dialog/index.d.ts → types/cute-widgets-base-dialog.d.ts} +81 -57
- 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/{navbar/index.d.ts → types/cute-widgets-base-navbar.d.ts} +24 -9
- package/{paginator/index.d.ts → types/cute-widgets-base-paginator.d.ts} +2 -2
- 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/{tabs/index.d.ts → types/cute-widgets-base-tabs.d.ts} +35 -14
- package/{tree/index.d.ts → types/cute-widgets-base-tree.d.ts} +2 -2
- package/collapse/index.d.ts +0 -97
- /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/{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/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/{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/{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/{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,34 +133,12 @@ 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: "
|
|
140
|
-
<ng-content select="cute-icon, i[class], [cuteMenuItemIcon]"></ng-content>
|
|
141
|
-
<span class="cute-menu-item-text"><ng-content></ng-content></span>
|
|
142
|
-
@if (_triggersSubmenu) {
|
|
143
|
-
<svg class="cute-menu-item-submenu-icon"
|
|
144
|
-
viewBox="0 0 5 10"
|
|
145
|
-
focusable="false"
|
|
146
|
-
aria-hidden="true">
|
|
147
|
-
<polygon points="0,0 5,5 0,10"/>
|
|
148
|
-
</svg>
|
|
149
|
-
}
|
|
150
|
-
`, isInline: true, 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 }); }
|
|
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 }); }
|
|
151
138
|
}
|
|
152
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
139
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CuteMenuItem, decorators: [{
|
|
153
140
|
type: Component,
|
|
154
|
-
args: [{ selector: '[cute-menu-item]', exportAs: 'cuteMenuItem',
|
|
155
|
-
<ng-content select="cute-icon, i[class], [cuteMenuItemIcon]"></ng-content>
|
|
156
|
-
<span class="cute-menu-item-text"><ng-content></ng-content></span>
|
|
157
|
-
@if (_triggersSubmenu) {
|
|
158
|
-
<svg class="cute-menu-item-submenu-icon"
|
|
159
|
-
viewBox="0 0 5 10"
|
|
160
|
-
focusable="false"
|
|
161
|
-
aria-hidden="true">
|
|
162
|
-
<polygon points="0,0 5,5 0,10"/>
|
|
163
|
-
</svg>
|
|
164
|
-
}
|
|
165
|
-
`, host: {
|
|
141
|
+
args: [{ selector: '[cute-menu-item]', exportAs: 'cuteMenuItem', host: {
|
|
166
142
|
'[attr.role]': 'role',
|
|
167
143
|
'class': 'cute-menu-item',
|
|
168
144
|
'[class.active]': '_highlighted',
|
|
@@ -170,10 +146,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
170
146
|
//'[class.disabled]': 'disabled',
|
|
171
147
|
'[attr.tabindex]': '_getTabIndex()',
|
|
172
148
|
'[attr.aria-disabled]': 'disabled',
|
|
173
|
-
'[disabled]': 'disabled || null',
|
|
149
|
+
'[attr.disabled]': 'disabled || null',
|
|
174
150
|
'(click)': '_checkDisabled($event)',
|
|
175
151
|
'(mouseenter)': '_handleMouseEnter()',
|
|
176
|
-
}, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, 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"] }]
|
|
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"] }]
|
|
177
153
|
}], ctorParameters: () => [], propDecorators: { role: [{
|
|
178
154
|
type: Input
|
|
179
155
|
}], disabled: [{
|
|
@@ -287,10 +263,10 @@ class CuteMenuContent {
|
|
|
287
263
|
this.detach();
|
|
288
264
|
this._outlet?.dispose();
|
|
289
265
|
}
|
|
290
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
291
|
-
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 }); }
|
|
292
268
|
}
|
|
293
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
269
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CuteMenuContent, decorators: [{
|
|
294
270
|
type: Directive,
|
|
295
271
|
args: [{
|
|
296
272
|
selector: 'ng-template[cuteMenuContent]',
|
|
@@ -299,70 +275,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
299
275
|
}]
|
|
300
276
|
}], ctorParameters: () => [] });
|
|
301
277
|
|
|
302
|
-
/**
|
|
303
|
-
* @license Apache-2.0
|
|
304
|
-
*
|
|
305
|
-
* Copyright (c) 2025 CuteWidgets Team. All Rights Reserved.
|
|
306
|
-
*
|
|
307
|
-
* You may not use this file except in compliance with the License
|
|
308
|
-
* that can be found at http://www.apache.org/licenses/LICENSE-2.0
|
|
309
|
-
*
|
|
310
|
-
* This code is a modification of the `@angular/material` original
|
|
311
|
-
* code licensed under MIT-style License (https://angular.dev/license).
|
|
312
|
-
*/
|
|
313
|
-
/**
|
|
314
|
-
* Animations used by the cute-menu component.
|
|
315
|
-
* Animation duration and timing values are based on:
|
|
316
|
-
* https://material.io/guidelines/components/menus.html#menus-usage
|
|
317
|
-
* @docs-private
|
|
318
|
-
*/
|
|
319
|
-
const cuteMenuAnimations = {
|
|
320
|
-
/**
|
|
321
|
-
* This animation controls the menu panel's entry and exit from the page.
|
|
322
|
-
*
|
|
323
|
-
* When the menu panel is added to the DOM, it scales in and fades in its border.
|
|
324
|
-
*
|
|
325
|
-
* When the menu panel is removed from the DOM, it simply fades out after a brief
|
|
326
|
-
* delay to display the ripple.
|
|
327
|
-
*/
|
|
328
|
-
transformMenu: trigger('transformMenu', [
|
|
329
|
-
state('void', style({
|
|
330
|
-
opacity: 0,
|
|
331
|
-
transform: 'scale(0.8)',
|
|
332
|
-
})),
|
|
333
|
-
transition('void => enter', animate('120ms cubic-bezier(0, 0, 0.2, 1)', style({
|
|
334
|
-
opacity: 1,
|
|
335
|
-
transform: 'scale(1)',
|
|
336
|
-
}))),
|
|
337
|
-
transition('* => void', animate('100ms 25ms linear', style({ opacity: 0 }))),
|
|
338
|
-
]),
|
|
339
|
-
/**
|
|
340
|
-
* This animation fades in the background color and content of the menu panel
|
|
341
|
-
* after its containing element is scaled in.
|
|
342
|
-
*/
|
|
343
|
-
fadeInItems: trigger('fadeInItems', [
|
|
344
|
-
// TODO(crisbeto): this is inside the `transformMenu`
|
|
345
|
-
// now. Remove next time we do breaking changes.
|
|
346
|
-
state('showing', style({ opacity: 1 })),
|
|
347
|
-
transition('void => *', [
|
|
348
|
-
style({ opacity: 0 }),
|
|
349
|
-
animate('400ms 100ms cubic-bezier(0.55, 0, 0.55, 0.2)'),
|
|
350
|
-
]),
|
|
351
|
-
]),
|
|
352
|
-
};
|
|
353
|
-
/**
|
|
354
|
-
* @deprecated
|
|
355
|
-
* @breaking-change 8.0.0
|
|
356
|
-
* @docs-private
|
|
357
|
-
*/
|
|
358
|
-
//export const fadeInItems = cuteMenuAnimations.fadeInItems;
|
|
359
|
-
/**
|
|
360
|
-
* @deprecated
|
|
361
|
-
* @breaking-change 8.0.0
|
|
362
|
-
* @docs-private
|
|
363
|
-
*/
|
|
364
|
-
//export const transformMenu = cuteMenuAnimations.transformMenu;
|
|
365
|
-
|
|
366
278
|
/**
|
|
367
279
|
* @license Apache-2.0
|
|
368
280
|
*
|
|
@@ -378,21 +290,17 @@ let menuPanelUid = 0;
|
|
|
378
290
|
/** Injection token to be used to override the default options for `mat-menu`. */
|
|
379
291
|
const CUTE_MENU_DEFAULT_OPTIONS = new InjectionToken('cute-menu-default-options', {
|
|
380
292
|
providedIn: 'root',
|
|
381
|
-
factory:
|
|
382
|
-
});
|
|
383
|
-
/**
|
|
384
|
-
* @docs-private
|
|
385
|
-
* @deprecated No longer used, will be removed.
|
|
386
|
-
* @breaking-change 21.0.0
|
|
387
|
-
*/
|
|
388
|
-
function CUTE_MENU_DEFAULT_OPTIONS_FACTORY() {
|
|
389
|
-
return {
|
|
293
|
+
factory: () => ({
|
|
390
294
|
overlapTrigger: false,
|
|
391
295
|
xPosition: 'after',
|
|
392
296
|
yPosition: 'below',
|
|
393
297
|
backdropClass: 'cdk-overlay-transparent-backdrop',
|
|
394
|
-
}
|
|
395
|
-
}
|
|
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';
|
|
396
304
|
class CuteMenu {
|
|
397
305
|
/** Position of the menu in the X axis. */
|
|
398
306
|
get xPosition() { return this._xPosition; }
|
|
@@ -415,7 +323,7 @@ class CuteMenu {
|
|
|
415
323
|
this.setPositionClasses();
|
|
416
324
|
}
|
|
417
325
|
/**
|
|
418
|
-
* 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
|
|
419
327
|
* menu template that displays in the overlay container. Otherwise, it's difficult
|
|
420
328
|
* to style the containing menu from outside the component.
|
|
421
329
|
* @param classes list of class names
|
|
@@ -443,14 +351,17 @@ class CuteMenu {
|
|
|
443
351
|
this._injector = inject(Injector);
|
|
444
352
|
this._elevationPrefix = 'shadow';
|
|
445
353
|
this._baseElevation = 1;
|
|
354
|
+
this._animationsDisabled = _animationsDisabled();
|
|
355
|
+
/** Only the direct descendant menu items. */
|
|
356
|
+
this._directDescendantItems = new QueryList();
|
|
446
357
|
/** Config object to be passed into the menu's ngClass */
|
|
447
358
|
this._classList = {};
|
|
448
359
|
/** Current state of the panel animation. */
|
|
449
360
|
this._panelAnimationState = 'void';
|
|
450
|
-
/** Only the direct descendant menu items. */
|
|
451
|
-
this._directDescendantItems = new QueryList();
|
|
452
361
|
/** Emits whenever an animation on the menu completes. */
|
|
453
362
|
this._animationDone = new Subject();
|
|
363
|
+
/** Whether the menu is animating. */
|
|
364
|
+
this._isAnimating = signal(false, ...(ngDevMode ? [{ debugName: "_isAnimating" }] : []));
|
|
454
365
|
/** Event emitted when the menu is closed. */
|
|
455
366
|
this.closed = new EventEmitter();
|
|
456
367
|
this.panelId = `cute-menu-panel-${menuPanelUid++}`;
|
|
@@ -619,8 +530,8 @@ class CuteMenu {
|
|
|
619
530
|
/**
|
|
620
531
|
* Adds classes to the menu panel based on its position. Can be used by
|
|
621
532
|
* consumers to add specific styling based on the position.
|
|
622
|
-
* @param posX Position of the menu along the x
|
|
623
|
-
* @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.
|
|
624
535
|
*/
|
|
625
536
|
setPositionClasses(posX = this.xPosition, posY = this.yPosition) {
|
|
626
537
|
const classes = this._classList;
|
|
@@ -630,32 +541,52 @@ class CuteMenu {
|
|
|
630
541
|
classes['cute-menu-below'] = posY === 'below';
|
|
631
542
|
this._changeDetectorRef.markForCheck();
|
|
632
543
|
}
|
|
633
|
-
/**
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
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
|
+
}
|
|
637
555
|
}
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
556
|
+
_onAnimationStart(state) {
|
|
557
|
+
if (state === ENTER_ANIMATION || state === EXIT_ANIMATION) {
|
|
558
|
+
this._isAnimating.set(true);
|
|
559
|
+
}
|
|
642
560
|
}
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
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
|
+
}
|
|
658
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();
|
|
659
590
|
}
|
|
660
591
|
/**
|
|
661
592
|
* Sets up a stream that will keep track of any newly-added menu items and will update the list
|
|
@@ -675,7 +606,7 @@ class CuteMenu {
|
|
|
675
606
|
_resolvePanel() {
|
|
676
607
|
let menuPanel = null;
|
|
677
608
|
if (this._directDescendantItems.length) {
|
|
678
|
-
// Because the `
|
|
609
|
+
// Because the `cute-menuPanel` is at the DOM insertion point, not inside the overlay, we don't
|
|
679
610
|
// have a nice way of getting a hold of the menuPanel panel. We can't use a `ViewChild` either
|
|
680
611
|
// because the panel is inside an `ng-template`. We work around it by starting from one of
|
|
681
612
|
// the items and walking up the DOM.
|
|
@@ -683,18 +614,21 @@ class CuteMenu {
|
|
|
683
614
|
}
|
|
684
615
|
return menuPanel;
|
|
685
616
|
}
|
|
686
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
687
|
-
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 }); }
|
|
688
619
|
}
|
|
689
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
620
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CuteMenu, decorators: [{
|
|
690
621
|
type: Component,
|
|
691
622
|
args: [{ selector: 'cute-menu', exportAs: 'cuteMenu', host: {
|
|
692
623
|
'class': 'cute-menu',
|
|
693
624
|
'[attr.aria-label]': 'null',
|
|
694
625
|
'[attr.aria-labelledby]': 'null',
|
|
695
626
|
'[attr.aria-describedby]': 'null',
|
|
696
|
-
},
|
|
697
|
-
}], 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: [{
|
|
698
632
|
type: Input
|
|
699
633
|
}], ariaLabel: [{
|
|
700
634
|
type: Input,
|
|
@@ -718,9 +652,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
718
652
|
}], templateRef: [{
|
|
719
653
|
type: ViewChild,
|
|
720
654
|
args: [TemplateRef]
|
|
721
|
-
}], _allItems: [{
|
|
722
|
-
type: ContentChildren,
|
|
723
|
-
args: [CuteMenuItem, { descendants: true }]
|
|
724
655
|
}], items: [{
|
|
725
656
|
type: ContentChildren,
|
|
726
657
|
args: [CuteMenuItem, { descendants: false }]
|
|
@@ -758,31 +689,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
758
689
|
* code licensed under MIT-style License (https://angular.dev/license).
|
|
759
690
|
*/
|
|
760
691
|
/** Injection token that determines the scroll handling while the menu is open. */
|
|
761
|
-
const CUTE_MENU_SCROLL_STRATEGY = new InjectionToken('cute-menu-scroll-strategy'
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
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();
|
|
774
707
|
/** Directive applied to an element that should trigger a `cute-menu`. */
|
|
775
|
-
class
|
|
776
|
-
/**
|
|
777
|
-
get
|
|
778
|
-
|
|
779
|
-
|
|
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) {
|
|
780
717
|
return;
|
|
781
718
|
}
|
|
782
|
-
this.
|
|
719
|
+
this._menuInternal = menu;
|
|
783
720
|
this._menuCloseSubscription.unsubscribe();
|
|
784
721
|
if (menu) {
|
|
785
|
-
if (menu === this._parentCuteMenu &&
|
|
722
|
+
if (menu === this._parentCuteMenu && (typeof ngDevMode === 'undefined' || ngDevMode)) {
|
|
786
723
|
throwCuteMenuRecursiveError();
|
|
787
724
|
}
|
|
788
725
|
this._menuCloseSubscription = menu.closed.subscribe((reason) => {
|
|
@@ -793,65 +730,42 @@ class CuteMenuTrigger {
|
|
|
793
730
|
}
|
|
794
731
|
});
|
|
795
732
|
}
|
|
796
|
-
this._menuItemInstance?._setTriggersSubmenu(this.
|
|
797
|
-
}
|
|
798
|
-
constructor(
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
this.
|
|
803
|
-
this.
|
|
804
|
-
this.
|
|
805
|
-
this.
|
|
806
|
-
this.
|
|
807
|
-
this.
|
|
808
|
-
this.
|
|
809
|
-
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();
|
|
810
747
|
this._overlayRef = null;
|
|
811
748
|
this._menuOpen = false;
|
|
812
749
|
this._closingActionsSubscription = Subscription.EMPTY;
|
|
813
|
-
this._hoverSubscription = Subscription.EMPTY;
|
|
814
750
|
this._menuCloseSubscription = Subscription.EMPTY;
|
|
815
|
-
|
|
816
|
-
/**
|
|
817
|
-
* Handles touch start events on the trigger.
|
|
818
|
-
* Needs to be an arrow function, so we can easily use addEventListener and removeEventListener.
|
|
819
|
-
*/
|
|
820
|
-
this._handleTouchStart = (event) => {
|
|
821
|
-
if (!isFakeTouchstartFromScreenReader(event)) {
|
|
822
|
-
this._openedBy = 'touch';
|
|
823
|
-
}
|
|
824
|
-
};
|
|
825
|
-
// 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
|
|
826
752
|
// the first item of the list when the menu is opened via the keyboard
|
|
827
753
|
this._openedBy = undefined;
|
|
828
|
-
this.
|
|
829
|
-
|
|
830
|
-
* Whether focus should be restored when the menu is closed.
|
|
831
|
-
* Note that disabling this option can have accessibility implications, and
|
|
832
|
-
* it's up to you to manage focus if you decide to turn it off.
|
|
833
|
-
*/
|
|
834
|
-
this.restoreFocus = true;
|
|
835
|
-
/** Event emitted when the associated menu is opened. */
|
|
836
|
-
this.menuOpened = new EventEmitter();
|
|
837
|
-
/** Event emitted when the associated menu is closed. */
|
|
838
|
-
this.menuClosed = new EventEmitter();
|
|
839
|
-
this._scrollStrategy = scrollStrategy;
|
|
754
|
+
this._menuInternal = null;
|
|
755
|
+
const parentMenu = inject(CUTE_MENU_PANEL, { optional: true });
|
|
840
756
|
this._parentCuteMenu = parentMenu instanceof CuteMenu ? parentMenu : undefined;
|
|
841
|
-
_element.nativeElement.addEventListener('touchstart', this._handleTouchStart, passiveEventListenerOptions);
|
|
842
|
-
}
|
|
843
|
-
ngAfterContentInit() {
|
|
844
|
-
this._handleHover();
|
|
845
757
|
}
|
|
846
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();
|
|
847
765
|
if (this._overlayRef) {
|
|
848
766
|
this._overlayRef.dispose();
|
|
849
767
|
this._overlayRef = null;
|
|
850
768
|
}
|
|
851
|
-
this._element.nativeElement.removeEventListener('touchstart', this._handleTouchStart, passiveEventListenerOptions);
|
|
852
|
-
this._menuCloseSubscription.unsubscribe();
|
|
853
|
-
this._closingActionsSubscription.unsubscribe();
|
|
854
|
-
this._hoverSubscription.unsubscribe();
|
|
855
769
|
}
|
|
856
770
|
/** Whether the menu is open. */
|
|
857
771
|
get menuOpen() {
|
|
@@ -861,53 +775,69 @@ class CuteMenuTrigger {
|
|
|
861
775
|
get dir() {
|
|
862
776
|
return this._dir && this._dir.value === 'rtl' ? 'rtl' : 'ltr';
|
|
863
777
|
}
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
/** Whether the menu triggers a submenu or a top-level one. */
|
|
868
|
-
triggersSubmenu() {
|
|
869
|
-
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);
|
|
870
781
|
}
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
return this._menuOpen ? this.closeMenu() : this.openMenu();
|
|
782
|
+
_closeMenu() {
|
|
783
|
+
this._menu?.closed.emit();
|
|
874
784
|
}
|
|
875
|
-
/**
|
|
876
|
-
|
|
877
|
-
|
|
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;
|
|
878
791
|
if (this._menuOpen || !menu) {
|
|
879
792
|
return;
|
|
880
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
|
+
}
|
|
881
802
|
const overlayRef = this._createOverlay(menu);
|
|
882
803
|
const overlayConfig = overlayRef.getConfig();
|
|
883
804
|
const positionStrategy = overlayConfig.positionStrategy;
|
|
884
805
|
this._setPosition(menu, positionStrategy);
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
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);
|
|
890
818
|
}
|
|
891
|
-
this._closingActionsSubscription = this._menuClosingActions().subscribe(() => this.
|
|
892
|
-
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);
|
|
893
827
|
if (menu instanceof CuteMenu) {
|
|
894
|
-
menu.
|
|
828
|
+
menu._setIsOpen(true);
|
|
895
829
|
menu._directDescendantItems.changes.pipe(takeUntil(menu.closed)).subscribe(() => {
|
|
896
|
-
// Re-adjust the position without locking when the
|
|
830
|
+
// Re-adjust the position without locking when the amount of items
|
|
897
831
|
// changes so that the overlay is allowed to pick a new optimal position.
|
|
898
832
|
positionStrategy.withLockedPosition(false).reapplyLastPosition();
|
|
899
833
|
positionStrategy.withLockedPosition(true);
|
|
900
834
|
});
|
|
901
835
|
}
|
|
902
836
|
}
|
|
903
|
-
/** Closes the menu. */
|
|
904
|
-
closeMenu() {
|
|
905
|
-
this.menu?.closed.emit();
|
|
906
|
-
}
|
|
907
837
|
/**
|
|
908
838
|
* Focuses the menu trigger.
|
|
909
839
|
* @param origin Source of the menu trigger's focus.
|
|
910
|
-
* @param options
|
|
840
|
+
* @param options Optional focus options.
|
|
911
841
|
*/
|
|
912
842
|
focus(origin, options) {
|
|
913
843
|
if (this._focusMonitor && origin) {
|
|
@@ -917,61 +847,47 @@ class CuteMenuTrigger {
|
|
|
917
847
|
this._element.nativeElement.focus(options);
|
|
918
848
|
}
|
|
919
849
|
}
|
|
920
|
-
/**
|
|
921
|
-
* Updates the position of the menu to ensure that it fits all options within the viewport.
|
|
922
|
-
*/
|
|
923
|
-
updatePosition() {
|
|
924
|
-
this._overlayRef?.updatePosition();
|
|
925
|
-
}
|
|
926
850
|
/** Closes the menu and does the necessary cleanup. */
|
|
927
851
|
_destroyMenu(reason) {
|
|
928
|
-
|
|
852
|
+
const overlayRef = this._overlayRef;
|
|
853
|
+
const menu = this._menu;
|
|
854
|
+
if (!overlayRef || !this.menuOpen) {
|
|
929
855
|
return;
|
|
930
856
|
}
|
|
931
|
-
const menu = this.menu;
|
|
932
857
|
this._closingActionsSubscription.unsubscribe();
|
|
933
|
-
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
|
+
}
|
|
934
881
|
// Always restore focus if the user is navigating using the keyboard or the menu was opened
|
|
935
882
|
// programmatically. We don't restore for non-root triggers, because it can prevent focus
|
|
936
883
|
// from making it back to the root trigger when closing a long chain of menus by clicking
|
|
937
884
|
// on the backdrop.
|
|
938
|
-
if (this.restoreFocus &&
|
|
885
|
+
if (this.restoreFocus &&
|
|
886
|
+
(reason === 'keydown' || !this._openedBy || !this._triggersSubmenu())) {
|
|
939
887
|
this.focus(this._openedBy);
|
|
940
888
|
}
|
|
941
889
|
this._openedBy = undefined;
|
|
942
|
-
|
|
943
|
-
menu._resetAnimation();
|
|
944
|
-
if (menu.lazyContent) {
|
|
945
|
-
// Wait for the exit animation to finish before detaching the content.
|
|
946
|
-
menu._animationDone
|
|
947
|
-
.pipe(filter(event => event.toState === 'void'), take(1),
|
|
948
|
-
// Interrupt if the content got re-attached.
|
|
949
|
-
takeUntil(menu.lazyContent._attached))
|
|
950
|
-
.subscribe({
|
|
951
|
-
next: () => menu.lazyContent.detach(),
|
|
952
|
-
// No matter whether the content got re-attached, reset the menu.
|
|
953
|
-
complete: () => this._setIsMenuOpen(false),
|
|
954
|
-
});
|
|
955
|
-
}
|
|
956
|
-
else {
|
|
957
|
-
this._setIsMenuOpen(false);
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
else {
|
|
961
|
-
this._setIsMenuOpen(false);
|
|
962
|
-
menu?.lazyContent?.detach();
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
/**
|
|
966
|
-
* This method sets the menu state to open and focuses the first item if
|
|
967
|
-
* the menu was opened via the keyboard.
|
|
968
|
-
*/
|
|
969
|
-
_initMenu(menu) {
|
|
970
|
-
menu.parentMenu = this.triggersSubmenu() ? this._parentCuteMenu : undefined;
|
|
971
|
-
menu.direction = this.dir;
|
|
972
|
-
this._setMenuElevation(menu);
|
|
973
|
-
menu.focusFirstItem(this._openedBy || 'program');
|
|
974
|
-
this._setIsMenuOpen(true);
|
|
890
|
+
this._setIsMenuOpen(false);
|
|
975
891
|
}
|
|
976
892
|
/** Updates the menu elevation based on the amount of parent menus that it has. */
|
|
977
893
|
_setMenuElevation(menu) {
|
|
@@ -990,7 +906,7 @@ class CuteMenuTrigger {
|
|
|
990
906
|
if (isOpen !== this._menuOpen) {
|
|
991
907
|
this._menuOpen = isOpen;
|
|
992
908
|
this._menuOpen ? this.menuOpened.emit() : this.menuClosed.emit();
|
|
993
|
-
if (this.
|
|
909
|
+
if (this._triggersSubmenu()) {
|
|
994
910
|
this._menuItemInstance._setHighlighted(isOpen);
|
|
995
911
|
}
|
|
996
912
|
this._changeDetectorRef.markForCheck();
|
|
@@ -1004,11 +920,12 @@ class CuteMenuTrigger {
|
|
|
1004
920
|
if (!this._overlayRef) {
|
|
1005
921
|
const config = this._getOverlayConfig(menu);
|
|
1006
922
|
this._subscribeToPositions(menu, config.positionStrategy);
|
|
1007
|
-
this._overlayRef = this.
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
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
|
+
});
|
|
1012
929
|
}
|
|
1013
930
|
return this._overlayRef;
|
|
1014
931
|
}
|
|
@@ -1018,16 +935,15 @@ class CuteMenuTrigger {
|
|
|
1018
935
|
*/
|
|
1019
936
|
_getOverlayConfig(menu) {
|
|
1020
937
|
return new OverlayConfig({
|
|
1021
|
-
positionStrategy: this.
|
|
1022
|
-
.position()
|
|
1023
|
-
.flexibleConnectedTo(this._element)
|
|
938
|
+
positionStrategy: createFlexibleConnectedPositionStrategy(this._injector, this._getOverlayOrigin())
|
|
1024
939
|
.withLockedPosition()
|
|
1025
940
|
.withGrowAfterOpen()
|
|
1026
941
|
.withTransformOriginOn('.cute-menu-panel'),
|
|
1027
942
|
backdropClass: menu.backdropClass || 'cdk-overlay-transparent-backdrop',
|
|
1028
943
|
panelClass: menu.overlayPanelClass,
|
|
1029
944
|
scrollStrategy: this._scrollStrategy(),
|
|
1030
|
-
direction: this._dir,
|
|
945
|
+
direction: this._dir || 'ltr',
|
|
946
|
+
disableAnimations: this._animationsDisabled,
|
|
1031
947
|
});
|
|
1032
948
|
}
|
|
1033
949
|
/**
|
|
@@ -1038,34 +954,28 @@ class CuteMenuTrigger {
|
|
|
1038
954
|
_subscribeToPositions(menu, position) {
|
|
1039
955
|
if (menu.setPositionClasses) {
|
|
1040
956
|
position.positionChanges.subscribe(change => {
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
// `positionChanges` fires outside of the `ngZone` and `setPositionClasses` might be
|
|
1045
|
-
// updating something in the view, so we need to bring it back in.
|
|
1046
|
-
if (this._ngZone) {
|
|
1047
|
-
this._ngZone.run(() => menu.setPositionClasses(posX, posY));
|
|
1048
|
-
}
|
|
1049
|
-
else {
|
|
957
|
+
this._ngZone.run(() => {
|
|
958
|
+
const posX = change.connectionPair.overlayX === 'start' ? 'after' : 'before';
|
|
959
|
+
const posY = change.connectionPair.overlayY === 'top' ? 'below' : 'above';
|
|
1050
960
|
menu.setPositionClasses(posX, posY);
|
|
1051
|
-
}
|
|
961
|
+
});
|
|
1052
962
|
});
|
|
1053
963
|
}
|
|
1054
964
|
}
|
|
1055
965
|
/**
|
|
1056
966
|
* Sets the appropriate positions on a position strategy
|
|
1057
967
|
* so the overlay connects with the trigger correctly.
|
|
1058
|
-
* @param menu Menu panel
|
|
1059
|
-
* @param positionStrategy Strategy whose position
|
|
968
|
+
* @param menu Menu panel.
|
|
969
|
+
* @param positionStrategy Strategy whose position to update.
|
|
1060
970
|
*/
|
|
1061
971
|
_setPosition(menu, positionStrategy) {
|
|
1062
972
|
let [originX, originFallbackX] = menu.xPosition === 'before' ? ['end', 'start'] : ['start', 'end'];
|
|
1063
973
|
let [overlayY, overlayFallbackY] = menu.yPosition === 'above' ? ['bottom', 'top'] : ['top', 'bottom'];
|
|
1064
974
|
let [originY, originFallbackY] = [overlayY, overlayFallbackY];
|
|
1065
975
|
let [overlayX, overlayFallbackX] = [originX, originFallbackX];
|
|
1066
|
-
let offsetY = 0;
|
|
1067
|
-
if (this.
|
|
1068
|
-
// 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
|
|
1069
979
|
// to the edges of the trigger, instead of overlapping it.
|
|
1070
980
|
overlayFallbackX = originX = menu.xPosition === 'before' ? 'start' : 'end';
|
|
1071
981
|
originFallbackX = overlayX = originX === 'end' ? 'start' : 'end';
|
|
@@ -1078,7 +988,6 @@ class CuteMenuTrigger {
|
|
|
1078
988
|
}
|
|
1079
989
|
}
|
|
1080
990
|
else if (!menu.overlapTrigger) {
|
|
1081
|
-
offsetY = 4;
|
|
1082
991
|
originY = overlayY === 'top' ? 'bottom' : 'top';
|
|
1083
992
|
originFallbackY = overlayFallbackY === 'top' ? 'bottom' : 'top';
|
|
1084
993
|
}
|
|
@@ -1103,13 +1012,147 @@ class CuteMenuTrigger {
|
|
|
1103
1012
|
}
|
|
1104
1013
|
/** Returns a stream that emits whenever an action that should close the menu occurs. */
|
|
1105
1014
|
_menuClosingActions() {
|
|
1106
|
-
const
|
|
1015
|
+
const outsideClicks = this._getOutsideClickStream(this._overlayRef);
|
|
1107
1016
|
const detachments = this._overlayRef.detachments();
|
|
1108
1017
|
const parentClose = this._parentCuteMenu ? this._parentCuteMenu.closed : of();
|
|
1109
1018
|
const hover = this._parentCuteMenu
|
|
1110
|
-
? this._parentCuteMenu
|
|
1019
|
+
? this._parentCuteMenu
|
|
1020
|
+
._hovered()
|
|
1021
|
+
.pipe(filter(active => this._menuOpen && active !== this._menuItemInstance))
|
|
1111
1022
|
: of();
|
|
1112
|
-
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();
|
|
1113
1156
|
}
|
|
1114
1157
|
/** Handles mouse presses on the trigger. */
|
|
1115
1158
|
_handleMousedown(event) {
|
|
@@ -1117,7 +1160,7 @@ class CuteMenuTrigger {
|
|
|
1117
1160
|
// Since right or middle button clicks won't trigger the `click` event,
|
|
1118
1161
|
// we shouldn't consider the menu as opened by mouse in those cases.
|
|
1119
1162
|
this._openedBy = event.button === 0 ? 'mouse' : undefined;
|
|
1120
|
-
// 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,
|
|
1121
1164
|
// we should prevent focus from moving onto it via click to avoid the
|
|
1122
1165
|
// highlight from lingering on the menu item.
|
|
1123
1166
|
if (this.triggersSubmenu()) {
|
|
@@ -1153,76 +1196,43 @@ class CuteMenuTrigger {
|
|
|
1153
1196
|
/** Handles the cases where the user hovers over the trigger. */
|
|
1154
1197
|
_handleHover() {
|
|
1155
1198
|
// Subscribe to changes in the hovered item in order to toggle the panel.
|
|
1156
|
-
if (
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
// before doing so. Also interrupt if the user moves to another item.
|
|
1170
|
-
if (this.menu instanceof CuteMenu && this.menu._isAnimating) {
|
|
1171
|
-
// We need the `delay(0)` here in order to avoid
|
|
1172
|
-
// 'changed after checked' errors in some cases. See #12194.
|
|
1173
|
-
this.menu._animationDone
|
|
1174
|
-
.pipe(take(1), delay(0, asapScheduler), takeUntil(this._parentCuteMenu._hovered()))
|
|
1175
|
-
.subscribe(() => this.openMenu());
|
|
1176
|
-
}
|
|
1177
|
-
else {
|
|
1178
|
-
this.openMenu();
|
|
1179
|
-
}
|
|
1180
|
-
});
|
|
1181
|
-
}
|
|
1182
|
-
/** Gets the portal that should be attached to the overlay. */
|
|
1183
|
-
_getPortal(menu) {
|
|
1184
|
-
// Note that we can avoid this check by keeping the portal on the menu panel.
|
|
1185
|
-
// While it would be cleaner, we'd have to introduce another required method on
|
|
1186
|
-
// `CuteMenuPanel`, making it harder to consume.
|
|
1187
|
-
if (!this._portal || this._portal.templateRef !== menu.templateRef) {
|
|
1188
|
-
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
|
+
});
|
|
1189
1212
|
}
|
|
1190
|
-
return this._portal;
|
|
1191
1213
|
}
|
|
1192
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1193
|
-
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 }); }
|
|
1194
1216
|
}
|
|
1195
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1217
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CuteMenuTrigger, decorators: [{
|
|
1196
1218
|
type: Directive,
|
|
1197
1219
|
args: [{
|
|
1198
|
-
selector:
|
|
1199
|
-
exportAs: 'cuteMenuTrigger',
|
|
1220
|
+
selector: '[cute-menu-trigger-for], [cuteMenuTriggerFor]',
|
|
1200
1221
|
host: {
|
|
1201
1222
|
'class': 'cute-menu-trigger',
|
|
1202
1223
|
'[attr.aria-haspopup]': 'menu ? "menu" : null',
|
|
1203
1224
|
'[attr.aria-expanded]': 'menuOpen',
|
|
1204
|
-
'[attr.aria-controls]': 'menuOpen ? menu
|
|
1225
|
+
'[attr.aria-controls]': 'menuOpen ? menu?.panelId : null',
|
|
1205
1226
|
'(click)': '_handleClick($event)',
|
|
1206
1227
|
'(mousedown)': '_handleMousedown($event)',
|
|
1207
1228
|
'(keydown)': '_handleKeydown($event)',
|
|
1208
1229
|
},
|
|
1209
|
-
|
|
1230
|
+
exportAs: 'cuteMenuTrigger',
|
|
1210
1231
|
}]
|
|
1211
|
-
}], ctorParameters: () => [
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
type: Inject,
|
|
1216
|
-
args: [CUTE_MENU_PANEL]
|
|
1217
|
-
}, {
|
|
1218
|
-
type: Optional
|
|
1219
|
-
}] }, { type: CuteMenuItem, decorators: [{
|
|
1220
|
-
type: Optional
|
|
1221
|
-
}, {
|
|
1222
|
-
type: Self
|
|
1223
|
-
}] }, { type: i3.Directionality, decorators: [{
|
|
1224
|
-
type: Optional
|
|
1225
|
-
}] }, { type: i4.FocusMonitor }, { type: i0.NgZone }], propDecorators: { menu: [{
|
|
1232
|
+
}], ctorParameters: () => [], propDecorators: { _deprecatedCuteMenuTriggerFor: [{
|
|
1233
|
+
type: Input,
|
|
1234
|
+
args: ['cute-menu-trigger-for']
|
|
1235
|
+
}], menu: [{
|
|
1226
1236
|
type: Input,
|
|
1227
1237
|
args: ['cuteMenuTriggerFor']
|
|
1228
1238
|
}], menuData: [{
|
|
@@ -1233,6 +1243,191 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
1233
1243
|
args: ['cuteMenuTriggerRestoreFocus']
|
|
1234
1244
|
}], menuOpened: [{
|
|
1235
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
|
|
1236
1431
|
}], menuClosed: [{
|
|
1237
1432
|
type: Output
|
|
1238
1433
|
}] } });
|
|
@@ -1246,32 +1441,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
1246
1441
|
* that can be found at http://www.apache.org/licenses/LICENSE-2.0
|
|
1247
1442
|
*/
|
|
1248
1443
|
const TYPES = [
|
|
1249
|
-
CommonModule,
|
|
1250
1444
|
CuteMenu,
|
|
1251
1445
|
CuteMenuItem,
|
|
1252
1446
|
CuteMenuContent,
|
|
1253
1447
|
CuteMenuTrigger,
|
|
1448
|
+
CuteContextMenuTrigger,
|
|
1254
1449
|
];
|
|
1255
1450
|
class CuteMenuModule {
|
|
1256
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1257
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "
|
|
1258
|
-
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,
|
|
1259
1453
|
CuteMenuItem,
|
|
1260
1454
|
CuteMenuContent,
|
|
1261
|
-
CuteMenuTrigger
|
|
1262
|
-
CuteMenu,
|
|
1455
|
+
CuteMenuTrigger,
|
|
1456
|
+
CuteContextMenuTrigger], exports: [CdkScrollableModule, CuteMenu,
|
|
1263
1457
|
CuteMenuItem,
|
|
1264
1458
|
CuteMenuContent,
|
|
1265
|
-
CuteMenuTrigger
|
|
1266
|
-
|
|
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] }); }
|
|
1267
1462
|
}
|
|
1268
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1463
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CuteMenuModule, decorators: [{
|
|
1269
1464
|
type: NgModule,
|
|
1270
1465
|
args: [{
|
|
1271
|
-
imports: [
|
|
1272
|
-
exports: TYPES,
|
|
1273
|
-
declarations: [],
|
|
1274
|
-
providers: [CUTE_MENU_SCROLL_STRATEGY_FACTORY_PROVIDER],
|
|
1466
|
+
imports: [OverlayModule, ...TYPES],
|
|
1467
|
+
exports: [CdkScrollableModule, ...TYPES],
|
|
1275
1468
|
}]
|
|
1276
1469
|
}] });
|
|
1277
1470
|
|
|
@@ -1279,5 +1472,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
1279
1472
|
* Generated bundle index. Do not edit.
|
|
1280
1473
|
*/
|
|
1281
1474
|
|
|
1282
|
-
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 };
|
|
1283
1476
|
//# sourceMappingURL=cute-widgets-base-menu.mjs.map
|