@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.
- package/LICENSE +21 -0
- package/README.md +195 -2
- package/dist/animation/animate-layout-mixin.d.ts +12 -0
- package/dist/animation/animate-layout-mixin.js +214 -0
- package/dist/animation/animate-layout-mixin.js.map +1 -0
- package/dist/animation/animate-mixins.d.ts +29 -0
- package/dist/animation/animate-mixins.js +235 -0
- package/dist/animation/animate-mixins.js.map +1 -0
- package/dist/animation/index.d.ts +6 -0
- package/dist/animation/index.js +5 -0
- package/dist/animation/index.js.map +1 -0
- package/dist/animation/spring.d.ts +75 -0
- package/dist/animation/spring.js +183 -0
- package/dist/animation/spring.js.map +1 -0
- package/dist/animation/tween.d.ts +70 -0
- package/dist/animation/tween.js +93 -0
- package/dist/animation/tween.js.map +1 -0
- package/dist/components/accordion/accordion.d.ts +92 -0
- package/dist/components/accordion/accordion.js +337 -0
- package/dist/components/accordion/accordion.js.map +1 -0
- package/dist/components/anchor/anchor.d.ts +13 -0
- package/dist/components/anchor/anchor.js +558 -0
- package/dist/components/anchor/anchor.js.map +1 -0
- package/dist/components/breadcrumbs/breadcrumbs.d.ts +11 -0
- package/dist/components/breadcrumbs/breadcrumbs.js +78 -0
- package/dist/components/breadcrumbs/breadcrumbs.js.map +1 -0
- package/dist/components/button/button.d.ts +23 -0
- package/dist/components/button/button.js +147 -0
- package/dist/components/button/button.js.map +1 -0
- package/dist/components/combobox/combobox.d.ts +101 -0
- package/dist/components/combobox/combobox.js +708 -0
- package/dist/components/combobox/combobox.js.map +1 -0
- package/dist/components/glyph/glyph.d.ts +14 -0
- package/dist/components/glyph/glyph.js +65 -0
- package/dist/components/glyph/glyph.js.map +1 -0
- package/dist/components/listbox/listbox.d.ts +67 -0
- package/dist/components/listbox/listbox.js +340 -0
- package/dist/components/listbox/listbox.js.map +1 -0
- package/dist/components/menu/hover-aim.d.ts +5 -0
- package/dist/components/menu/hover-aim.js +308 -0
- package/dist/components/menu/hover-aim.js.map +1 -0
- package/dist/components/menu/menu.d.ts +164 -0
- package/dist/components/menu/menu.js +1106 -0
- package/dist/components/menu/menu.js.map +1 -0
- package/dist/components/popover/popover.d.ts +35 -0
- package/dist/components/popover/popover.js +138 -0
- package/dist/components/popover/popover.js.map +1 -0
- package/dist/components/select/select.d.ts +67 -0
- package/dist/components/select/select.js +397 -0
- package/dist/components/select/select.js.map +1 -0
- package/dist/components/separator/separator.d.ts +2 -0
- package/dist/components/separator/separator.js +15 -0
- package/dist/components/separator/separator.js.map +1 -0
- package/dist/components/tabs/tabs.d.ts +78 -0
- package/dist/components/tabs/tabs.js +341 -0
- package/dist/components/tabs/tabs.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/interactions/keydown/keydown.d.ts +1 -0
- package/dist/interactions/keydown/keydown.js +8 -0
- package/dist/interactions/keydown/keydown.js.map +1 -0
- package/dist/interactions/outside-click/outside-click-mixin.d.ts +2 -0
- package/dist/interactions/outside-click/outside-click-mixin.js +29 -0
- package/dist/interactions/outside-click/outside-click-mixin.js.map +1 -0
- package/dist/interactions/typeahead/typeahead-mixin.d.ts +8 -0
- package/dist/interactions/typeahead/typeahead-mixin.js +59 -0
- package/dist/interactions/typeahead/typeahead-mixin.js.map +1 -0
- package/dist/jsx-runtime.d.ts +2 -0
- package/dist/jsx-runtime.js +3 -0
- package/dist/jsx-runtime.js.map +1 -0
- package/dist/runtime/client-entries.d.ts +91 -0
- package/dist/runtime/client-entries.js +40 -0
- package/dist/runtime/client-entries.js.map +1 -0
- package/dist/runtime/component.d.ts +223 -0
- package/dist/runtime/component.js +152 -0
- package/dist/runtime/component.js.map +1 -0
- package/dist/runtime/create-element.d.ts +10 -0
- package/dist/runtime/create-element.js +29 -0
- package/dist/runtime/create-element.js.map +1 -0
- package/dist/runtime/diff-dom.d.ts +2 -0
- package/dist/runtime/diff-dom.js +364 -0
- package/dist/runtime/diff-dom.js.map +1 -0
- package/dist/runtime/diff-props.d.ts +9 -0
- package/dist/runtime/diff-props.js +195 -0
- package/dist/runtime/diff-props.js.map +1 -0
- package/dist/runtime/document-state.d.ts +11 -0
- package/dist/runtime/document-state.js +106 -0
- package/dist/runtime/document-state.js.map +1 -0
- package/dist/runtime/dom.d.ts +3189 -0
- package/dist/runtime/dom.js +2 -0
- package/dist/runtime/dom.js.map +1 -0
- package/dist/runtime/error-event.d.ts +20 -0
- package/dist/runtime/error-event.js +19 -0
- package/dist/runtime/error-event.js.map +1 -0
- package/dist/runtime/event-listeners.d.ts +50 -0
- package/dist/runtime/event-listeners.js +31 -0
- package/dist/runtime/event-listeners.js.map +1 -0
- package/dist/runtime/frame.d.ts +119 -0
- package/dist/runtime/frame.js +915 -0
- package/dist/runtime/frame.js.map +1 -0
- package/dist/runtime/invariant.d.ts +23 -0
- package/dist/runtime/invariant.js +33 -0
- package/dist/runtime/invariant.js.map +1 -0
- package/dist/runtime/jsx.d.ts +320 -0
- package/dist/runtime/jsx.js +31 -0
- package/dist/runtime/jsx.js.map +1 -0
- package/dist/runtime/mixins/attrs-mixin.d.ts +9 -0
- package/dist/runtime/mixins/attrs-mixin.js +23 -0
- package/dist/runtime/mixins/attrs-mixin.js.map +1 -0
- package/dist/runtime/mixins/link-mixin.d.ts +17 -0
- package/dist/runtime/mixins/link-mixin.js +101 -0
- package/dist/runtime/mixins/link-mixin.js.map +1 -0
- package/dist/runtime/mixins/mixin.d.ts +145 -0
- package/dist/runtime/mixins/mixin.js +570 -0
- package/dist/runtime/mixins/mixin.js.map +1 -0
- package/dist/runtime/mixins/on-mixin.d.ts +16 -0
- package/dist/runtime/mixins/on-mixin.js +49 -0
- package/dist/runtime/mixins/on-mixin.js.map +1 -0
- package/dist/runtime/mixins/ref-mixin.d.ts +9 -0
- package/dist/runtime/mixins/ref-mixin.js +21 -0
- package/dist/runtime/mixins/ref-mixin.js.map +1 -0
- package/dist/runtime/navigation.d.ts +22 -0
- package/dist/runtime/navigation.js +102 -0
- package/dist/runtime/navigation.js.map +1 -0
- package/dist/runtime/reconcile.d.ts +11 -0
- package/dist/runtime/reconcile.js +1240 -0
- package/dist/runtime/reconcile.js.map +1 -0
- package/dist/runtime/render.d.ts +54 -0
- package/dist/runtime/render.js +50 -0
- package/dist/runtime/render.js.map +1 -0
- package/dist/runtime/run.d.ts +45 -0
- package/dist/runtime/run.js +68 -0
- package/dist/runtime/run.js.map +1 -0
- package/dist/runtime/scheduler.d.ts +30 -0
- package/dist/runtime/scheduler.js +185 -0
- package/dist/runtime/scheduler.js.map +1 -0
- package/dist/runtime/svg-attributes.d.ts +5 -0
- package/dist/runtime/svg-attributes.js +124 -0
- package/dist/runtime/svg-attributes.js.map +1 -0
- package/dist/runtime/to-vnode.d.ts +3 -0
- package/dist/runtime/to-vnode.js +43 -0
- package/dist/runtime/to-vnode.js.map +1 -0
- package/dist/runtime/typed-event-target.d.ts +50 -0
- package/dist/runtime/typed-event-target.js +6 -0
- package/dist/runtime/typed-event-target.js.map +1 -0
- package/dist/runtime/vdom.d.ts +55 -0
- package/dist/runtime/vdom.js +198 -0
- package/dist/runtime/vdom.js.map +1 -0
- package/dist/runtime/vnode.d.ts +79 -0
- package/dist/runtime/vnode.js +38 -0
- package/dist/runtime/vnode.js.map +1 -0
- package/dist/server/stream.d.ts +57 -0
- package/dist/server/stream.js +1007 -0
- package/dist/server/stream.js.map +1 -0
- package/dist/style/css-mixin.d.ts +8 -0
- package/dist/style/css-mixin.js +54 -0
- package/dist/style/css-mixin.js.map +1 -0
- package/dist/style/index.d.ts +5 -0
- package/dist/style/index.js +4 -0
- package/dist/style/index.js.map +1 -0
- package/dist/style/style.d.ts +24 -0
- package/dist/style/style.js +269 -0
- package/dist/style/style.js.map +1 -0
- package/dist/style/stylesheet.d.ts +11 -0
- package/dist/style/stylesheet.js +174 -0
- package/dist/style/stylesheet.js.map +1 -0
- package/dist/test.d.ts +2 -0
- package/dist/test.js +2 -0
- package/dist/test.js.map +1 -0
- package/dist/theme/contract.d.ts +259 -0
- package/dist/theme/contract.js +131 -0
- package/dist/theme/contract.js.map +1 -0
- package/dist/theme/glyph-contract.d.ts +13 -0
- package/dist/theme/glyph-contract.js +34 -0
- package/dist/theme/glyph-contract.js.map +1 -0
- package/dist/theme/presets/rmx-01/glyphs.d.ts +2 -0
- package/dist/theme/presets/rmx-01/glyphs.js +252 -0
- package/dist/theme/presets/rmx-01/glyphs.js.map +1 -0
- package/dist/theme/presets/rmx-01/index.d.ts +2 -0
- package/dist/theme/presets/rmx-01/index.js +119 -0
- package/dist/theme/presets/rmx-01/index.js.map +1 -0
- package/dist/theme/runtime.d.ts +2 -0
- package/dist/theme/runtime.js +81 -0
- package/dist/theme/runtime.js.map +1 -0
- package/dist/theme/theme.d.ts +6 -0
- package/dist/theme/theme.js +5 -0
- package/dist/theme/theme.js.map +1 -0
- package/dist/utils/flash-attribute.d.ts +1 -0
- package/dist/utils/flash-attribute.js +11 -0
- package/dist/utils/flash-attribute.js.map +1 -0
- package/dist/utils/scroll-lock.d.ts +3 -0
- package/dist/utils/scroll-lock.js +69 -0
- package/dist/utils/scroll-lock.js.map +1 -0
- package/dist/utils/wait-for-css-transition.d.ts +1 -0
- package/dist/utils/wait-for-css-transition.js +58 -0
- package/dist/utils/wait-for-css-transition.js.map +1 -0
- package/dist/utils/wait.d.ts +1 -0
- package/dist/utils/wait.js +6 -0
- package/dist/utils/wait.js.map +1 -0
- package/package.json +125 -5
- package/src/animation/animate-layout-mixin.ts +266 -0
- package/src/animation/animate-mixins.ts +292 -0
- package/src/animation/index.ts +6 -0
- package/src/animation/spring.ts +299 -0
- package/src/animation/tween.ts +134 -0
- package/src/components/accordion/accordion.tsx +565 -0
- package/src/components/anchor/anchor.ts +904 -0
- package/src/components/breadcrumbs/breadcrumbs.tsx +124 -0
- package/src/components/button/README.md +44 -0
- package/src/components/button/button.tsx +188 -0
- package/src/components/combobox/README.md +145 -0
- package/src/components/combobox/combobox.tsx +1014 -0
- package/src/components/glyph/glyph.tsx +110 -0
- package/src/components/listbox/listbox.ts +447 -0
- package/src/components/menu/hover-aim.ts +428 -0
- package/src/components/menu/menu.tsx +1547 -0
- package/src/components/popover/README.md +122 -0
- package/src/components/popover/popover.ts +201 -0
- package/src/components/select/select.tsx +585 -0
- package/src/components/separator/separator.ts +19 -0
- package/src/components/tabs/README.md +105 -0
- package/src/components/tabs/tabs.tsx +499 -0
- package/src/index.ts +72 -0
- package/src/interactions/keydown/keydown.ts +14 -0
- package/src/interactions/outside-click/outside-click-mixin.ts +55 -0
- package/src/interactions/typeahead/typeahead-mixin.ts +89 -0
- package/src/jsx-runtime.ts +2 -0
- package/src/runtime/client-entries.ts +137 -0
- package/src/runtime/component.ts +402 -0
- package/src/runtime/create-element.ts +38 -0
- package/src/runtime/diff-dom.ts +404 -0
- package/src/runtime/diff-props.ts +209 -0
- package/src/runtime/document-state.ts +130 -0
- package/src/runtime/dom.ts +3971 -0
- package/src/runtime/error-event.ts +26 -0
- package/src/runtime/event-listeners.ts +171 -0
- package/src/runtime/frame.ts +1211 -0
- package/src/runtime/invariant.ts +31 -0
- package/src/runtime/jsx.ts +398 -0
- package/src/runtime/mixins/attrs-mixin.ts +35 -0
- package/src/runtime/mixins/link-mixin.ts +131 -0
- package/src/runtime/mixins/mixin.ts +908 -0
- package/src/runtime/mixins/on-mixin.ts +89 -0
- package/src/runtime/mixins/ref-mixin.ts +32 -0
- package/src/runtime/navigation.ts +136 -0
- package/src/runtime/reconcile.ts +1722 -0
- package/src/runtime/render.ts +101 -0
- package/src/runtime/run.ts +100 -0
- package/src/runtime/scheduler.ts +262 -0
- package/src/runtime/svg-attributes.ts +133 -0
- package/src/runtime/to-vnode.ts +50 -0
- package/src/runtime/typed-event-target.ts +67 -0
- package/src/runtime/vdom.ts +295 -0
- package/src/runtime/vnode.ts +137 -0
- package/src/server/stream.ts +1342 -0
- package/src/style/css-mixin.ts +82 -0
- package/src/style/index.ts +10 -0
- package/src/style/style.ts +312 -0
- package/src/style/stylesheet.ts +209 -0
- package/src/test/setup.ts +7 -0
- package/src/test/utils.ts +45 -0
- package/src/test.ts +2 -0
- package/src/theme/contract.ts +183 -0
- package/src/theme/glyph-contract.ts +57 -0
- package/src/theme/presets/rmx-01/glyphs.tsx +308 -0
- package/src/theme/presets/rmx-01/index.ts +121 -0
- package/src/theme/runtime.ts +116 -0
- package/src/theme/theme.ts +15 -0
- package/src/utils/flash-attribute.ts +11 -0
- package/src/utils/scroll-lock.ts +97 -0
- package/src/utils/wait-for-css-transition.ts +66 -0
- 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
|