@raintonic/formaui 0.3.1 → 0.9.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 (238) hide show
  1. package/CHANGELOG.md +80 -35
  2. package/README.md +22 -26
  3. package/fesm2022/raintonic-formaui-cdk-drag-drop.mjs +39 -41
  4. package/fesm2022/raintonic-formaui-cdk-drag-drop.mjs.map +1 -1
  5. package/fesm2022/raintonic-formaui-cdk-form-field.mjs +207 -3
  6. package/fesm2022/raintonic-formaui-cdk-form-field.mjs.map +1 -1
  7. package/fesm2022/raintonic-formaui-cdk-overlay.mjs +27 -2
  8. package/fesm2022/raintonic-formaui-cdk-overlay.mjs.map +1 -1
  9. package/fesm2022/raintonic-formaui-cdk-virtual-scroll.mjs +5 -12
  10. package/fesm2022/raintonic-formaui-cdk-virtual-scroll.mjs.map +1 -1
  11. package/fesm2022/raintonic-formaui-components-accordion.mjs +8 -5
  12. package/fesm2022/raintonic-formaui-components-accordion.mjs.map +1 -1
  13. package/fesm2022/raintonic-formaui-components-alert.mjs +16 -2
  14. package/fesm2022/raintonic-formaui-components-alert.mjs.map +1 -1
  15. package/fesm2022/raintonic-formaui-components-autocomplete.mjs +255 -462
  16. package/fesm2022/raintonic-formaui-components-autocomplete.mjs.map +1 -1
  17. package/fesm2022/raintonic-formaui-components-avatar.mjs +34 -59
  18. package/fesm2022/raintonic-formaui-components-avatar.mjs.map +1 -1
  19. package/fesm2022/raintonic-formaui-components-badge.mjs +2 -2
  20. package/fesm2022/raintonic-formaui-components-badge.mjs.map +1 -1
  21. package/fesm2022/raintonic-formaui-components-breadcrumb.mjs +4 -4
  22. package/fesm2022/raintonic-formaui-components-breadcrumb.mjs.map +1 -1
  23. package/fesm2022/raintonic-formaui-components-button-group.mjs +2 -2
  24. package/fesm2022/raintonic-formaui-components-button-group.mjs.map +1 -1
  25. package/fesm2022/raintonic-formaui-components-button.mjs +15 -20
  26. package/fesm2022/raintonic-formaui-components-button.mjs.map +1 -1
  27. package/fesm2022/raintonic-formaui-components-card.mjs +2 -2
  28. package/fesm2022/raintonic-formaui-components-card.mjs.map +1 -1
  29. package/fesm2022/raintonic-formaui-components-checkbox.mjs +2 -2
  30. package/fesm2022/raintonic-formaui-components-checkbox.mjs.map +1 -1
  31. package/fesm2022/raintonic-formaui-components-chip.mjs +97 -0
  32. package/fesm2022/raintonic-formaui-components-chip.mjs.map +1 -0
  33. package/fesm2022/raintonic-formaui-components-data-table.mjs +69 -29
  34. package/fesm2022/raintonic-formaui-components-data-table.mjs.map +1 -1
  35. package/fesm2022/raintonic-formaui-components-date-picker.mjs +223 -144
  36. package/fesm2022/raintonic-formaui-components-date-picker.mjs.map +1 -1
  37. package/fesm2022/raintonic-formaui-components-divider.mjs +2 -2
  38. package/fesm2022/raintonic-formaui-components-divider.mjs.map +1 -1
  39. package/fesm2022/raintonic-formaui-components-drawer.mjs +2 -2
  40. package/fesm2022/raintonic-formaui-components-drawer.mjs.map +1 -1
  41. package/fesm2022/raintonic-formaui-components-dropdown-menu.mjs +888 -0
  42. package/fesm2022/raintonic-formaui-components-dropdown-menu.mjs.map +1 -0
  43. package/fesm2022/raintonic-formaui-components-dual-tier-navigation.mjs +774 -0
  44. package/fesm2022/raintonic-formaui-components-dual-tier-navigation.mjs.map +1 -0
  45. package/fesm2022/raintonic-formaui-components-empty-state.mjs +2 -2
  46. package/fesm2022/raintonic-formaui-components-empty-state.mjs.map +1 -1
  47. package/fesm2022/raintonic-formaui-components-file-upload.mjs +2 -2
  48. package/fesm2022/raintonic-formaui-components-file-upload.mjs.map +1 -1
  49. package/fesm2022/raintonic-formaui-components-form-field.mjs +81 -50
  50. package/fesm2022/raintonic-formaui-components-form-field.mjs.map +1 -1
  51. package/fesm2022/raintonic-formaui-components-icon.mjs +2 -2
  52. package/fesm2022/raintonic-formaui-components-icon.mjs.map +1 -1
  53. package/fesm2022/raintonic-formaui-components-input.mjs +47 -12
  54. package/fesm2022/raintonic-formaui-components-input.mjs.map +1 -1
  55. package/fesm2022/raintonic-formaui-components-list.mjs +4 -4
  56. package/fesm2022/raintonic-formaui-components-list.mjs.map +1 -1
  57. package/fesm2022/raintonic-formaui-components-number-input.mjs +20 -12
  58. package/fesm2022/raintonic-formaui-components-number-input.mjs.map +1 -1
  59. package/fesm2022/raintonic-formaui-components-paginator.mjs +2 -2
  60. package/fesm2022/raintonic-formaui-components-paginator.mjs.map +1 -1
  61. package/fesm2022/raintonic-formaui-components-password-input.mjs +35 -110
  62. package/fesm2022/raintonic-formaui-components-password-input.mjs.map +1 -1
  63. package/fesm2022/raintonic-formaui-components-popover.mjs +3 -2
  64. package/fesm2022/raintonic-formaui-components-popover.mjs.map +1 -1
  65. package/fesm2022/raintonic-formaui-components-progressbar.mjs +3 -2
  66. package/fesm2022/raintonic-formaui-components-progressbar.mjs.map +1 -1
  67. package/fesm2022/raintonic-formaui-components-radio.mjs +5 -6
  68. package/fesm2022/raintonic-formaui-components-radio.mjs.map +1 -1
  69. package/fesm2022/raintonic-formaui-components-select.mjs +257 -412
  70. package/fesm2022/raintonic-formaui-components-select.mjs.map +1 -1
  71. package/fesm2022/raintonic-formaui-components-side-panel.mjs +2 -2
  72. package/fesm2022/raintonic-formaui-components-side-panel.mjs.map +1 -1
  73. package/fesm2022/raintonic-formaui-components-sidebar-nav-menu.mjs +525 -0
  74. package/fesm2022/raintonic-formaui-components-sidebar-nav-menu.mjs.map +1 -0
  75. package/fesm2022/raintonic-formaui-components-skeleton.mjs +2 -2
  76. package/fesm2022/raintonic-formaui-components-skeleton.mjs.map +1 -1
  77. package/fesm2022/raintonic-formaui-components-slider.mjs +2 -2
  78. package/fesm2022/raintonic-formaui-components-slider.mjs.map +1 -1
  79. package/fesm2022/raintonic-formaui-components-spinner.mjs +2 -2
  80. package/fesm2022/raintonic-formaui-components-spinner.mjs.map +1 -1
  81. package/fesm2022/raintonic-formaui-components-stepper.mjs +50 -45
  82. package/fesm2022/raintonic-formaui-components-stepper.mjs.map +1 -1
  83. package/fesm2022/raintonic-formaui-components-strength-meter.mjs +149 -0
  84. package/fesm2022/raintonic-formaui-components-strength-meter.mjs.map +1 -0
  85. package/fesm2022/raintonic-formaui-components-tab.mjs +2 -2
  86. package/fesm2022/raintonic-formaui-components-tab.mjs.map +1 -1
  87. package/fesm2022/raintonic-formaui-components-time-picker.mjs +194 -154
  88. package/fesm2022/raintonic-formaui-components-time-picker.mjs.map +1 -1
  89. package/fesm2022/raintonic-formaui-components-toggle-group.mjs +302 -0
  90. package/fesm2022/raintonic-formaui-components-toggle-group.mjs.map +1 -0
  91. package/fesm2022/raintonic-formaui-components-toggle.mjs +2 -2
  92. package/fesm2022/raintonic-formaui-components-toggle.mjs.map +1 -1
  93. package/fesm2022/raintonic-formaui-components-toolbar.mjs +2 -2
  94. package/fesm2022/raintonic-formaui-components-toolbar.mjs.map +1 -1
  95. package/fesm2022/raintonic-formaui-components-tooltip.mjs +10 -4
  96. package/fesm2022/raintonic-formaui-components-tooltip.mjs.map +1 -1
  97. package/fesm2022/raintonic-formaui-components-topbar.mjs +60 -0
  98. package/fesm2022/raintonic-formaui-components-topbar.mjs.map +1 -0
  99. package/fesm2022/raintonic-formaui-components-tree-select.mjs +59 -69
  100. package/fesm2022/raintonic-formaui-components-tree-select.mjs.map +1 -1
  101. package/fesm2022/raintonic-formaui-components-tree-table.mjs +2 -2
  102. package/fesm2022/raintonic-formaui-components-tree-table.mjs.map +1 -1
  103. package/fesm2022/raintonic-formaui-components-tree.mjs +31 -5
  104. package/fesm2022/raintonic-formaui-components-tree.mjs.map +1 -1
  105. package/fesm2022/raintonic-formaui-core.mjs +279 -1
  106. package/fesm2022/raintonic-formaui-core.mjs.map +1 -1
  107. package/fesm2022/raintonic-formaui-services-breakpoint.mjs +93 -0
  108. package/fesm2022/raintonic-formaui-services-breakpoint.mjs.map +1 -0
  109. package/fesm2022/raintonic-formaui-services-dialog.mjs +314 -16
  110. package/fesm2022/raintonic-formaui-services-dialog.mjs.map +1 -1
  111. package/fesm2022/raintonic-formaui-services-notification.mjs +93 -29
  112. package/fesm2022/raintonic-formaui-services-notification.mjs.map +1 -1
  113. package/fesm2022/raintonic-formaui-services-theme.mjs +46 -196
  114. package/fesm2022/raintonic-formaui-services-theme.mjs.map +1 -1
  115. package/fesm2022/raintonic-formaui.mjs +1 -1
  116. package/fesm2022/raintonic-formaui.mjs.map +1 -1
  117. package/llms-full.txt +2329 -450
  118. package/llms.txt +36 -33
  119. package/package.json +42 -19
  120. package/styles/fonts/Geist-Bold.woff2 +0 -0
  121. package/styles/fonts/Geist-Italic.woff2 +0 -0
  122. package/styles/fonts/Geist-Light.woff2 +0 -0
  123. package/styles/fonts/Geist-Medium.woff2 +0 -0
  124. package/styles/fonts/Geist-Regular.woff2 +0 -0
  125. package/styles/fonts/Geist-SemiBold.woff2 +0 -0
  126. package/styles/fonts/GeistMono-Regular.woff2 +0 -0
  127. package/styles/generated/_tokens.scss +906 -0
  128. package/styles/index.scss +11 -10
  129. package/styles/partials/_brand.scss +46 -0
  130. package/styles/partials/_constants.scss +22 -20
  131. package/styles/partials/_fonts.scss +54 -10
  132. package/styles/partials/_grid.scss +29 -18
  133. package/styles/partials/_mixins.scss +69 -27
  134. package/styles/partials/_motion.scss +28 -33
  135. package/styles/partials/_theme.scss +28 -255
  136. package/styles/partials/_type.scss +117 -0
  137. package/styles/partials/_typography.scss +45 -45
  138. package/styles/partials/_utilities.scss +198 -98
  139. package/styles/partials/components/_button.scss +144 -75
  140. package/styles/partials/components/_dialog.scss +181 -180
  141. package/styles/partials/components/_overlay.scss +87 -87
  142. package/styles/partials/themes/_dark.scss +3 -268
  143. package/styles/partials/themes/_light.scss +4 -268
  144. package/styles/styles.css +7744 -0
  145. package/styles/styles.entry.scss +3 -0
  146. package/styles/utilities.css +4802 -0
  147. package/styles/utilities.entry.scss +3 -0
  148. package/types/raintonic-formaui-cdk-drag-drop.d.ts +0 -1
  149. package/types/raintonic-formaui-cdk-drag-drop.d.ts.map +1 -1
  150. package/types/raintonic-formaui-cdk-form-field.d.ts +118 -2
  151. package/types/raintonic-formaui-cdk-form-field.d.ts.map +1 -1
  152. package/types/raintonic-formaui-cdk-overlay.d.ts +2 -0
  153. package/types/raintonic-formaui-cdk-overlay.d.ts.map +1 -1
  154. package/types/raintonic-formaui-cdk-virtual-scroll.d.ts +0 -1
  155. package/types/raintonic-formaui-cdk-virtual-scroll.d.ts.map +1 -1
  156. package/types/raintonic-formaui-components-accordion.d.ts +1 -1
  157. package/types/raintonic-formaui-components-accordion.d.ts.map +1 -1
  158. package/types/raintonic-formaui-components-alert.d.ts +6 -1
  159. package/types/raintonic-formaui-components-alert.d.ts.map +1 -1
  160. package/types/raintonic-formaui-components-autocomplete.d.ts +73 -116
  161. package/types/raintonic-formaui-components-autocomplete.d.ts.map +1 -1
  162. package/types/raintonic-formaui-components-avatar.d.ts +13 -31
  163. package/types/raintonic-formaui-components-avatar.d.ts.map +1 -1
  164. package/types/raintonic-formaui-components-button.d.ts +4 -10
  165. package/types/raintonic-formaui-components-button.d.ts.map +1 -1
  166. package/types/raintonic-formaui-components-chip.d.ts +43 -0
  167. package/types/raintonic-formaui-components-chip.d.ts.map +1 -0
  168. package/types/raintonic-formaui-components-data-table.d.ts +48 -11
  169. package/types/raintonic-formaui-components-data-table.d.ts.map +1 -1
  170. package/types/raintonic-formaui-components-date-picker.d.ts +59 -23
  171. package/types/raintonic-formaui-components-date-picker.d.ts.map +1 -1
  172. package/types/raintonic-formaui-components-dropdown-menu.d.ts +394 -0
  173. package/types/raintonic-formaui-components-dropdown-menu.d.ts.map +1 -0
  174. package/types/raintonic-formaui-components-dual-tier-navigation.d.ts +87 -0
  175. package/types/raintonic-formaui-components-dual-tier-navigation.d.ts.map +1 -0
  176. package/types/raintonic-formaui-components-form-field.d.ts +51 -21
  177. package/types/raintonic-formaui-components-form-field.d.ts.map +1 -1
  178. package/types/raintonic-formaui-components-input.d.ts +20 -11
  179. package/types/raintonic-formaui-components-input.d.ts.map +1 -1
  180. package/types/raintonic-formaui-components-number-input.d.ts +5 -3
  181. package/types/raintonic-formaui-components-number-input.d.ts.map +1 -1
  182. package/types/raintonic-formaui-components-password-input.d.ts +18 -32
  183. package/types/raintonic-formaui-components-password-input.d.ts.map +1 -1
  184. package/types/raintonic-formaui-components-popover.d.ts.map +1 -1
  185. package/types/raintonic-formaui-components-progressbar.d.ts +1 -1
  186. package/types/raintonic-formaui-components-progressbar.d.ts.map +1 -1
  187. package/types/raintonic-formaui-components-radio.d.ts +1 -2
  188. package/types/raintonic-formaui-components-radio.d.ts.map +1 -1
  189. package/types/raintonic-formaui-components-select.d.ts +107 -76
  190. package/types/raintonic-formaui-components-select.d.ts.map +1 -1
  191. package/types/raintonic-formaui-components-sidebar-nav-menu.d.ts +223 -0
  192. package/types/raintonic-formaui-components-sidebar-nav-menu.d.ts.map +1 -0
  193. package/types/raintonic-formaui-components-stepper.d.ts +4 -2
  194. package/types/raintonic-formaui-components-stepper.d.ts.map +1 -1
  195. package/types/raintonic-formaui-components-strength-meter.d.ts +78 -0
  196. package/types/raintonic-formaui-components-strength-meter.d.ts.map +1 -0
  197. package/types/raintonic-formaui-components-time-picker.d.ts +44 -24
  198. package/types/raintonic-formaui-components-time-picker.d.ts.map +1 -1
  199. package/types/raintonic-formaui-components-toggle-group.d.ts +100 -0
  200. package/types/raintonic-formaui-components-toggle-group.d.ts.map +1 -0
  201. package/types/raintonic-formaui-components-tooltip.d.ts +2 -1
  202. package/types/raintonic-formaui-components-tooltip.d.ts.map +1 -1
  203. package/types/raintonic-formaui-components-topbar.d.ts +48 -0
  204. package/types/raintonic-formaui-components-topbar.d.ts.map +1 -0
  205. package/types/raintonic-formaui-components-tree-select.d.ts +25 -9
  206. package/types/raintonic-formaui-components-tree-select.d.ts.map +1 -1
  207. package/types/raintonic-formaui-components-tree.d.ts +12 -1
  208. package/types/raintonic-formaui-components-tree.d.ts.map +1 -1
  209. package/types/raintonic-formaui-core.d.ts +243 -5
  210. package/types/raintonic-formaui-core.d.ts.map +1 -1
  211. package/types/raintonic-formaui-services-breakpoint.d.ts +44 -0
  212. package/types/raintonic-formaui-services-breakpoint.d.ts.map +1 -0
  213. package/types/raintonic-formaui-services-dialog.d.ts +141 -2
  214. package/types/raintonic-formaui-services-dialog.d.ts.map +1 -1
  215. package/types/raintonic-formaui-services-notification.d.ts +24 -2
  216. package/types/raintonic-formaui-services-notification.d.ts.map +1 -1
  217. package/types/raintonic-formaui-services-theme.d.ts +13 -103
  218. package/types/raintonic-formaui-services-theme.d.ts.map +1 -1
  219. package/types/raintonic-formaui.d.ts +1 -1
  220. package/fesm2022/raintonic-formaui-components-big-menu.mjs +0 -86
  221. package/fesm2022/raintonic-formaui-components-big-menu.mjs.map +0 -1
  222. package/fesm2022/raintonic-formaui-components-menu.mjs +0 -896
  223. package/fesm2022/raintonic-formaui-components-menu.mjs.map +0 -1
  224. package/fesm2022/raintonic-formaui-components-sidebar.mjs +0 -275
  225. package/fesm2022/raintonic-formaui-components-sidebar.mjs.map +0 -1
  226. package/fesm2022/raintonic-formaui-components-tag.mjs +0 -95
  227. package/fesm2022/raintonic-formaui-components-tag.mjs.map +0 -1
  228. package/styles/_fonts-entry.scss +0 -3
  229. package/styles/fonts/inter-tight-latin-italic.woff2 +0 -0
  230. package/styles/fonts/inter-tight-latin.woff2 +0 -0
  231. package/types/raintonic-formaui-components-big-menu.d.ts +0 -73
  232. package/types/raintonic-formaui-components-big-menu.d.ts.map +0 -1
  233. package/types/raintonic-formaui-components-menu.d.ts +0 -403
  234. package/types/raintonic-formaui-components-menu.d.ts.map +0 -1
  235. package/types/raintonic-formaui-components-sidebar.d.ts +0 -185
  236. package/types/raintonic-formaui-components-sidebar.d.ts.map +0 -1
  237. package/types/raintonic-formaui-components-tag.d.ts +0 -43
  238. package/types/raintonic-formaui-components-tag.d.ts.map +0 -1
