@remix-run/ui 0.0.0 → 0.1.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 (273) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +195 -2
  3. package/dist/animation/animate-layout-mixin.d.ts +12 -0
  4. package/dist/animation/animate-layout-mixin.js +214 -0
  5. package/dist/animation/animate-layout-mixin.js.map +1 -0
  6. package/dist/animation/animate-mixins.d.ts +29 -0
  7. package/dist/animation/animate-mixins.js +235 -0
  8. package/dist/animation/animate-mixins.js.map +1 -0
  9. package/dist/animation/index.d.ts +6 -0
  10. package/dist/animation/index.js +5 -0
  11. package/dist/animation/index.js.map +1 -0
  12. package/dist/animation/spring.d.ts +75 -0
  13. package/dist/animation/spring.js +183 -0
  14. package/dist/animation/spring.js.map +1 -0
  15. package/dist/animation/tween.d.ts +70 -0
  16. package/dist/animation/tween.js +93 -0
  17. package/dist/animation/tween.js.map +1 -0
  18. package/dist/components/accordion/accordion.d.ts +92 -0
  19. package/dist/components/accordion/accordion.js +337 -0
  20. package/dist/components/accordion/accordion.js.map +1 -0
  21. package/dist/components/anchor/anchor.d.ts +13 -0
  22. package/dist/components/anchor/anchor.js +558 -0
  23. package/dist/components/anchor/anchor.js.map +1 -0
  24. package/dist/components/breadcrumbs/breadcrumbs.d.ts +11 -0
  25. package/dist/components/breadcrumbs/breadcrumbs.js +78 -0
  26. package/dist/components/breadcrumbs/breadcrumbs.js.map +1 -0
  27. package/dist/components/button/button.d.ts +23 -0
  28. package/dist/components/button/button.js +147 -0
  29. package/dist/components/button/button.js.map +1 -0
  30. package/dist/components/combobox/combobox.d.ts +101 -0
  31. package/dist/components/combobox/combobox.js +708 -0
  32. package/dist/components/combobox/combobox.js.map +1 -0
  33. package/dist/components/glyph/glyph.d.ts +14 -0
  34. package/dist/components/glyph/glyph.js +65 -0
  35. package/dist/components/glyph/glyph.js.map +1 -0
  36. package/dist/components/listbox/listbox.d.ts +67 -0
  37. package/dist/components/listbox/listbox.js +340 -0
  38. package/dist/components/listbox/listbox.js.map +1 -0
  39. package/dist/components/menu/hover-aim.d.ts +5 -0
  40. package/dist/components/menu/hover-aim.js +308 -0
  41. package/dist/components/menu/hover-aim.js.map +1 -0
  42. package/dist/components/menu/menu.d.ts +164 -0
  43. package/dist/components/menu/menu.js +1106 -0
  44. package/dist/components/menu/menu.js.map +1 -0
  45. package/dist/components/popover/popover.d.ts +35 -0
  46. package/dist/components/popover/popover.js +138 -0
  47. package/dist/components/popover/popover.js.map +1 -0
  48. package/dist/components/select/select.d.ts +67 -0
  49. package/dist/components/select/select.js +397 -0
  50. package/dist/components/select/select.js.map +1 -0
  51. package/dist/components/separator/separator.d.ts +2 -0
  52. package/dist/components/separator/separator.js +15 -0
  53. package/dist/components/separator/separator.js.map +1 -0
  54. package/dist/components/tabs/tabs.d.ts +78 -0
  55. package/dist/components/tabs/tabs.js +341 -0
  56. package/dist/components/tabs/tabs.js.map +1 -0
  57. package/dist/index.d.ts +28 -0
  58. package/dist/index.js +21 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/interactions/keydown/keydown.d.ts +1 -0
  61. package/dist/interactions/keydown/keydown.js +8 -0
  62. package/dist/interactions/keydown/keydown.js.map +1 -0
  63. package/dist/interactions/outside-click/outside-click-mixin.d.ts +2 -0
  64. package/dist/interactions/outside-click/outside-click-mixin.js +29 -0
  65. package/dist/interactions/outside-click/outside-click-mixin.js.map +1 -0
  66. package/dist/interactions/typeahead/typeahead-mixin.d.ts +8 -0
  67. package/dist/interactions/typeahead/typeahead-mixin.js +59 -0
  68. package/dist/interactions/typeahead/typeahead-mixin.js.map +1 -0
  69. package/dist/jsx-runtime.d.ts +2 -0
  70. package/dist/jsx-runtime.js +3 -0
  71. package/dist/jsx-runtime.js.map +1 -0
  72. package/dist/runtime/client-entries.d.ts +91 -0
  73. package/dist/runtime/client-entries.js +40 -0
  74. package/dist/runtime/client-entries.js.map +1 -0
  75. package/dist/runtime/component.d.ts +223 -0
  76. package/dist/runtime/component.js +152 -0
  77. package/dist/runtime/component.js.map +1 -0
  78. package/dist/runtime/create-element.d.ts +10 -0
  79. package/dist/runtime/create-element.js +29 -0
  80. package/dist/runtime/create-element.js.map +1 -0
  81. package/dist/runtime/diff-dom.d.ts +2 -0
  82. package/dist/runtime/diff-dom.js +364 -0
  83. package/dist/runtime/diff-dom.js.map +1 -0
  84. package/dist/runtime/diff-props.d.ts +9 -0
  85. package/dist/runtime/diff-props.js +195 -0
  86. package/dist/runtime/diff-props.js.map +1 -0
  87. package/dist/runtime/document-state.d.ts +11 -0
  88. package/dist/runtime/document-state.js +106 -0
  89. package/dist/runtime/document-state.js.map +1 -0
  90. package/dist/runtime/dom.d.ts +3189 -0
  91. package/dist/runtime/dom.js +2 -0
  92. package/dist/runtime/dom.js.map +1 -0
  93. package/dist/runtime/error-event.d.ts +20 -0
  94. package/dist/runtime/error-event.js +19 -0
  95. package/dist/runtime/error-event.js.map +1 -0
  96. package/dist/runtime/event-listeners.d.ts +50 -0
  97. package/dist/runtime/event-listeners.js +31 -0
  98. package/dist/runtime/event-listeners.js.map +1 -0
  99. package/dist/runtime/frame.d.ts +119 -0
  100. package/dist/runtime/frame.js +915 -0
  101. package/dist/runtime/frame.js.map +1 -0
  102. package/dist/runtime/invariant.d.ts +23 -0
  103. package/dist/runtime/invariant.js +33 -0
  104. package/dist/runtime/invariant.js.map +1 -0
  105. package/dist/runtime/jsx.d.ts +320 -0
  106. package/dist/runtime/jsx.js +31 -0
  107. package/dist/runtime/jsx.js.map +1 -0
  108. package/dist/runtime/mixins/attrs-mixin.d.ts +9 -0
  109. package/dist/runtime/mixins/attrs-mixin.js +23 -0
  110. package/dist/runtime/mixins/attrs-mixin.js.map +1 -0
  111. package/dist/runtime/mixins/link-mixin.d.ts +17 -0
  112. package/dist/runtime/mixins/link-mixin.js +101 -0
  113. package/dist/runtime/mixins/link-mixin.js.map +1 -0
  114. package/dist/runtime/mixins/mixin.d.ts +145 -0
  115. package/dist/runtime/mixins/mixin.js +570 -0
  116. package/dist/runtime/mixins/mixin.js.map +1 -0
  117. package/dist/runtime/mixins/on-mixin.d.ts +16 -0
  118. package/dist/runtime/mixins/on-mixin.js +49 -0
  119. package/dist/runtime/mixins/on-mixin.js.map +1 -0
  120. package/dist/runtime/mixins/ref-mixin.d.ts +9 -0
  121. package/dist/runtime/mixins/ref-mixin.js +21 -0
  122. package/dist/runtime/mixins/ref-mixin.js.map +1 -0
  123. package/dist/runtime/navigation.d.ts +22 -0
  124. package/dist/runtime/navigation.js +102 -0
  125. package/dist/runtime/navigation.js.map +1 -0
  126. package/dist/runtime/reconcile.d.ts +11 -0
  127. package/dist/runtime/reconcile.js +1240 -0
  128. package/dist/runtime/reconcile.js.map +1 -0
  129. package/dist/runtime/render.d.ts +54 -0
  130. package/dist/runtime/render.js +50 -0
  131. package/dist/runtime/render.js.map +1 -0
  132. package/dist/runtime/run.d.ts +45 -0
  133. package/dist/runtime/run.js +68 -0
  134. package/dist/runtime/run.js.map +1 -0
  135. package/dist/runtime/scheduler.d.ts +30 -0
  136. package/dist/runtime/scheduler.js +185 -0
  137. package/dist/runtime/scheduler.js.map +1 -0
  138. package/dist/runtime/svg-attributes.d.ts +5 -0
  139. package/dist/runtime/svg-attributes.js +124 -0
  140. package/dist/runtime/svg-attributes.js.map +1 -0
  141. package/dist/runtime/to-vnode.d.ts +3 -0
  142. package/dist/runtime/to-vnode.js +43 -0
  143. package/dist/runtime/to-vnode.js.map +1 -0
  144. package/dist/runtime/typed-event-target.d.ts +50 -0
  145. package/dist/runtime/typed-event-target.js +6 -0
  146. package/dist/runtime/typed-event-target.js.map +1 -0
  147. package/dist/runtime/vdom.d.ts +55 -0
  148. package/dist/runtime/vdom.js +198 -0
  149. package/dist/runtime/vdom.js.map +1 -0
  150. package/dist/runtime/vnode.d.ts +79 -0
  151. package/dist/runtime/vnode.js +38 -0
  152. package/dist/runtime/vnode.js.map +1 -0
  153. package/dist/server/stream.d.ts +57 -0
  154. package/dist/server/stream.js +1007 -0
  155. package/dist/server/stream.js.map +1 -0
  156. package/dist/style/css-mixin.d.ts +8 -0
  157. package/dist/style/css-mixin.js +54 -0
  158. package/dist/style/css-mixin.js.map +1 -0
  159. package/dist/style/index.d.ts +5 -0
  160. package/dist/style/index.js +4 -0
  161. package/dist/style/index.js.map +1 -0
  162. package/dist/style/style.d.ts +24 -0
  163. package/dist/style/style.js +269 -0
  164. package/dist/style/style.js.map +1 -0
  165. package/dist/style/stylesheet.d.ts +11 -0
  166. package/dist/style/stylesheet.js +174 -0
  167. package/dist/style/stylesheet.js.map +1 -0
  168. package/dist/test.d.ts +2 -0
  169. package/dist/test.js +2 -0
  170. package/dist/test.js.map +1 -0
  171. package/dist/theme/contract.d.ts +259 -0
  172. package/dist/theme/contract.js +131 -0
  173. package/dist/theme/contract.js.map +1 -0
  174. package/dist/theme/glyph-contract.d.ts +13 -0
  175. package/dist/theme/glyph-contract.js +34 -0
  176. package/dist/theme/glyph-contract.js.map +1 -0
  177. package/dist/theme/presets/rmx-01/glyphs.d.ts +2 -0
  178. package/dist/theme/presets/rmx-01/glyphs.js +252 -0
  179. package/dist/theme/presets/rmx-01/glyphs.js.map +1 -0
  180. package/dist/theme/presets/rmx-01/index.d.ts +2 -0
  181. package/dist/theme/presets/rmx-01/index.js +119 -0
  182. package/dist/theme/presets/rmx-01/index.js.map +1 -0
  183. package/dist/theme/runtime.d.ts +2 -0
  184. package/dist/theme/runtime.js +81 -0
  185. package/dist/theme/runtime.js.map +1 -0
  186. package/dist/theme/theme.d.ts +6 -0
  187. package/dist/theme/theme.js +5 -0
  188. package/dist/theme/theme.js.map +1 -0
  189. package/dist/utils/flash-attribute.d.ts +1 -0
  190. package/dist/utils/flash-attribute.js +11 -0
  191. package/dist/utils/flash-attribute.js.map +1 -0
  192. package/dist/utils/scroll-lock.d.ts +3 -0
  193. package/dist/utils/scroll-lock.js +69 -0
  194. package/dist/utils/scroll-lock.js.map +1 -0
  195. package/dist/utils/wait-for-css-transition.d.ts +1 -0
  196. package/dist/utils/wait-for-css-transition.js +58 -0
  197. package/dist/utils/wait-for-css-transition.js.map +1 -0
  198. package/dist/utils/wait.d.ts +1 -0
  199. package/dist/utils/wait.js +6 -0
  200. package/dist/utils/wait.js.map +1 -0
  201. package/package.json +125 -5
  202. package/src/animation/animate-layout-mixin.ts +266 -0
  203. package/src/animation/animate-mixins.ts +292 -0
  204. package/src/animation/index.ts +6 -0
  205. package/src/animation/spring.ts +299 -0
  206. package/src/animation/tween.ts +134 -0
  207. package/src/components/accordion/accordion.tsx +565 -0
  208. package/src/components/anchor/anchor.ts +904 -0
  209. package/src/components/breadcrumbs/breadcrumbs.tsx +124 -0
  210. package/src/components/button/README.md +44 -0
  211. package/src/components/button/button.tsx +188 -0
  212. package/src/components/combobox/README.md +145 -0
  213. package/src/components/combobox/combobox.tsx +1014 -0
  214. package/src/components/glyph/glyph.tsx +110 -0
  215. package/src/components/listbox/listbox.ts +447 -0
  216. package/src/components/menu/hover-aim.ts +428 -0
  217. package/src/components/menu/menu.tsx +1547 -0
  218. package/src/components/popover/README.md +122 -0
  219. package/src/components/popover/popover.ts +201 -0
  220. package/src/components/select/select.tsx +585 -0
  221. package/src/components/separator/separator.ts +19 -0
  222. package/src/components/tabs/README.md +105 -0
  223. package/src/components/tabs/tabs.tsx +499 -0
  224. package/src/index.ts +72 -0
  225. package/src/interactions/keydown/keydown.ts +14 -0
  226. package/src/interactions/outside-click/outside-click-mixin.ts +55 -0
  227. package/src/interactions/typeahead/typeahead-mixin.ts +89 -0
  228. package/src/jsx-runtime.ts +2 -0
  229. package/src/runtime/client-entries.ts +137 -0
  230. package/src/runtime/component.ts +402 -0
  231. package/src/runtime/create-element.ts +38 -0
  232. package/src/runtime/diff-dom.ts +404 -0
  233. package/src/runtime/diff-props.ts +209 -0
  234. package/src/runtime/document-state.ts +130 -0
  235. package/src/runtime/dom.ts +3971 -0
  236. package/src/runtime/error-event.ts +26 -0
  237. package/src/runtime/event-listeners.ts +171 -0
  238. package/src/runtime/frame.ts +1211 -0
  239. package/src/runtime/invariant.ts +31 -0
  240. package/src/runtime/jsx.ts +398 -0
  241. package/src/runtime/mixins/attrs-mixin.ts +35 -0
  242. package/src/runtime/mixins/link-mixin.ts +131 -0
  243. package/src/runtime/mixins/mixin.ts +908 -0
  244. package/src/runtime/mixins/on-mixin.ts +89 -0
  245. package/src/runtime/mixins/ref-mixin.ts +32 -0
  246. package/src/runtime/navigation.ts +136 -0
  247. package/src/runtime/reconcile.ts +1722 -0
  248. package/src/runtime/render.ts +101 -0
  249. package/src/runtime/run.ts +100 -0
  250. package/src/runtime/scheduler.ts +262 -0
  251. package/src/runtime/svg-attributes.ts +133 -0
  252. package/src/runtime/to-vnode.ts +50 -0
  253. package/src/runtime/typed-event-target.ts +67 -0
  254. package/src/runtime/vdom.ts +295 -0
  255. package/src/runtime/vnode.ts +137 -0
  256. package/src/server/stream.ts +1342 -0
  257. package/src/style/css-mixin.ts +82 -0
  258. package/src/style/index.ts +10 -0
  259. package/src/style/style.ts +312 -0
  260. package/src/style/stylesheet.ts +209 -0
  261. package/src/test/setup.ts +7 -0
  262. package/src/test/utils.ts +45 -0
  263. package/src/test.ts +2 -0
  264. package/src/theme/contract.ts +183 -0
  265. package/src/theme/glyph-contract.ts +57 -0
  266. package/src/theme/presets/rmx-01/glyphs.tsx +308 -0
  267. package/src/theme/presets/rmx-01/index.ts +121 -0
  268. package/src/theme/runtime.ts +116 -0
  269. package/src/theme/theme.ts +15 -0
  270. package/src/utils/flash-attribute.ts +11 -0
  271. package/src/utils/scroll-lock.ts +97 -0
  272. package/src/utils/wait-for-css-transition.ts +66 -0
  273. package/src/utils/wait.ts +5 -0
