@sanity/ui 2.1.15-canary.6 → 2.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/dist/index.d.mts +2 -7
- package/dist/index.d.ts +2 -7
- package/dist/index.esm.js +2964 -2988
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +2964 -2987
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2964 -2988
- package/dist/index.mjs.map +1 -1
- package/dist/theme.esm.js +478 -1283
- package/dist/theme.esm.js.map +1 -1
- package/dist/theme.js +478 -1283
- package/dist/theme.js.map +1 -1
- package/dist/theme.mjs +478 -1283
- package/dist/theme.mjs.map +1 -1
- package/package.json +39 -37
- package/src/core/components/autocomplete/autocomplete.tsx +12 -9
- package/src/core/components/dialog/dialog.tsx +32 -20
- package/src/core/components/menu/menu.tsx +6 -8
- package/src/core/components/menu/menuItem.tsx +10 -10
- package/src/core/components/tab/tab.tsx +12 -9
- package/src/core/components/tree/tree.tsx +8 -21
- package/src/core/hooks/useArrayProp.ts +10 -1
- package/src/core/hooks/useForwardedRef.ts +11 -9
- package/src/core/hooks/useIsomorphicEffect.ts +0 -1
- package/src/core/primitives/card/__workshop__/props.tsx +2 -0
- package/src/core/primitives/card/card.tsx +3 -0
- package/src/core/primitives/card/styles.ts +2 -2
- package/src/core/primitives/card/types.ts +1 -0
- package/src/core/primitives/checkbox/checkbox.tsx +5 -10
- package/src/core/primitives/popover/popover.tsx +13 -13
- package/src/core/primitives/radio/radio.tsx +3 -8
- package/src/core/primitives/select/select.tsx +3 -8
- package/src/core/primitives/switch/switch.tsx +4 -8
- package/src/core/primitives/textArea/textArea.tsx +3 -8
- package/src/core/primitives/textInput/textInput.tsx +4 -16
- package/src/core/primitives/tooltip/tooltip.tsx +5 -8
- package/src/core/utils/elementQuery/elementQuery.tsx +11 -10
- package/src/core/utils/layer/layer.tsx +13 -8
- package/src/core/utils/virtualList/virtualList.tsx +8 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/ui",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"sanity",
|
|
6
6
|
"ui",
|
|
@@ -51,6 +51,32 @@
|
|
|
51
51
|
"src",
|
|
52
52
|
"theme.js"
|
|
53
53
|
],
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "run-s clean pkg:build pkg:check figma:pkg:build",
|
|
56
|
+
"clean": "rimraf .workshop dist",
|
|
57
|
+
"commit": "cz",
|
|
58
|
+
"cypress:dev": "run-p dev cypress:open",
|
|
59
|
+
"cypress:open": "cypress open",
|
|
60
|
+
"cypress:run": "cypress run",
|
|
61
|
+
"dev": "run-p storybook:dev workshop:dev",
|
|
62
|
+
"figma:pkg:build": "cd figma && pnpm build",
|
|
63
|
+
"format": "prettier --write --cache --ignore-unknown .",
|
|
64
|
+
"lint": "eslint . --ext .cjs,.js,.jsx,.mjs,.ts,.tsx",
|
|
65
|
+
"pkg:build": "pkg build --strict",
|
|
66
|
+
"pkg:check": "pkg --strict",
|
|
67
|
+
"prepare": "husky install",
|
|
68
|
+
"prepublishOnly": "pnpm build",
|
|
69
|
+
"release": "semantic-release",
|
|
70
|
+
"storybook:build": "storybook build",
|
|
71
|
+
"storybook:dev": "storybook dev -p 6006",
|
|
72
|
+
"test": "jest",
|
|
73
|
+
"test:browser": "start-server-and-test 'run-s workshop:build workshop:start' http://localhost:1337 'run-s cypress:run'",
|
|
74
|
+
"ts:check": "tsc",
|
|
75
|
+
"watch": "pkg watch --strict",
|
|
76
|
+
"workshop:build": "workshop build",
|
|
77
|
+
"workshop:dev": "workshop dev",
|
|
78
|
+
"workshop:start": "http-server -a localhost -c-0 -p 1337 -s -P http://localhost:1337/index.html? dist"
|
|
79
|
+
},
|
|
54
80
|
"commitlint": {
|
|
55
81
|
"extends": [
|
|
56
82
|
"@commitlint/config-conventional"
|
|
@@ -93,7 +119,7 @@
|
|
|
93
119
|
"@commitlint/cli": "^19.3.0",
|
|
94
120
|
"@commitlint/config-conventional": "^19.2.2",
|
|
95
121
|
"@juggle/resize-observer": "^3.4.0",
|
|
96
|
-
"@sanity/pkg-utils": "6.8.
|
|
122
|
+
"@sanity/pkg-utils": "^6.8.18",
|
|
97
123
|
"@sanity/semantic-release-preset": "^4.1.7",
|
|
98
124
|
"@sanity/ui-workshop": "^2.0.13",
|
|
99
125
|
"@storybook/addon-a11y": "^8.0.8",
|
|
@@ -123,7 +149,6 @@
|
|
|
123
149
|
"@types/testing-library__jest-dom": "^5.14.9",
|
|
124
150
|
"@typescript-eslint/eslint-plugin": "^7.11.0",
|
|
125
151
|
"@typescript-eslint/parser": "^7.11.0",
|
|
126
|
-
"babel-plugin-react-compiler": "0.0.0-experimental-938cd9a-20240601",
|
|
127
152
|
"commitizen": "^4.3.0",
|
|
128
153
|
"cypress": "^13.10.0",
|
|
129
154
|
"cypress-real-events": "^1.12.0",
|
|
@@ -135,7 +160,7 @@
|
|
|
135
160
|
"eslint-plugin-jsx-a11y": "^6.8.0",
|
|
136
161
|
"eslint-plugin-prettier": "^5.1.3",
|
|
137
162
|
"eslint-plugin-react": "^7.34.2",
|
|
138
|
-
"eslint-plugin-react-compiler": "0.0.0-experimental-
|
|
163
|
+
"eslint-plugin-react-compiler": "0.0.0-experimental-c8b3f72-20240517",
|
|
139
164
|
"eslint-plugin-react-hooks": "^4.6.2",
|
|
140
165
|
"eslint-plugin-storybook": "^0.8.0",
|
|
141
166
|
"http-server": "^14.1.1",
|
|
@@ -146,11 +171,11 @@
|
|
|
146
171
|
"lint-staged": "^14.0.1",
|
|
147
172
|
"module-alias": "^2.2.3",
|
|
148
173
|
"npm-run-all2": "^5.0.2",
|
|
149
|
-
"prettier": "^3.
|
|
174
|
+
"prettier": "^3.3.0",
|
|
150
175
|
"prettier-plugin-packagejson": "^2.5.0",
|
|
151
|
-
"react": "
|
|
152
|
-
"react-dom": "
|
|
153
|
-
"react-is": "
|
|
176
|
+
"react": "^18.3.1",
|
|
177
|
+
"react-dom": "^18.3.1",
|
|
178
|
+
"react-is": "^18.3.1",
|
|
154
179
|
"refractor": "^4.8.1",
|
|
155
180
|
"rimraf": "^5.0.5",
|
|
156
181
|
"semantic-release": "^23.1.1",
|
|
@@ -163,11 +188,12 @@
|
|
|
163
188
|
"vite-tsconfig-paths": "^4.3.2"
|
|
164
189
|
},
|
|
165
190
|
"peerDependencies": {
|
|
166
|
-
"react": "
|
|
167
|
-
"react-dom": "
|
|
168
|
-
"react-is": "
|
|
169
|
-
"styled-components": "^6
|
|
191
|
+
"react": "^18",
|
|
192
|
+
"react-dom": "^18",
|
|
193
|
+
"react-is": "^18",
|
|
194
|
+
"styled-components": "^5.2 || ^6"
|
|
170
195
|
},
|
|
196
|
+
"packageManager": "pnpm@9.1.3",
|
|
171
197
|
"engines": {
|
|
172
198
|
"node": ">=14.0.0"
|
|
173
199
|
},
|
|
@@ -176,29 +202,5 @@
|
|
|
176
202
|
},
|
|
177
203
|
"esm.sh": {
|
|
178
204
|
"bundle": false
|
|
179
|
-
},
|
|
180
|
-
"scripts": {
|
|
181
|
-
"build": "run-s clean pkg:build pkg:check figma:pkg:build",
|
|
182
|
-
"clean": "rimraf .workshop dist",
|
|
183
|
-
"commit": "cz",
|
|
184
|
-
"cypress:dev": "run-p dev cypress:open",
|
|
185
|
-
"cypress:open": "cypress open",
|
|
186
|
-
"cypress:run": "cypress run",
|
|
187
|
-
"dev": "run-p storybook:dev workshop:dev",
|
|
188
|
-
"figma:pkg:build": "cd figma && pnpm build",
|
|
189
|
-
"format": "prettier --write --cache --ignore-unknown .",
|
|
190
|
-
"lint": "eslint . --ext .cjs,.js,.jsx,.mjs,.ts,.tsx",
|
|
191
|
-
"pkg:build": "pkg build --strict",
|
|
192
|
-
"pkg:check": "pkg --strict",
|
|
193
|
-
"release": "semantic-release",
|
|
194
|
-
"storybook:build": "storybook build",
|
|
195
|
-
"storybook:dev": "storybook dev -p 6006",
|
|
196
|
-
"test": "jest",
|
|
197
|
-
"test:browser": "start-server-and-test 'run-s workshop:build workshop:start' http://localhost:1337 'run-s cypress:run'",
|
|
198
|
-
"ts:check": "tsc",
|
|
199
|
-
"watch": "pkg watch --strict",
|
|
200
|
-
"workshop:build": "workshop build",
|
|
201
|
-
"workshop:dev": "workshop dev",
|
|
202
|
-
"workshop:start": "http-server -a localhost -c-0 -p 1337 -s -P http://localhost:1337/index.html? dist"
|
|
203
205
|
}
|
|
204
|
-
}
|
|
206
|
+
}
|
|
@@ -13,14 +13,13 @@ import {
|
|
|
13
13
|
forwardRef,
|
|
14
14
|
useCallback,
|
|
15
15
|
useEffect,
|
|
16
|
-
useImperativeHandle,
|
|
17
16
|
useMemo,
|
|
18
17
|
useReducer,
|
|
19
18
|
useRef,
|
|
20
19
|
} from 'react'
|
|
21
20
|
import {EMPTY_ARRAY, EMPTY_RECORD} from '../../constants'
|
|
22
21
|
import {_hasFocus, _raf, focusFirstDescendant} from '../../helpers'
|
|
23
|
-
import {useArrayProp} from '../../hooks'
|
|
22
|
+
import {useArrayProp, useForwardedRef} from '../../hooks'
|
|
24
23
|
import {
|
|
25
24
|
Box,
|
|
26
25
|
BoxProps,
|
|
@@ -120,7 +119,7 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
120
119
|
| 'type'
|
|
121
120
|
| 'value'
|
|
122
121
|
>,
|
|
123
|
-
|
|
122
|
+
ref: Ref<HTMLInputElement>,
|
|
124
123
|
) {
|
|
125
124
|
const {
|
|
126
125
|
border = true,
|
|
@@ -192,11 +191,7 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
192
191
|
const valuePropRef = useRef(valueProp)
|
|
193
192
|
const popoverMouseWithinRef = useRef(false)
|
|
194
193
|
|
|
195
|
-
|
|
196
|
-
useImperativeHandle<HTMLInputElement | null, HTMLInputElement | null>(
|
|
197
|
-
forwardedRef,
|
|
198
|
-
() => inputElementRef.current,
|
|
199
|
-
)
|
|
194
|
+
const forwardedRef = useForwardedRef(ref)
|
|
200
195
|
|
|
201
196
|
const listBoxId = `${id}-listbox`
|
|
202
197
|
const options = Array.isArray(optionsProp) ? optionsProp : EMPTY_ARRAY
|
|
@@ -437,6 +432,14 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
437
432
|
}
|
|
438
433
|
}, [activeValue, filteredOptions])
|
|
439
434
|
|
|
435
|
+
const setRef = useCallback(
|
|
436
|
+
(el: HTMLInputElement | null) => {
|
|
437
|
+
inputElementRef.current = el
|
|
438
|
+
forwardedRef.current = el
|
|
439
|
+
},
|
|
440
|
+
[forwardedRef],
|
|
441
|
+
)
|
|
442
|
+
|
|
440
443
|
const clearButton = useMemo(() => {
|
|
441
444
|
if (!loading && !disabled && value) {
|
|
442
445
|
return {
|
|
@@ -546,7 +549,7 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
546
549
|
prefix={prefix}
|
|
547
550
|
radius={radius}
|
|
548
551
|
readOnly={readOnly}
|
|
549
|
-
ref={
|
|
552
|
+
ref={setRef}
|
|
550
553
|
role="combobox"
|
|
551
554
|
spellCheck={false}
|
|
552
555
|
suffix={suffix || openButtonNode}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {CloseIcon} from '@sanity/icons'
|
|
2
2
|
import {ThemeColorSchemeKey} from '@sanity/ui/theme'
|
|
3
|
-
import {forwardRef, useCallback, useEffect,
|
|
3
|
+
import {forwardRef, useCallback, useEffect, useRef, useState} from 'react'
|
|
4
4
|
import {styled} from 'styled-components'
|
|
5
5
|
import {
|
|
6
6
|
containsOrEqualsElement,
|
|
@@ -8,7 +8,13 @@ import {
|
|
|
8
8
|
focusLastDescendant,
|
|
9
9
|
isHTMLElement,
|
|
10
10
|
} from '../../helpers'
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
useArrayProp,
|
|
13
|
+
useClickOutside,
|
|
14
|
+
useForwardedRef,
|
|
15
|
+
useGlobalKeyDown,
|
|
16
|
+
usePrefersReducedMotion,
|
|
17
|
+
} from '../../hooks'
|
|
12
18
|
import {Box, Button, Card, Container, Flex, Text} from '../../primitives'
|
|
13
19
|
import {ResponsivePaddingProps, ResponsiveWidthProps} from '../../primitives/types'
|
|
14
20
|
import {responsivePaddingStyle, ResponsivePaddingStyleProps} from '../../styles/internal'
|
|
@@ -145,13 +151,13 @@ const DialogFooter = styled(Box)`
|
|
|
145
151
|
|
|
146
152
|
const DialogCard = forwardRef(function DialogCard(
|
|
147
153
|
props: DialogCardProps,
|
|
148
|
-
|
|
154
|
+
ref: React.ForwardedRef<HTMLDivElement>,
|
|
149
155
|
) {
|
|
150
156
|
const {
|
|
151
157
|
__unstable_autoFocus: autoFocus,
|
|
152
158
|
__unstable_hideCloseButton: hideCloseButton,
|
|
153
159
|
children,
|
|
154
|
-
contentRef
|
|
160
|
+
contentRef,
|
|
155
161
|
footer,
|
|
156
162
|
header,
|
|
157
163
|
id,
|
|
@@ -169,29 +175,23 @@ const DialogCard = forwardRef(function DialogCard(
|
|
|
169
175
|
const radius = useArrayProp(radiusProp)
|
|
170
176
|
const shadow = useArrayProp(shadowProp)
|
|
171
177
|
const width = useArrayProp(widthProp)
|
|
172
|
-
const
|
|
178
|
+
const forwardedRef = useForwardedRef(ref)
|
|
173
179
|
const [rootElement, setRootElement] = useState<HTMLDivElement | null>(null)
|
|
174
|
-
const
|
|
180
|
+
const localContentRef = useRef<HTMLDivElement | null>(null)
|
|
175
181
|
const layer = useLayer()
|
|
176
182
|
const {isTopLayer} = layer
|
|
177
183
|
const labelId = `${id}_label`
|
|
178
184
|
const showCloseButton = Boolean(onClose) && hideCloseButton === false
|
|
179
185
|
const showHeader = Boolean(header) || showCloseButton
|
|
180
186
|
|
|
181
|
-
useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(forwardedRef, () => ref.current)
|
|
182
|
-
useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(
|
|
183
|
-
forwardedContentRef,
|
|
184
|
-
() => contentRef.current,
|
|
185
|
-
)
|
|
186
|
-
|
|
187
187
|
useEffect(() => {
|
|
188
188
|
if (!autoFocus) return
|
|
189
189
|
|
|
190
190
|
// On mount: focus the first focusable element
|
|
191
|
-
if (
|
|
192
|
-
focusFirstDescendant(
|
|
191
|
+
if (forwardedRef.current) {
|
|
192
|
+
focusFirstDescendant(forwardedRef.current)
|
|
193
193
|
}
|
|
194
|
-
}, [autoFocus,
|
|
194
|
+
}, [autoFocus, forwardedRef])
|
|
195
195
|
|
|
196
196
|
useGlobalKeyDown(
|
|
197
197
|
useCallback(
|
|
@@ -234,10 +234,22 @@ const DialogCard = forwardRef(function DialogCard(
|
|
|
234
234
|
[rootElement],
|
|
235
235
|
)
|
|
236
236
|
|
|
237
|
-
const setRef = useCallback(
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
237
|
+
const setRef = useCallback(
|
|
238
|
+
(el: HTMLDivElement | null) => {
|
|
239
|
+
setRootElement(el)
|
|
240
|
+
forwardedRef.current = el
|
|
241
|
+
},
|
|
242
|
+
[forwardedRef],
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
const setContentRef = useCallback(
|
|
246
|
+
(el: HTMLDivElement | null) => {
|
|
247
|
+
localContentRef.current = el
|
|
248
|
+
if (typeof contentRef === 'function') contentRef(el)
|
|
249
|
+
else if (contentRef) contentRef.current = el
|
|
250
|
+
},
|
|
251
|
+
[contentRef],
|
|
252
|
+
)
|
|
241
253
|
|
|
242
254
|
return (
|
|
243
255
|
<DialogContainer data-ui="DialogCard" width={width}>
|
|
@@ -269,7 +281,7 @@ const DialogCard = forwardRef(function DialogCard(
|
|
|
269
281
|
</DialogHeader>
|
|
270
282
|
)}
|
|
271
283
|
|
|
272
|
-
<DialogContent flex={1} ref={
|
|
284
|
+
<DialogContent flex={1} ref={setContentRef} tabIndex={-1}>
|
|
273
285
|
{children}
|
|
274
286
|
</DialogContent>
|
|
275
287
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {forwardRef, useCallback, useEffect,
|
|
1
|
+
import {forwardRef, useCallback, useEffect, useMemo} from 'react'
|
|
2
2
|
import {styled} from 'styled-components'
|
|
3
|
-
import {useClickOutside, useGlobalKeyDown} from '../../hooks'
|
|
3
|
+
import {useClickOutside, useForwardedRef, useGlobalKeyDown} from '../../hooks'
|
|
4
4
|
import {Box, Stack} from '../../primitives'
|
|
5
5
|
import {ResponsivePaddingProps} from '../../primitives/types'
|
|
6
6
|
import {useLayer} from '../../utils'
|
|
@@ -41,7 +41,7 @@ const Root = styled(Box)`
|
|
|
41
41
|
*/
|
|
42
42
|
export const Menu = forwardRef(function Menu(
|
|
43
43
|
props: MenuProps & Omit<React.HTMLProps<HTMLDivElement>, 'as' | 'height' | 'role' | 'tabIndex'>,
|
|
44
|
-
|
|
44
|
+
ref: React.ForwardedRef<HTMLDivElement>,
|
|
45
45
|
) {
|
|
46
46
|
const {
|
|
47
47
|
children,
|
|
@@ -62,9 +62,7 @@ export const Menu = forwardRef(function Menu(
|
|
|
62
62
|
...restProps
|
|
63
63
|
} = props
|
|
64
64
|
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(forwardedRef, () => ref.current)
|
|
65
|
+
const forwardedRef = useForwardedRef(ref)
|
|
68
66
|
|
|
69
67
|
const {isTopLayer} = useLayer()
|
|
70
68
|
|
|
@@ -82,9 +80,9 @@ export const Menu = forwardRef(function Menu(
|
|
|
82
80
|
const handleRefChange = useCallback(
|
|
83
81
|
(el: HTMLDivElement | null) => {
|
|
84
82
|
setRootElement(el)
|
|
85
|
-
|
|
83
|
+
forwardedRef.current = el
|
|
86
84
|
},
|
|
87
|
-
[setRootElement],
|
|
85
|
+
[forwardedRef, setRootElement],
|
|
88
86
|
)
|
|
89
87
|
|
|
90
88
|
// Trigger `onItemSelect` when active index changes
|
|
@@ -4,13 +4,11 @@ import {
|
|
|
4
4
|
isValidElement,
|
|
5
5
|
useCallback,
|
|
6
6
|
useEffect,
|
|
7
|
-
useImperativeHandle,
|
|
8
7
|
useMemo,
|
|
9
|
-
useRef,
|
|
10
8
|
useState,
|
|
11
9
|
} from 'react'
|
|
12
10
|
import {isValidElementType} from 'react-is'
|
|
13
|
-
import {useArrayProp} from '../../hooks'
|
|
11
|
+
import {useArrayProp, useForwardedRef} from '../../hooks'
|
|
14
12
|
import {Box, Flex, Text} from '../../primitives'
|
|
15
13
|
import {Selectable} from '../../primitives/_selectable'
|
|
16
14
|
import {ResponsivePaddingProps, ResponsiveRadiusProps} from '../../primitives/types'
|
|
@@ -78,12 +76,11 @@ export const MenuItem = forwardRef(function MenuItem(
|
|
|
78
76
|
} = menu
|
|
79
77
|
const [rootElement, setRootElement] = useState<HTMLDivElement | null>(null)
|
|
80
78
|
const active = Boolean(activeElement) && activeElement === rootElement
|
|
81
|
-
const ref = useRef<HTMLDivElement | null>(null)
|
|
82
|
-
|
|
83
|
-
useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(forwardedRef, () => ref.current)
|
|
84
79
|
|
|
85
80
|
useEffect(() => mount(rootElement, selectedProp), [mount, rootElement, selectedProp])
|
|
86
81
|
|
|
82
|
+
const ref = useForwardedRef(forwardedRef)
|
|
83
|
+
|
|
87
84
|
const handleClick = useCallback(
|
|
88
85
|
(event: React.MouseEvent<HTMLDivElement>) => {
|
|
89
86
|
if (disabled) return
|
|
@@ -108,10 +105,13 @@ export const MenuItem = forwardRef(function MenuItem(
|
|
|
108
105
|
|
|
109
106
|
const hotkeysFontSize = useArrayProp(fontSize).map((s) => s - 1)
|
|
110
107
|
|
|
111
|
-
const setRef = useCallback(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
108
|
+
const setRef = useCallback(
|
|
109
|
+
(el: HTMLDivElement | null) => {
|
|
110
|
+
ref.current = el
|
|
111
|
+
setRootElement(el)
|
|
112
|
+
},
|
|
113
|
+
[ref],
|
|
114
|
+
)
|
|
115
115
|
|
|
116
116
|
return (
|
|
117
117
|
<Selectable
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {forwardRef, useCallback, useEffect,
|
|
1
|
+
import {forwardRef, useCallback, useEffect, useRef} from 'react'
|
|
2
2
|
import {styled} from 'styled-components'
|
|
3
|
+
import {useForwardedRef} from '../../hooks'
|
|
3
4
|
import {Button} from '../../primitives'
|
|
4
5
|
import {ButtonTone} from '../../types'
|
|
5
6
|
|
|
@@ -48,14 +49,9 @@ export const Tab = forwardRef(function Tab(
|
|
|
48
49
|
selected,
|
|
49
50
|
...restProps
|
|
50
51
|
} = props
|
|
51
|
-
const
|
|
52
|
+
const elementRef = useRef<HTMLButtonElement | null>(null)
|
|
52
53
|
const focusedRef = useRef(false)
|
|
53
54
|
|
|
54
|
-
useImperativeHandle<HTMLButtonElement | null, HTMLButtonElement | null>(
|
|
55
|
-
forwardedRef,
|
|
56
|
-
() => ref.current,
|
|
57
|
-
)
|
|
58
|
-
|
|
59
55
|
const handleBlur = useCallback(() => {
|
|
60
56
|
focusedRef.current = false
|
|
61
57
|
}, [])
|
|
@@ -68,13 +64,20 @@ export const Tab = forwardRef(function Tab(
|
|
|
68
64
|
[onFocus],
|
|
69
65
|
)
|
|
70
66
|
|
|
67
|
+
const ref = useForwardedRef(forwardedRef)
|
|
68
|
+
|
|
71
69
|
useEffect(() => {
|
|
72
70
|
if (focused && !focusedRef.current) {
|
|
73
|
-
if (
|
|
71
|
+
if (elementRef.current) elementRef.current.focus()
|
|
74
72
|
focusedRef.current = true
|
|
75
73
|
}
|
|
76
74
|
}, [focused])
|
|
77
75
|
|
|
76
|
+
const setRef = (el: HTMLButtonElement | null) => {
|
|
77
|
+
elementRef.current = el
|
|
78
|
+
ref.current = el
|
|
79
|
+
}
|
|
80
|
+
|
|
78
81
|
return (
|
|
79
82
|
<CustomButton
|
|
80
83
|
data-ui="Tab"
|
|
@@ -88,7 +91,7 @@ export const Tab = forwardRef(function Tab(
|
|
|
88
91
|
onBlur={handleBlur}
|
|
89
92
|
onFocus={handleFocus}
|
|
90
93
|
padding={padding}
|
|
91
|
-
ref={
|
|
94
|
+
ref={setRef}
|
|
92
95
|
role="tab"
|
|
93
96
|
selected={selected}
|
|
94
97
|
tabIndex={selected ? 0 : -1}
|
|
@@ -1,13 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
memo,
|
|
4
|
-
useCallback,
|
|
5
|
-
useEffect,
|
|
6
|
-
useImperativeHandle,
|
|
7
|
-
useMemo,
|
|
8
|
-
useRef,
|
|
9
|
-
useState,
|
|
10
|
-
} from 'react'
|
|
1
|
+
import {forwardRef, memo, useCallback, useEffect, useMemo, useRef, useState} from 'react'
|
|
2
|
+
import {useForwardedRef} from '../../hooks'
|
|
11
3
|
import {Stack} from '../../primitives'
|
|
12
4
|
import {_findNextItemElement, _findPrevItemElement, _focusItemElement} from './helpers'
|
|
13
5
|
import {TreeContext} from './treeContext'
|
|
@@ -29,10 +21,10 @@ export const Tree = memo(
|
|
|
29
21
|
forwardRef(function Tree(
|
|
30
22
|
props: TreeProps &
|
|
31
23
|
Omit<React.HTMLProps<HTMLDivElement>, 'align' | 'as' | 'height' | 'ref' | 'role' | 'wrap'>,
|
|
32
|
-
|
|
24
|
+
ref: React.ForwardedRef<HTMLDivElement>,
|
|
33
25
|
): React.ReactElement {
|
|
34
26
|
const {children, space = 1, onFocus, ...restProps} = props
|
|
35
|
-
const
|
|
27
|
+
const forwardedRef = useForwardedRef(ref)
|
|
36
28
|
const [focusedElement, setFocusedElement] = useState<HTMLElement | null>(null)
|
|
37
29
|
const focusedElementRef = useRef(focusedElement)
|
|
38
30
|
const path: string[] = useMemo(() => [], [])
|
|
@@ -40,11 +32,6 @@ export const Tree = memo(
|
|
|
40
32
|
const [state, setState] = useState<TreeState>({})
|
|
41
33
|
const stateRef = useRef(state)
|
|
42
34
|
|
|
43
|
-
useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(
|
|
44
|
-
forwardedRef,
|
|
45
|
-
() => ref.current,
|
|
46
|
-
)
|
|
47
|
-
|
|
48
35
|
useEffect(() => {
|
|
49
36
|
focusedElementRef.current = focusedElement
|
|
50
37
|
}, [focusedElement])
|
|
@@ -207,13 +194,13 @@ export const Tree = memo(
|
|
|
207
194
|
)
|
|
208
195
|
|
|
209
196
|
useEffect(() => {
|
|
210
|
-
if (!
|
|
197
|
+
if (!forwardedRef.current) return
|
|
211
198
|
const _itemElements = Array.from(
|
|
212
|
-
|
|
199
|
+
forwardedRef.current.querySelectorAll('[data-ui="TreeItem"]'),
|
|
213
200
|
) as HTMLElement[]
|
|
214
201
|
|
|
215
202
|
setItemElements(_itemElements)
|
|
216
|
-
}, [children,
|
|
203
|
+
}, [children, forwardedRef])
|
|
217
204
|
|
|
218
205
|
return (
|
|
219
206
|
<TreeContext.Provider value={contextValue}>
|
|
@@ -223,7 +210,7 @@ export const Tree = memo(
|
|
|
223
210
|
{...restProps}
|
|
224
211
|
onFocus={handleFocus}
|
|
225
212
|
onKeyDown={handleKeyDown}
|
|
226
|
-
ref={
|
|
213
|
+
ref={forwardedRef}
|
|
227
214
|
role="tree"
|
|
228
215
|
space={space}
|
|
229
216
|
>
|
|
@@ -12,5 +12,14 @@ export function useArrayProp<T extends ArrayPropPrimitive = ArrayPropPrimitive>(
|
|
|
12
12
|
val: T | T[] | undefined,
|
|
13
13
|
defaultVal?: T[],
|
|
14
14
|
): T[] {
|
|
15
|
-
|
|
15
|
+
// JSON.stringify is fast, but it's not faster than useMemo's referencial equality check
|
|
16
|
+
const __perf_hash__ = useMemo(() => JSON.stringify(val ?? defaultVal), [defaultVal, val])
|
|
17
|
+
|
|
18
|
+
return useMemo(
|
|
19
|
+
() => _getArrayProp(val, defaultVal),
|
|
20
|
+
|
|
21
|
+
// Improve performance: Keep object identify for a given hash of the value
|
|
22
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
23
|
+
[__perf_hash__],
|
|
24
|
+
)
|
|
16
25
|
}
|
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {useRef} from 'react'
|
|
2
|
+
import {useIsomorphicEffect} from './useIsomorphicEffect'
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* @beta
|
|
5
|
-
* @deprecated use `useImperativeHandle` instead
|
|
6
|
-
* @example
|
|
7
|
-
* ```diff
|
|
8
|
-
* -const ref = useForwardedRef(forwardedRef)
|
|
9
|
-
* +const ref = useRef(null)
|
|
10
|
-
* +useImperativeHandle(forwardedRef, () => ref.current)
|
|
11
|
-
* ```
|
|
12
6
|
*/
|
|
13
7
|
export function useForwardedRef<T>(ref: React.ForwardedRef<T>): React.MutableRefObject<T | null> {
|
|
14
8
|
const innerRef = useRef<T | null>(null)
|
|
15
9
|
|
|
16
|
-
|
|
10
|
+
useIsomorphicEffect(() => {
|
|
11
|
+
if (!ref) return
|
|
12
|
+
|
|
13
|
+
if (typeof ref === 'function') {
|
|
14
|
+
ref(innerRef.current)
|
|
15
|
+
} else {
|
|
16
|
+
ref.current = innerRef.current
|
|
17
|
+
}
|
|
18
|
+
})
|
|
17
19
|
|
|
18
20
|
return innerRef
|
|
19
21
|
}
|
|
@@ -12,6 +12,7 @@ export default function PropsStory() {
|
|
|
12
12
|
const as = useSelect('As', WORKSHOP_CARD_AS_OPTIONS, 'div', 'Props')
|
|
13
13
|
const border = useBoolean('Border', false, 'Props')
|
|
14
14
|
const checkered = useBoolean('Checkered', false, 'Props')
|
|
15
|
+
const muted = useBoolean('Muted', false, 'Props')
|
|
15
16
|
const padding = useSelect('Padding', WORKSHOP_SPACE_OPTIONS, 0, 'Props')
|
|
16
17
|
const radius = useSelect('Radius', WORKSHOP_RADIUS_OPTIONS, 0, 'Props')
|
|
17
18
|
const selected = useBoolean('Selected', false, 'Props')
|
|
@@ -24,6 +25,7 @@ export default function PropsStory() {
|
|
|
24
25
|
__unstable_checkered={checkered}
|
|
25
26
|
as={as}
|
|
26
27
|
border={border}
|
|
28
|
+
muted={muted}
|
|
27
29
|
onClick={useAction('onClick')}
|
|
28
30
|
padding={padding}
|
|
29
31
|
radius={radius}
|
|
@@ -36,6 +36,7 @@ export interface CardProps
|
|
|
36
36
|
* @beta
|
|
37
37
|
*/
|
|
38
38
|
__unstable_focusRing?: boolean
|
|
39
|
+
muted?: boolean
|
|
39
40
|
pressed?: boolean
|
|
40
41
|
scheme?: ThemeColorSchemeKey
|
|
41
42
|
tone?: CardTone
|
|
@@ -67,6 +68,7 @@ export const Card = forwardRef(function Card(
|
|
|
67
68
|
borderRight,
|
|
68
69
|
borderBottom,
|
|
69
70
|
borderLeft,
|
|
71
|
+
muted,
|
|
70
72
|
pressed,
|
|
71
73
|
radius = 0,
|
|
72
74
|
scheme,
|
|
@@ -96,6 +98,7 @@ export const Card = forwardRef(function Card(
|
|
|
96
98
|
$borderLeft={useArrayProp(borderLeft)}
|
|
97
99
|
$checkered={checkered}
|
|
98
100
|
$focusRing={focusRing}
|
|
101
|
+
$muted={muted}
|
|
99
102
|
$radius={useArrayProp(radius)}
|
|
100
103
|
$shadow={useArrayProp(shadow)}
|
|
101
104
|
$tone={tone}
|
|
@@ -49,7 +49,7 @@ export function cardBaseStyle(props: CardStyleProps & ThemeProps): ReturnType<ty
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
export function cardColorStyle(props: CardStyleProps & ThemeProps): ReturnType<typeof css> {
|
|
52
|
-
const {$checkered, $focusRing} = props
|
|
52
|
+
const {$checkered, $focusRing, $muted} = props
|
|
53
53
|
const {card, color, style} = getTheme_v2(props.theme)
|
|
54
54
|
const border = {width: card.border.width, color: 'var(--card-border-color)'}
|
|
55
55
|
|
|
@@ -58,7 +58,7 @@ export function cardColorStyle(props: CardStyleProps & ThemeProps): ReturnType<t
|
|
|
58
58
|
|
|
59
59
|
${_cardColorStyle(color, color, $checkered)}
|
|
60
60
|
|
|
61
|
-
background-color: var(--card-bg-color);
|
|
61
|
+
background-color: ${$muted ? 'var(--card-muted-bg-color)' : 'var(--card-bg-color)'};
|
|
62
62
|
color: var(--card-fg-color);
|
|
63
63
|
|
|
64
64
|
/* &:is(button) */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {CheckmarkIcon, RemoveIcon} from '@sanity/icons'
|
|
2
|
-
import {forwardRef, useEffect
|
|
2
|
+
import {forwardRef, useEffect} from 'react'
|
|
3
3
|
import {styled} from 'styled-components'
|
|
4
|
-
import {useCustomValidity} from '../../hooks'
|
|
4
|
+
import {useForwardedRef, useCustomValidity} from '../../hooks'
|
|
5
5
|
import {checkboxBaseStyles, inputElementStyles} from './styles'
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -34,21 +34,16 @@ export const Checkbox = forwardRef(function Checkbox(
|
|
|
34
34
|
style,
|
|
35
35
|
...restProps
|
|
36
36
|
} = props
|
|
37
|
-
const ref =
|
|
37
|
+
const ref = useForwardedRef(forwardedRef)
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
forwardedRef,
|
|
41
|
-
() => ref.current,
|
|
42
|
-
)
|
|
39
|
+
useCustomValidity(ref, customValidity)
|
|
43
40
|
|
|
44
41
|
useEffect(() => {
|
|
45
42
|
if (ref.current) {
|
|
46
43
|
// Set the indeterminate state
|
|
47
44
|
ref.current.indeterminate = indeterminate || false
|
|
48
45
|
}
|
|
49
|
-
}, [indeterminate])
|
|
50
|
-
|
|
51
|
-
useCustomValidity(ref, customValidity)
|
|
46
|
+
}, [indeterminate, ref])
|
|
52
47
|
|
|
53
48
|
return (
|
|
54
49
|
<Root className={className} data-ui="Checkbox" style={style}>
|