@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,122 @@
1
+ # Popover
2
+
3
+ `popover` is a low-level primitive for anchored, dismissible floating panels.
4
+
5
+ Use it for custom surfaces like filters, inspectors, and view options. Higher-level widgets like `menu`, `select`, and `combobox` should build on top of it instead of exposing raw `popover.*` mixins directly.
6
+
7
+ ## Usage
8
+
9
+ ```tsx
10
+ import { css, on, type Handle } from 'remix/ui'
11
+ import { Button } from '@remix-run/ui/button'
12
+ import { Glyph } from '@remix-run/ui/glyph'
13
+ import { popover } from '@remix-run/ui/popover'
14
+
15
+ export function ViewOptions(handle: Handle) {
16
+ let open = false
17
+
18
+ function openPopover() {
19
+ open = true
20
+ void handle.update()
21
+ }
22
+
23
+ function closePopover() {
24
+ open = false
25
+ void handle.update()
26
+ }
27
+
28
+ return () => (
29
+ <popover.Context>
30
+ <Button
31
+ endIcon={<Glyph name="chevronDown" />}
32
+ mix={[
33
+ popover.anchor({ placement: 'bottom-end' }),
34
+ popover.focusOnHide(),
35
+ on('click', openPopover),
36
+ ]}
37
+ tone="secondary"
38
+ >
39
+ View options
40
+ </Button>
41
+
42
+ <div
43
+ mix={[
44
+ popover.surfaceStyle,
45
+ popover.surface({
46
+ open,
47
+ onHide() {
48
+ closePopover()
49
+ },
50
+ }),
51
+ ]}
52
+ >
53
+ <div mix={popover.contentStyle}>
54
+ <Button mix={[popover.focusOnShow(), on('click', closePopover)]} tone="ghost">
55
+ Close
56
+ </Button>
57
+ <div mix={panelBody}>Panel content</div>
58
+ </div>
59
+ </div>
60
+ </popover.Context>
61
+ )
62
+ }
63
+
64
+ let panelBody = css({
65
+ padding: '12px',
66
+ })
67
+ ```
68
+
69
+ ## `popover.*`
70
+
71
+ ### `popover.Context`
72
+
73
+ Provides shared coordination for one popover instance. Render the anchor, any focus targets, and the surface inside the same context.
74
+
75
+ ### `popover.anchor(options)`
76
+
77
+ Registers the host element as the anchor for the current popover surface.
78
+
79
+ - Accepts standard `AnchorOptions`.
80
+ - The stored anchor controls where the surface is positioned when it opens.
81
+ - Apply it to the button or other element the surface should attach to.
82
+
83
+ ### `popover.surface({ open, onHide, ... })`
84
+
85
+ Turns the host into the controlled popover surface.
86
+
87
+ - Wires `popover="manual"` and native `showPopover()` / `hidePopover()` behavior.
88
+ - Calls `onHide` for `Escape` and outside clicks with a `PopoverHideRequest`.
89
+ - Restores focus to the registered hide target unless `restoreFocusOnHide: false`.
90
+ - Accepts `closeOnAnchorClick: false` when the anchor must stay interactive while open.
91
+
92
+ Apply this to the actual floating root, not a nested child.
93
+
94
+ ### `popover.focusOnShow()`
95
+
96
+ Registers the element that should receive focus when the popover opens.
97
+
98
+ ### `popover.focusOnHide()`
99
+
100
+ Registers the element that should receive focus again when the popover closes.
101
+
102
+ ### `popover.surfaceStyle`
103
+
104
+ Default floating-surface presentation for popovers.
105
+
106
+ ### `popover.contentStyle`
107
+
108
+ Default inner scroll container for popover content.
109
+
110
+ ## Behavior Notes
111
+
112
+ - Opening anchors the surface to the registered anchor and locks page scrolling until close.
113
+ - `onHide` receives `{ reason: 'escape-key' | 'outside-click', target? }`.
114
+ - `popover.focusOnShow()` wins on open when present.
115
+ - `popover.focusOnHide()` is used on close by default when focus restoration is enabled.
116
+ - `closeOnAnchorClick: false` keeps anchor clicks inside the current session, which is useful for input-driven popovers like comboboxes.
117
+
118
+ ## When To Use Something Else
119
+
120
+ - Use `menu` for command surfaces.
121
+ - Use `listbox` or `select` for committed value picking.
122
+ - Use `combobox` for input-first popup selection.
@@ -0,0 +1,201 @@
1
+ import {
2
+ attrs,
3
+ createMixin,
4
+ css,
5
+ on,
6
+ type CSSMixinDescriptor,
7
+ type Handle,
8
+ type RemixNode,
9
+ } from '@remix-run/ui'
10
+ import { anchor as positionAnchor, type AnchorOptions } from '../anchor/anchor.ts'
11
+ import { onOutsideClick } from '../../interactions/outside-click/outside-click-mixin.ts'
12
+ import { theme } from '../../theme/theme.ts'
13
+ import { lockScroll } from '../../utils/scroll-lock.ts'
14
+
15
+ export interface PopoverContext {
16
+ hideFocusTarget: HTMLElement | null
17
+ showFocusTarget: HTMLElement | null
18
+ surface: HTMLElement | null
19
+ anchor: AnchorRef | null
20
+ }
21
+
22
+ export interface PopoverProps {
23
+ children?: RemixNode
24
+ }
25
+
26
+ interface AnchorRef {
27
+ node: HTMLElement
28
+ options: AnchorOptions
29
+ }
30
+
31
+ const popupViewportClampMaxHeight = '50dvh'
32
+
33
+ const popoverSurfaceTransitionCss: CSSMixinDescriptor = css({
34
+ opacity: 0,
35
+ '&:popover-open': {
36
+ opacity: 1,
37
+ },
38
+ '&:not(:popover-open)': {
39
+ pointerEvents: 'none',
40
+ transition: 'opacity 180ms ease-in, overlay 180ms ease-in, display 180ms ease-in',
41
+ transitionBehavior: 'allow-discrete',
42
+ },
43
+ })
44
+
45
+ const popoverContentCss: CSSMixinDescriptor = css({
46
+ flex: '1 1 auto',
47
+ minHeight: 0,
48
+ padding: theme.space.xs,
49
+ overflow: 'auto',
50
+ overscrollBehavior: 'contain',
51
+ })
52
+
53
+ const popoverSurfaceCss: CSSMixinDescriptor = css({
54
+ position: 'fixed',
55
+ inset: 'auto',
56
+ margin: 0,
57
+ padding: theme.space.none,
58
+ display: 'flex',
59
+ flexDirection: 'column',
60
+ minHeight: 0,
61
+ minWidth: '12rem',
62
+ maxWidth: `min(24rem, calc(100vw - (${theme.space.lg} * 2)))`,
63
+ maxHeight: popupViewportClampMaxHeight,
64
+ border: `1px solid ${theme.colors.border.subtle}`,
65
+ borderRadius: theme.radius.lg,
66
+ backgroundColor: theme.surface.lvl0,
67
+ color: theme.colors.text.primary,
68
+ overflow: 'hidden',
69
+ boxShadow: `${theme.shadow.xs}, ${theme.shadow.md}`,
70
+ '&::backdrop': {
71
+ background: 'transparent',
72
+ },
73
+ })
74
+
75
+ export const contentStyle = popoverContentCss
76
+ export const surfaceStyle = [popoverSurfaceCss, popoverSurfaceTransitionCss] as const
77
+
78
+ function PopoverProvider(handle: Handle<PopoverProps, PopoverContext>) {
79
+ handle.context.set({
80
+ hideFocusTarget: null,
81
+ showFocusTarget: null,
82
+ surface: null,
83
+ anchor: null,
84
+ })
85
+
86
+ return () => handle.props.children
87
+ }
88
+
89
+ export interface PopoverSurfaceOptions {
90
+ open: boolean
91
+ onHide: (request?: PopoverHideRequest) => void
92
+ closeOnAnchorClick?: boolean
93
+ restoreFocusOnHide?: boolean
94
+ stopOutsideClickPropagation?: boolean
95
+ }
96
+
97
+ export interface PopoverHideRequest {
98
+ reason: 'escape-key' | 'outside-click'
99
+ target?: Node | null
100
+ }
101
+
102
+ const anchorMixin = createMixin<HTMLElement, [options: AnchorOptions]>((handle) => {
103
+ let context = handle.context.get(PopoverProvider)
104
+ return (options) => {
105
+ handle.queueTask((node) => {
106
+ context.anchor = { node, options }
107
+ })
108
+ }
109
+ })
110
+
111
+ const surfaceMixin = createMixin<HTMLElement, [options: PopoverSurfaceOptions]>((handle) => {
112
+ let openProp = false
113
+ let cleanupAnchor = () => {}
114
+ let unlockScroll = () => {}
115
+ let context = handle.context.get(PopoverProvider)
116
+
117
+ handle.queueTask((node, signal) => {
118
+ context.surface = node
119
+
120
+ signal.addEventListener('abort', () => {
121
+ if (context.surface === node) {
122
+ context.surface = null
123
+ }
124
+ })
125
+ })
126
+
127
+ return (options) => {
128
+ let wasOpen = openProp
129
+ openProp = options.open
130
+
131
+ handle.queueTask(async (node) => {
132
+ if (openProp && !wasOpen) {
133
+ node.showPopover()
134
+ } else if (!openProp && wasOpen) {
135
+ node.hidePopover()
136
+ }
137
+ })
138
+
139
+ return [
140
+ attrs({ popover: 'manual' }),
141
+
142
+ on('beforetoggle', (event) => {
143
+ if (event.newState === 'open') {
144
+ cleanupAnchor = positionAnchor(
145
+ event.currentTarget,
146
+ context.anchor!.node,
147
+ context.anchor!.options,
148
+ )
149
+ unlockScroll = lockScroll()
150
+ } else if (event.newState === 'closed') {
151
+ cleanupAnchor()
152
+ unlockScroll()
153
+ }
154
+ }),
155
+
156
+ on('toggle', async (event) => {
157
+ if (event.newState === 'open') {
158
+ context.showFocusTarget?.focus()
159
+ } else if (event.newState === 'closed' && options.restoreFocusOnHide !== false) {
160
+ context.hideFocusTarget?.focus()
161
+ }
162
+ }),
163
+
164
+ on('keydown', (event) => {
165
+ if (event.key === 'Escape') {
166
+ event.preventDefault()
167
+ options.onHide({ reason: 'escape-key' })
168
+ }
169
+ }),
170
+
171
+ onOutsideClick(
172
+ openProp,
173
+ (target) => {
174
+ options.onHide({ reason: 'outside-click', target })
175
+ },
176
+ (target) => options.closeOnAnchorClick === false && !!context.anchor?.node.contains(target),
177
+ options.stopOutsideClickPropagation ?? true,
178
+ ),
179
+ ]
180
+ }
181
+ })
182
+
183
+ const focusOnShowMixin = createMixin<HTMLElement, []>((handle) => {
184
+ handle.addEventListener('insert', (event) => {
185
+ let context = handle.context.get(PopoverProvider)
186
+ context.showFocusTarget = event.node
187
+ })
188
+ })
189
+
190
+ const focusOnHideMixin = createMixin<HTMLElement, []>((handle) => {
191
+ handle.addEventListener('insert', (event) => {
192
+ let context = handle.context.get(PopoverProvider)
193
+ context.hideFocusTarget = event.node
194
+ })
195
+ })
196
+
197
+ export const Context = PopoverProvider
198
+ export const anchor = anchorMixin
199
+ export const surface = surfaceMixin
200
+ export const focusOnHide = focusOnHideMixin
201
+ export const focusOnShow = focusOnShowMixin