@@ -0,0 +1,1106 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@remix-run/ui/jsx-runtime";
2
+ // @jsxRuntime classic
3
+ // @jsx createElement
4
+ import { attrs, createElement, createMixin, css, on, ref, } from '@remix-run/ui';
5
+ import {} from "../anchor/anchor.js";
6
+ import * as button from "../button/button.js";
7
+ import { Glyph } from "../glyph/glyph.js";
8
+ import * as popover from "../popover/popover.js";
9
+ import { theme } from "../../theme/theme.js";
10
+ import { hiddenTypeahead, matchNextItemBySearchText, } from "../../interactions/typeahead/typeahead-mixin.js";
11
+ import { waitForCssTransition } from "../../utils/wait-for-css-transition.js";
12
+ import { wait } from "../../utils/wait.js";
13
+ import { createHoverAim } from "./hover-aim.js";
14
+ const MENU_SELECT_EVENT = 'rmx:menu-select';
15
+ const MENU_FLASH_DURATION_MS = 60;
16
+ const SUBMENU_OPEN_DELAY_MS = 300;
17
+ const SUBMENU_ANCHOR_RELATIVE_TO = '[role="menu"] > [role^="menuitem"]';
18
+ const menuButtonCss = css({
19
+ display: 'grid',
20
+ gridTemplateColumns: 'minmax(0, 1fr) auto',
21
+ alignItems: 'center',
22
+ borderRadius: theme.radius.md,
23
+ paddingInlineEnd: theme.space.sm,
24
+ textAlign: 'left',
25
+ '&[aria-expanded="true"], &[aria-expanded="true"]:hover, &[aria-expanded="true"]:focus-visible': {
26
+ backgroundColor: theme.surface.lvl3,
27
+ color: theme.colors.text.primary,
28
+ },
29
+ });
30
+ const menuPopoverCss = css({
31
+ '&[data-menu-submenu="true"][data-anchor-placement^="right"]': {
32
+ marginLeft: `calc(${theme.space.xs} * -1)`,
33
+ },
34
+ '&[data-menu-submenu="true"][data-anchor-placement^="left"]': {
35
+ marginLeft: theme.space.xs,
36
+ },
37
+ '&[data-close-animation="none"]:not(:popover-open)': {
38
+ transition: 'none',
39
+ transitionBehavior: 'normal',
40
+ },
41
+ });
42
+ const menuListCss = css({
43
+ display: 'flex',
44
+ flexDirection: 'column',
45
+ flex: '1 1 auto',
46
+ minHeight: 0,
47
+ paddingBlock: theme.space.xs,
48
+ paddingInline: theme.space.none,
49
+ overflow: 'auto',
50
+ overscrollBehavior: 'contain',
51
+ outline: 'none',
52
+ userSelect: 'none',
53
+ WebkitUserSelect: 'none',
54
+ '--rmx-ui-item-inset': `calc(${theme.space.sm} + ${theme.space.xs})`,
55
+ '--rmx-ui-item-indicator-gap': 'var(--rmx-menu-item-slot-gap)',
56
+ '--rmx-ui-item-indicator-width': 'var(--rmx-menu-item-slot-width)',
57
+ '--rmx-menu-item-slot-gap': theme.space.none,
58
+ '--rmx-menu-item-slot-width': theme.space.none,
59
+ '&:has(> [role="menuitemcheckbox"])': {
60
+ '--rmx-menu-item-slot-gap': theme.space.xs,
61
+ '--rmx-menu-item-slot-width': theme.fontSize.md,
62
+ },
63
+ '&:has(> [role="menuitemradio"])': {
64
+ '--rmx-menu-item-slot-gap': theme.space.xs,
65
+ '--rmx-menu-item-slot-width': theme.fontSize.md,
66
+ },
67
+ });
68
+ const menuItemCss = css({
69
+ display: 'flex',
70
+ alignItems: 'center',
71
+ width: '100%',
72
+ minWidth: 0,
73
+ minHeight: theme.control.height.md,
74
+ boxSizing: 'border-box',
75
+ position: 'relative',
76
+ isolation: 'isolate',
77
+ paddingInline: `calc(${theme.space.sm} + ${theme.space.xs})`,
78
+ color: theme.colors.text.primary,
79
+ fontFamily: theme.fontFamily.sans,
80
+ fontSize: theme.fontSize.sm,
81
+ fontWeight: theme.fontWeight.normal,
82
+ lineHeight: theme.lineHeight.normal,
83
+ textAlign: 'left',
84
+ userSelect: 'none',
85
+ WebkitUserSelect: 'none',
86
+ '--rmx-menu-item-indicator-opacity': '0',
87
+ '&::before': {
88
+ content: '""',
89
+ position: 'absolute',
90
+ insetBlock: 0,
91
+ insetInline: theme.space.xs,
92
+ borderRadius: theme.radius.md,
93
+ backgroundColor: 'transparent',
94
+ pointerEvents: 'none',
95
+ zIndex: -1,
96
+ },
97
+ '&:focus': {
98
+ outline: 'none',
99
+ },
100
+ '&[data-highlighted="true"]': {
101
+ color: theme.colors.action.primary.foreground,
102
+ },
103
+ '&[data-highlighted="true"]::before': {
104
+ backgroundColor: theme.colors.action.primary.background,
105
+ },
106
+ '&[aria-haspopup="menu"][aria-expanded="true"]:not(:focus)': {
107
+ color: theme.colors.text.primary,
108
+ },
109
+ '&[aria-haspopup="menu"][aria-expanded="true"]:not(:focus)::before': {
110
+ backgroundColor: theme.surface.lvl2,
111
+ },
112
+ '&[data-submenu-state="selecting"], &[data-submenu-state="dismissing"]': {
113
+ color: theme.colors.text.primary,
114
+ },
115
+ '&[data-submenu-state="selecting"]::before, &[data-submenu-state="dismissing"]::before': {
116
+ backgroundColor: theme.surface.lvl2,
117
+ },
118
+ '&[data-menu-flash="true"]': {
119
+ color: theme.colors.text.primary,
120
+ },
121
+ '&[data-menu-flash="true"]::before': {
122
+ backgroundColor: 'transparent',
123
+ },
124
+ '&[aria-disabled="true"]': {
125
+ opacity: 0.5,
126
+ },
127
+ '&[aria-checked="true"], &[aria-selected="true"]': {
128
+ '--rmx-menu-item-indicator-opacity': '1',
129
+ },
130
+ });
131
+ const menuItemSlotCss = css({
132
+ display: 'inline-flex',
133
+ alignItems: 'center',
134
+ justifyContent: 'center',
135
+ flex: '0 0 var(--rmx-menu-item-slot-width)',
136
+ width: 'var(--rmx-menu-item-slot-width)',
137
+ minWidth: 0,
138
+ marginInlineEnd: 'var(--rmx-menu-item-slot-gap)',
139
+ overflow: 'hidden',
140
+ });
141
+ const menuItemLabelCss = css({
142
+ display: 'inline-flex',
143
+ alignItems: 'center',
144
+ flex: '1 1 auto',
145
+ minWidth: 0,
146
+ });
147
+ const menuItemGlyphCss = css({
148
+ display: 'inline-flex',
149
+ alignItems: 'center',
150
+ justifyContent: 'center',
151
+ width: theme.fontSize.md,
152
+ height: theme.fontSize.md,
153
+ color: 'currentColor',
154
+ flexShrink: 0,
155
+ '& > svg': {
156
+ display: 'block',
157
+ width: '100%',
158
+ height: '100%',
159
+ },
160
+ opacity: 'var(--rmx-menu-item-indicator-opacity)',
161
+ });
162
+ const menuTriggerGlyphCss = css({
163
+ display: 'inline-flex',
164
+ alignItems: 'center',
165
+ justifyContent: 'center',
166
+ width: theme.fontSize.md,
167
+ height: theme.fontSize.md,
168
+ color: 'currentColor',
169
+ flexShrink: 0,
170
+ marginInlineStart: 'auto',
171
+ '& > svg': {
172
+ display: 'block',
173
+ width: '100%',
174
+ height: '100%',
175
+ },
176
+ });
177
+ export const buttonStyle = menuButtonCss;
178
+ export const popoverStyle = menuPopoverCss;
179
+ export const listStyle = menuListCss;
180
+ export const itemStyle = menuItemCss;
181
+ export const itemSlotStyle = menuItemSlotCss;
182
+ export const itemLabelStyle = menuItemLabelCss;
183
+ export const itemGlyphStyle = menuItemGlyphCss;
184
+ export const triggerGlyphStyle = menuTriggerGlyphCss;
185
+ var NavigationStrategy;
186
+ (function (NavigationStrategy) {
187
+ NavigationStrategy[NavigationStrategy["Next"] = 0] = "Next";
188
+ NavigationStrategy[NavigationStrategy["Previous"] = 1] = "Previous";
189
+ NavigationStrategy[NavigationStrategy["First"] = 2] = "First";
190
+ NavigationStrategy[NavigationStrategy["Last"] = 3] = "Last";
191
+ })(NavigationStrategy || (NavigationStrategy = {}));
192
+ var State;
193
+ (function (State) {
194
+ State["Idle"] = "idle";
195
+ State["Dismissing"] = "dismissing";
196
+ State["Selecting"] = "selecting";
197
+ })(State || (State = {}));
198
+ export class MenuSelectEvent extends Event {
199
+ item;
200
+ constructor(item) {
201
+ super(MENU_SELECT_EVENT, { bubbles: true });
202
+ this.item = item;
203
+ }
204
+ }
205
+ function isPrintableKey(event) {
206
+ return event.key.length === 1 && !event.ctrlKey && !event.altKey && !event.metaKey;
207
+ }
208
+ function normalizeText(text) {
209
+ return (text ?? '').replace(/\s+/g, ' ').trim();
210
+ }
211
+ function getItemLabel(node, label) {
212
+ return normalizeText(label ?? node?.textContent);
213
+ }
214
+ function getItemRole(type) {
215
+ switch (type) {
216
+ case 'checkbox':
217
+ return 'menuitemcheckbox';
218
+ case 'radio':
219
+ return 'menuitemradio';
220
+ default:
221
+ return 'menuitem';
222
+ }
223
+ }
224
+ function eventBelongsToCurrentMenu(event) {
225
+ if (!(event.target instanceof Element) || !(event.currentTarget instanceof Element)) {
226
+ return false;
227
+ }
228
+ return event.target.closest('[role="menu"]') === event.currentTarget;
229
+ }
230
+ function shouldClearHighlightOnPointerLeave(event) {
231
+ if (!(event.currentTarget instanceof Element)) {
232
+ return false;
233
+ }
234
+ if (event.relatedTarget instanceof Node && event.currentTarget.contains(event.relatedTarget)) {
235
+ return false;
236
+ }
237
+ if (!(event.relatedTarget instanceof Element)) {
238
+ return true;
239
+ }
240
+ let currentMenu = event.currentTarget.closest('[role="menu"]');
241
+ let nextItem = event.relatedTarget.closest('[role^="menuitem"]');
242
+ return !currentMenu || !nextItem || nextItem.closest('[role="menu"]') !== currentMenu;
243
+ }
244
+ function focusNode(node) {
245
+ if (!node || !node.isConnected || document.activeElement === node) {
246
+ return;
247
+ }
248
+ node.focus();
249
+ if ('scrollIntoView' in node) {
250
+ node.scrollIntoView({
251
+ block: 'nearest',
252
+ inline: 'nearest',
253
+ });
254
+ }
255
+ }
256
+ function MenuProvider(handle) {
257
+ let parent = handle.context.get(MenuProvider);
258
+ let activeId = null;
259
+ let childMenus = [];
260
+ let closeAnimation = 'none';
261
+ let flashState = null;
262
+ let items = [];
263
+ let listRef;
264
+ let open = false;
265
+ let state = State.Idle;
266
+ let suppressNextPointerLeaveClear = false;
267
+ let surfaceRef;
268
+ let triggerId;
269
+ let triggerRef;
270
+ let hoverAim = createHoverAim();
271
+ function getItem(id) {
272
+ return items.find((item) => item.id === id);
273
+ }
274
+ function isVisibleItem(item) {
275
+ return !!item?.node?.isConnected && !item.hidden;
276
+ }
277
+ function isInteractableItem(item) {
278
+ return isVisibleItem(item) && !item.disabled;
279
+ }
280
+ function getInteractableItems() {
281
+ return items.filter(isInteractableItem);
282
+ }
283
+ function getOpenChild() {
284
+ return childMenus.find((menu) => menu.isOpen);
285
+ }
286
+ function closeChildrenSync(updates, options = { animation: 'none' }) {
287
+ for (let child of childMenus) {
288
+ child.closeSync(updates, options);
289
+ }
290
+ }
291
+ function focusOpenStrategy(strategy, nextActiveId) {
292
+ let activeItem = getItem(nextActiveId);
293
+ if (strategy === 'list' || strategy === 'none' || !isInteractableItem(activeItem)) {
294
+ focusNode(listRef);
295
+ return;
296
+ }
297
+ focusNode(activeItem.node);
298
+ }
299
+ async function openMenu({ strategy = 'list', focus = strategy !== 'none', } = {}) {
300
+ if (state !== State.Idle) {
301
+ return;
302
+ }
303
+ let updates = [];
304
+ closeChildrenSync(updates, { animation: 'none' });
305
+ let nextActiveId = null;
306
+ let interactableItems = getInteractableItems();
307
+ if (strategy === 'first') {
308
+ nextActiveId = interactableItems[0]?.id ?? null;
309
+ }
310
+ else if (strategy === 'last') {
311
+ nextActiveId = interactableItems[interactableItems.length - 1]?.id ?? null;
312
+ }
313
+ let shouldUpdate = !open || activeId !== nextActiveId;
314
+ open = true;
315
+ activeId = nextActiveId;
316
+ if (shouldUpdate) {
317
+ updates.push(handle.update());
318
+ }
319
+ if (updates.length > 0) {
320
+ let signals = await Promise.all(updates);
321
+ if (signals.some((signal) => signal.aborted)) {
322
+ return;
323
+ }
324
+ }
325
+ if (focus) {
326
+ focusOpenStrategy(strategy, nextActiveId);
327
+ }
328
+ }
329
+ function closeSync(updates, { animation = 'none' } = {}) {
330
+ let wasOpen = open;
331
+ closeAnimation = animation;
332
+ closeChildrenSync(updates, { animation });
333
+ if (!open && (animation === 'fade' || activeId === null)) {
334
+ return;
335
+ }
336
+ open = false;
337
+ if (animation === 'none') {
338
+ activeId = null;
339
+ state = State.Idle;
340
+ }
341
+ else if (wasOpen && state === State.Idle) {
342
+ state = State.Dismissing;
343
+ }
344
+ updates.push(handle.update());
345
+ }
346
+ function finishDismissalSync(updates) {
347
+ for (let child of childMenus) {
348
+ child.finishDismissalSync(updates);
349
+ }
350
+ if (open) {
351
+ return;
352
+ }
353
+ let shouldUpdate = false;
354
+ if (activeId !== null) {
355
+ activeId = null;
356
+ shouldUpdate = true;
357
+ }
358
+ if (state !== State.Idle) {
359
+ state = State.Idle;
360
+ shouldUpdate = true;
361
+ }
362
+ if (closeAnimation !== 'none') {
363
+ closeAnimation = 'none';
364
+ shouldUpdate = true;
365
+ }
366
+ if (shouldUpdate) {
367
+ updates.push(handle.update());
368
+ }
369
+ }
370
+ async function closeBranch({ focusTrigger = false } = {}) {
371
+ let updates = [];
372
+ closeSync(updates, { animation: 'none' });
373
+ if (updates.length > 0) {
374
+ let signals = await Promise.all(updates);
375
+ if (signals.some((signal) => signal.aborted)) {
376
+ return;
377
+ }
378
+ }
379
+ if (focusTrigger) {
380
+ parent?.suppressNextPointerLeaveClear();
381
+ focusNode(triggerRef);
382
+ }
383
+ }
384
+ async function closeAll({ focusRoot = true } = {}) {
385
+ if (parent) {
386
+ await context.root.closeAll({ focusRoot });
387
+ return;
388
+ }
389
+ let shouldWaitForTransition = open && !!surfaceRef?.isConnected;
390
+ let updates = [];
391
+ closeSync(updates, { animation: 'fade' });
392
+ if (updates.length > 0) {
393
+ let signals = await Promise.all(updates);
394
+ if (signals.some((signal) => signal.aborted)) {
395
+ return;
396
+ }
397
+ }
398
+ if (shouldWaitForTransition && surfaceRef?.isConnected) {
399
+ await waitForCssTransition(surfaceRef, handle.signal);
400
+ }
401
+ updates = [];
402
+ finishDismissalSync(updates);
403
+ if (updates.length > 0) {
404
+ let signals = await Promise.all(updates);
405
+ if (signals.some((signal) => signal.aborted)) {
406
+ return;
407
+ }
408
+ }
409
+ if (focusRoot) {
410
+ focusNode(triggerRef);
411
+ }
412
+ }
413
+ function highlight(id, { focus = false } = {}) {
414
+ if (state !== State.Idle) {
415
+ return;
416
+ }
417
+ let nextItem = id ? getItem(id) : undefined;
418
+ if (id && !isInteractableItem(nextItem)) {
419
+ return;
420
+ }
421
+ let updates = [];
422
+ let openChild = getOpenChild();
423
+ if (openChild && nextItem?.submenu !== openChild) {
424
+ openChild.closeSync(updates, { animation: 'none' });
425
+ }
426
+ if (activeId !== id) {
427
+ activeId = id;
428
+ updates.push(handle.update());
429
+ }
430
+ if (!focus) {
431
+ return;
432
+ }
433
+ let focusTarget = nextItem?.node;
434
+ if (updates.length === 0) {
435
+ focusNode(focusTarget);
436
+ return;
437
+ }
438
+ void Promise.all(updates).then((signals) => {
439
+ if (signals.some((signal) => signal.aborted)) {
440
+ return;
441
+ }
442
+ focusNode(focusTarget);
443
+ });
444
+ }
445
+ function navigate(strategy) {
446
+ if (state !== State.Idle) {
447
+ return;
448
+ }
449
+ let interactableItems = getInteractableItems();
450
+ let activeIndex = interactableItems.findIndex((item) => item.id === activeId);
451
+ let nextItem;
452
+ switch (strategy) {
453
+ case NavigationStrategy.Next:
454
+ nextItem =
455
+ activeIndex === -1
456
+ ? interactableItems[0]
457
+ : (interactableItems[activeIndex + 1] ?? interactableItems[activeIndex]);
458
+ break;
459
+ case NavigationStrategy.Previous:
460
+ nextItem =
461
+ activeIndex === -1
462
+ ? interactableItems[interactableItems.length - 1]
463
+ : interactableItems[activeIndex - 1];
464
+ break;
465
+ case NavigationStrategy.First:
466
+ nextItem = interactableItems[0];
467
+ break;
468
+ case NavigationStrategy.Last:
469
+ nextItem = interactableItems[interactableItems.length - 1];
470
+ break;
471
+ }
472
+ if (nextItem) {
473
+ highlight(nextItem.id, { focus: true });
474
+ }
475
+ }
476
+ function highlightSearchMatch(text) {
477
+ if (state !== State.Idle) {
478
+ return;
479
+ }
480
+ let interactableItems = getInteractableItems();
481
+ let activeIndex = interactableItems.findIndex((item) => item.id === activeId);
482
+ let nextItem = matchNextItemBySearchText(text, interactableItems, {
483
+ fromIndex: activeIndex,
484
+ getSearchValues(item) {
485
+ return item.searchValue ?? item.label;
486
+ },
487
+ });
488
+ if (nextItem) {
489
+ highlight(nextItem.id, { focus: true });
490
+ }
491
+ }
492
+ async function activateItem(id) {
493
+ if (state !== State.Idle) {
494
+ return;
495
+ }
496
+ let item = getItem(id);
497
+ if (!isInteractableItem(item)) {
498
+ return;
499
+ }
500
+ if (item.submenu) {
501
+ await item.submenu.open({ strategy: 'first' });
502
+ return;
503
+ }
504
+ let committedChecked = item.type === 'checkbox' ? !item.checked : item.type === 'radio' ? true : undefined;
505
+ state = State.Selecting;
506
+ activeId = item.id;
507
+ flashState = item.type === 'item' ? { id: item.id } : { checked: committedChecked, id: item.id };
508
+ let signal = await handle.update();
509
+ if (signal.aborted) {
510
+ return;
511
+ }
512
+ item.node.dispatchEvent(new MenuSelectEvent({
513
+ checked: committedChecked,
514
+ id: item.id,
515
+ label: item.label,
516
+ name: item.name,
517
+ type: item.type,
518
+ value: item.value ?? null,
519
+ }));
520
+ await wait(MENU_FLASH_DURATION_MS);
521
+ if (handle.signal.aborted) {
522
+ return;
523
+ }
524
+ flashState = null;
525
+ signal = await handle.update();
526
+ if (signal.aborted) {
527
+ return;
528
+ }
529
+ await closeAll();
530
+ state = State.Idle;
531
+ signal = await handle.update();
532
+ if (signal.aborted) {
533
+ return;
534
+ }
535
+ }
536
+ async function activateActive() {
537
+ if (!activeId) {
538
+ return;
539
+ }
540
+ await activateItem(activeId);
541
+ }
542
+ async function openActiveSubmenu() {
543
+ if (state !== State.Idle) {
544
+ return;
545
+ }
546
+ if (!activeId) {
547
+ return;
548
+ }
549
+ let item = getItem(activeId);
550
+ if (!isInteractableItem(item) || !item.submenu) {
551
+ return;
552
+ }
553
+ await item.submenu.open({ strategy: 'first' });
554
+ }
555
+ let context = {
556
+ get activeId() {
557
+ return activeId ?? undefined;
558
+ },
559
+ get closeAnimation() {
560
+ return closeAnimation;
561
+ },
562
+ get flashingChecked() {
563
+ return flashState?.checked;
564
+ },
565
+ get flashingId() {
566
+ return flashState?.id;
567
+ },
568
+ get isOpen() {
569
+ return open;
570
+ },
571
+ get isRoot() {
572
+ return !parent;
573
+ },
574
+ get label() {
575
+ return handle.props.label;
576
+ },
577
+ get listId() {
578
+ return `${handle.id}-list`;
579
+ },
580
+ get parent() {
581
+ return parent;
582
+ },
583
+ get root() {
584
+ return parent?.root ?? context;
585
+ },
586
+ get state() {
587
+ return state;
588
+ },
589
+ get surfaceNode() {
590
+ return surfaceRef;
591
+ },
592
+ get triggerId() {
593
+ return triggerId;
594
+ },
595
+ activateActive,
596
+ activateItem,
597
+ allowsPointer(event) {
598
+ return hoverAim.accepts(event);
599
+ },
600
+ closeAll,
601
+ closeBranch,
602
+ closeSync,
603
+ consumePointerLeaveClearSuppression() {
604
+ if (!suppressNextPointerLeaveClear) {
605
+ return false;
606
+ }
607
+ suppressNextPointerLeaveClear = false;
608
+ return true;
609
+ },
610
+ finishDismissalSync,
611
+ getOpenChild,
612
+ hasOpenChild() {
613
+ return !!getOpenChild();
614
+ },
615
+ highlight,
616
+ highlightSearchMatch,
617
+ navigate,
618
+ openActiveSubmenu,
619
+ open: openMenu,
620
+ registerChild(menu) {
621
+ childMenus.push(menu);
622
+ },
623
+ registerItem(item) {
624
+ items.push(item);
625
+ },
626
+ registerList(node) {
627
+ listRef = node;
628
+ },
629
+ registerSurface(node) {
630
+ surfaceRef = node;
631
+ },
632
+ registerTrigger(node, id) {
633
+ triggerRef = node;
634
+ triggerId = id;
635
+ },
636
+ suppressNextPointerLeaveClear() {
637
+ suppressNextPointerLeaveClear = true;
638
+ },
639
+ startHoverAim(source, target, event) {
640
+ if (!source || !target) {
641
+ return false;
642
+ }
643
+ return hoverAim.start(source, target, event);
644
+ },
645
+ unregisterList(node) {
646
+ if (listRef === node) {
647
+ listRef = undefined;
648
+ }
649
+ },
650
+ unregisterSurface(node) {
651
+ if (surfaceRef === node) {
652
+ surfaceRef = undefined;
653
+ }
654
+ },
655
+ unregisterTrigger(node) {
656
+ if (triggerRef === node) {
657
+ triggerRef = undefined;
658
+ triggerId = undefined;
659
+ }
660
+ },
661
+ };
662
+ handle.context.set(context);
663
+ return () => {
664
+ childMenus = [];
665
+ items = [];
666
+ parent?.registerChild(context);
667
+ return _jsx(popover.Context, { children: handle.props.children });
668
+ };
669
+ }
670
+ const triggerMixin = createMixin((handle) => {
671
+ let context = handle.context.get(MenuProvider);
672
+ return (options = {}) => [
673
+ attrs({
674
+ id: handle.id,
675
+ 'aria-controls': context.listId,
676
+ 'aria-expanded': context.isOpen ? 'true' : 'false',
677
+ 'aria-haspopup': 'menu',
678
+ }),
679
+ ref((node, signal) => {
680
+ context.registerTrigger(node, handle.id);
681
+ signal.addEventListener('abort', () => {
682
+ context.unregisterTrigger(node);
683
+ });
684
+ }),
685
+ popover.anchor({
686
+ placement: 'bottom-start',
687
+ ...options,
688
+ }),
689
+ on('click', () => {
690
+ if (context.isOpen) {
691
+ void context.closeAll();
692
+ }
693
+ else {
694
+ void context.open({ strategy: 'list' });
695
+ }
696
+ }),
697
+ on('keydown', (event) => {
698
+ switch (event.key) {
699
+ case 'ArrowDown':
700
+ event.preventDefault();
701
+ event.stopPropagation();
702
+ void context.open({ strategy: 'first' });
703
+ break;
704
+ case 'ArrowUp':
705
+ event.preventDefault();
706
+ event.stopPropagation();
707
+ void context.open({ strategy: 'last' });
708
+ break;
709
+ case 'Enter':
710
+ case ' ':
711
+ event.preventDefault();
712
+ event.stopPropagation();
713
+ void context.open({ strategy: 'list' });
714
+ }
715
+ }),
716
+ ];
717
+ });
718
+ const popoverMixin = createMixin((handle) => {
719
+ let context = handle.context.get(MenuProvider);
720
+ return () => [
721
+ attrs({
722
+ 'data-close-animation': context.closeAnimation === 'none' ? 'none' : undefined,
723
+ 'data-menu-submenu': context.isRoot ? undefined : 'true',
724
+ }),
725
+ ref((node, signal) => {
726
+ context.registerSurface(node);
727
+ signal.addEventListener('abort', () => {
728
+ context.unregisterSurface(node);
729
+ });
730
+ }),
731
+ popover.surface({
732
+ open: context.isOpen,
733
+ onHide(request) {
734
+ if (context.isRoot || request?.reason === 'escape-key') {
735
+ void context.closeAll();
736
+ return;
737
+ }
738
+ if (request?.reason === 'outside-click' &&
739
+ request.target instanceof Node &&
740
+ context.root.surfaceNode?.contains(request.target)) {
741
+ void context.closeBranch();
742
+ }
743
+ },
744
+ closeOnAnchorClick: false,
745
+ restoreFocusOnHide: false,
746
+ stopOutsideClickPropagation: false,
747
+ }),
748
+ ];
749
+ });
750
+ const listMixin = createMixin((handle) => {
751
+ let context = handle.context.get(MenuProvider);
752
+ return () => [
753
+ attrs({
754
+ id: context.listId,
755
+ role: 'menu',
756
+ tabIndex: -1,
757
+ 'aria-label': context.label,
758
+ 'aria-labelledby': context.label ? undefined : context.triggerId,
759
+ }),
760
+ ref((node, signal) => {
761
+ context.registerList(node);
762
+ signal.addEventListener('abort', () => {
763
+ context.unregisterList(node);
764
+ });
765
+ }),
766
+ on('pointerleave', (event) => {
767
+ if (event.relatedTarget instanceof Node &&
768
+ event.currentTarget.contains(event.relatedTarget)) {
769
+ return;
770
+ }
771
+ if (!context.hasOpenChild()) {
772
+ if (context.consumePointerLeaveClearSuppression()) {
773
+ return;
774
+ }
775
+ context.highlight(null);
776
+ }
777
+ }),
778
+ on('keydown', (event) => {
779
+ if (!eventBelongsToCurrentMenu(event)) {
780
+ return;
781
+ }
782
+ let isTypeaheadKey = isPrintableKey(event) || event.key === 'Backspace';
783
+ if (isTypeaheadKey) {
784
+ event.stopPropagation();
785
+ return;
786
+ }
787
+ switch (event.key) {
788
+ case 'ArrowDown':
789
+ event.preventDefault();
790
+ event.stopPropagation();
791
+ context.navigate(NavigationStrategy.Next);
792
+ return;
793
+ case 'ArrowUp':
794
+ event.preventDefault();
795
+ event.stopPropagation();
796
+ context.navigate(NavigationStrategy.Previous);
797
+ return;
798
+ case 'Home':
799
+ event.preventDefault();
800
+ event.stopPropagation();
801
+ context.navigate(NavigationStrategy.First);
802
+ return;
803
+ case 'End':
804
+ event.preventDefault();
805
+ event.stopPropagation();
806
+ context.navigate(NavigationStrategy.Last);
807
+ return;
808
+ case 'Enter':
809
+ case ' ':
810
+ event.preventDefault();
811
+ event.stopPropagation();
812
+ void context.activateActive();
813
+ return;
814
+ case 'ArrowRight':
815
+ event.preventDefault();
816
+ event.stopPropagation();
817
+ void context.openActiveSubmenu();
818
+ return;
819
+ case 'ArrowLeft':
820
+ if (!context.parent) {
821
+ return;
822
+ }
823
+ event.preventDefault();
824
+ event.stopPropagation();
825
+ void context.closeBranch({ focusTrigger: true });
826
+ return;
827
+ case 'Escape':
828
+ event.preventDefault();
829
+ event.stopPropagation();
830
+ void context.closeAll();
831
+ return;
832
+ case 'Tab':
833
+ event.stopPropagation();
834
+ void context.closeAll({ focusRoot: false });
835
+ }
836
+ }),
837
+ hiddenTypeahead((text) => {
838
+ context.highlightSearchMatch(text);
839
+ }),
840
+ ];
841
+ });
842
+ const itemMixin = createMixin((handle) => {
843
+ let itemRef;
844
+ handle.queueTask((node) => {
845
+ itemRef = node;
846
+ });
847
+ return (options) => {
848
+ let context = handle.context.get(MenuProvider);
849
+ let type = options.type ?? 'item';
850
+ let isFlashing = context.flashingId === handle.id;
851
+ context.registerItem({
852
+ checked: options.checked,
853
+ disabled: options.disabled,
854
+ id: handle.id,
855
+ name: options.name,
856
+ searchValue: options.searchValue,
857
+ type,
858
+ value: options.value,
859
+ get hidden() {
860
+ return itemRef?.hidden === true;
861
+ },
862
+ get label() {
863
+ return getItemLabel(itemRef, options.label);
864
+ },
865
+ get node() {
866
+ return itemRef;
867
+ },
868
+ });
869
+ return [
870
+ attrs({
871
+ id: handle.id,
872
+ role: getItemRole(type),
873
+ tabIndex: -1,
874
+ 'aria-checked': type === 'item'
875
+ ? undefined
876
+ : isFlashing
877
+ ? context.flashingChecked
878
+ ? 'true'
879
+ : 'false'
880
+ : options.checked
881
+ ? 'true'
882
+ : 'false',
883
+ 'aria-disabled': options.disabled ? 'true' : undefined,
884
+ 'data-menu-flash': isFlashing ? 'true' : undefined,
885
+ 'data-highlighted': context.activeId === handle.id ? 'true' : undefined,
886
+ }),
887
+ !options.disabled && [
888
+ on('click', () => {
889
+ void context.activateItem(handle.id);
890
+ }),
891
+ on('pointermove', (event) => {
892
+ if (!context.allowsPointer(event)) {
893
+ return;
894
+ }
895
+ context.highlight(handle.id, { focus: true });
896
+ }),
897
+ on('pointerleave', (event) => {
898
+ if (context.activeId !== handle.id) {
899
+ return;
900
+ }
901
+ if (!shouldClearHighlightOnPointerLeave(event)) {
902
+ return;
903
+ }
904
+ context.highlight(null);
905
+ }),
906
+ on('focus', () => {
907
+ context.highlight(handle.id);
908
+ }),
909
+ ],
910
+ ];
911
+ };
912
+ });
913
+ const submenuTriggerMixin = createMixin((handle) => {
914
+ let itemRef;
915
+ let openTimeoutId = 0;
916
+ let aborted = false;
917
+ let childMenu = handle.context.get(MenuProvider);
918
+ let parentMenu = childMenu.parent;
919
+ let signal = handle.signal;
920
+ handle.queueTask((node) => {
921
+ itemRef = node;
922
+ });
923
+ function clearScheduledOpen() {
924
+ clearTimeout(openTimeoutId);
925
+ openTimeoutId = 0;
926
+ }
927
+ signal.addEventListener('abort', () => {
928
+ aborted = true;
929
+ clearScheduledOpen();
930
+ });
931
+ return (options) => {
932
+ if (!parentMenu) {
933
+ return [];
934
+ }
935
+ let parent = parentMenu;
936
+ parent.registerItem({
937
+ disabled: options.disabled,
938
+ id: handle.id,
939
+ searchValue: options.searchValue,
940
+ submenu: childMenu,
941
+ type: 'item',
942
+ value: options.value,
943
+ get hidden() {
944
+ return itemRef?.hidden === true;
945
+ },
946
+ get label() {
947
+ return getItemLabel(itemRef, options.label);
948
+ },
949
+ get node() {
950
+ return itemRef;
951
+ },
952
+ });
953
+ function scheduleOpen() {
954
+ clearScheduledOpen();
955
+ if (options.disabled || childMenu.isOpen) {
956
+ return;
957
+ }
958
+ openTimeoutId = window.setTimeout(() => {
959
+ if (aborted || parent.activeId !== handle.id) {
960
+ return;
961
+ }
962
+ void childMenu.open({ focus: false, strategy: 'none' });
963
+ }, SUBMENU_OPEN_DELAY_MS);
964
+ }
965
+ return [
966
+ attrs({
967
+ id: handle.id,
968
+ role: 'menuitem',
969
+ tabIndex: -1,
970
+ 'aria-controls': childMenu.listId,
971
+ 'aria-disabled': options.disabled ? 'true' : undefined,
972
+ 'aria-expanded': childMenu.isOpen ? 'true' : 'false',
973
+ 'aria-haspopup': 'menu',
974
+ 'data-submenu-state': childMenu.state === State.Idle ? undefined : childMenu.state,
975
+ 'data-highlighted': parent.activeId === handle.id ? 'true' : undefined,
976
+ }),
977
+ ref((node, signal) => {
978
+ childMenu.registerTrigger(node, handle.id);
979
+ signal.addEventListener('abort', () => {
980
+ childMenu.unregisterTrigger(node);
981
+ });
982
+ }),
983
+ popover.anchor({
984
+ placement: 'right-start',
985
+ relativeTo: SUBMENU_ANCHOR_RELATIVE_TO,
986
+ }),
987
+ !options.disabled && [
988
+ on('click', () => {
989
+ parent.highlight(handle.id, { focus: true });
990
+ void childMenu.open({ focus: false, strategy: 'none' });
991
+ }),
992
+ on('pointermove', (event) => {
993
+ if (!parent.allowsPointer(event)) {
994
+ return;
995
+ }
996
+ let shouldScheduleOpen = document.activeElement === itemRef && openTimeoutId === 0;
997
+ parent.highlight(handle.id, { focus: true });
998
+ if (shouldScheduleOpen) {
999
+ scheduleOpen();
1000
+ }
1001
+ }),
1002
+ on('pointerleave', (event) => {
1003
+ clearScheduledOpen();
1004
+ let childSurface = childMenu.surfaceNode;
1005
+ if (childMenu.isOpen &&
1006
+ parent.startHoverAim(itemRef ?? null, childSurface ?? null, event)) {
1007
+ return;
1008
+ }
1009
+ if (parent.activeId !== handle.id) {
1010
+ return;
1011
+ }
1012
+ if (!shouldClearHighlightOnPointerLeave(event)) {
1013
+ return;
1014
+ }
1015
+ parent.highlight(null);
1016
+ }),
1017
+ on('focus', (event) => {
1018
+ parent.highlight(handle.id);
1019
+ if (event.relatedTarget instanceof Element &&
1020
+ event.relatedTarget.closest('[role="menu"]')?.id === childMenu.listId) {
1021
+ return;
1022
+ }
1023
+ scheduleOpen();
1024
+ }),
1025
+ on('blur', () => {
1026
+ clearScheduledOpen();
1027
+ }),
1028
+ ],
1029
+ ];
1030
+ };
1031
+ });
1032
+ export const Context = MenuProvider;
1033
+ export const item = itemMixin;
1034
+ export const list = listMixin;
1035
+ export { popoverMixin as popover };
1036
+ export const submenuTrigger = submenuTriggerMixin;
1037
+ export const trigger = triggerMixin;
1038
+ const menu = {
1039
+ Context,
1040
+ item,
1041
+ list,
1042
+ popover: popoverMixin,
1043
+ submenuTrigger,
1044
+ trigger,
1045
+ };
1046
+ export function onMenuSelect(handler, captureBoolean) {
1047
+ return on(MENU_SELECT_EVENT, handler, captureBoolean);
1048
+ }
1049
+ export function Menu(handle) {
1050
+ let buttonRef;
1051
+ return () => {
1052
+ let { children, label, menuLabel, mix, type, ...buttonProps } = handle.props;
1053
+ return (_jsxs(menu.Context, { label: menuLabel, children: [_jsxs("button", { ...buttonProps, type: type ?? 'button', mix: [
1054
+ button.baseStyle,
1055
+ button.ghostStyle,
1056
+ buttonStyle,
1057
+ menu.trigger(),
1058
+ ref((node, signal) => {
1059
+ buttonRef = node;
1060
+ signal.addEventListener('abort', () => {
1061
+ if (buttonRef === node) {
1062
+ buttonRef = undefined;
1063
+ }
1064
+ });
1065
+ }),
1066
+ mix,
1067
+ ], children: [_jsx("span", { mix: button.labelStyle, children: label }), _jsx(Glyph, { mix: button.iconStyle, name: "chevronDown" })] }), _jsx(MenuList, { mix: onMenuSelect((event) => {
1068
+ if (!buttonRef) {
1069
+ return;
1070
+ }
1071
+ event.stopPropagation();
1072
+ buttonRef.dispatchEvent(new MenuSelectEvent(event.item));
1073
+ }), children: children })] }));
1074
+ };
1075
+ }
1076
+ export function MenuList(handle) {
1077
+ return () => {
1078
+ let { children, mix, ...divProps } = handle.props;
1079
+ return (_jsx("div", { mix: [popover.surfaceStyle, popoverStyle, menu.popover()], children: _jsx("div", { ...divProps, mix: [listStyle, menu.list(), mix], children: children }) }));
1080
+ };
1081
+ }
1082
+ export function MenuItem(handle) {
1083
+ return () => {
1084
+ let { checked, children, disabled, label, mix, name, searchValue, type, value, ...divProps } = handle.props;
1085
+ return (_jsxs("div", { ...divProps, mix: [
1086
+ itemStyle,
1087
+ menu.item({ checked, disabled, label, name, searchValue, type, value }),
1088
+ mix,
1089
+ ], children: [_jsx("span", { mix: itemSlotStyle, children: _jsx(Glyph, { mix: itemGlyphStyle, name: "check" }) }), _jsx("span", { mix: itemLabelStyle, children: children ?? label })] }));
1090
+ };
1091
+ }
1092
+ export function Submenu(handle) {
1093
+ return () => {
1094
+ let { children, disabled, label, listProps, menuLabel, mix, searchValue, value, ...divProps } = handle.props;
1095
+ return (_jsxs(menu.Context, { label: menuLabel, children: [_jsxs("div", { ...divProps, mix: [
1096
+ itemStyle,
1097
+ menu.submenuTrigger({
1098
+ disabled,
1099
+ searchValue,
1100
+ value,
1101
+ }),
1102
+ mix,
1103
+ ], children: [_jsx("span", { mix: itemSlotStyle, children: _jsx(Glyph, { mix: itemGlyphStyle, name: "check" }) }), _jsx("span", { mix: itemLabelStyle, children: label }), _jsx(Glyph, { mix: triggerGlyphStyle, name: "chevronRight" })] }), _jsx(MenuList, { ...listProps, children: children })] }));
1104
+ };
1105
+ }
1106
+ //# sourceMappingURL=menu.js.map