@sanity/ui 1.1.0-beta.2 → 1.2.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 +1 -1
- package/dist/index.cjs.mjs +120 -0
- package/dist/index.d.ts +26 -4
- package/dist/{index.cjs → index.esm.js} +1987 -1946
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +2114 -1819
- package/dist/index.js.map +1 -1
- package/package.json +47 -44
- package/src/components/dialog/__workshop__/activate.tsx +134 -0
- package/src/components/dialog/__workshop__/index.ts +3 -0
- package/src/components/dialog/__workshop__/nested.tsx +14 -3
- package/src/components/dialog/__workshop__/panes.tsx +86 -0
- package/src/components/dialog/__workshop__/wrapped.tsx +78 -0
- package/src/components/dialog/dialog.tsx +110 -25
- package/src/components/toast/toastProvider.tsx +55 -52
- package/src/constants.ts +2 -2
- package/src/helpers/element.ts +7 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useArrayProp.ts +2 -1
- package/src/hooks/useMediaIndex/useMediaIndex.test.tsx +31 -0
- package/src/hooks/useMediaIndex/useMediaIndex.ts +98 -6
- package/src/hooks/useMounted.ts +14 -0
- package/src/hooks/usePrefersDark.hydration.test.tsx +56 -0
- package/src/hooks/usePrefersDark.test.tsx +18 -0
- package/src/hooks/usePrefersDark.ts +51 -18
- package/src/hooks/usePrefersReducedMotion.hydration.test.tsx +56 -0
- package/src/hooks/usePrefersReducedMotion.test.tsx +18 -0
- package/src/hooks/usePrefersReducedMotion.ts +62 -0
- package/src/observers/resizeObserver.ts +1 -1
- package/src/primitives/code/styles.ts +6 -0
- package/src/primitives/heading/styles.ts +6 -0
- package/src/primitives/label/styles.ts +6 -0
- package/src/primitives/popover/__workshop__/OpenOnMountStory.tsx +11 -0
- package/src/primitives/popover/__workshop__/index.ts +5 -0
- package/src/primitives/text/styles.ts +6 -0
- package/src/utils/layer/__workshop__/_debug.tsx +9 -3
- package/src/utils/layer/__workshop__/index.ts +3 -3
- package/src/utils/layer/__workshop__/multipleRoots.tsx +2 -1
- package/src/utils/layer/__workshop__/{plain.tsx → nested.tsx} +14 -4
- package/src/utils/layer/getLayerContext.test.ts +30 -0
- package/src/utils/layer/getLayerContext.ts +21 -0
- package/src/utils/layer/layer.test.tsx +1 -0
- package/src/utils/layer/layer.tsx +49 -7
- package/src/utils/layer/layerContext.ts +2 -2
- package/src/utils/layer/layerProvider.tsx +73 -12
- package/src/utils/layer/types.ts +3 -4
- package/src/utils/layer/useLayer.ts +9 -8
- package/src/utils/portal/portalContext.ts +1 -1
- package/src/utils/portal/portalProvider.tsx +1 -1
- package/dist/index.cjs.map +0 -1
- package/src/hooks/useMediaIndex/lib/media.ts +0 -88
package/package.json
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
{
|
|
2
|
-
"type": "module",
|
|
3
2
|
"name": "@sanity/ui",
|
|
4
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
5
4
|
"sideEffects": false,
|
|
6
5
|
"types": "./dist/index.d.ts",
|
|
7
6
|
"source": "./src/index.ts",
|
|
8
|
-
"module": "./dist/index.js",
|
|
9
|
-
"main": "./dist/index.
|
|
7
|
+
"module": "./dist/index.esm.js",
|
|
8
|
+
"main": "./dist/index.js",
|
|
10
9
|
"exports": {
|
|
11
10
|
".": {
|
|
12
11
|
"types": "./dist/index.d.ts",
|
|
13
12
|
"source": "./src/index.ts",
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
|
|
13
|
+
"require": "./dist/index.js",
|
|
14
|
+
"node": {
|
|
15
|
+
"import": "./dist/index.cjs.mjs",
|
|
16
|
+
"require": "./dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"import": "./dist/index.esm.js",
|
|
19
|
+
"default": "./dist/index.esm.js"
|
|
17
20
|
},
|
|
18
21
|
"./package.json": "./package.json"
|
|
19
22
|
},
|
|
@@ -41,73 +44,73 @@
|
|
|
41
44
|
"test": "jest",
|
|
42
45
|
"test:browser": "start-server-and-test 'run-s workshop:build workshop:start' http://localhost:1337 'run-s cypress:run'",
|
|
43
46
|
"watch": "pkg watch --strict",
|
|
44
|
-
"workshop:build": "node
|
|
45
|
-
"workshop:dev": "node
|
|
47
|
+
"workshop:build": "node -r esbuild-register -r tsconfig-paths/register scripts/build",
|
|
48
|
+
"workshop:dev": "node -r esbuild-register -r tsconfig-paths/register scripts/dev",
|
|
46
49
|
"workshop:start": "http-server -a localhost -c-0 -p 1337 -s -P http://localhost:1337/index.html? dist"
|
|
47
50
|
},
|
|
48
51
|
"dependencies": {
|
|
49
|
-
"@floating-ui/react-dom": "
|
|
50
|
-
"@sanity/color": "2.2.
|
|
51
|
-
"@sanity/icons": "2.2.
|
|
52
|
+
"@floating-ui/react-dom": "1.1.1",
|
|
53
|
+
"@sanity/color": "^2.2.2",
|
|
54
|
+
"@sanity/icons": "^2.2.2",
|
|
52
55
|
"csstype": "^3.1.1",
|
|
53
|
-
"framer-motion": "^
|
|
56
|
+
"framer-motion": "^8.5.2",
|
|
54
57
|
"react-refractor": "^2.1.7"
|
|
55
58
|
},
|
|
56
59
|
"devDependencies": {
|
|
57
|
-
"@babel/core": "^7.20.
|
|
60
|
+
"@babel/core": "^7.20.12",
|
|
58
61
|
"@babel/preset-env": "^7.20.2",
|
|
59
62
|
"@babel/preset-react": "^7.18.6",
|
|
60
63
|
"@babel/preset-typescript": "^7.18.6",
|
|
61
|
-
"@commitlint/cli": "^17.
|
|
62
|
-
"@commitlint/config-conventional": "^17.
|
|
64
|
+
"@commitlint/cli": "^17.4.2",
|
|
65
|
+
"@commitlint/config-conventional": "^17.4.2",
|
|
63
66
|
"@juggle/resize-observer": "^3.4.0",
|
|
64
|
-
"@sanity/pkg-utils": "2.
|
|
65
|
-
"@sanity/semantic-release-preset": "^
|
|
66
|
-
"@sanity/ui-workshop": "^1.0
|
|
67
|
-
"@testing-library/dom": "^8.
|
|
67
|
+
"@sanity/pkg-utils": "^2.2.3",
|
|
68
|
+
"@sanity/semantic-release-preset": "^4.0.0",
|
|
69
|
+
"@sanity/ui-workshop": "^1.2.0",
|
|
70
|
+
"@testing-library/dom": "^8.20.0",
|
|
68
71
|
"@testing-library/jest-dom": "^5.16.5",
|
|
69
72
|
"@testing-library/react": "^13.4.0",
|
|
70
|
-
"@types/jest": "^29.2.
|
|
73
|
+
"@types/jest": "^29.2.6",
|
|
71
74
|
"@types/jest-axe": "^3.5.5",
|
|
72
|
-
"@types/node": "^18.11.
|
|
73
|
-
"@types/react": "^18.0.
|
|
74
|
-
"@types/react-dom": "^18.0.
|
|
75
|
+
"@types/node": "^18.11.18",
|
|
76
|
+
"@types/react": "^18.0.27",
|
|
77
|
+
"@types/react-dom": "^18.0.10",
|
|
75
78
|
"@types/react-is": "^17.0.3",
|
|
76
79
|
"@types/refractor": "^3.0.2",
|
|
77
80
|
"@types/styled-components": "^5.1.26",
|
|
78
81
|
"@types/testing-library__jest-dom": "^5.14.5",
|
|
79
|
-
"@typescript-eslint/eslint-plugin": "^5.
|
|
80
|
-
"@typescript-eslint/parser": "^5.
|
|
81
|
-
"commitizen": "^4.
|
|
82
|
-
"cypress": "^
|
|
83
|
-
"cypress-real-events": "^1.7.
|
|
82
|
+
"@typescript-eslint/eslint-plugin": "^5.49.0",
|
|
83
|
+
"@typescript-eslint/parser": "^5.49.0",
|
|
84
|
+
"commitizen": "^4.3.0",
|
|
85
|
+
"cypress": "^12.4.0",
|
|
86
|
+
"cypress-real-events": "^1.7.6",
|
|
84
87
|
"cz-conventional-changelog": "^3.3.0",
|
|
85
|
-
"esbuild": "^0.
|
|
88
|
+
"esbuild": "^0.17.4",
|
|
86
89
|
"esbuild-register": "^3.4.2",
|
|
87
|
-
"eslint": "^8.
|
|
88
|
-
"eslint-config-prettier": "^8.
|
|
89
|
-
"eslint-plugin-import": "^2.
|
|
90
|
-
"eslint-plugin-jsx-a11y": "^6.
|
|
90
|
+
"eslint": "^8.32.0",
|
|
91
|
+
"eslint-config-prettier": "^8.6.0",
|
|
92
|
+
"eslint-plugin-import": "^2.27.5",
|
|
93
|
+
"eslint-plugin-jsx-a11y": "^6.7.1",
|
|
91
94
|
"eslint-plugin-prettier": "^4.2.1",
|
|
92
|
-
"eslint-plugin-react": "^7.
|
|
95
|
+
"eslint-plugin-react": "^7.32.1",
|
|
93
96
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
94
97
|
"http-server": "^14.1.1",
|
|
95
|
-
"husky": "^8.0.
|
|
96
|
-
"jest": "^29.
|
|
98
|
+
"husky": "^8.0.3",
|
|
99
|
+
"jest": "^29.4.0",
|
|
97
100
|
"jest-axe": "^7.0.0",
|
|
98
|
-
"jest-environment-jsdom": "^29.
|
|
101
|
+
"jest-environment-jsdom": "^29.4.0",
|
|
99
102
|
"lint-staged": "^13.1.0",
|
|
100
103
|
"module-alias": "^2.2.2",
|
|
101
104
|
"npm-run-all": "^4.1.5",
|
|
102
|
-
"prettier": "^2.8.
|
|
105
|
+
"prettier": "^2.8.3",
|
|
103
106
|
"react": "^18.2.0",
|
|
104
107
|
"react-dom": "^18.2.0",
|
|
105
108
|
"react-is": "^18.2.0",
|
|
106
|
-
"rimraf": "^
|
|
107
|
-
"semantic-release": "^
|
|
108
|
-
"start-server-and-test": "^1.15.
|
|
109
|
+
"rimraf": "^4.1.2",
|
|
110
|
+
"semantic-release": "^20.0.4",
|
|
111
|
+
"start-server-and-test": "^1.15.3",
|
|
109
112
|
"styled-components": "^5.3.6",
|
|
110
|
-
"tsconfig-paths": "^4.1.
|
|
113
|
+
"tsconfig-paths": "^4.1.2",
|
|
111
114
|
"typescript": "^4.9.4"
|
|
112
115
|
},
|
|
113
116
|
"peerDependencies": {
|
|
@@ -143,7 +146,7 @@
|
|
|
143
146
|
"prettier --write --cache --ignore-unknown"
|
|
144
147
|
]
|
|
145
148
|
},
|
|
146
|
-
"packageManager": "pnpm@7.
|
|
149
|
+
"packageManager": "pnpm@7.26.0",
|
|
147
150
|
"publishConfig": {
|
|
148
151
|
"access": "public"
|
|
149
152
|
},
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import {useState} from 'react'
|
|
2
|
+
import {Stack, Flex, Button, Text} from '../../../primitives'
|
|
3
|
+
import {Layer, LayerProvider} from '../../../utils'
|
|
4
|
+
import {Menu, MenuButton, MenuItem} from '../../menu'
|
|
5
|
+
import {Dialog} from '../dialog'
|
|
6
|
+
|
|
7
|
+
export default function LayeringFocusStory() {
|
|
8
|
+
const [firstDialogOpen, setFirstDialogOpen] = useState<boolean>(false)
|
|
9
|
+
const [secondDialogOpen, setSecondDialogOpen] = useState<boolean>(false)
|
|
10
|
+
const [thirdDialogOpen, setThirdDialogOpen] = useState<boolean>(false)
|
|
11
|
+
const [fourthDialogOpen, setFourthDialogOpen] = useState<boolean>(false)
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<LayerProvider>
|
|
15
|
+
<Flex align="center" height="fill" justify="center">
|
|
16
|
+
<Layer onActivate={({activeElement}) => activeElement?.focus()}>
|
|
17
|
+
<Button
|
|
18
|
+
text="Open dialog 1"
|
|
19
|
+
onClick={() => setFirstDialogOpen(true)}
|
|
20
|
+
tone="primary"
|
|
21
|
+
id="open-dialog-1-button"
|
|
22
|
+
/>
|
|
23
|
+
|
|
24
|
+
{firstDialogOpen && (
|
|
25
|
+
<Dialog
|
|
26
|
+
header="Dialog 1"
|
|
27
|
+
id="1"
|
|
28
|
+
onActivate={({activeElement}) => activeElement?.focus()}
|
|
29
|
+
onClose={() => setFirstDialogOpen(false)}
|
|
30
|
+
>
|
|
31
|
+
<Stack space={2} padding={3}>
|
|
32
|
+
<Button
|
|
33
|
+
mode="ghost"
|
|
34
|
+
text="Open dialog 2"
|
|
35
|
+
onClick={() => setSecondDialogOpen(true)}
|
|
36
|
+
id="open-dialog-2-button-1"
|
|
37
|
+
/>
|
|
38
|
+
<Button
|
|
39
|
+
mode="ghost"
|
|
40
|
+
text="Open dialog 2"
|
|
41
|
+
onClick={() => setSecondDialogOpen(true)}
|
|
42
|
+
id="open-dialog-2-button-2"
|
|
43
|
+
/>
|
|
44
|
+
<Button
|
|
45
|
+
mode="ghost"
|
|
46
|
+
text="Open dialog 2"
|
|
47
|
+
onClick={() => setSecondDialogOpen(true)}
|
|
48
|
+
id="open-dialog-2-button-3"
|
|
49
|
+
/>
|
|
50
|
+
</Stack>
|
|
51
|
+
|
|
52
|
+
{secondDialogOpen && (
|
|
53
|
+
<Dialog
|
|
54
|
+
header="Dialog 2"
|
|
55
|
+
id="2"
|
|
56
|
+
onActivate={({activeElement}) => activeElement?.focus()}
|
|
57
|
+
onClose={() => setSecondDialogOpen(false)}
|
|
58
|
+
>
|
|
59
|
+
<Stack space={2} padding={3}>
|
|
60
|
+
<Button
|
|
61
|
+
mode="ghost"
|
|
62
|
+
text="Open dialog 3"
|
|
63
|
+
onClick={() => setThirdDialogOpen(true)}
|
|
64
|
+
id="open-dialog-3-button-1"
|
|
65
|
+
/>
|
|
66
|
+
<Button
|
|
67
|
+
mode="ghost"
|
|
68
|
+
text="Open dialog 3"
|
|
69
|
+
onClick={() => setThirdDialogOpen(true)}
|
|
70
|
+
id="open-dialog-3-button-2"
|
|
71
|
+
/>
|
|
72
|
+
<Button
|
|
73
|
+
mode="ghost"
|
|
74
|
+
text="Open dialog 3"
|
|
75
|
+
onClick={() => setThirdDialogOpen(true)}
|
|
76
|
+
id="open-dialog-3-button-3"
|
|
77
|
+
/>
|
|
78
|
+
</Stack>
|
|
79
|
+
|
|
80
|
+
{thirdDialogOpen && (
|
|
81
|
+
<Dialog
|
|
82
|
+
header="Dialog 3"
|
|
83
|
+
id="3"
|
|
84
|
+
onActivate={({activeElement}) => activeElement?.focus()}
|
|
85
|
+
onClose={() => setThirdDialogOpen(false)}
|
|
86
|
+
>
|
|
87
|
+
<Stack space={2} padding={3}>
|
|
88
|
+
<MenuButton
|
|
89
|
+
id="menu"
|
|
90
|
+
button={
|
|
91
|
+
<Button mode="ghost" text="Open menu" id="open-dialog-4-menu-button" />
|
|
92
|
+
}
|
|
93
|
+
menu={
|
|
94
|
+
<Menu>
|
|
95
|
+
<MenuItem
|
|
96
|
+
text="Open dialog 4"
|
|
97
|
+
onClick={() => setFourthDialogOpen(true)}
|
|
98
|
+
/>
|
|
99
|
+
<MenuItem
|
|
100
|
+
text="Open dialog 4"
|
|
101
|
+
onClick={() => setFourthDialogOpen(true)}
|
|
102
|
+
/>
|
|
103
|
+
<MenuItem
|
|
104
|
+
text="Open dialog 4"
|
|
105
|
+
onClick={() => setFourthDialogOpen(true)}
|
|
106
|
+
/>
|
|
107
|
+
</Menu>
|
|
108
|
+
}
|
|
109
|
+
/>
|
|
110
|
+
</Stack>
|
|
111
|
+
|
|
112
|
+
{fourthDialogOpen && (
|
|
113
|
+
<Dialog
|
|
114
|
+
header="Dialog 4"
|
|
115
|
+
id="4"
|
|
116
|
+
onActivate={({activeElement}) => activeElement?.focus()}
|
|
117
|
+
onClose={() => setFourthDialogOpen(false)}
|
|
118
|
+
>
|
|
119
|
+
<Stack space={2} padding={3}>
|
|
120
|
+
<Text>👋</Text>
|
|
121
|
+
</Stack>
|
|
122
|
+
</Dialog>
|
|
123
|
+
)}
|
|
124
|
+
</Dialog>
|
|
125
|
+
)}
|
|
126
|
+
</Dialog>
|
|
127
|
+
)}
|
|
128
|
+
</Dialog>
|
|
129
|
+
)}
|
|
130
|
+
</Layer>
|
|
131
|
+
</Flex>
|
|
132
|
+
</LayerProvider>
|
|
133
|
+
)
|
|
134
|
+
}
|
|
@@ -12,5 +12,8 @@ export default defineScope({
|
|
|
12
12
|
{name: 'position', title: 'Position', component: lazy(() => import('./position'))},
|
|
13
13
|
{name: 'provider', title: 'Provider', component: lazy(() => import('./provider'))},
|
|
14
14
|
{name: 'auto-focus', title: 'AutoFocus', component: lazy(() => import('./autoFocus'))},
|
|
15
|
+
{name: 'panes', title: 'Panes', component: lazy(() => import('./panes'))},
|
|
16
|
+
{name: 'activate', title: 'Activate', component: lazy(() => import('./activate'))},
|
|
17
|
+
{name: 'wrapped', title: 'Wrapped', component: lazy(() => import('./wrapped'))},
|
|
15
18
|
],
|
|
16
19
|
})
|
|
@@ -1,23 +1,34 @@
|
|
|
1
1
|
import {Box, Button, Dialog, LayerProvider, Menu, MenuButton, MenuItem} from '@sanity/ui'
|
|
2
|
-
import {useState} from 'react'
|
|
2
|
+
import {useEffect, useRef, useState} from 'react'
|
|
3
3
|
|
|
4
4
|
export default function NestedStory() {
|
|
5
5
|
const [open1] = useState(true)
|
|
6
6
|
const [open2, setOpen2] = useState(false)
|
|
7
7
|
const [open3, setOpen3] = useState(false)
|
|
8
8
|
|
|
9
|
+
const dialog2Button = useRef<HTMLButtonElement>(null)
|
|
10
|
+
const dialog3Button = useRef<HTMLButtonElement>(null)
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (!open2) dialog2Button.current?.focus()
|
|
14
|
+
}, [open2])
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (!open3) dialog3Button.current?.focus()
|
|
18
|
+
}, [open3])
|
|
19
|
+
|
|
9
20
|
return (
|
|
10
21
|
<LayerProvider>
|
|
11
22
|
{open1 && (
|
|
12
23
|
<Dialog cardShadow={1} header="Dialog 1" id="dialog1">
|
|
13
24
|
<Box padding={4}>
|
|
14
|
-
<Button onClick={() => setOpen2(true)} text="Open Dialog 2" />
|
|
25
|
+
<Button onClick={() => setOpen2(true)} ref={dialog2Button} text="Open Dialog 2" />
|
|
15
26
|
</Box>
|
|
16
27
|
|
|
17
28
|
{open2 && (
|
|
18
29
|
<Dialog cardShadow={2} header="Dialog 2" id="dialog2" onClose={() => setOpen2(false)}>
|
|
19
30
|
<Box padding={4}>
|
|
20
|
-
<Button onClick={() => setOpen3(true)} text="Open Dialog 3" />
|
|
31
|
+
<Button onClick={() => setOpen3(true)} ref={dialog3Button} text="Open Dialog 3" />
|
|
21
32
|
</Box>
|
|
22
33
|
|
|
23
34
|
{open3 && (
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import {useCallback, useState} from 'react'
|
|
2
|
+
import styled from 'styled-components'
|
|
3
|
+
import {Box, Button, Card, Flex} from '../../../primitives'
|
|
4
|
+
import {BoundaryElementProvider, PortalProvider} from '../../../utils'
|
|
5
|
+
import {Menu, MenuButton, MenuItem} from '../../menu'
|
|
6
|
+
import {Dialog} from '../dialog'
|
|
7
|
+
|
|
8
|
+
export default function PanesStory() {
|
|
9
|
+
return (
|
|
10
|
+
<Flex height="fill">
|
|
11
|
+
<Pane id="A" />
|
|
12
|
+
<Pane id="B" borderLeft />
|
|
13
|
+
</Flex>
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const PaneRoot = styled(Card)`
|
|
18
|
+
position: relative;
|
|
19
|
+
`
|
|
20
|
+
|
|
21
|
+
const PanePortal = styled.div`
|
|
22
|
+
position: absolute;
|
|
23
|
+
left: 0;
|
|
24
|
+
right: 0;
|
|
25
|
+
top: 0;
|
|
26
|
+
bottom: 0;
|
|
27
|
+
pointer-events: none;
|
|
28
|
+
|
|
29
|
+
& > * {
|
|
30
|
+
pointer-events: auto;
|
|
31
|
+
}
|
|
32
|
+
`
|
|
33
|
+
|
|
34
|
+
function Pane(props: {borderLeft?: boolean; id: string}) {
|
|
35
|
+
const {borderLeft, id} = props
|
|
36
|
+
const [element, setElement] = useState<HTMLDivElement | null>(null)
|
|
37
|
+
const [portalElement, setPortalElement] = useState<HTMLDivElement | null>(null)
|
|
38
|
+
const [dialogOpen, setDialogOpen] = useState(false)
|
|
39
|
+
|
|
40
|
+
const handleClose = useCallback(() => {
|
|
41
|
+
setDialogOpen(false)
|
|
42
|
+
}, [])
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<BoundaryElementProvider element={element}>
|
|
46
|
+
<PortalProvider element={portalElement}>
|
|
47
|
+
<PaneRoot borderLeft={borderLeft} flex={1} ref={setElement}>
|
|
48
|
+
<Box padding={4}>
|
|
49
|
+
<Button
|
|
50
|
+
onClick={() => setDialogOpen(true)}
|
|
51
|
+
selected={dialogOpen}
|
|
52
|
+
text="Open dialog"
|
|
53
|
+
tone="primary"
|
|
54
|
+
/>
|
|
55
|
+
</Box>
|
|
56
|
+
|
|
57
|
+
<PanePortal ref={setPortalElement} />
|
|
58
|
+
</PaneRoot>
|
|
59
|
+
|
|
60
|
+
{dialogOpen && (
|
|
61
|
+
<Dialog
|
|
62
|
+
header={`Dialog ${id}`}
|
|
63
|
+
id={id}
|
|
64
|
+
onClickOutside={handleClose}
|
|
65
|
+
onClose={handleClose}
|
|
66
|
+
position="absolute"
|
|
67
|
+
>
|
|
68
|
+
<Box padding={4}>
|
|
69
|
+
<MenuButton
|
|
70
|
+
button={<Button text="Open menu" tone="primary" />}
|
|
71
|
+
id={`${id}-menu`}
|
|
72
|
+
menu={
|
|
73
|
+
<Menu>
|
|
74
|
+
<MenuItem text="Item 1" />
|
|
75
|
+
<MenuItem text="Item 2" />
|
|
76
|
+
<MenuItem text="Item 3" />
|
|
77
|
+
</Menu>
|
|
78
|
+
}
|
|
79
|
+
/>
|
|
80
|
+
</Box>
|
|
81
|
+
</Dialog>
|
|
82
|
+
)}
|
|
83
|
+
</PortalProvider>
|
|
84
|
+
</BoundaryElementProvider>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import {ReactNode, useCallback, useEffect, useRef, useState} from 'react'
|
|
2
|
+
import {Box, Button} from '../../../primitives'
|
|
3
|
+
import {LayerProvider, useLayer} from '../../../utils'
|
|
4
|
+
import {Dialog, DialogProps} from '../dialog'
|
|
5
|
+
|
|
6
|
+
export default function WrappedStory() {
|
|
7
|
+
return (
|
|
8
|
+
<LayerProvider zOffset={100}>
|
|
9
|
+
<Box padding={4}>
|
|
10
|
+
<DialogButton level={0} />
|
|
11
|
+
</Box>
|
|
12
|
+
</LayerProvider>
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function DialogButton(props: {level: number}) {
|
|
17
|
+
const {level} = props
|
|
18
|
+
const [open, setOpen] = useState(false)
|
|
19
|
+
|
|
20
|
+
const openRef = useRef(open)
|
|
21
|
+
const buttonRef = useRef<HTMLButtonElement>(null)
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (openRef.current !== open) {
|
|
25
|
+
openRef.current = open
|
|
26
|
+
|
|
27
|
+
if (!open) {
|
|
28
|
+
// focus the button that opened the dialog
|
|
29
|
+
// buttonRef.current?.focus()
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}, [open])
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<>
|
|
36
|
+
<Button onClick={() => setOpen(true)} ref={buttonRef} text={`Open dialog ${level + 1}`} />
|
|
37
|
+
|
|
38
|
+
{open && (
|
|
39
|
+
<WrappedDialog
|
|
40
|
+
header={`WrappedDialog ${level + 1}`}
|
|
41
|
+
id={`wrapped-${level + 1}`}
|
|
42
|
+
onClickOutside={() => setOpen(false)}
|
|
43
|
+
onClose={() => setOpen(false)}
|
|
44
|
+
>
|
|
45
|
+
<Box padding={4}>
|
|
46
|
+
<DialogButton level={level + 1} />
|
|
47
|
+
</Box>
|
|
48
|
+
</WrappedDialog>
|
|
49
|
+
)}
|
|
50
|
+
</>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function WrappedDialog(props: DialogProps & {children?: ReactNode}) {
|
|
55
|
+
const layer = useLayer()
|
|
56
|
+
const isTopLayer = layer.size === 1
|
|
57
|
+
|
|
58
|
+
const dialogRef = useRef<HTMLDivElement>(null)
|
|
59
|
+
|
|
60
|
+
const [lastFocusedElement, setLastFocusedElement] = useState<HTMLElement | null>(null)
|
|
61
|
+
|
|
62
|
+
const handleContentFocus = useCallback(() => {
|
|
63
|
+
const containsActiveElement = dialogRef.current?.contains(document.activeElement)
|
|
64
|
+
|
|
65
|
+
if (containsActiveElement) {
|
|
66
|
+
setLastFocusedElement(document.activeElement as HTMLElement)
|
|
67
|
+
}
|
|
68
|
+
}, [])
|
|
69
|
+
|
|
70
|
+
// Set focus on the last focused element when the dialog becomes the top layer.
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
if (isTopLayer && lastFocusedElement) {
|
|
73
|
+
lastFocusedElement.focus()
|
|
74
|
+
}
|
|
75
|
+
}, [isTopLayer, lastFocusedElement])
|
|
76
|
+
|
|
77
|
+
return <Dialog {...props} data-size={layer.size} onFocus={handleContentFocus} ref={dialogRef} />
|
|
78
|
+
}
|