@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.
Files changed (171) hide show
  1. package/CHANGELOG.md +18 -6
  2. package/fesm2022/cute-widgets-base-abstract.mjs +19 -29
  3. package/fesm2022/cute-widgets-base-abstract.mjs.map +1 -1
  4. package/fesm2022/cute-widgets-base-alert.mjs +10 -10
  5. package/fesm2022/cute-widgets-base-alert.mjs.map +1 -1
  6. package/fesm2022/cute-widgets-base-autocomplete.mjs +14 -14
  7. package/fesm2022/cute-widgets-base-autocomplete.mjs.map +1 -1
  8. package/fesm2022/cute-widgets-base-badge.mjs +46 -14
  9. package/fesm2022/cute-widgets-base-badge.mjs.map +1 -1
  10. package/fesm2022/cute-widgets-base-bottom-sheet.mjs +11 -11
  11. package/fesm2022/cute-widgets-base-bottom-sheet.mjs.map +1 -1
  12. package/fesm2022/cute-widgets-base-button-toggle.mjs +61 -37
  13. package/fesm2022/cute-widgets-base-button-toggle.mjs.map +1 -1
  14. package/fesm2022/cute-widgets-base-button.mjs +176 -112
  15. package/fesm2022/cute-widgets-base-button.mjs.map +1 -1
  16. package/fesm2022/cute-widgets-base-card.mjs +40 -40
  17. package/fesm2022/cute-widgets-base-card.mjs.map +1 -1
  18. package/fesm2022/cute-widgets-base-checkbox.mjs +28 -28
  19. package/fesm2022/cute-widgets-base-checkbox.mjs.map +1 -1
  20. package/fesm2022/cute-widgets-base-chips.mjs +49 -61
  21. package/fesm2022/cute-widgets-base-chips.mjs.map +1 -1
  22. package/fesm2022/cute-widgets-base-collapse.mjs +120 -132
  23. package/fesm2022/cute-widgets-base-collapse.mjs.map +1 -1
  24. package/fesm2022/cute-widgets-base-core-datetime.mjs +11 -11
  25. package/fesm2022/cute-widgets-base-core-datetime.mjs.map +1 -1
  26. package/fesm2022/cute-widgets-base-core-directives.mjs +21 -21
  27. package/fesm2022/cute-widgets-base-core-directives.mjs.map +1 -1
  28. package/fesm2022/cute-widgets-base-core-error.mjs +6 -6
  29. package/fesm2022/cute-widgets-base-core-error.mjs.map +1 -1
  30. package/fesm2022/cute-widgets-base-core-layout.mjs +13 -1
  31. package/fesm2022/cute-widgets-base-core-layout.mjs.map +1 -1
  32. package/fesm2022/cute-widgets-base-core-line.mjs +7 -7
  33. package/fesm2022/cute-widgets-base-core-line.mjs.map +1 -1
  34. package/fesm2022/cute-widgets-base-core-nav.mjs +30 -28
  35. package/fesm2022/cute-widgets-base-core-nav.mjs.map +1 -1
  36. package/fesm2022/cute-widgets-base-core-observers.mjs +16 -16
  37. package/fesm2022/cute-widgets-base-core-observers.mjs.map +1 -1
  38. package/fesm2022/cute-widgets-base-core-option.mjs +10 -10
  39. package/fesm2022/cute-widgets-base-core-option.mjs.map +1 -1
  40. package/fesm2022/cute-widgets-base-core-pipes.mjs +6 -6
  41. package/fesm2022/cute-widgets-base-core-pipes.mjs.map +1 -1
  42. package/fesm2022/cute-widgets-base-core-ripple.mjs +3 -3
  43. package/fesm2022/cute-widgets-base-core-ripple.mjs.map +1 -1
  44. package/fesm2022/cute-widgets-base-core-theming.mjs +7 -7
  45. package/fesm2022/cute-widgets-base-core-theming.mjs.map +1 -1
  46. package/fesm2022/cute-widgets-base-core-utils.mjs +3 -3
  47. package/fesm2022/cute-widgets-base-core-utils.mjs.map +1 -1
  48. package/fesm2022/cute-widgets-base-core.mjs +50 -38
  49. package/fesm2022/cute-widgets-base-core.mjs.map +1 -1
  50. package/fesm2022/cute-widgets-base-datepicker.mjs +100 -99
  51. package/fesm2022/cute-widgets-base-datepicker.mjs.map +1 -1
  52. package/fesm2022/cute-widgets-base-dialog.mjs +170 -135
  53. package/fesm2022/cute-widgets-base-dialog.mjs.map +1 -1
  54. package/fesm2022/cute-widgets-base-divider.mjs +7 -7
  55. package/fesm2022/cute-widgets-base-divider.mjs.map +1 -1
  56. package/fesm2022/cute-widgets-base-expansion.mjs +27 -27
  57. package/fesm2022/cute-widgets-base-expansion.mjs.map +1 -1
  58. package/fesm2022/cute-widgets-base-form-field.mjs +28 -28
  59. package/fesm2022/cute-widgets-base-form-field.mjs.map +1 -1
  60. package/fesm2022/cute-widgets-base-grid-list.mjs +22 -22
  61. package/fesm2022/cute-widgets-base-grid-list.mjs.map +1 -1
  62. package/fesm2022/cute-widgets-base-icon.mjs +10 -10
  63. package/fesm2022/cute-widgets-base-icon.mjs.map +1 -1
  64. package/fesm2022/cute-widgets-base-input.mjs +7 -7
  65. package/fesm2022/cute-widgets-base-input.mjs.map +1 -1
  66. package/fesm2022/cute-widgets-base-layout-container.mjs +10 -10
  67. package/fesm2022/cute-widgets-base-layout-container.mjs.map +1 -1
  68. package/fesm2022/cute-widgets-base-layout-stack.mjs +13 -13
  69. package/fesm2022/cute-widgets-base-layout-stack.mjs.map +1 -1
  70. package/fesm2022/cute-widgets-base-layout.mjs +23 -23
  71. package/fesm2022/cute-widgets-base-layout.mjs.map +1 -1
  72. package/fesm2022/cute-widgets-base-list.mjs +56 -56
  73. package/fesm2022/cute-widgets-base-list.mjs.map +1 -1
  74. package/fesm2022/cute-widgets-base-menu.mjs +581 -388
  75. package/fesm2022/cute-widgets-base-menu.mjs.map +1 -1
  76. package/fesm2022/cute-widgets-base-navbar.mjs +101 -61
  77. package/fesm2022/cute-widgets-base-navbar.mjs.map +1 -1
  78. package/fesm2022/cute-widgets-base-paginator.mjs +13 -13
  79. package/fesm2022/cute-widgets-base-paginator.mjs.map +1 -1
  80. package/fesm2022/cute-widgets-base-progress.mjs +10 -10
  81. package/fesm2022/cute-widgets-base-progress.mjs.map +1 -1
  82. package/fesm2022/cute-widgets-base-radio.mjs +11 -11
  83. package/fesm2022/cute-widgets-base-radio.mjs.map +1 -1
  84. package/fesm2022/cute-widgets-base-select.mjs +11 -11
  85. package/fesm2022/cute-widgets-base-select.mjs.map +1 -1
  86. package/fesm2022/cute-widgets-base-sidenav.mjs +26 -26
  87. package/fesm2022/cute-widgets-base-sidenav.mjs.map +1 -1
  88. package/fesm2022/cute-widgets-base-slider.mjs +10 -10
  89. package/fesm2022/cute-widgets-base-slider.mjs.map +1 -1
  90. package/fesm2022/cute-widgets-base-snack-bar.mjs +28 -28
  91. package/fesm2022/cute-widgets-base-snack-bar.mjs.map +1 -1
  92. package/fesm2022/cute-widgets-base-sort.mjs +13 -13
  93. package/fesm2022/cute-widgets-base-sort.mjs.map +1 -1
  94. package/fesm2022/cute-widgets-base-spinner.mjs +8 -8
  95. package/fesm2022/cute-widgets-base-spinner.mjs.map +1 -1
  96. package/fesm2022/cute-widgets-base-stepper.mjs +40 -40
  97. package/fesm2022/cute-widgets-base-stepper.mjs.map +1 -1
  98. package/fesm2022/cute-widgets-base-table.mjs +58 -66
  99. package/fesm2022/cute-widgets-base-table.mjs.map +1 -1
  100. package/fesm2022/cute-widgets-base-tabs.mjs +100 -75
  101. package/fesm2022/cute-widgets-base-tabs.mjs.map +1 -1
  102. package/fesm2022/cute-widgets-base-timepicker.mjs +34 -63
  103. package/fesm2022/cute-widgets-base-timepicker.mjs.map +1 -1
  104. package/fesm2022/cute-widgets-base-toolbar.mjs +13 -13
  105. package/fesm2022/cute-widgets-base-toolbar.mjs.map +1 -1
  106. package/fesm2022/cute-widgets-base-tooltip.mjs +10 -10
  107. package/fesm2022/cute-widgets-base-tooltip.mjs.map +1 -1
  108. package/fesm2022/cute-widgets-base-tree.mjs +25 -25
  109. package/fesm2022/cute-widgets-base-tree.mjs.map +1 -1
  110. package/fesm2022/cute-widgets-base.mjs +4 -4
  111. package/package.json +121 -121
  112. package/{abstract/index.d.ts → types/cute-widgets-base-abstract.d.ts} +9 -3
  113. package/{autocomplete/index.d.ts → types/cute-widgets-base-autocomplete.d.ts} +2 -2
  114. package/{badge/index.d.ts → types/cute-widgets-base-badge.d.ts} +6 -5
  115. package/{bottom-sheet/index.d.ts → types/cute-widgets-base-bottom-sheet.d.ts} +1 -1
  116. package/{button-toggle/index.d.ts → types/cute-widgets-base-button-toggle.d.ts} +15 -5
  117. package/{button/index.d.ts → types/cute-widgets-base-button.d.ts} +44 -22
  118. package/{checkbox/index.d.ts → types/cute-widgets-base-checkbox.d.ts} +1 -1
  119. package/{chips/index.d.ts → types/cute-widgets-base-chips.d.ts} +1 -1
  120. package/types/cute-widgets-base-collapse.d.ts +98 -0
  121. package/{core/layout/index.d.ts → types/cute-widgets-base-core-layout.d.ts} +7 -1
  122. package/{core/nav/index.d.ts → types/cute-widgets-base-core-nav.d.ts} +2 -1
  123. package/{core/observers/index.d.ts → types/cute-widgets-base-core-observers.d.ts} +1 -1
  124. package/{core/option/index.d.ts → types/cute-widgets-base-core-option.d.ts} +1 -1
  125. package/{core/index.d.ts → types/cute-widgets-base-core.d.ts} +7 -1
  126. package/{datepicker/index.d.ts → types/cute-widgets-base-datepicker.d.ts} +2 -2
  127. package/{dialog/index.d.ts → types/cute-widgets-base-dialog.d.ts} +81 -57
  128. package/{expansion/index.d.ts → types/cute-widgets-base-expansion.d.ts} +4 -14
  129. package/{form-field/index.d.ts → types/cute-widgets-base-form-field.d.ts} +1 -1
  130. package/{icon/index.d.ts → types/cute-widgets-base-icon.d.ts} +1 -1
  131. package/{input/index.d.ts → types/cute-widgets-base-input.d.ts} +1 -1
  132. package/{menu/index.d.ts → types/cute-widgets-base-menu.d.ts} +202 -121
  133. package/{navbar/index.d.ts → types/cute-widgets-base-navbar.d.ts} +24 -9
  134. package/{paginator/index.d.ts → types/cute-widgets-base-paginator.d.ts} +2 -2
  135. package/{progress/index.d.ts → types/cute-widgets-base-progress.d.ts} +1 -1
  136. package/{radio/index.d.ts → types/cute-widgets-base-radio.d.ts} +2 -2
  137. package/{sidenav/index.d.ts → types/cute-widgets-base-sidenav.d.ts} +1 -1
  138. package/{snack-bar/index.d.ts → types/cute-widgets-base-snack-bar.d.ts} +1 -1
  139. package/{sort/index.d.ts → types/cute-widgets-base-sort.d.ts} +1 -1
  140. package/{table/index.d.ts → types/cute-widgets-base-table.d.ts} +1 -1
  141. package/{tabs/index.d.ts → types/cute-widgets-base-tabs.d.ts} +35 -14
  142. package/{tree/index.d.ts → types/cute-widgets-base-tree.d.ts} +2 -2
  143. package/collapse/index.d.ts +0 -97
  144. /package/{alert/index.d.ts → types/cute-widgets-base-alert.d.ts} +0 -0
  145. /package/{card/index.d.ts → types/cute-widgets-base-card.d.ts} +0 -0
  146. /package/{core/animation/index.d.ts → types/cute-widgets-base-core-animation.d.ts} +0 -0
  147. /package/{core/datetime/index.d.ts → types/cute-widgets-base-core-datetime.d.ts} +0 -0
  148. /package/{core/directives/index.d.ts → types/cute-widgets-base-core-directives.d.ts} +0 -0
  149. /package/{core/error/index.d.ts → types/cute-widgets-base-core-error.d.ts} +0 -0
  150. /package/{core/interfaces/index.d.ts → types/cute-widgets-base-core-interfaces.d.ts} +0 -0
  151. /package/{core/line/index.d.ts → types/cute-widgets-base-core-line.d.ts} +0 -0
  152. /package/{core/pipes/index.d.ts → types/cute-widgets-base-core-pipes.d.ts} +0 -0
  153. /package/{core/ripple/index.d.ts → types/cute-widgets-base-core-ripple.d.ts} +0 -0
  154. /package/{core/testing/index.d.ts → types/cute-widgets-base-core-testing.d.ts} +0 -0
  155. /package/{core/theming/index.d.ts → types/cute-widgets-base-core-theming.d.ts} +0 -0
  156. /package/{core/types/index.d.ts → types/cute-widgets-base-core-types.d.ts} +0 -0
  157. /package/{core/utils/index.d.ts → types/cute-widgets-base-core-utils.d.ts} +0 -0
  158. /package/{divider/index.d.ts → types/cute-widgets-base-divider.d.ts} +0 -0
  159. /package/{grid-list/index.d.ts → types/cute-widgets-base-grid-list.d.ts} +0 -0
  160. /package/{layout/container/index.d.ts → types/cute-widgets-base-layout-container.d.ts} +0 -0
  161. /package/{layout/stack/index.d.ts → types/cute-widgets-base-layout-stack.d.ts} +0 -0
  162. /package/{layout/index.d.ts → types/cute-widgets-base-layout.d.ts} +0 -0
  163. /package/{list/index.d.ts → types/cute-widgets-base-list.d.ts} +0 -0
  164. /package/{select/index.d.ts → types/cute-widgets-base-select.d.ts} +0 -0
  165. /package/{slider/index.d.ts → types/cute-widgets-base-slider.d.ts} +0 -0
  166. /package/{spinner/index.d.ts → types/cute-widgets-base-spinner.d.ts} +0 -0
  167. /package/{stepper/index.d.ts → types/cute-widgets-base-stepper.d.ts} +0 -0
  168. /package/{timepicker/index.d.ts → types/cute-widgets-base-timepicker.d.ts} +0 -0
  169. /package/{toolbar/index.d.ts → types/cute-widgets-base-toolbar.d.ts} +0 -0
  170. /package/{tooltip/index.d.ts → types/cute-widgets-base-tooltip.d.ts} +0 -0
  171. /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, Inject, Optional, Self, NgModule } from '@angular/core';
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, asapScheduler } from 'rxjs';
7
- import { startWith, switchMap, takeUntil, filter, take, delay } from 'rxjs/operators';
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 { trigger, state, transition, style, animate } from '@angular/animations';
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 * as i1$1 from '@angular/cdk/overlay';
13
- import { Overlay, OverlayConfig } from '@angular/cdk/overlay';
14
- import { normalizePassiveListenerOptions } from '@angular/cdk/platform';
15
- import * as i3 from '@angular/cdk/bidi';
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: "20.3.15", ngImport: i0, type: CuteMenuItem, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
139
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", 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", "disabled": "disabled || null" }, classAttribute: "cute-menu-item" }, exportAs: ["cuteMenuItem"], ngImport: i0, template: `
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: "20.3.15", ngImport: i0, type: CuteMenuItem, decorators: [{
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', template: `
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: "20.3.15", ngImport: i0, type: CuteMenuContent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
291
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.15", type: CuteMenuContent, isStandalone: true, selector: "ng-template[cuteMenuContent]", providers: [{ provide: CUTE_MENU_CONTENT, useExisting: CuteMenuContent }], ngImport: i0 }); }
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: "20.3.15", ngImport: i0, type: CuteMenuContent, decorators: [{
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: CUTE_MENU_DEFAULT_OPTIONS_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 mat-menu element and applies them on the
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 axis.
623
- * @param posY Position of the menu along the y axis.
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
- /** Starts the entered animation. */
634
- _startAnimation() {
635
- // @breaking-change 8.0.0 Combine with _resetAnimation.
636
- this._panelAnimationState = 'enter';
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
- /** Resets the panel animation to its initial state. */
639
- _resetAnimation() {
640
- // @breaking-change 8.0.0 Combine with _startAnimation.
641
- this._panelAnimationState = 'void';
556
+ _onAnimationStart(state) {
557
+ if (state === ENTER_ANIMATION || state === EXIT_ANIMATION) {
558
+ this._isAnimating.set(true);
559
+ }
642
560
  }
643
- /** Callback that is invoked when the panel animation completes. */
644
- _onAnimationDone(event) {
645
- this._animationDone.next(event);
646
- this._isAnimating = false;
647
- }
648
- _onAnimationStart(event) {
649
- this._isAnimating = true;
650
- // Scroll the content element to the top as soon as the animation starts. This is necessary,
651
- // because we move focus to the first item while it's still being animated, which can throw
652
- // the browser off when it determines the scroll position. Alternatively, we can move focus
653
- // when the animation is done, however, moving focus asynchronously will interrupt screen
654
- // readers which are in the process of reading out the menu already. We take the `element`
655
- // from the `event` since we can't use a `ViewChild` to access the pane.
656
- if (event.toState === 'enter' && this._keyManager?.activeItemIndex === 0) {
657
- event.element.scrollTop = 0;
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 `mat-menuPanel` is at the DOM insertion point, not inside the overlay, we don't
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: "20.3.15", ngImport: i0, type: CuteMenu, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
687
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "20.3.15", 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\"\r\n [id]=\"panelId\"\r\n [class]=\"_classList\"\r\n (keydown)=\"_handleKeydown($event)\"\r\n (click)=\"onClick($event)\"\r\n [@transformMenu]=\"_panelAnimationState\"\r\n (@transformMenu.start)=\"_onAnimationStart($event)\"\r\n (@transformMenu.done)=\"_onAnimationDone($event)\"\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:block}.cute-menu-panel{min-width:112px;max-width:280px;overflow:hidden;-webkit-overflow-scrolling:touch;box-sizing:border-box;outline:0;will-change:transform,opacity}.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}\n"], animations: [cuteMenuAnimations.transformMenu, cuteMenuAnimations.fadeInItems], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
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: "20.3.15", ngImport: i0, type: CuteMenu, decorators: [{
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
- }, animations: [cuteMenuAnimations.transformMenu, cuteMenuAnimations.fadeInItems], providers: [{ provide: CUTE_MENU_PANEL, useExisting: CuteMenu }], standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, hostDirectives: [CdkMenu], template: "<ng-template>\r\n <div\r\n class=\"cute-menu-panel\"\r\n [id]=\"panelId\"\r\n [class]=\"_classList\"\r\n (keydown)=\"_handleKeydown($event)\"\r\n (click)=\"onClick($event)\"\r\n [@transformMenu]=\"_panelAnimationState\"\r\n (@transformMenu.start)=\"_onAnimationStart($event)\"\r\n (@transformMenu.done)=\"_onAnimationDone($event)\"\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:block}.cute-menu-panel{min-width:112px;max-width:280px;overflow:hidden;-webkit-overflow-scrolling:touch;box-sizing:border-box;outline:0;will-change:transform,opacity}.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}\n"] }]
697
- }], ctorParameters: () => [], propDecorators: { backdropClass: [{
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
- /** @docs-private */
763
- function CUTE_MENU_SCROLL_STRATEGY_FACTORY(overlay) {
764
- return () => overlay.scrollStrategies.reposition();
765
- }
766
- /** @docs-private */
767
- const CUTE_MENU_SCROLL_STRATEGY_FACTORY_PROVIDER = {
768
- provide: CUTE_MENU_SCROLL_STRATEGY,
769
- deps: [Overlay],
770
- useFactory: CUTE_MENU_SCROLL_STRATEGY_FACTORY,
771
- };
772
- /** Options for binding a passive event listener. */
773
- const passiveEventListenerOptions = normalizePassiveListenerOptions({ passive: true });
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 CuteMenuTrigger {
776
- /** References the menu instance that the trigger is associated with. */
777
- get menu() { return this._menu; }
778
- set menu(menu) {
779
- if (menu === this._menu) {
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._menu = menu;
719
+ this._menuInternal = menu;
783
720
  this._menuCloseSubscription.unsubscribe();
784
721
  if (menu) {
785
- if (menu === this._parentCuteMenu && isDevMode()) {
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.triggersSubmenu());
797
- }
798
- constructor(_overlay, _element, _viewContainerRef, scrollStrategy, parentMenu,
799
- // `CuteMenuTrigger` is commonly used in combination with a `CuteMenuItem`.
800
- // tslint:disable-next-line: lightweight-tokens
801
- _menuItemInstance, _dir, _focusMonitor, _ngZone) {
802
- this._overlay = _overlay;
803
- this._element = _element;
804
- this._viewContainerRef = _viewContainerRef;
805
- this._menuItemInstance = _menuItemInstance;
806
- this._dir = _dir;
807
- this._focusMonitor = _focusMonitor;
808
- this._ngZone = _ngZone;
809
- this._portal = null;
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
- this._changeDetectorRef = inject(ChangeDetectorRef);
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._menu = null;
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
- get elementRef() {
865
- return this._element;
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
- /** Toggles the menu between the open and closed states. */
872
- toggleMenu() {
873
- return this._menuOpen ? this.closeMenu() : this.openMenu();
782
+ _closeMenu() {
783
+ this._menu?.closed.emit();
874
784
  }
875
- /** Opens the menu. */
876
- openMenu() {
877
- const menu = this.menu;
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
- overlayConfig.hasBackdrop =
886
- menu.hasBackdrop == null ? !this.triggersSubmenu() : menu.hasBackdrop;
887
- overlayRef.attach(this._getPortal(menu));
888
- if (menu.lazyContent) {
889
- menu.lazyContent.attach(this.menuData);
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.closeMenu());
892
- this._initMenu(menu);
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._startAnimation();
828
+ menu._setIsOpen(true);
895
829
  menu._directDescendantItems.changes.pipe(takeUntil(menu.closed)).subscribe(() => {
896
- // Re-adjust the position without locking when the number of items
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 Focus 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
- if (!this._overlayRef || !this.menuOpen) {
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._overlayRef.detach();
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 && (reason === 'keydown' || !this._openedBy || !this.triggersSubmenu())) {
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
- if (menu instanceof CuteMenu) {
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.triggersSubmenu()) {
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._overlay.create(config);
1008
- // Consume the `keydownEvents` in order to prevent them from going to another overlay.
1009
- // Ideally we'd also have our keyboard event logic in here, however doing so will
1010
- // break anybody that may have implemented the `CuteMenuPanel` themselves.
1011
- this._overlayRef.keydownEvents().subscribe();
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._overlay
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
- const posX = change.connectionPair.overlayX === 'start' ? 'after' : 'before';
1042
- const posY = change.connectionPair.overlayY === 'top' ? 'below' : 'above';
1043
- // @breaking-change 15.0.0 Remove null check for `ngZone`.
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 is to update.
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.triggersSubmenu()) {
1068
- // When the menu is a submenu, it should always align itself
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 backdrop = this._overlayRef.backdropClick();
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._hovered().pipe(filter(active => active !== this._menuItemInstance), filter(() => this._menuOpen))
1019
+ ? this._parentCuteMenu
1020
+ ._hovered()
1021
+ .pipe(filter(active => this._menuOpen && active !== this._menuItemInstance))
1111
1022
  : of();
1112
- return merge(backdrop, parentClose, hover, detachments);
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 submenu,
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 (!this.triggersSubmenu() || !this._parentCuteMenu) {
1157
- return;
1158
- }
1159
- this._hoverSubscription = this._parentCuteMenu
1160
- ._hovered()
1161
- // Since we might have multiple competing triggers for the same menu (e.g. a sub-menu
1162
- // with different data and triggers), we have to delay it by a tick to ensure that
1163
- // it won't be closed immediately after it is opened.
1164
- .pipe(filter(active => active === this._menuItemInstance && !active.disabled), delay(0, asapScheduler))
1165
- .subscribe(() => {
1166
- this._openedBy = 'mouse';
1167
- // If the same menu is used between multiple triggers, it might still be animating
1168
- // while the new trigger tries to re-open it. Wait for the animation to finish
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: "20.3.15", ngImport: i0, type: CuteMenuTrigger, deps: [{ token: i1$1.Overlay }, { token: i0.ElementRef }, { token: i0.ViewContainerRef }, { token: CUTE_MENU_SCROLL_STRATEGY }, { token: CUTE_MENU_PANEL, optional: true }, { token: CuteMenuItem, optional: true, self: true }, { token: i3.Directionality, optional: true }, { token: i4.FocusMonitor }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive }); }
1193
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.15", type: CuteMenuTrigger, isStandalone: true, selector: "[cuteMenuTriggerFor]", inputs: { menu: ["cuteMenuTriggerFor", "menu"], menuData: ["cuteMenuTriggerData", "menuData"], restoreFocus: ["cuteMenuTriggerRestoreFocus", "restoreFocus"] }, outputs: { menuOpened: "menuOpened", menuClosed: "menuClosed" }, 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"], ngImport: i0 }); }
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: "20.3.15", ngImport: i0, type: CuteMenuTrigger, decorators: [{
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: `[cuteMenuTriggerFor]`,
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.panelId : null',
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
- standalone: true,
1230
+ exportAs: 'cuteMenuTrigger',
1210
1231
  }]
1211
- }], ctorParameters: () => [{ type: i1$1.Overlay }, { type: i0.ElementRef }, { type: i0.ViewContainerRef }, { type: undefined, decorators: [{
1212
- type: Inject,
1213
- args: [CUTE_MENU_SCROLL_STRATEGY]
1214
- }] }, { type: undefined, decorators: [{
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: "20.3.15", ngImport: i0, type: CuteMenuModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
1257
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.15", ngImport: i0, type: CuteMenuModule, imports: [CommonModule, CommonModule,
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], exports: [CommonModule,
1262
- CuteMenu,
1455
+ CuteMenuTrigger,
1456
+ CuteContextMenuTrigger], exports: [CdkScrollableModule, CuteMenu,
1263
1457
  CuteMenuItem,
1264
1458
  CuteMenuContent,
1265
- CuteMenuTrigger] }); }
1266
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: CuteMenuModule, providers: [CUTE_MENU_SCROLL_STRATEGY_FACTORY_PROVIDER], imports: [CommonModule, CommonModule, CommonModule] }); }
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: "20.3.15", ngImport: i0, type: CuteMenuModule, decorators: [{
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: [CommonModule, ...TYPES],
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, CUTE_MENU_DEFAULT_OPTIONS_FACTORY, CUTE_MENU_SCROLL_STRATEGY, CUTE_MENU_SCROLL_STRATEGY_FACTORY, CUTE_MENU_SCROLL_STRATEGY_FACTORY_PROVIDER, CuteMenu, CuteMenuContent, CuteMenuItem, CuteMenuModule, CuteMenuTrigger };
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