@@ -1,896 +0,0 @@
1
- import * as i0 from '@angular/core';
2
- import { input, booleanAttribute, output, signal, computed, inject, ElementRef, Renderer2, HostListener, Component, NgZone, contentChildren, effect, ViewChild, Directive } from '@angular/core';
3
- import { DOCUMENT } from '@angular/common';
4
- import { Subscription, fromEvent } from 'rxjs';
5
- import { filter } from 'rxjs/operators';
6
- import { FuiOverlayService } from '@raintonic/formaui/cdk/overlay';
7
-
8
- const MENU_ITEM_VARIANTS = ['default', 'danger'];
9
- /**
10
- * # FuiMenuItem Component
11
- *
12
- * A menu item component designed to be used within fui-menu.
13
- * Provides consistent styling and behavior for menu options.
14
- *
15
- * ## Features
16
- * - Default and danger variants
17
- * - Full accessibility support (ARIA attributes, keyboard navigation)
18
- * - Icon support with proper spacing
19
- * - Disabled state support
20
- * - Hover and focus states
21
- * - Keyboard activation (Enter and Space)
22
- *
23
- * ## Usage
24
- *
25
- * ### Basic Menu Item
26
- * ```html
27
- * <fui-menu-item>Profile</fui-menu-item>
28
- * ```
29
- *
30
- * ### Menu Item with Icon
31
- * ```html
32
- * <fui-menu-item>
33
- * <fui-icon name="user" fuiPrefix></fui-icon>
34
- * Profile
35
- * </fui-menu-item>
36
- * ```
37
- *
38
- * ### Danger Menu Item
39
- * ```html
40
- * <fui-menu-item variant="danger">
41
- * <fui-icon name="trash" fuiPrefix></fui-icon>
42
- * Delete Account
43
- * </fui-menu-item>
44
- * ```
45
- *
46
- * ### Disabled Menu Item
47
- * ```html
48
- * <fui-menu-item [disabled]="true">
49
- * Unavailable Option
50
- * </fui-menu-item>
51
- * ```
52
- *
53
- * @example
54
- * ```typescript
55
- * import { FuiMenuItemComponent } from '@raintonic/formaui/components/menu';
56
- *
57
- * @Component({
58
- * standalone: true,
59
- * imports: [FuiMenuItemComponent],
60
- * templateUrl: './my-component.component.html',
61
- * styleUrl: './my-component.component.scss'
62
- * })
63
- * export class MyComponent {
64
- * onItemClick(event: Event) {
65
- * console.log('Menu item clicked:', event);
66
- * }
67
- * }
68
- * ```
69
- */
70
- class FuiMenuItemComponent {
71
- /**
72
- * Menu item variant that determines the visual style
73
- * @default 'default'
74
- */
75
- variant = input('default', { ...(ngDevMode ? { debugName: "variant" } : /* istanbul ignore next */ {}), transform: (v) => (MENU_ITEM_VARIANTS.includes(v) ? v : 'default') });
76
- /**
77
- * Whether the menu item is disabled
78
- * @default false
79
- */
80
- disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
81
- /**
82
- * Emitted when the menu item is clicked or activated
83
- */
84
- selected = output();
85
- /**
86
- * Internal tabindex for roving tabindex pattern.
87
- * Managed by the parent FuiMenuComponent.
88
- * @internal
89
- */
90
- tabIndex = signal('-1', ...(ngDevMode ? [{ debugName: "tabIndex" }] : /* istanbul ignore next */ []));
91
- // Computed properties
92
- computedClasses = computed(() => {
93
- const classes = ['fui-menu-item', `fui-menu-item--${this.variant()}`];
94
- if (this.disabled()) {
95
- classes.push('fui-menu-item--disabled');
96
- }
97
- return classes.join(' ');
98
- }, ...(ngDevMode ? [{ debugName: "computedClasses" }] : /* istanbul ignore next */ []));
99
- /** @internal */ _elementRef = inject(ElementRef);
100
- _renderer = inject(Renderer2);
101
- onClick(event) {
102
- if (this.disabled()) {
103
- event.preventDefault();
104
- event.stopPropagation();
105
- return;
106
- }
107
- this.selected.emit(event);
108
- }
109
- onKeydown(event) {
110
- if (this.disabled()) {
111
- return;
112
- }
113
- switch (event.key) {
114
- case 'Enter':
115
- case ' ':
116
- event.preventDefault();
117
- // Dispatch a synthetic click so the parent menu's click handler
118
- // can detect the activation and close the menu.
119
- this._elementRef.nativeElement.click();
120
- break;
121
- }
122
- }
123
- /**
124
- * Focuses the menu item
125
- */
126
- focus() {
127
- this._elementRef.nativeElement.focus();
128
- }
129
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiMenuItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
130
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.6", type: FuiMenuItemComponent, isStandalone: true, selector: "fui-menu-item", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selected: "selected" }, host: { listeners: { "click": "onClick($event)", "keydown": "onKeydown($event)" }, properties: { "class": "computedClasses()", "attr.role": "\"menuitem\"", "attr.tabindex": "disabled() ? \"-1\" : tabIndex()", "attr.aria-disabled": "disabled() ? \"true\" : null" } }, ngImport: i0, template: "<ng-content></ng-content>\r\n", styles: ["@keyframes fui-skeleton-pulse{0%{opacity:1}50%{opacity:.4}to{opacity:1}}@keyframes fui-spin{to{transform:rotate(360deg)}}@keyframes fui-shake{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-2px)}20%,40%,60%,80%{transform:translate(2px)}}.fui-motion-fade-in{transition:opacity var(--fui-duration-fast-02) var(--fui-ease-entrance) 0ms}.fui-motion-fade-out{transition:opacity var(--fui-duration-fast-01) var(--fui-ease-exit) 0ms}.fui-motion-slide-in-bottom{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-bottom.fui-motion-entering{transform:translateY(1rem)}.fui-motion-slide-in-top{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-top.fui-motion-entering{transform:translateY(-1rem)}.fui-motion-scale-in{transition:transform,opacity var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:scale(1);opacity:1}.fui-motion-scale-in.fui-motion-entering{transform:scale(.95);opacity:0}.fui-no-motion{transition:none!important;animation:none!important}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}@keyframes fui-pulse{0%{transform:scale(1);opacity:1}50%{transform:scale(1.05)}to{transform:scale(1);opacity:1}}@keyframes fui-slide-in{0%{transform:translate(120%)}to{transform:translate(0)}}.fui-slide-in{animation:fui-slide-in var(--fui-duration-moderate-01) var(--fui-ease-entrance)}@keyframes fui-popover-enter{0%{opacity:0;transform:translateY(-14px)}60%{opacity:1}to{opacity:1;transform:translateY(0)}}:host.fui-menu-item{transition:background-color var(--fui-duration-fast-02) var(--fui-ease-standard) 0ms;display:flex;align-items:center;gap:var(--fui-spacing-03);width:100%;padding:var(--fui-spacing-03) var(--fui-spacing-04);margin:0;background-color:transparent;border:none;border-radius:var(--fui-border-radius-sm);font-family:var(--fui-font-family-sans);letter-spacing:.56px;font-size:var(--fui-font-size-02);font-weight:var(--fui-font-weight-regular);line-height:var(--fui-line-height-body);text-align:left;text-decoration:none;color:var(--fui-text-secondary);cursor:pointer;-webkit-user-select:none;user-select:none;outline:none}:host.fui-menu-item:focus-visible{background-color:var(--fui-surface-02);outline:2px solid var(--fui-primary);outline-offset:-2px}:host.fui-menu-item:hover:not(.fui-menu-item--disabled){background-color:var(--fui-surface-02)}:host.fui-menu-item:active:not(.fui-menu-item--disabled){background-color:var(--fui-surface-05)}:host.fui-menu-item--danger{color:var(--fui-danger-100)}:host.fui-menu-item--danger:hover:not(.fui-menu-item--disabled){background-color:var(--fui-danger-10);color:var(--fui-danger-70)}:host.fui-menu-item--danger:active:not(.fui-menu-item--disabled){background-color:var(--fui-danger-20);color:var(--fui-danger-80)}:host.fui-menu-item--danger:focus-visible{background-color:var(--fui-danger-10);outline-color:var(--fui-danger-60)}:host.fui-menu-item--disabled{color:var(--fui-text-secondary);cursor:not-allowed;opacity:var(--fui-opacity-disabled)}:host.fui-menu-item--disabled:hover,:host.fui-menu-item--disabled:active,:host.fui-menu-item--disabled:focus{background-color:transparent}:host.fui-menu-item .fui-icon{flex-shrink:0;width:var(--fui-icon-size-sm);height:var(--fui-icon-size-sm);font-size:var(--fui-icon-size-md)}:host.fui-menu-item .fui-icon[fuiPrefix]{margin-right:var(--fui-spacing-02)}:host.fui-menu-item .fui-icon[fuiSuffix]{margin-left:auto;margin-right:0}:host.fui-menu-item .fui-menu-item__shortcut{margin-left:auto;font-size:var(--fui-font-size-01);color:var(--fui-text-secondary);font-family:var(--fui-font-family-mono)}@media(prefers-contrast:more){:host.fui-menu-item:focus-visible{outline-width:3px}:host.fui-menu-item--danger:focus-visible{outline-width:3px}}@media(prefers-reduced-motion:reduce){:host.fui-menu-item{transition:none}}\n"] });
131
- }
132
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiMenuItemComponent, decorators: [{
133
- type: Component,
134
- args: [{ selector: 'fui-menu-item', standalone: true, imports: [], host: {
135
- '[class]': 'computedClasses()',
136
- '[attr.role]': '"menuitem"',
137
- '[attr.tabindex]': 'disabled() ? "-1" : tabIndex()',
138
- '[attr.aria-disabled]': 'disabled() ? "true" : null',
139
- }, template: "<ng-content></ng-content>\r\n", styles: ["@keyframes fui-skeleton-pulse{0%{opacity:1}50%{opacity:.4}to{opacity:1}}@keyframes fui-spin{to{transform:rotate(360deg)}}@keyframes fui-shake{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-2px)}20%,40%,60%,80%{transform:translate(2px)}}.fui-motion-fade-in{transition:opacity var(--fui-duration-fast-02) var(--fui-ease-entrance) 0ms}.fui-motion-fade-out{transition:opacity var(--fui-duration-fast-01) var(--fui-ease-exit) 0ms}.fui-motion-slide-in-bottom{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-bottom.fui-motion-entering{transform:translateY(1rem)}.fui-motion-slide-in-top{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-top.fui-motion-entering{transform:translateY(-1rem)}.fui-motion-scale-in{transition:transform,opacity var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:scale(1);opacity:1}.fui-motion-scale-in.fui-motion-entering{transform:scale(.95);opacity:0}.fui-no-motion{transition:none!important;animation:none!important}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}@keyframes fui-pulse{0%{transform:scale(1);opacity:1}50%{transform:scale(1.05)}to{transform:scale(1);opacity:1}}@keyframes fui-slide-in{0%{transform:translate(120%)}to{transform:translate(0)}}.fui-slide-in{animation:fui-slide-in var(--fui-duration-moderate-01) var(--fui-ease-entrance)}@keyframes fui-popover-enter{0%{opacity:0;transform:translateY(-14px)}60%{opacity:1}to{opacity:1;transform:translateY(0)}}:host.fui-menu-item{transition:background-color var(--fui-duration-fast-02) var(--fui-ease-standard) 0ms;display:flex;align-items:center;gap:var(--fui-spacing-03);width:100%;padding:var(--fui-spacing-03) var(--fui-spacing-04);margin:0;background-color:transparent;border:none;border-radius:var(--fui-border-radius-sm);font-family:var(--fui-font-family-sans);letter-spacing:.56px;font-size:var(--fui-font-size-02);font-weight:var(--fui-font-weight-regular);line-height:var(--fui-line-height-body);text-align:left;text-decoration:none;color:var(--fui-text-secondary);cursor:pointer;-webkit-user-select:none;user-select:none;outline:none}:host.fui-menu-item:focus-visible{background-color:var(--fui-surface-02);outline:2px solid var(--fui-primary);outline-offset:-2px}:host.fui-menu-item:hover:not(.fui-menu-item--disabled){background-color:var(--fui-surface-02)}:host.fui-menu-item:active:not(.fui-menu-item--disabled){background-color:var(--fui-surface-05)}:host.fui-menu-item--danger{color:var(--fui-danger-100)}:host.fui-menu-item--danger:hover:not(.fui-menu-item--disabled){background-color:var(--fui-danger-10);color:var(--fui-danger-70)}:host.fui-menu-item--danger:active:not(.fui-menu-item--disabled){background-color:var(--fui-danger-20);color:var(--fui-danger-80)}:host.fui-menu-item--danger:focus-visible{background-color:var(--fui-danger-10);outline-color:var(--fui-danger-60)}:host.fui-menu-item--disabled{color:var(--fui-text-secondary);cursor:not-allowed;opacity:var(--fui-opacity-disabled)}:host.fui-menu-item--disabled:hover,:host.fui-menu-item--disabled:active,:host.fui-menu-item--disabled:focus{background-color:transparent}:host.fui-menu-item .fui-icon{flex-shrink:0;width:var(--fui-icon-size-sm);height:var(--fui-icon-size-sm);font-size:var(--fui-icon-size-md)}:host.fui-menu-item .fui-icon[fuiPrefix]{margin-right:var(--fui-spacing-02)}:host.fui-menu-item .fui-icon[fuiSuffix]{margin-left:auto;margin-right:0}:host.fui-menu-item .fui-menu-item__shortcut{margin-left:auto;font-size:var(--fui-font-size-01);color:var(--fui-text-secondary);font-family:var(--fui-font-family-mono)}@media(prefers-contrast:more){:host.fui-menu-item:focus-visible{outline-width:3px}:host.fui-menu-item--danger:focus-visible{outline-width:3px}}@media(prefers-reduced-motion:reduce){:host.fui-menu-item{transition:none}}\n"] }]
140
- }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], selected: [{ type: i0.Output, args: ["selected"] }], onClick: [{
141
- type: HostListener,
142
- args: ['click', ['$event']]
143
- }], onKeydown: [{
144
- type: HostListener,
145
- args: ['keydown', ['$event']]
146
- }] } });
147
-
148
- const RT_MENU_POSITIONS = [
149
- 'top-start',
150
- 'top',
151
- 'top-end',
152
- 'bottom-start',
153
- 'bottom',
154
- 'bottom-end',
155
- 'left-start',
156
- 'left',
157
- 'left-end',
158
- 'right-start',
159
- 'right',
160
- 'right-end',
161
- ];
162
- const RT_MENU_SIZES = ['sm', 'md', 'lg'];
163
- /**
164
- * # FuiMenu Component
165
- *
166
- * A dropdown menu component that provides a list of options or actions.
167
- * Designed to work with external triggers using the fuiMenuTrigger directive.
168
- *
169
- * ## Features
170
- * - Multiple positioning options relative to trigger
171
- * - Keyboard navigation (Arrow keys, Enter, Escape)
172
- * - Click outside to close
173
- * - Full accessibility support (ARIA attributes, focus management)
174
- * - Customizable size variants
175
- * - Auto-positioning with collision detection
176
- * - Portal attachment to document body to avoid clipping issues
177
- *
178
- * ## Usage
179
- *
180
- * ### Basic Menu with External Trigger
181
- * ```html
182
- * <button fuiButton fuiMenuTrigger [fuiMenuTriggerFor]="menu">
183
- * Open Menu
184
- * </button>
185
- * <fui-menu #menu>
186
- * <fui-menu-item>Option 1</fui-menu-item>
187
- * <fui-menu-item>Option 2</fui-menu-item>
188
- * </fui-menu>
189
- * ```
190
- *
191
- * ### Menu with Custom Position
192
- * ```html
193
- * <button fuiButton fuiMenuTrigger [fuiMenuTriggerFor]="menu">
194
- * Open Menu
195
- * </button>
196
- * <fui-menu #menu position="top-start" size="lg">
197
- * <fui-menu-item>Profile</fui-menu-item>
198
- * <fui-menu-item variant="danger">Logout</fui-menu-item>
199
- * </fui-menu>
200
- * ```
201
- *
202
- * ### Menu without Portal (for special cases)
203
- * ```html
204
- * <button fuiButton fuiMenuTrigger [fuiMenuTriggerFor]="menu">
205
- * Open Menu
206
- * </button>
207
- * <fui-menu #menu [attachToBody]="false">
208
- * <fui-menu-item>Option 1</fui-menu-item>
209
- * <fui-menu-item>Option 2</fui-menu-item>
210
- * </fui-menu>
211
- * ```
212
- *
213
- * ### Menu with Data Passed from Trigger
214
- * ```html
215
- * <button fuiButton fuiMenuTrigger
216
- * [fuiMenuTriggerFor]="dynamicMenu"
217
- * [menuTriggerData]="{ user: currentUser, items: menuItems }">
218
- * Open Menu
219
- * </button>
220
- * <fui-menu #dynamicMenu>
221
- * <!-- Access data in component using menu.menuData() -->
222
- * </fui-menu>
223
- * ```
224
- *
225
- * ```typescript
226
- * @Component({
227
- * template: `
228
- * <fui-menu #menu>
229
- * <fui-menu-item *ngFor="let item of menu.menuData()?.items">
230
- * {{ item.label }}
231
- * </fui-menu-item>
232
- * </fui-menu>
233
- * `
234
- * })
235
- * export class MyComponent { }
236
- * ```
237
- */
238
- class FuiMenuComponent {
239
- /**
240
- * Menu position relative to the trigger element
241
- * @default 'bottom-start'
242
- */
243
- position = input('bottom-start', ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
244
- /**
245
- * Menu size variant
246
- * @default 'md'
247
- */
248
- size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
249
- /**
250
- * Whether the menu should close when clicking outside
251
- * @default true
252
- */
253
- closeOnClickOutside = input(true, { ...(ngDevMode ? { debugName: "closeOnClickOutside" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
254
- /**
255
- * Whether the menu should close when pressing Escape
256
- * @default true
257
- */
258
- closeOnEscape = input(true, { ...(ngDevMode ? { debugName: "closeOnEscape" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
259
- /**
260
- * Whether the menu is disabled
261
- * @default false
262
- */
263
- disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
264
- /**
265
- * Whether to attach the menu panel to the document body to avoid clipping issues
266
- * @default true
267
- */
268
- attachToBody = input(true, { ...(ngDevMode ? { debugName: "attachToBody" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
269
- /**
270
- * Emitted when the menu open state changes
271
- */
272
- openChange = output();
273
- /**
274
- * Emitted when a menu item is selected
275
- */
276
- itemSelected = output();
277
- // Internal state
278
- _isOpen = signal(false, ...(ngDevMode ? [{ debugName: "_isOpen" }] : /* istanbul ignore next */ []));
279
- _animationState = signal('void', ...(ngDevMode ? [{ debugName: "_animationState" }] : /* istanbul ignore next */ []));
280
- _triggerElement = signal(null, ...(ngDevMode ? [{ debugName: "_triggerElement" }] : /* istanbul ignore next */ []));
281
- _menuData = signal(null, ...(ngDevMode ? [{ debugName: "_menuData" }] : /* istanbul ignore next */ []));
282
- _previousFocusedElement = null;
283
- _overlayRef = null;
284
- _overlaySubscriptions = new Subscription();
285
- _closeAnimationTimeout = null;
286
- // Computed properties
287
- computedClasses = computed(() => {
288
- const classes = ['fui-menu', `fui-menu--${this.position()}`, `fui-menu--${this.size()}`];
289
- if (this.disabled()) {
290
- classes.push('fui-menu--disabled');
291
- }
292
- return classes.join(' ');
293
- }, ...(ngDevMode ? [{ debugName: "computedClasses" }] : /* istanbul ignore next */ []));
294
- // Injected dependencies
295
- _elementRef = inject(ElementRef);
296
- _overlayService = inject(FuiOverlayService);
297
- _document = inject(DOCUMENT);
298
- _ngZone = inject(NgZone);
299
- _outsideClickSub;
300
- // View references
301
- menuPanel;
302
- // Content children for roving tabindex management
303
- _menuItems = contentChildren(FuiMenuItemComponent, { ...(ngDevMode ? { debugName: "_menuItems" } : /* istanbul ignore next */ {}), descendants: true });
304
- constructor() {
305
- // Handle open state changes with animation
306
- effect(() => {
307
- if (this._isOpen()) {
308
- requestAnimationFrame(() => {
309
- this._animationState.set('enter');
310
- this._openMenu();
311
- });
312
- }
313
- else {
314
- this._startCloseAnimation();
315
- }
316
- });
317
- // Emit open change events
318
- effect(() => {
319
- this.openChange.emit(this._isOpen());
320
- });
321
- }
322
- /**
323
- * Whether the menu is currently open
324
- */
325
- isOpen() {
326
- return this._isOpen();
327
- }
328
- /**
329
- * Opens the menu
330
- */
331
- open() {
332
- if (this.disabled())
333
- return;
334
- this._isOpen.set(true);
335
- }
336
- /**
337
- * Closes the menu
338
- */
339
- close() {
340
- this._isOpen.set(false);
341
- }
342
- /**
343
- * Toggles the menu open/closed state
344
- */
345
- toggle() {
346
- if (this._isOpen()) {
347
- this.close();
348
- }
349
- else {
350
- this.open();
351
- }
352
- }
353
- /**
354
- * Sets the trigger element for positioning (called by trigger directive)
355
- */
356
- setTriggerElement(element) {
357
- this._triggerElement.set(element);
358
- }
359
- /**
360
- * Gets the menu data passed from the trigger
361
- * Returns a signal containing the data
362
- */
363
- menuData() {
364
- return this._menuData();
365
- }
366
- /**
367
- * Sets the menu data (called by trigger directive)
368
- * @param data The data to pass to the menu
369
- */
370
- setMenuData(data) {
371
- this._menuData.set(data);
372
- }
373
- // Start listening for outside clicks when menu opens
374
- _listenForOutsideClicks() {
375
- this._outsideClickSub?.unsubscribe();
376
- if (!this.closeOnClickOutside())
377
- return;
378
- this._ngZone.runOutsideAngular(() => {
379
- setTimeout(() => {
380
- this._outsideClickSub = fromEvent(this._document, 'click')
381
- .pipe(filter(() => this._isOpen()), filter((event) => {
382
- const target = event.target;
383
- const menuElement = this._elementRef.nativeElement;
384
- const triggerElement = this._triggerElement();
385
- return !menuElement.contains(target) && !triggerElement?.contains(target);
386
- }))
387
- .subscribe(() => {
388
- this._ngZone.run(() => {
389
- this.close();
390
- });
391
- });
392
- });
393
- });
394
- }
395
- onDocumentKeydown(event) {
396
- if (!this._isOpen() || !this._isTopmostOverlay()) {
397
- return;
398
- }
399
- switch (event.key) {
400
- case 'Escape':
401
- if (this.closeOnEscape()) {
402
- event.preventDefault();
403
- this.close();
404
- }
405
- break;
406
- case 'ArrowDown':
407
- event.preventDefault();
408
- this._focusNextItem();
409
- break;
410
- case 'ArrowUp':
411
- event.preventDefault();
412
- this._focusPreviousItem();
413
- break;
414
- case 'Home':
415
- event.preventDefault();
416
- this._focusFirstItem();
417
- break;
418
- case 'End':
419
- event.preventDefault();
420
- this._focusLastItem();
421
- break;
422
- }
423
- }
424
- onMenuClick(event) {
425
- // Check if the click was on a menu item
426
- const target = event.target;
427
- const menuItem = target.closest('fui-menu-item');
428
- if (menuItem && this._isOpen()) {
429
- // Emit the itemSelected event and close the menu
430
- this.itemSelected.emit(event);
431
- this.close();
432
- }
433
- }
434
- ngOnDestroy() {
435
- this._clearCloseTimeout();
436
- this._outsideClickSub?.unsubscribe();
437
- this._restoreFocus();
438
- this._disposeOverlay();
439
- }
440
- _openMenu() {
441
- // Create overlay after the view updates
442
- requestAnimationFrame(() => {
443
- this._createOverlay();
444
- this._listenForOutsideClicks();
445
- const triggerElement = this._triggerElement();
446
- if (triggerElement) {
447
- triggerElement.setAttribute('aria-expanded', 'true');
448
- this._previousFocusedElement = document.activeElement;
449
- }
450
- });
451
- }
452
- _closeMenu() {
453
- this._outsideClickSub?.unsubscribe();
454
- const triggerElement = this._triggerElement();
455
- if (triggerElement) {
456
- triggerElement.setAttribute('aria-expanded', 'false');
457
- }
458
- // Reset roving tabindex on all items
459
- this._resetRovingTabindex();
460
- this._disposeOverlay();
461
- this._restoreFocus();
462
- }
463
- _createOverlay() {
464
- if (this._overlayRef || !this.menuPanel) {
465
- return;
466
- }
467
- const triggerElement = this._triggerElement();
468
- if (!triggerElement) {
469
- return;
470
- }
471
- // Create overlay with positioning strategy
472
- const positions = this._getPositionsForMenuPosition(this.position());
473
- const positionStrategy = this._overlayService
474
- .position()
475
- .connectedTo(triggerElement, positions)
476
- .withPush(true)
477
- .withViewportMargin(8);
478
- // Create overlay
479
- this._overlayRef = this._overlayService.create({
480
- positionStrategy,
481
- scrollStrategy: this.closeOnClickOutside()
482
- ? this._overlayService.scrollStrategies.close()
483
- : this._overlayService.scrollStrategies.reposition(),
484
- hasBackdrop: this.closeOnClickOutside(),
485
- backdropClass: 'fui-menu-backdrop',
486
- backdropClickBehavior: 'close',
487
- panelClass: ['fui-menu-panel', `fui-menu-panel--${this.size()}`],
488
- });
489
- // Track overlay subscriptions for proper cleanup
490
- this._overlaySubscriptions.unsubscribe();
491
- this._overlaySubscriptions = new Subscription();
492
- if (this.closeOnClickOutside()) {
493
- this._overlaySubscriptions.add(this._overlayRef.backdropClick.subscribe(() => {
494
- this.close();
495
- }));
496
- }
497
- if (this.closeOnEscape()) {
498
- this._overlaySubscriptions.add(this._overlayRef.keydownEvents.subscribe((event) => {
499
- if (event.key === 'Escape') {
500
- this.close();
501
- }
502
- }));
503
- }
504
- // Attach menu panel to overlay
505
- const menuElement = this.menuPanel.nativeElement;
506
- this._overlayRef.attach(menuElement);
507
- // Focus first item after attachment
508
- setTimeout(() => {
509
- this._focusFirstItem();
510
- }, 0);
511
- }
512
- _getPositionsForMenuPosition(position) {
513
- const offset = 4;
514
- switch (position) {
515
- case 'bottom-start':
516
- return [
517
- { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: offset },
518
- { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -offset },
519
- ];
520
- case 'bottom':
521
- return [
522
- { originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top', offsetY: offset },
523
- { originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom', offsetY: -offset },
524
- ];
525
- case 'bottom-end':
526
- return [
527
- { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetY: offset },
528
- { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetY: -offset },
529
- ];
530
- case 'top-start':
531
- return [
532
- { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -offset },
533
- { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: offset },
534
- ];
535
- case 'top':
536
- return [
537
- { originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom', offsetY: -offset },
538
- { originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top', offsetY: offset },
539
- ];
540
- case 'top-end':
541
- return [
542
- { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetY: -offset },
543
- { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetY: offset },
544
- ];
545
- case 'right-start':
546
- return [
547
- { originX: 'end', originY: 'top', overlayX: 'start', overlayY: 'top', offsetX: offset },
548
- { originX: 'start', originY: 'top', overlayX: 'end', overlayY: 'top', offsetX: -offset },
549
- ];
550
- case 'right':
551
- return [
552
- { originX: 'end', originY: 'center', overlayX: 'start', overlayY: 'center', offsetX: offset },
553
- { originX: 'start', originY: 'center', overlayX: 'end', overlayY: 'center', offsetX: -offset },
554
- ];
555
- case 'right-end':
556
- return [
557
- { originX: 'end', originY: 'bottom', overlayX: 'start', overlayY: 'bottom', offsetX: offset },
558
- { originX: 'start', originY: 'bottom', overlayX: 'end', overlayY: 'bottom', offsetX: -offset },
559
- ];
560
- case 'left-start':
561
- return [
562
- { originX: 'start', originY: 'top', overlayX: 'end', overlayY: 'top', offsetX: -offset },
563
- { originX: 'end', originY: 'top', overlayX: 'start', overlayY: 'top', offsetX: offset },
564
- ];
565
- case 'left':
566
- return [
567
- { originX: 'start', originY: 'center', overlayX: 'end', overlayY: 'center', offsetX: -offset },
568
- { originX: 'end', originY: 'center', overlayX: 'start', overlayY: 'center', offsetX: offset },
569
- ];
570
- case 'left-end':
571
- return [
572
- { originX: 'start', originY: 'bottom', overlayX: 'end', overlayY: 'bottom', offsetX: -offset },
573
- { originX: 'end', originY: 'bottom', overlayX: 'start', overlayY: 'bottom', offsetX: offset },
574
- ];
575
- default:
576
- return [{ originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: offset }];
577
- }
578
- }
579
- _restoreFocus() {
580
- // Prefer the trigger element for focus restoration
581
- const triggerElement = this._triggerElement();
582
- if (triggerElement) {
583
- triggerElement.focus();
584
- }
585
- else if (this._previousFocusedElement) {
586
- this._previousFocusedElement.focus();
587
- }
588
- this._previousFocusedElement = null;
589
- }
590
- _getMenuItems() {
591
- const menuElement = this.menuPanel?.nativeElement;
592
- if (!menuElement)
593
- return [];
594
- return Array.from(menuElement.querySelectorAll('fui-menu-item:not([aria-disabled="true"])'));
595
- }
596
- /** @internal Called by FuiMenuTriggerDirective */
597
- _focusFirstItem() {
598
- const items = this._getMenuItems();
599
- if (items.length > 0) {
600
- this._focusItem(items[0]);
601
- }
602
- }
603
- /** @internal Called by FuiMenuTriggerDirective */
604
- _focusLastItem() {
605
- const items = this._getMenuItems();
606
- if (items.length > 0) {
607
- this._focusItem(items[items.length - 1]);
608
- }
609
- }
610
- _focusNextItem() {
611
- const items = this._getMenuItems();
612
- if (items.length === 0) {
613
- return;
614
- }
615
- const currentIndex = items.findIndex((item) => item === document.activeElement);
616
- const nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0;
617
- this._focusItem(items[nextIndex]);
618
- }
619
- _focusPreviousItem() {
620
- const items = this._getMenuItems();
621
- if (items.length === 0) {
622
- return;
623
- }
624
- const currentIndex = items.findIndex((item) => item === document.activeElement);
625
- const prevIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1;
626
- this._focusItem(items[prevIndex]);
627
- }
628
- /**
629
- * Focuses a menu item and updates roving tabindex across all items.
630
- */
631
- _focusItem(element) {
632
- this._updateRovingTabindex(element);
633
- element.focus();
634
- }
635
- /**
636
- * Resets all items to tabindex="-1" when the menu closes.
637
- */
638
- _resetRovingTabindex() {
639
- const items = this._menuItems();
640
- for (const item of items) {
641
- item.tabIndex.set('-1');
642
- }
643
- }
644
- /**
645
- * Updates roving tabindex: sets tabindex="0" on the target item
646
- * and tabindex="-1" on all other items.
647
- */
648
- _updateRovingTabindex(targetElement) {
649
- const items = this._menuItems();
650
- for (const item of items) {
651
- if (item._elementRef.nativeElement === targetElement) {
652
- item.tabIndex.set('0');
653
- }
654
- else {
655
- item.tabIndex.set('-1');
656
- }
657
- }
658
- }
659
- _disposeOverlay() {
660
- this._overlaySubscriptions.unsubscribe();
661
- if (this._overlayRef) {
662
- this._overlayRef.dispose();
663
- this._overlayRef = null;
664
- }
665
- }
666
- _startCloseAnimation() {
667
- // Clear any existing timeout
668
- this._clearCloseTimeout();
669
- // Trigger leave animation
670
- this._animationState.set('leave');
671
- // Wait for animation to complete before closing (matches CSS transition duration)
672
- this._closeAnimationTimeout = window.setTimeout(() => {
673
- this._closeMenu();
674
- this._animationState.set('void');
675
- }, 150); // Match the CSS transition duration
676
- }
677
- _clearCloseTimeout() {
678
- if (this._closeAnimationTimeout !== null) {
679
- clearTimeout(this._closeAnimationTimeout);
680
- this._closeAnimationTimeout = null;
681
- }
682
- }
683
- /**
684
- * Checks if this menu's overlay is the topmost (most recently opened) overlay.
685
- * This ensures that only the topmost menu responds to keyboard events when
686
- * multiple menus are open (e.g., nested menus).
687
- */
688
- _isTopmostOverlay() {
689
- if (!this._overlayRef) {
690
- return false;
691
- }
692
- const activeOverlays = this._overlayService.getActiveOverlays();
693
- // The last overlay in the array is the most recently created (topmost)
694
- const topmostOverlay = activeOverlays[activeOverlays.length - 1];
695
- return topmostOverlay === this._overlayRef;
696
- }
697
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
698
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.6", type: FuiMenuComponent, isStandalone: true, selector: "fui-menu", inputs: { position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, closeOnClickOutside: { classPropertyName: "closeOnClickOutside", publicName: "closeOnClickOutside", isSignal: true, isRequired: false, transformFunction: null }, closeOnEscape: { classPropertyName: "closeOnEscape", publicName: "closeOnEscape", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, attachToBody: { classPropertyName: "attachToBody", publicName: "attachToBody", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { openChange: "openChange", itemSelected: "itemSelected" }, host: { listeners: { "document:keydown": "onDocumentKeydown($event)", "click": "onMenuClick($event)" }, properties: { "class": "computedClasses()", "attr.data-open": "isOpen() ? \"true\" : null" } }, queries: [{ propertyName: "_menuItems", predicate: FuiMenuItemComponent, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "menuPanel", first: true, predicate: ["menuPanel"], descendants: true }], ngImport: i0, template: "@if (_isOpen() || _animationState() === 'leave') {\r\n <div class=\"fui-menu__panel\" #menuPanel role=\"menu\" [attr.data-animation-state]=\"_animationState()\">\r\n <ng-content></ng-content>\r\n </div>\r\n}\r\n", styles: ["@keyframes fui-skeleton-pulse{0%{opacity:1}50%{opacity:.4}to{opacity:1}}@keyframes fui-spin{to{transform:rotate(360deg)}}@keyframes fui-shake{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-2px)}20%,40%,60%,80%{transform:translate(2px)}}.fui-motion-fade-in{transition:opacity var(--fui-duration-fast-02) var(--fui-ease-entrance) 0ms}.fui-motion-fade-out{transition:opacity var(--fui-duration-fast-01) var(--fui-ease-exit) 0ms}.fui-motion-slide-in-bottom{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-bottom.fui-motion-entering{transform:translateY(1rem)}.fui-motion-slide-in-top{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-top.fui-motion-entering{transform:translateY(-1rem)}.fui-motion-scale-in{transition:transform,opacity var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:scale(1);opacity:1}.fui-motion-scale-in.fui-motion-entering{transform:scale(.95);opacity:0}.fui-no-motion{transition:none!important;animation:none!important}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}@keyframes fui-pulse{0%{transform:scale(1);opacity:1}50%{transform:scale(1.05)}to{transform:scale(1);opacity:1}}@keyframes fui-slide-in{0%{transform:translate(120%)}to{transform:translate(0)}}.fui-slide-in{animation:fui-slide-in var(--fui-duration-moderate-01) var(--fui-ease-entrance)}@keyframes fui-popover-enter{0%{opacity:0;transform:translateY(-14px)}60%{opacity:1}to{opacity:1;transform:translateY(0)}}::ng-deep .fui-menu{position:absolute}::ng-deep .fui-menu--disabled{opacity:var(--fui-opacity-disabled);pointer-events:none}::ng-deep .fui-menu__panel{--fui-menu-min-width: 150px;--fui-menu-max-width: 500px;--fui-menu-border-radius: var(--fui-border-radius-sm);--fui-menu-padding: var(--fui-spacing-02);--fui-menu-bg: var(--fui-surface-00);--fui-menu-shadow: var(--fui-shadow-04);z-index:var(--fui-z-popover);min-width:var(--fui-menu-min-width);max-width:var(--fui-menu-max-width);background-color:var(--fui-menu-bg);border-radius:var(--fui-menu-border-radius);padding:var(--fui-menu-padding);box-shadow:var(--fui-menu-shadow);overflow:hidden;transform-origin:top center;will-change:transform,opacity;opacity:0;transform:translateY(-14px);transition:opacity var(--fui-duration-moderate-02) var(--fui-ease-exit),transform var(--fui-duration-moderate-02) var(--fui-ease-exit)}::ng-deep .fui-menu__panel:focus{outline:none}::ng-deep .fui-menu__panel[data-animation-state=enter]{opacity:1;transform:translateY(0);transition:opacity var(--fui-duration-moderate-01) var(--fui-ease-entrance),transform var(--fui-duration-moderate-01) var(--fui-ease-entrance)}::ng-deep .fui-menu__panel[data-animation-state=leave]{opacity:0;transform:translateY(-14px)}::ng-deep .fui-menu-panel--sm .fui-menu__panel{min-width:8rem;font-size:var(--fui-font-size-01)}::ng-deep .fui-menu-panel--md .fui-menu__panel{min-width:12rem;font-size:var(--fui-font-size-02)}::ng-deep .fui-menu-panel--lg .fui-menu__panel{min-width:16rem;font-size:var(--fui-font-size-03)}@media(prefers-contrast:more){::ng-deep .fui-menu__panel{border-width:2px;box-shadow:var(--fui-shadow-04)}}@media(prefers-reduced-motion:reduce){::ng-deep .fui-menu__panel{transition:none}}@media print{::ng-deep .fui-menu__panel{display:none!important}}\n"] });
699
- }
700
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiMenuComponent, decorators: [{
701
- type: Component,
702
- args: [{ selector: 'fui-menu', standalone: true, imports: [], host: {
703
- '[class]': 'computedClasses()',
704
- '[attr.data-open]': 'isOpen() ? "true" : null',
705
- }, template: "@if (_isOpen() || _animationState() === 'leave') {\r\n <div class=\"fui-menu__panel\" #menuPanel role=\"menu\" [attr.data-animation-state]=\"_animationState()\">\r\n <ng-content></ng-content>\r\n </div>\r\n}\r\n", styles: ["@keyframes fui-skeleton-pulse{0%{opacity:1}50%{opacity:.4}to{opacity:1}}@keyframes fui-spin{to{transform:rotate(360deg)}}@keyframes fui-shake{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-2px)}20%,40%,60%,80%{transform:translate(2px)}}.fui-motion-fade-in{transition:opacity var(--fui-duration-fast-02) var(--fui-ease-entrance) 0ms}.fui-motion-fade-out{transition:opacity var(--fui-duration-fast-01) var(--fui-ease-exit) 0ms}.fui-motion-slide-in-bottom{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-bottom.fui-motion-entering{transform:translateY(1rem)}.fui-motion-slide-in-top{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-top.fui-motion-entering{transform:translateY(-1rem)}.fui-motion-scale-in{transition:transform,opacity var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:scale(1);opacity:1}.fui-motion-scale-in.fui-motion-entering{transform:scale(.95);opacity:0}.fui-no-motion{transition:none!important;animation:none!important}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}@keyframes fui-pulse{0%{transform:scale(1);opacity:1}50%{transform:scale(1.05)}to{transform:scale(1);opacity:1}}@keyframes fui-slide-in{0%{transform:translate(120%)}to{transform:translate(0)}}.fui-slide-in{animation:fui-slide-in var(--fui-duration-moderate-01) var(--fui-ease-entrance)}@keyframes fui-popover-enter{0%{opacity:0;transform:translateY(-14px)}60%{opacity:1}to{opacity:1;transform:translateY(0)}}::ng-deep .fui-menu{position:absolute}::ng-deep .fui-menu--disabled{opacity:var(--fui-opacity-disabled);pointer-events:none}::ng-deep .fui-menu__panel{--fui-menu-min-width: 150px;--fui-menu-max-width: 500px;--fui-menu-border-radius: var(--fui-border-radius-sm);--fui-menu-padding: var(--fui-spacing-02);--fui-menu-bg: var(--fui-surface-00);--fui-menu-shadow: var(--fui-shadow-04);z-index:var(--fui-z-popover);min-width:var(--fui-menu-min-width);max-width:var(--fui-menu-max-width);background-color:var(--fui-menu-bg);border-radius:var(--fui-menu-border-radius);padding:var(--fui-menu-padding);box-shadow:var(--fui-menu-shadow);overflow:hidden;transform-origin:top center;will-change:transform,opacity;opacity:0;transform:translateY(-14px);transition:opacity var(--fui-duration-moderate-02) var(--fui-ease-exit),transform var(--fui-duration-moderate-02) var(--fui-ease-exit)}::ng-deep .fui-menu__panel:focus{outline:none}::ng-deep .fui-menu__panel[data-animation-state=enter]{opacity:1;transform:translateY(0);transition:opacity var(--fui-duration-moderate-01) var(--fui-ease-entrance),transform var(--fui-duration-moderate-01) var(--fui-ease-entrance)}::ng-deep .fui-menu__panel[data-animation-state=leave]{opacity:0;transform:translateY(-14px)}::ng-deep .fui-menu-panel--sm .fui-menu__panel{min-width:8rem;font-size:var(--fui-font-size-01)}::ng-deep .fui-menu-panel--md .fui-menu__panel{min-width:12rem;font-size:var(--fui-font-size-02)}::ng-deep .fui-menu-panel--lg .fui-menu__panel{min-width:16rem;font-size:var(--fui-font-size-03)}@media(prefers-contrast:more){::ng-deep .fui-menu__panel{border-width:2px;box-shadow:var(--fui-shadow-04)}}@media(prefers-reduced-motion:reduce){::ng-deep .fui-menu__panel{transition:none}}@media print{::ng-deep .fui-menu__panel{display:none!important}}\n"] }]
706
- }], ctorParameters: () => [], propDecorators: { position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], closeOnClickOutside: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnClickOutside", required: false }] }], closeOnEscape: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnEscape", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], attachToBody: [{ type: i0.Input, args: [{ isSignal: true, alias: "attachToBody", required: false }] }], openChange: [{ type: i0.Output, args: ["openChange"] }], itemSelected: [{ type: i0.Output, args: ["itemSelected"] }], menuPanel: [{
707
- type: ViewChild,
708
- args: ['menuPanel', { static: false }]
709
- }], _menuItems: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => FuiMenuItemComponent), { ...{ descendants: true }, isSignal: true }] }], onDocumentKeydown: [{
710
- type: HostListener,
711
- args: ['document:keydown', ['$event']]
712
- }], onMenuClick: [{
713
- type: HostListener,
714
- args: ['click', ['$event']]
715
- }] } });
716
-
717
- /**
718
- * # fuiMenuTrigger Directive
719
- *
720
- * A directive that marks an element as a menu trigger, similar to Angular Material's matMenuTriggerFor.
721
- * This directive should be used in conjunction with FuiMenuComponent.
722
- *
723
- * ## Usage
724
- *
725
- * ### Basic Usage
726
- * ```html
727
- * <button fuiMenuTrigger [menuTriggerFor]="menu">Open Menu</button>
728
- * <fui-menu #menu>
729
- * <fui-menu-item>Option 1</fui-menu-item>
730
- * <fui-menu-item>Option 2</fui-menu-item>
731
- * </fui-menu>
732
- * ```
733
- *
734
- * ### With Menu Reference
735
- * ```html
736
- * <button fuiMenuTrigger [menuTriggerFor]="userMenu">
737
- * <fui-icon name="user"></fui-icon>
738
- * User Menu
739
- * </button>
740
- *
741
- * <fui-menu #userMenu position="bottom-end">
742
- * <fui-menu-item>Profile</fui-menu-item>
743
- * <fui-menu-item>Settings</fui-menu-item>
744
- * <fui-menu-item variant="danger">Logout</fui-menu-item>
745
- * </fui-menu>
746
- * ```
747
- *
748
- * ### Passing Data to Menu
749
- * ```html
750
- * <button fuiMenuTrigger
751
- * [menuTriggerFor]="dynamicMenu"
752
- * [menuTriggerData]="{ user: currentUser, role: 'admin' }">
753
- * Open Menu
754
- * </button>
755
- *
756
- * <fui-menu #dynamicMenu>
757
- * <!-- Access menu data in your component via menu.menuData() -->
758
- * </fui-menu>
759
- * ```
760
- *
761
- * @example
762
- * ```typescript
763
- * import { FuiMenuTriggerDirective, FuiMenuComponent, FuiMenuItemComponent } from '@raintonic/formaui/components/menu';
764
- *
765
- * @Component({
766
- * standalone: true,
767
- * imports: [FuiMenuTriggerDirective, FuiMenuComponent, FuiMenuItemComponent],
768
- * template: `
769
- * <button fuiMenuTrigger [menuTriggerFor]="menu">Open Menu</button>
770
- * <fui-menu #menu>
771
- * <fui-menu-item>Option 1</fui-menu-item>
772
- * <fui-menu-item>Option 2</fui-menu-item>
773
- * </fui-menu>
774
- * `
775
- * })
776
- * export class MyComponent { }
777
- * ```
778
- */
779
- class FuiMenuTriggerDirective {
780
- _elementRef = inject((ElementRef));
781
- /** The menu instance that this trigger should open */
782
- menuTriggerFor = input(...(ngDevMode ? [undefined, { debugName: "menuTriggerFor" }] : /* istanbul ignore next */ []));
783
- /**
784
- * Data to be passed to the menu.
785
- * Can be accessed in the menu component or menu items.
786
- * Similar to Angular Material's matMenuTriggerData.
787
- */
788
- menuTriggerData = input(...(ngDevMode ? [undefined, { debugName: "menuTriggerData" }] : /* istanbul ignore next */ []));
789
- /** The menu instance that this trigger is associated with */
790
- menu = null;
791
- constructor() {
792
- // Set up the menu reference when fuiMenuTriggerFor changes
793
- effect(() => {
794
- const menuRef = this.menuTriggerFor();
795
- if (menuRef) {
796
- this.menu = menuRef;
797
- // Set the trigger element on the menu for positioning
798
- menuRef.setTriggerElement(this._elementRef.nativeElement);
799
- }
800
- });
801
- }
802
- ngAfterViewInit() {
803
- // Ensure the menu reference is set after view initialization
804
- const menuRef = this.menuTriggerFor();
805
- if (menuRef) {
806
- this.menu = menuRef;
807
- menuRef.setTriggerElement(this._elementRef.nativeElement);
808
- }
809
- }
810
- onClick(event) {
811
- if (this.menu) {
812
- event.preventDefault();
813
- // Update trigger element to ensure correct positioning when multiple triggers exist
814
- this.menu.setTriggerElement(this._elementRef.nativeElement);
815
- // Pass data to menu before opening/toggling
816
- const data = this.menuTriggerData();
817
- if (data !== undefined && this.menu.setMenuData) {
818
- this.menu.setMenuData(data);
819
- }
820
- this.menu.toggle();
821
- }
822
- }
823
- onKeydown(event) {
824
- const menu = this.menu;
825
- if (!menu)
826
- return;
827
- // Update trigger element and pass data to menu before opening
828
- const prepareMenu = () => {
829
- // Update trigger element to ensure correct positioning when multiple triggers exist
830
- menu.setTriggerElement(this._elementRef.nativeElement);
831
- // Pass data to menu
832
- const data = this.menuTriggerData();
833
- if (data !== undefined) {
834
- menu.setMenuData(data);
835
- }
836
- };
837
- switch (event.key) {
838
- case 'Enter':
839
- case ' ':
840
- event.preventDefault();
841
- prepareMenu();
842
- menu.toggle();
843
- break;
844
- case 'ArrowDown':
845
- event.preventDefault();
846
- prepareMenu();
847
- menu.open();
848
- // Focus first item after menu opens
849
- setTimeout(() => {
850
- menu._focusFirstItem();
851
- }, 0);
852
- break;
853
- case 'ArrowUp':
854
- event.preventDefault();
855
- prepareMenu();
856
- menu.open();
857
- // Focus last item after menu opens
858
- setTimeout(() => {
859
- menu._focusLastItem();
860
- }, 0);
861
- break;
862
- }
863
- }
864
- /** Gets the trigger element */
865
- getElement() {
866
- return this._elementRef.nativeElement;
867
- }
868
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiMenuTriggerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
869
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.6", type: FuiMenuTriggerDirective, isStandalone: true, selector: "[fuiMenuTrigger]", inputs: { menuTriggerFor: { classPropertyName: "menuTriggerFor", publicName: "menuTriggerFor", isSignal: true, isRequired: false, transformFunction: null }, menuTriggerData: { classPropertyName: "menuTriggerData", publicName: "menuTriggerData", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "onClick($event)", "keydown": "onKeydown($event)" }, properties: { "attr.aria-haspopup": "\"true\"", "attr.aria-expanded": "menu?.isOpen() ? \"true\" : \"false\"" } }, ngImport: i0 });
870
- }
871
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiMenuTriggerDirective, decorators: [{
872
- type: Directive,
873
- args: [{
874
- selector: '[fuiMenuTrigger]',
875
- standalone: true,
876
- host: {
877
- '[attr.aria-haspopup]': '"true"',
878
- '[attr.aria-expanded]': 'menu?.isOpen() ? "true" : "false"',
879
- },
880
- }]
881
- }], ctorParameters: () => [], propDecorators: { menuTriggerFor: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuTriggerFor", required: false }] }], menuTriggerData: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuTriggerData", required: false }] }], onClick: [{
882
- type: HostListener,
883
- args: ['click', ['$event']]
884
- }], onKeydown: [{
885
- type: HostListener,
886
- args: ['keydown', ['$event']]
887
- }] } });
888
-
889
- // Public API for menu components
890
-
891
- /**
892
- * Generated bundle index. Do not edit.
893
- */
894
-
895
- export { FuiMenuComponent, FuiMenuItemComponent, FuiMenuTriggerDirective, MENU_ITEM_VARIANTS, RT_MENU_POSITIONS, RT_MENU_SIZES };
896
- //# sourceMappingURL=raintonic-formaui-components-menu.mjs.map