@duro-app/ui 5.4.0 → 5.6.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/dist/{Table-Cm5cCnZD.js → Table-B1sWL3GA.js} +668 -650
- package/dist/Table-B1sWL3GA.js.map +1 -0
- package/dist/components/Breadcrumb/Breadcrumb.d.ts +37 -0
- package/dist/components/Breadcrumb/Breadcrumb.d.ts.map +1 -0
- package/dist/components/Breadcrumb/Breadcrumb.meta.d.ts +3 -0
- package/dist/components/Breadcrumb/Breadcrumb.meta.d.ts.map +1 -0
- package/dist/components/Breadcrumb/Breadcrumb.stories.d.ts +18 -0
- package/dist/components/Breadcrumb/Breadcrumb.stories.d.ts.map +1 -0
- package/dist/components/Breadcrumb/styles.css.d.ts +46 -0
- package/dist/components/Breadcrumb/styles.css.d.ts.map +1 -0
- package/dist/components/Button/Button.d.ts +2 -1
- package/dist/components/Button/Button.d.ts.map +1 -1
- package/dist/components/Button/styles.css.d.ts +1 -0
- package/dist/components/Button/styles.css.d.ts.map +1 -1
- package/dist/components/ButtonGroup/ButtonGroup.stories.d.ts +14 -0
- package/dist/components/ButtonGroup/ButtonGroup.stories.d.ts.map +1 -1
- package/dist/components/Inline/Inline.meta.d.ts.map +1 -1
- package/dist/components/LinkButton/LinkButton.d.ts +8 -1
- package/dist/components/LinkButton/LinkButton.d.ts.map +1 -1
- package/dist/components/LinkButton/LinkButton.stories.d.ts +9 -0
- package/dist/components/LinkButton/LinkButton.stories.d.ts.map +1 -1
- package/dist/components/LinkButton/styles.css.d.ts +1 -0
- package/dist/components/LinkButton/styles.css.d.ts.map +1 -1
- package/dist/components/Menu/Menu.d.ts +4 -1
- package/dist/components/Menu/Menu.d.ts.map +1 -1
- package/dist/components/Menu/styles.css.d.ts +14 -0
- package/dist/components/Menu/styles.css.d.ts.map +1 -1
- package/dist/components/PageNav/PageNav.d.ts +30 -0
- package/dist/components/PageNav/PageNav.d.ts.map +1 -0
- package/dist/components/PageNav/PageNav.meta.d.ts +3 -0
- package/dist/components/PageNav/PageNav.meta.d.ts.map +1 -0
- package/dist/components/PageNav/PageNav.stories.d.ts +16 -0
- package/dist/components/PageNav/PageNav.stories.d.ts.map +1 -0
- package/dist/components/PageNav/styles.css.d.ts +51 -0
- package/dist/components/PageNav/styles.css.d.ts.map +1 -0
- package/dist/components/Popover/Popover.d.ts +14 -2
- package/dist/components/Popover/Popover.d.ts.map +1 -1
- package/dist/components/Popover/Popover.meta.d.ts.map +1 -1
- package/dist/components/Popover/PopoverLayerContext.d.ts +3 -0
- package/dist/components/Popover/PopoverLayerContext.d.ts.map +1 -1
- package/dist/components/Popover/PopoverRaised.stories.d.ts +28 -0
- package/dist/components/Popover/PopoverRaised.stories.d.ts.map +1 -0
- package/dist/components/Popover/styles.css.d.ts +15 -0
- package/dist/components/Popover/styles.css.d.ts.map +1 -1
- package/dist/components/Select/Select.d.ts +4 -1
- package/dist/components/Select/Select.d.ts.map +1 -1
- package/dist/components/Select/styles.css.d.ts +9 -0
- package/dist/components/Select/styles.css.d.ts.map +1 -1
- package/dist/components/TableOfContents/TableOfContents.d.ts +53 -0
- package/dist/components/TableOfContents/TableOfContents.d.ts.map +1 -0
- package/dist/components/TableOfContents/TableOfContents.meta.d.ts +3 -0
- package/dist/components/TableOfContents/TableOfContents.meta.d.ts.map +1 -0
- package/dist/components/TableOfContents/TableOfContents.stories.d.ts +21 -0
- package/dist/components/TableOfContents/TableOfContents.stories.d.ts.map +1 -0
- package/dist/components/TableOfContents/TableOfContentsContext.d.ts +11 -0
- package/dist/components/TableOfContents/TableOfContentsContext.d.ts.map +1 -0
- package/dist/components/TableOfContents/styles.css.d.ts +114 -0
- package/dist/components/TableOfContents/styles.css.d.ts.map +1 -0
- package/dist/components/TextLink/TextLink.d.ts +8 -1
- package/dist/components/TextLink/TextLink.d.ts.map +1 -1
- package/dist/components/TextLink/TextLink.stories.d.ts +9 -0
- package/dist/components/TextLink/TextLink.stories.d.ts.map +1 -1
- package/dist/components/Toggle/Toggle.d.ts +2 -1
- package/dist/components/Toggle/Toggle.d.ts.map +1 -1
- package/dist/components/Toggle/styles.css.d.ts +1 -0
- package/dist/components/Toggle/styles.css.d.ts.map +1 -1
- package/dist/components/Tree/Tree.d.ts +13 -1
- package/dist/components/Tree/Tree.d.ts.map +1 -1
- package/dist/components/Tree/Tree.meta.d.ts.map +1 -1
- package/dist/components/Tree/Tree.stories.d.ts +9 -0
- package/dist/components/Tree/Tree.stories.d.ts.map +1 -1
- package/dist/components/Tree/styles.css.d.ts +5 -0
- package/dist/components/Tree/styles.css.d.ts.map +1 -1
- package/dist/docs/clickLink.d.ts +4 -0
- package/dist/docs/clickLink.d.ts.map +1 -0
- package/dist/docs/coarsePointer.d.ts +15 -0
- package/dist/docs/coarsePointer.d.ts.map +1 -0
- package/dist/docs/themedSurface.d.ts +8 -0
- package/dist/docs/themedSurface.d.ts.map +1 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2718 -2212
- package/dist/index.js.map +1 -1
- package/dist/shared/ModalContext.d.ts +7 -0
- package/dist/shared/ModalContext.d.ts.map +1 -1
- package/dist/shared/exports.typecheck.d.ts +9 -0
- package/dist/shared/exports.typecheck.d.ts.map +1 -0
- package/dist/shared/navigate.d.ts +25 -0
- package/dist/shared/navigate.d.ts.map +1 -0
- package/dist/shared/types.d.ts +2 -0
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/table.js +1 -1
- package/package.json +2 -2
- package/src/components/Breadcrumb/Breadcrumb.meta.ts +37 -0
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +110 -0
- package/src/components/Breadcrumb/Breadcrumb.tsx +95 -0
- package/src/components/Breadcrumb/styles.css.ts +76 -0
- package/src/components/Button/Button.tsx +2 -1
- package/src/components/Button/styles.css.ts +3 -0
- package/src/components/ButtonGroup/ButtonGroup.stories.tsx +172 -14
- package/src/components/Inline/Inline.meta.ts +2 -1
- package/src/components/LinkButton/LinkButton.meta.ts +1 -1
- package/src/components/LinkButton/LinkButton.stories.tsx +42 -1
- package/src/components/LinkButton/LinkButton.tsx +9 -0
- package/src/components/LinkButton/styles.css.ts +4 -0
- package/src/components/Menu/Menu.meta.ts +1 -1
- package/src/components/Menu/Menu.tsx +17 -2
- package/src/components/Menu/styles.css.ts +18 -0
- package/src/components/PageNav/PageNav.meta.ts +35 -0
- package/src/components/PageNav/PageNav.stories.tsx +146 -0
- package/src/components/PageNav/PageNav.tsx +93 -0
- package/src/components/PageNav/styles.css.ts +100 -0
- package/src/components/Popover/Popover.meta.ts +2 -1
- package/src/components/Popover/Popover.tsx +29 -4
- package/src/components/Popover/PopoverLayerContext.ts +3 -0
- package/src/components/Popover/PopoverRaised.stories.tsx +334 -0
- package/src/components/Popover/styles.css.ts +21 -0
- package/src/components/Select/Select.meta.ts +1 -1
- package/src/components/Select/Select.tsx +5 -1
- package/src/components/Select/styles.css.ts +12 -0
- package/src/components/TableOfContents/TableOfContents.meta.ts +41 -0
- package/src/components/TableOfContents/TableOfContents.stories.tsx +222 -0
- package/src/components/TableOfContents/TableOfContents.tsx +173 -0
- package/src/components/TableOfContents/TableOfContentsContext.ts +20 -0
- package/src/components/TableOfContents/styles.css.ts +147 -0
- package/src/components/TextLink/TextLink.meta.ts +1 -1
- package/src/components/TextLink/TextLink.stories.tsx +42 -1
- package/src/components/TextLink/TextLink.tsx +9 -0
- package/src/components/Toggle/Toggle.tsx +2 -1
- package/src/components/Toggle/styles.css.ts +2 -0
- package/src/components/Tree/Tree.meta.ts +2 -1
- package/src/components/Tree/Tree.stories.tsx +93 -1
- package/src/components/Tree/Tree.tsx +69 -27
- package/src/components/Tree/styles.css.ts +6 -0
- package/src/docs/clickLink.ts +24 -0
- package/src/docs/coarsePointer.ts +42 -0
- package/src/docs/themedSurface.tsx +23 -0
- package/src/index.ts +11 -0
- package/src/shared/ModalContext.ts +8 -1
- package/src/shared/exports.typecheck.ts +16 -0
- package/src/shared/linkParts.typecheck.tsx +47 -0
- package/src/shared/navigate.ts +62 -0
- package/src/shared/types.ts +3 -0
- package/dist/Table-Cm5cCnZD.js.map +0 -1
- package/dist/shared/length.typecheck.d.ts +0 -31
- package/dist/shared/length.typecheck.d.ts.map +0 -1
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import {createContext, type ReactNode, useContext} from 'react'
|
|
2
|
+
import {html} from 'react-strict-dom'
|
|
3
|
+
import {styles} from './styles.css'
|
|
4
|
+
import {linkClickHandler, type OnNavigate} from '../../shared/navigate'
|
|
5
|
+
|
|
6
|
+
/* PageNav — the previous and next pages in reading order, at the foot of a
|
|
7
|
+
* page: a labelled `nav` holding up to two link cards. Each card shows a small
|
|
8
|
+
* direction label ("Previous", from your text catalog) over the page title.
|
|
9
|
+
* Prev holds the left slot and Next the right one even when the other is
|
|
10
|
+
* missing, so Next stays right-aligned on the last-but-one page too; on a
|
|
11
|
+
* narrow container (below the `xs` breakpoint) the two stack.
|
|
12
|
+
*
|
|
13
|
+
* Not `Pagination` (numbered result pages, in `@duro-app/ui/table`). */
|
|
14
|
+
|
|
15
|
+
// Prev and Next place themselves in Root's two-column grid.
|
|
16
|
+
const PageNavContext = createContext(false)
|
|
17
|
+
|
|
18
|
+
function usePageNav() {
|
|
19
|
+
if (!useContext(PageNavContext))
|
|
20
|
+
throw new Error('PageNav compound components must be used within PageNav.Root')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// --- Root ---
|
|
24
|
+
|
|
25
|
+
interface RootProps {
|
|
26
|
+
/** Names the landmark ("Pages"); required, from your text catalog. */
|
|
27
|
+
'aria-label': string
|
|
28
|
+
/** A `PageNav.Prev` and/or a `PageNav.Next`. */
|
|
29
|
+
children: ReactNode
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function Root({'aria-label': ariaLabel, children}: RootProps) {
|
|
33
|
+
return (
|
|
34
|
+
<PageNavContext.Provider value={true}>
|
|
35
|
+
<html.nav aria-label={ariaLabel} style={styles.root}>
|
|
36
|
+
<html.div style={styles.grid}>{children}</html.div>
|
|
37
|
+
</html.nav>
|
|
38
|
+
</PageNavContext.Provider>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// --- Prev / Next ---
|
|
43
|
+
|
|
44
|
+
interface LinkProps {
|
|
45
|
+
href: string
|
|
46
|
+
/** The page's title. */
|
|
47
|
+
title: ReactNode
|
|
48
|
+
/** The direction label above the title ("Previous", "Next"), from your text catalog. */
|
|
49
|
+
label: ReactNode
|
|
50
|
+
/**
|
|
51
|
+
* Client-side navigation: called for a plain primary click (no modifier
|
|
52
|
+
* key). Call `event.preventDefault()`, then your router's navigate.
|
|
53
|
+
*/
|
|
54
|
+
onNavigate?: OnNavigate
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function Prev({href, title, label, onNavigate}: LinkProps) {
|
|
58
|
+
usePageNav()
|
|
59
|
+
return (
|
|
60
|
+
<html.a
|
|
61
|
+
href={href}
|
|
62
|
+
onClick={linkClickHandler(href, onNavigate)}
|
|
63
|
+
style={[styles.card, styles.prev]}
|
|
64
|
+
>
|
|
65
|
+
<html.span style={styles.label}>
|
|
66
|
+
<html.span aria-hidden>←</html.span> {label}
|
|
67
|
+
</html.span>
|
|
68
|
+
<html.span style={styles.title}>{title}</html.span>
|
|
69
|
+
</html.a>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function Next({href, title, label, onNavigate}: LinkProps) {
|
|
74
|
+
usePageNav()
|
|
75
|
+
return (
|
|
76
|
+
<html.a
|
|
77
|
+
href={href}
|
|
78
|
+
onClick={linkClickHandler(href, onNavigate)}
|
|
79
|
+
style={[styles.card, styles.next]}
|
|
80
|
+
>
|
|
81
|
+
<html.span style={styles.label}>
|
|
82
|
+
{label} <html.span aria-hidden>→</html.span>
|
|
83
|
+
</html.span>
|
|
84
|
+
<html.span style={styles.title}>{title}</html.span>
|
|
85
|
+
</html.a>
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const PageNav = {
|
|
90
|
+
Root,
|
|
91
|
+
Prev,
|
|
92
|
+
Next,
|
|
93
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import {css} from 'react-strict-dom'
|
|
2
|
+
import {colors} from '@duro-app/tokens/tokens/colors.css'
|
|
3
|
+
import {radii, spacing} from '@duro-app/tokens/tokens/spacing.css'
|
|
4
|
+
import {typography} from '@duro-app/tokens/tokens/typography.css'
|
|
5
|
+
import {borders} from '@duro-app/tokens/tokens/borders.css'
|
|
6
|
+
import {breakpoints} from '@duro-app/tokens/tokens/breakpoints.css'
|
|
7
|
+
import {duration, easing} from '@duro-app/tokens/tokens/motion.css'
|
|
8
|
+
|
|
9
|
+
// Below this container width the two cards stack (Mobile.dc.html); above it
|
|
10
|
+
// they sit side by side, each in its own half (Reader.dc.html).
|
|
11
|
+
const STACK_BP = breakpoints.xs
|
|
12
|
+
const STACKED = `@container (max-width: ${STACK_BP})`
|
|
13
|
+
|
|
14
|
+
// The picked docs browser (docs/mockups/docs-nav/Reader.dc.html and
|
|
15
|
+
// Mobile.dc.html, `.pn .card`): two interactive cards, a small muted
|
|
16
|
+
// direction label over the title, Next aligned right.
|
|
17
|
+
export const styles = css.create({
|
|
18
|
+
// hosts the container query: a container cannot query itself
|
|
19
|
+
root: {
|
|
20
|
+
containerType: 'inline-size',
|
|
21
|
+
minWidth: 0,
|
|
22
|
+
},
|
|
23
|
+
grid: {
|
|
24
|
+
display: 'grid',
|
|
25
|
+
gridTemplateColumns: {
|
|
26
|
+
default: 'minmax(0, 1fr) minmax(0, 1fr)',
|
|
27
|
+
[STACKED]: 'minmax(0, 1fr)',
|
|
28
|
+
},
|
|
29
|
+
gap: spacing.md,
|
|
30
|
+
},
|
|
31
|
+
card: {
|
|
32
|
+
display: 'flex',
|
|
33
|
+
flexDirection: 'column',
|
|
34
|
+
padding: spacing.md,
|
|
35
|
+
fontFamily: typography.fontFamily,
|
|
36
|
+
color: colors.text,
|
|
37
|
+
textDecorationLine: 'none',
|
|
38
|
+
backgroundColor: {
|
|
39
|
+
default: colors.bgCard,
|
|
40
|
+
':hover': colors.bgCardHover,
|
|
41
|
+
},
|
|
42
|
+
borderWidth: borders.hairline,
|
|
43
|
+
borderStyle: 'solid',
|
|
44
|
+
borderColor: colors.border,
|
|
45
|
+
borderRadius: radii.md,
|
|
46
|
+
cursor: 'pointer',
|
|
47
|
+
transitionProperty: 'background-color',
|
|
48
|
+
transitionDuration: duration.fast,
|
|
49
|
+
transitionTimingFunction: easing.standard,
|
|
50
|
+
outlineWidth: {
|
|
51
|
+
default: 0,
|
|
52
|
+
':focus-visible': borders.focusRing,
|
|
53
|
+
},
|
|
54
|
+
outlineStyle: {
|
|
55
|
+
default: 'none',
|
|
56
|
+
':focus-visible': 'solid',
|
|
57
|
+
},
|
|
58
|
+
outlineColor: {
|
|
59
|
+
default: 'transparent',
|
|
60
|
+
':focus-visible': colors.accent,
|
|
61
|
+
},
|
|
62
|
+
outlineOffset: {
|
|
63
|
+
default: 0,
|
|
64
|
+
':focus-visible': borders.focusOffset,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
// each side keeps its slot: Next stays in the right half without a Prev
|
|
68
|
+
prev: {
|
|
69
|
+
gridColumn: {
|
|
70
|
+
default: '1',
|
|
71
|
+
[STACKED]: 'auto',
|
|
72
|
+
},
|
|
73
|
+
gridRow: {
|
|
74
|
+
default: '1',
|
|
75
|
+
[STACKED]: 'auto',
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
next: {
|
|
79
|
+
gridColumn: {
|
|
80
|
+
default: '2',
|
|
81
|
+
[STACKED]: 'auto',
|
|
82
|
+
},
|
|
83
|
+
gridRow: {
|
|
84
|
+
default: '1',
|
|
85
|
+
[STACKED]: 'auto',
|
|
86
|
+
},
|
|
87
|
+
textAlign: {
|
|
88
|
+
default: 'right',
|
|
89
|
+
[STACKED]: 'left',
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
label: {
|
|
93
|
+
fontSize: typography.fontSizeXs,
|
|
94
|
+
color: colors.textMuted,
|
|
95
|
+
},
|
|
96
|
+
title: {
|
|
97
|
+
fontSize: typography.fontSizeSm,
|
|
98
|
+
overflowWrap: 'anywhere',
|
|
99
|
+
},
|
|
100
|
+
})
|
|
@@ -2,10 +2,11 @@ import type {ComponentMeta} from '../component-meta'
|
|
|
2
2
|
|
|
3
3
|
export const meta: ComponentMeta = {
|
|
4
4
|
description:
|
|
5
|
-
'Non-modal anchored overlay for small interactive content. Compound component — Root is required (throws without it). Anchored to Popover.Trigger, or to a virtual anchor (a () => DOMRect) when there is no Trigger; call reposition() through a ref when the anchor moves.',
|
|
5
|
+
'Non-modal anchored overlay for small interactive content. Compound component — Root is required (throws without it). Anchored to Popover.Trigger, or to a virtual anchor (a () => DOMRect) when there is no Trigger; call reposition() through a ref when the anchor moves. Popover.Trigger takes size="small" beside small Toggles. Popover.Popup raised draws it over another floating surface (floatingRaised, popoverRaised in a modal), never over a modal.',
|
|
6
6
|
whenToUse: [
|
|
7
7
|
'A small form or controls tied to one element that stay open while you work in them',
|
|
8
8
|
'Anchoring to a spot that is not a button (a canvas point, a text selection) with a virtual anchor',
|
|
9
|
+
'One floating surface over another (a link editor over a selection format bar) — Popover.Popup raised',
|
|
9
10
|
'Holding a Select or Combobox: their portalled list counts as inside the popover, so choosing an item keeps it open; Escape closes an open Select, Combobox or Menu before the popover',
|
|
10
11
|
],
|
|
11
12
|
whenNotToUse: [
|
|
@@ -22,6 +22,7 @@ import {PopoverLayerContext, type PopoverLayerContextValue} from './PopoverLayer
|
|
|
22
22
|
import {useGroupedControl} from '../ButtonGroup/useGroupedControl'
|
|
23
23
|
import {ControlContextBoundary} from '../Toolbar/ControlContextBoundary'
|
|
24
24
|
import {mergeRefs} from '../../shared/mergeRefs'
|
|
25
|
+
import type {ControlSize} from '../../shared/types'
|
|
25
26
|
import {useInModal} from '../../shared/ModalContext'
|
|
26
27
|
|
|
27
28
|
// --- Context ---
|
|
@@ -166,6 +167,8 @@ function Root({
|
|
|
166
167
|
unregisterParent?.()
|
|
167
168
|
}
|
|
168
169
|
},
|
|
170
|
+
// Inside a raised Popup, this Popover is raised too.
|
|
171
|
+
raised: parentLayer?.raised,
|
|
169
172
|
}),
|
|
170
173
|
[parentLayer],
|
|
171
174
|
)
|
|
@@ -201,11 +204,13 @@ interface TriggerProps {
|
|
|
201
204
|
children: ReactNode
|
|
202
205
|
/** Accessible name: required when the label is an icon only. */
|
|
203
206
|
'aria-label'?: string
|
|
207
|
+
/** `small` matches a small Toggle or Button beside it. Default: `default`. */
|
|
208
|
+
size?: ControlSize
|
|
204
209
|
/** The trigger <button>, e.g. to measure it or return focus to it. */
|
|
205
210
|
ref?: Ref<HTMLButtonElement>
|
|
206
211
|
}
|
|
207
212
|
|
|
208
|
-
function Trigger({children, 'aria-label': ariaLabel, ref}: TriggerProps) {
|
|
213
|
+
function Trigger({children, 'aria-label': ariaLabel, size = 'default', ref}: TriggerProps) {
|
|
209
214
|
const {open, setOpen, popoverId, triggerRef} = usePopover('Trigger')
|
|
210
215
|
const grouped = useGroupedControl<HTMLButtonElement>()
|
|
211
216
|
|
|
@@ -220,7 +225,7 @@ function Trigger({children, 'aria-label': ariaLabel, ref}: TriggerProps) {
|
|
|
220
225
|
aria-haspopup="dialog"
|
|
221
226
|
aria-expanded={open}
|
|
222
227
|
aria-controls={open ? popoverId : undefined}
|
|
223
|
-
style={[styles.trigger, grouped.style]}
|
|
228
|
+
style={[styles.trigger, size === 'small' && styles.triggerSmall, grouped.style]}
|
|
224
229
|
>
|
|
225
230
|
{children}
|
|
226
231
|
</html.button>
|
|
@@ -233,9 +238,18 @@ interface PopupProps {
|
|
|
233
238
|
children: ReactNode
|
|
234
239
|
/** Accessible name for the dialog. */
|
|
235
240
|
label: string
|
|
241
|
+
/**
|
|
242
|
+
* Draw over another floating surface: `layers.floatingRaised` outside a
|
|
243
|
+
* modal, `layers.popoverRaised` inside a Dialog or Drawer, instead of
|
|
244
|
+
* `floating` / `popover`. For one floating thing that must cover a sibling
|
|
245
|
+
* one (a link editor over a format bar), never for covering a modal: a
|
|
246
|
+
* Dialog opened later still covers it. A Popover nested inside a raised one
|
|
247
|
+
* is raised too. Default: inherited, else `false`.
|
|
248
|
+
*/
|
|
249
|
+
raised?: boolean
|
|
236
250
|
}
|
|
237
251
|
|
|
238
|
-
function Popup({children, label}: PopupProps) {
|
|
252
|
+
function Popup({children, label, raised}: PopupProps) {
|
|
239
253
|
const {open, popoverId, side, align, anchor, triggerRef, popupRef, repositionRef} =
|
|
240
254
|
usePopover('Popup')
|
|
241
255
|
const [coords, setCoords] = useState({top: 0, left: 0})
|
|
@@ -243,6 +257,14 @@ function Popup({children, label}: PopupProps) {
|
|
|
243
257
|
// tokens cascade into the popup and it stacks above dialogs.
|
|
244
258
|
const mount = usePortalMount()
|
|
245
259
|
const inModal = useInModal()
|
|
260
|
+
const layer = useContext(PopoverLayerContext)
|
|
261
|
+
const isRaised = raised ?? layer?.raised ?? false
|
|
262
|
+
// Re-provided around the children: the Root can't see this prop, and a
|
|
263
|
+
// Popover nested in here reads it to stay above this one.
|
|
264
|
+
const raisedLayer = useMemo<PopoverLayerContextValue | null>(
|
|
265
|
+
() => (layer ? {...layer, raised: isRaised} : null),
|
|
266
|
+
[layer, isRaised],
|
|
267
|
+
)
|
|
246
268
|
|
|
247
269
|
// Measure before paint on open; re-measure on scroll/resize (capture phase
|
|
248
270
|
// catches any scrolling ancestor) and when Root.reposition() is called.
|
|
@@ -291,10 +313,13 @@ function Popup({children, label}: PopupProps) {
|
|
|
291
313
|
style={[
|
|
292
314
|
styles.popup,
|
|
293
315
|
inModal && styles.popupInModal,
|
|
316
|
+
isRaised && (inModal ? styles.popupInModalRaised : styles.popupRaised),
|
|
294
317
|
styles.popupPosition(coords.top, coords.left),
|
|
295
318
|
]}
|
|
296
319
|
>
|
|
297
|
-
<
|
|
320
|
+
<PopoverLayerContext.Provider value={raisedLayer}>
|
|
321
|
+
<ControlContextBoundary>{children}</ControlContextBoundary>
|
|
322
|
+
</PopoverLayerContext.Provider>
|
|
298
323
|
</html.div>,
|
|
299
324
|
mount ?? document.body,
|
|
300
325
|
)
|
|
@@ -5,6 +5,9 @@ import {type RefObject, createContext, useContext, useLayoutEffect} from 'react'
|
|
|
5
5
|
// on them counts as inside the Popover.
|
|
6
6
|
export interface PopoverLayerContextValue {
|
|
7
7
|
register: (el: Element) => () => void
|
|
8
|
+
/** Set by a `raised` Popup for what it holds, so a Popover nested in it is
|
|
9
|
+
* raised too and is not drawn under its parent. */
|
|
10
|
+
raised?: boolean
|
|
8
11
|
}
|
|
9
12
|
|
|
10
13
|
export const PopoverLayerContext = createContext<PopoverLayerContextValue | null>(null)
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import {useState, type ReactNode} from 'react'
|
|
2
|
+
import type {Meta, StoryObj} from '@storybook/react'
|
|
3
|
+
import {expect, userEvent, waitFor, within} from 'storybook/test'
|
|
4
|
+
import {html} from 'react-strict-dom'
|
|
5
|
+
import {LAYERS} from '@duro-app/tokens/keys'
|
|
6
|
+
import {Popover} from './Popover'
|
|
7
|
+
import {Button} from '../Button/Button'
|
|
8
|
+
import {Dialog} from '../Dialog/Dialog'
|
|
9
|
+
import {Drawer} from '../Drawer/Drawer'
|
|
10
|
+
import {Select} from '../Select/Select'
|
|
11
|
+
import {Stack} from '../Stack/Stack'
|
|
12
|
+
import {Text} from '../Text/Text'
|
|
13
|
+
import {useInModal} from '../../shared/ModalContext'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* `Popover.Popup raised`: one floating surface over another (a link editor
|
|
17
|
+
* over a selection format bar). Outside a modal it is `floatingRaised` (60),
|
|
18
|
+
* inside a Dialog or Drawer `popoverRaised` (1041). The raised Popover renders
|
|
19
|
+
* first, so without the layer the plain one, portalled after it, would be on
|
|
20
|
+
* top.
|
|
21
|
+
*/
|
|
22
|
+
const meta: Meta = {
|
|
23
|
+
title: 'Components/Popover/Raised',
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default meta
|
|
27
|
+
type Story = StoryObj
|
|
28
|
+
|
|
29
|
+
const page = () => within(document.body)
|
|
30
|
+
|
|
31
|
+
/** A small rect at the viewport's centre: every Popover here anchors to it. */
|
|
32
|
+
const centre = () => new DOMRect(window.innerWidth / 2 - 10, window.innerHeight / 2 - 10, 20, 20)
|
|
33
|
+
|
|
34
|
+
/** True when the element drawn at (x, y) is `el` or inside it. */
|
|
35
|
+
const drawnAt = (el: Element, x: number, y: number) => {
|
|
36
|
+
const hit = document.elementFromPoint(x, y)
|
|
37
|
+
return hit !== null && el.contains(hit)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const centreOf = (el: Element) => {
|
|
41
|
+
const r = el.getBoundingClientRect()
|
|
42
|
+
return [r.left + r.width / 2, r.top + r.height / 2] as const
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const zIndexOf = (el: Element) => getComputedStyle(el).zIndex
|
|
46
|
+
|
|
47
|
+
/** The format bar: wider and taller than the link editor drawn over it. */
|
|
48
|
+
function FormatBar() {
|
|
49
|
+
return (
|
|
50
|
+
<Popover.Root defaultOpen anchor={centre} align="center">
|
|
51
|
+
<Popover.Popup label="Format bar">
|
|
52
|
+
<Stack gap="md">
|
|
53
|
+
<Text>Bold · Italic · Underline · Strikethrough · Code · Link</Text>
|
|
54
|
+
<Text>Second row of formatting</Text>
|
|
55
|
+
<Text>Third row of formatting</Text>
|
|
56
|
+
</Stack>
|
|
57
|
+
</Popover.Popup>
|
|
58
|
+
</Popover.Root>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function LinkEditor({children}: {children?: ReactNode}) {
|
|
63
|
+
return (
|
|
64
|
+
<Popover.Root defaultOpen anchor={centre} align="center">
|
|
65
|
+
<Popover.Popup label="Link editor" raised>
|
|
66
|
+
<Stack gap="sm">
|
|
67
|
+
<Text>https://</Text>
|
|
68
|
+
{children}
|
|
69
|
+
</Stack>
|
|
70
|
+
</Popover.Popup>
|
|
71
|
+
</Popover.Root>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Raised first, then the plain one: DOM order alone would put the bar on top. */
|
|
76
|
+
function Pair({children}: {children?: ReactNode}) {
|
|
77
|
+
return (
|
|
78
|
+
<>
|
|
79
|
+
<LinkEditor>{children}</LinkEditor>
|
|
80
|
+
<FormatBar />
|
|
81
|
+
</>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function OpenDialog({children}: {children: ReactNode}) {
|
|
86
|
+
return (
|
|
87
|
+
<Dialog.Root open>
|
|
88
|
+
<Dialog.Portal size="sm">
|
|
89
|
+
<Dialog.Header>
|
|
90
|
+
<Dialog.Title>Editor</Dialog.Title>
|
|
91
|
+
</Dialog.Header>
|
|
92
|
+
<Dialog.Body>
|
|
93
|
+
<Stack gap="md">
|
|
94
|
+
{/* Enough body for the panel to reach under the Popovers. */}
|
|
95
|
+
{['First', 'Second', 'Third', 'Fourth', 'Fifth', 'Sixth'].map((n) => (
|
|
96
|
+
<Text key={n}>{`${n} paragraph of the document being edited.`}</Text>
|
|
97
|
+
))}
|
|
98
|
+
{children}
|
|
99
|
+
</Stack>
|
|
100
|
+
</Dialog.Body>
|
|
101
|
+
</Dialog.Portal>
|
|
102
|
+
</Dialog.Root>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function expectRaisedOverBar(layer: number) {
|
|
107
|
+
const editor = await page().findByRole('dialog', {name: 'Link editor'})
|
|
108
|
+
const bar = await page().findByRole('dialog', {name: 'Format bar'})
|
|
109
|
+
await expect(zIndexOf(editor)).toBe(String(layer))
|
|
110
|
+
await waitFor(() => expect(drawnAt(editor, ...centreOf(editor))).toBe(true))
|
|
111
|
+
// The bar is still drawn where the editor does not cover it.
|
|
112
|
+
const b = bar.getBoundingClientRect()
|
|
113
|
+
const e = editor.getBoundingClientRect()
|
|
114
|
+
await expect(b.left).toBeLessThan(e.left)
|
|
115
|
+
await expect(drawnAt(bar, b.left + 2, b.top + b.height / 2)).toBe(true)
|
|
116
|
+
return {editor, bar}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Outside a modal: the raised editor (60) is over the bar (50). */
|
|
120
|
+
export const RaisedOverFloating: Story = {
|
|
121
|
+
render: () => <Pair />,
|
|
122
|
+
play: async () => {
|
|
123
|
+
const {bar} = await expectRaisedOverBar(LAYERS.floatingRaised)
|
|
124
|
+
await expect(zIndexOf(bar)).toBe(String(LAYERS.floating))
|
|
125
|
+
},
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Inside an open Dialog: the raised editor (1041) is over the bar (1040),
|
|
129
|
+
* and both are over the Dialog panel. */
|
|
130
|
+
export const RaisedInDialog: Story = {
|
|
131
|
+
render: () => (
|
|
132
|
+
<OpenDialog>
|
|
133
|
+
<Pair />
|
|
134
|
+
</OpenDialog>
|
|
135
|
+
),
|
|
136
|
+
play: async () => {
|
|
137
|
+
const {editor, bar} = await expectRaisedOverBar(LAYERS.popoverRaised)
|
|
138
|
+
await expect(zIndexOf(bar)).toBe(String(LAYERS.popover))
|
|
139
|
+
// Both overlap the panel and are drawn there: the editor at its centre,
|
|
140
|
+
// the bar where the editor leaves it uncovered.
|
|
141
|
+
const panel = page().getByRole('dialog', {name: 'Editor'})
|
|
142
|
+
const p = panel.getBoundingClientRect()
|
|
143
|
+
const b = bar.getBoundingClientRect()
|
|
144
|
+
const e = editor.getBoundingClientRect()
|
|
145
|
+
// Under the editor, inside the bar.
|
|
146
|
+
const x = b.left + b.width / 2
|
|
147
|
+
const y = (e.bottom + b.bottom) / 2
|
|
148
|
+
await expect(x > p.left && x < p.right && y > p.top && y < p.bottom).toBe(true)
|
|
149
|
+
await expect(drawnAt(bar, x, y)).toBe(true)
|
|
150
|
+
const [ex, ey] = centreOf(editor)
|
|
151
|
+
await expect(ex > p.left && ex < p.right && ey > p.top && ey < p.bottom).toBe(true)
|
|
152
|
+
await expect(drawnAt(editor, ex, ey)).toBe(true)
|
|
153
|
+
},
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** The Settings dialog's centre once it is open, the viewport's before. */
|
|
157
|
+
const settingsCentre = () => {
|
|
158
|
+
const dialog = page().queryByRole('dialog', {name: 'Settings'})
|
|
159
|
+
if (!dialog) return centre()
|
|
160
|
+
// Just above the centre: the editor opens below its anchor, over the centre.
|
|
161
|
+
const [x, y] = centreOf(dialog)
|
|
162
|
+
return new DOMRect(x - 10, y - 30, 20, 20)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function RaisedThenDialog() {
|
|
166
|
+
const [dialogOpen, setDialogOpen] = useState(false)
|
|
167
|
+
return (
|
|
168
|
+
<>
|
|
169
|
+
<Popover.Root open anchor={settingsCentre} align="center">
|
|
170
|
+
<Popover.Popup label="Link editor" raised>
|
|
171
|
+
<Text>Stays open: controlled</Text>
|
|
172
|
+
</Popover.Popup>
|
|
173
|
+
</Popover.Root>
|
|
174
|
+
<html.button type="button" onClick={() => setDialogOpen(true)}>
|
|
175
|
+
Open dialog
|
|
176
|
+
</html.button>
|
|
177
|
+
<Dialog.Root open={dialogOpen} onOpenChange={setDialogOpen}>
|
|
178
|
+
<Dialog.Portal size="sm">
|
|
179
|
+
<Dialog.Header>
|
|
180
|
+
<Dialog.Title>Settings</Dialog.Title>
|
|
181
|
+
</Dialog.Header>
|
|
182
|
+
<Dialog.Body>
|
|
183
|
+
<Text>A modal opened over a raised Popover.</Text>
|
|
184
|
+
</Dialog.Body>
|
|
185
|
+
</Dialog.Portal>
|
|
186
|
+
</Dialog.Root>
|
|
187
|
+
</>
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** A raised Popover held open, then a Dialog: the Popover is still mounted
|
|
192
|
+
* and the Dialog covers it at the Dialog's centre. */
|
|
193
|
+
export const DialogOverRaised: Story = {
|
|
194
|
+
render: () => <RaisedThenDialog />,
|
|
195
|
+
play: async ({canvas}) => {
|
|
196
|
+
const editor = await page().findByRole('dialog', {name: 'Link editor'})
|
|
197
|
+
await userEvent.click(canvas.getByRole('button', {name: 'Open dialog'}))
|
|
198
|
+
const dialog = await page().findByRole('dialog', {name: 'Settings'})
|
|
199
|
+
await expect(editor).toBeInTheDocument()
|
|
200
|
+
// Re-place the editor against the open Dialog, so it sits under the
|
|
201
|
+
// Dialog's centre: the check below is not vacuous.
|
|
202
|
+
window.dispatchEvent(new Event('resize'))
|
|
203
|
+
await waitFor(() => {
|
|
204
|
+
const [x, y] = centreOf(dialog)
|
|
205
|
+
const e = editor.getBoundingClientRect()
|
|
206
|
+
expect(x >= e.left && x <= e.right && y >= e.top && y <= e.bottom).toBe(true)
|
|
207
|
+
})
|
|
208
|
+
const [x, y] = centreOf(dialog)
|
|
209
|
+
await waitFor(() => expect(drawnAt(dialog, x, y)).toBe(true))
|
|
210
|
+
},
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** A Popover nested in a raised one inherits `raised`: it is drawn over its
|
|
214
|
+
* parent, not at the plain layer under it. */
|
|
215
|
+
function NestedInRaised() {
|
|
216
|
+
return (
|
|
217
|
+
<Popover.Root defaultOpen anchor={centre} align="center">
|
|
218
|
+
<Popover.Popup label="Link editor" raised>
|
|
219
|
+
<Stack gap="md">
|
|
220
|
+
<Popover.Root align="center">
|
|
221
|
+
<Popover.Trigger>Open child</Popover.Trigger>
|
|
222
|
+
<Popover.Popup label="Child">
|
|
223
|
+
<Text>Child</Text>
|
|
224
|
+
</Popover.Popup>
|
|
225
|
+
</Popover.Root>
|
|
226
|
+
<Text>Parent: a raised Popover, wider than the child</Text>
|
|
227
|
+
<Text>Second row</Text>
|
|
228
|
+
<Text>Third row</Text>
|
|
229
|
+
<Text>Fourth row</Text>
|
|
230
|
+
</Stack>
|
|
231
|
+
</Popover.Popup>
|
|
232
|
+
</Popover.Root>
|
|
233
|
+
)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async function expectChildOnTop(layer: number) {
|
|
237
|
+
await userEvent.click(await page().findByRole('button', {name: 'Open child'}))
|
|
238
|
+
const child = await page().findByRole('dialog', {name: 'Child'})
|
|
239
|
+
// Non-vacuous: the child overlaps its parent.
|
|
240
|
+
const parent = page().getByRole('dialog', {name: 'Link editor'}).getBoundingClientRect()
|
|
241
|
+
const [cx, cy] = centreOf(child)
|
|
242
|
+
await expect(cx > parent.left && cx < parent.right && cy > parent.top && cy < parent.bottom).toBe(
|
|
243
|
+
true,
|
|
244
|
+
)
|
|
245
|
+
await expect(zIndexOf(child)).toBe(String(layer))
|
|
246
|
+
await waitFor(() => expect(drawnAt(child, ...centreOf(child))).toBe(true))
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export const NestedInRaised_NoModal: Story = {
|
|
250
|
+
name: 'Nested in raised, no modal',
|
|
251
|
+
render: () => <NestedInRaised />,
|
|
252
|
+
play: async () => expectChildOnTop(LAYERS.floatingRaised),
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export const NestedInRaised_InDialog: Story = {
|
|
256
|
+
name: 'Nested in raised, in a Dialog',
|
|
257
|
+
render: () => (
|
|
258
|
+
<OpenDialog>
|
|
259
|
+
<NestedInRaised />
|
|
260
|
+
</OpenDialog>
|
|
261
|
+
),
|
|
262
|
+
play: async () => expectChildOnTop(LAYERS.popoverRaised),
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/** A Select opened from a raised Popover in a Dialog: its listbox (popup,
|
|
266
|
+
* 1050) is on top. */
|
|
267
|
+
export const SelectInRaisedInDialog: Story = {
|
|
268
|
+
render: () => (
|
|
269
|
+
<OpenDialog>
|
|
270
|
+
<Pair>
|
|
271
|
+
<Select.Root defaultValue="https">
|
|
272
|
+
<Select.Trigger aria-label="Scheme">
|
|
273
|
+
<Select.Value />
|
|
274
|
+
<Select.Icon />
|
|
275
|
+
</Select.Trigger>
|
|
276
|
+
<Select.Popup>
|
|
277
|
+
<Select.Item value="https">https</Select.Item>
|
|
278
|
+
<Select.Item value="mailto">mailto</Select.Item>
|
|
279
|
+
<Select.Item value="tel">tel</Select.Item>
|
|
280
|
+
</Select.Popup>
|
|
281
|
+
</Select.Root>
|
|
282
|
+
</Pair>
|
|
283
|
+
</OpenDialog>
|
|
284
|
+
),
|
|
285
|
+
play: async () => {
|
|
286
|
+
await userEvent.click(await page().findByRole('combobox', {name: 'Scheme'}))
|
|
287
|
+
const listbox = await page().findByRole('listbox')
|
|
288
|
+
await expect(zIndexOf(listbox)).toBe(String(LAYERS.popup))
|
|
289
|
+
for (const name of ['https', 'mailto', 'tel']) {
|
|
290
|
+
const option = await page().findByRole('option', {name})
|
|
291
|
+
await waitFor(() => expect(drawnAt(option, ...centreOf(option))).toBe(true))
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function InModalProbe({label}: {label: string}) {
|
|
297
|
+
return <Text>{`${label}: ${String(useInModal())}`}</Text>
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/** `useInModal()`: true in a Dialog and a Drawer, false outside. Its export
|
|
301
|
+
* from the package root is checked by shared/exports.typecheck.ts. */
|
|
302
|
+
export const UseInModal: Story = {
|
|
303
|
+
render: () => (
|
|
304
|
+
<>
|
|
305
|
+
<InModalProbe label="page" />
|
|
306
|
+
<Button>Unrelated</Button>
|
|
307
|
+
<Dialog.Root open>
|
|
308
|
+
<Dialog.Portal size="sm">
|
|
309
|
+
<Dialog.Header>
|
|
310
|
+
<Dialog.Title>In a dialog</Dialog.Title>
|
|
311
|
+
</Dialog.Header>
|
|
312
|
+
<Dialog.Body>
|
|
313
|
+
<InModalProbe label="dialog" />
|
|
314
|
+
</Dialog.Body>
|
|
315
|
+
</Dialog.Portal>
|
|
316
|
+
</Dialog.Root>
|
|
317
|
+
<Drawer.Root open>
|
|
318
|
+
<Drawer.Portal>
|
|
319
|
+
<Drawer.Header>
|
|
320
|
+
<Drawer.Title>In a drawer</Drawer.Title>
|
|
321
|
+
</Drawer.Header>
|
|
322
|
+
<Drawer.Body>
|
|
323
|
+
<InModalProbe label="drawer" />
|
|
324
|
+
</Drawer.Body>
|
|
325
|
+
</Drawer.Portal>
|
|
326
|
+
</Drawer.Root>
|
|
327
|
+
</>
|
|
328
|
+
),
|
|
329
|
+
play: async () => {
|
|
330
|
+
await expect(await page().findByText('page: false')).toBeInTheDocument()
|
|
331
|
+
await expect(await page().findByText('dialog: true')).toBeInTheDocument()
|
|
332
|
+
await expect(await page().findByText('drawer: true')).toBeInTheDocument()
|
|
333
|
+
},
|
|
334
|
+
}
|
|
@@ -4,6 +4,7 @@ import {spacing, radii} from '@duro-app/tokens/tokens/spacing.css'
|
|
|
4
4
|
import {typography} from '@duro-app/tokens/tokens/typography.css'
|
|
5
5
|
import {shadows} from '@duro-app/tokens/tokens/shadows.css'
|
|
6
6
|
import {duration} from '@duro-app/tokens/tokens/motion.css'
|
|
7
|
+
import {sizes} from '@duro-app/tokens/tokens/sizes.css'
|
|
7
8
|
import {borders} from '@duro-app/tokens/tokens/borders.css'
|
|
8
9
|
import {layers} from '@duro-app/tokens/tokens/layers.css'
|
|
9
10
|
|
|
@@ -31,11 +32,31 @@ export const styles = css.create({
|
|
|
31
32
|
transitionProperty: 'background-color, border-color',
|
|
32
33
|
transitionDuration: duration.fast,
|
|
33
34
|
},
|
|
35
|
+
// size="small": Toggle sizeSmall's values, as longhands so they win over the
|
|
36
|
+
// trigger's own longhands. Every small control is at least controlSm tall,
|
|
37
|
+
// whatever it holds, and the touch target under a coarse pointer.
|
|
38
|
+
triggerSmall: {
|
|
39
|
+
gap: spacing.xs,
|
|
40
|
+
paddingTop: spacing.xs,
|
|
41
|
+
paddingBottom: spacing.xs,
|
|
42
|
+
paddingLeft: spacing.sm,
|
|
43
|
+
paddingRight: spacing.sm,
|
|
44
|
+
fontSize: typography.fontSizeXs,
|
|
45
|
+
minHeight: {default: sizes.controlSm, '@media (pointer: coarse)': sizes.touchTarget},
|
|
46
|
+
},
|
|
34
47
|
// Inside a Dialog or Drawer: above that modal (same portal mount), below a
|
|
35
48
|
// Select or Menu opened from inside the Popover.
|
|
36
49
|
popupInModal: {
|
|
37
50
|
zIndex: layers.popover,
|
|
38
51
|
},
|
|
52
|
+
// `raised`: over another floating surface, outside a modal (still under a
|
|
53
|
+
// Dialog or Drawer opened later) and inside one (still under its popups).
|
|
54
|
+
popupRaised: {
|
|
55
|
+
zIndex: layers.floatingRaised,
|
|
56
|
+
},
|
|
57
|
+
popupInModalRaised: {
|
|
58
|
+
zIndex: layers.popoverRaised,
|
|
59
|
+
},
|
|
39
60
|
popup: {
|
|
40
61
|
position: 'fixed',
|
|
41
62
|
top: 0,
|