@sanity/ui 2.8.19 → 2.8.21
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 +12 -10
- package/dist/index.d.ts +12 -10
- package/dist/index.esm.js +425 -161
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +424 -160
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +425 -161
- package/dist/index.mjs.map +1 -1
- package/package.json +21 -21
- package/src/core/components/autocomplete/autocomplete.tsx +42 -31
- package/src/core/components/menu/menu.tsx +12 -10
- package/src/core/components/menu/menuButton.tsx +14 -36
- package/src/core/components/toast/toastProvider.tsx +5 -6
- package/src/core/components/toast/toastState.ts +6 -0
- package/src/core/constants.ts +1 -1
- package/src/core/hooks/useArrayProp.ts +14 -9
- package/src/core/primitives/avatar/avatarStack.tsx +2 -5
- package/src/core/utils/elementQuery/elementQuery.tsx +16 -17
- package/src/core/utils/layer/layerProvider.tsx +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/ui",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.21",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"sanity",
|
|
6
6
|
"ui",
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
"@sanity/icons": "^3.4.0",
|
|
111
111
|
"csstype": "^3.1.3",
|
|
112
112
|
"framer-motion": "11.0.8",
|
|
113
|
-
"react-compiler-runtime": "19.0.0-beta-
|
|
113
|
+
"react-compiler-runtime": "19.0.0-beta-63b359f-20241101",
|
|
114
114
|
"react-refractor": "^2.2.0",
|
|
115
115
|
"use-effect-event": "^1.0.2"
|
|
116
116
|
},
|
|
@@ -122,24 +122,24 @@
|
|
|
122
122
|
"@commitlint/cli": "^19.4.0",
|
|
123
123
|
"@commitlint/config-conventional": "^19.2.2",
|
|
124
124
|
"@juggle/resize-observer": "^3.4.0",
|
|
125
|
-
"@sanity/pkg-utils": "^6.11.
|
|
125
|
+
"@sanity/pkg-utils": "^6.11.9",
|
|
126
126
|
"@sanity/prettier-config": "^1.0.2",
|
|
127
127
|
"@sanity/semantic-release-preset": "^5.0.0",
|
|
128
128
|
"@sanity/ui-workshop": "^2.0.16",
|
|
129
|
-
"@storybook/addon-a11y": "^8.4.
|
|
130
|
-
"@storybook/addon-docs": "^8.4.
|
|
131
|
-
"@storybook/addon-essentials": "^8.4.
|
|
132
|
-
"@storybook/addon-interactions": "^8.4.
|
|
133
|
-
"@storybook/addon-links": "^8.4.
|
|
134
|
-
"@storybook/addon-mdx-gfm": "^8.4.
|
|
135
|
-
"@storybook/addon-storysource": "^8.4.
|
|
136
|
-
"@storybook/addon-themes": "^8.4.
|
|
137
|
-
"@storybook/blocks": "^8.4.
|
|
138
|
-
"@storybook/manager-api": "^8.4.
|
|
139
|
-
"@storybook/react": "^8.4.
|
|
140
|
-
"@storybook/react-vite": "^8.4.
|
|
141
|
-
"@storybook/test": "^8.4.
|
|
142
|
-
"@storybook/theming": "^8.4.
|
|
129
|
+
"@storybook/addon-a11y": "^8.4.2",
|
|
130
|
+
"@storybook/addon-docs": "^8.4.2",
|
|
131
|
+
"@storybook/addon-essentials": "^8.4.2",
|
|
132
|
+
"@storybook/addon-interactions": "^8.4.2",
|
|
133
|
+
"@storybook/addon-links": "^8.4.2",
|
|
134
|
+
"@storybook/addon-mdx-gfm": "^8.4.2",
|
|
135
|
+
"@storybook/addon-storysource": "^8.4.2",
|
|
136
|
+
"@storybook/addon-themes": "^8.4.2",
|
|
137
|
+
"@storybook/blocks": "^8.4.2",
|
|
138
|
+
"@storybook/manager-api": "^8.4.2",
|
|
139
|
+
"@storybook/react": "^8.4.2",
|
|
140
|
+
"@storybook/react-vite": "^8.4.2",
|
|
141
|
+
"@storybook/test": "^8.4.2",
|
|
142
|
+
"@storybook/theming": "^8.4.2",
|
|
143
143
|
"@testing-library/dom": "^10.4.0",
|
|
144
144
|
"@testing-library/jest-dom": "^6.6.2",
|
|
145
145
|
"@testing-library/react": "^16.0.1",
|
|
@@ -154,7 +154,7 @@
|
|
|
154
154
|
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
|
155
155
|
"@typescript-eslint/parser": "^7.18.0",
|
|
156
156
|
"@vitejs/plugin-react": "^4.3.3",
|
|
157
|
-
"babel-plugin-react-compiler": "beta",
|
|
157
|
+
"babel-plugin-react-compiler": "19.0.0-beta-63b359f-20241101",
|
|
158
158
|
"commitizen": "^4.3.0",
|
|
159
159
|
"cypress": "^13.13.2",
|
|
160
160
|
"cypress-real-events": "^1.13.0",
|
|
@@ -166,9 +166,9 @@
|
|
|
166
166
|
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
167
167
|
"eslint-plugin-prettier": "^5.2.1",
|
|
168
168
|
"eslint-plugin-react": "^7.37.2",
|
|
169
|
-
"eslint-plugin-react-compiler": "beta",
|
|
169
|
+
"eslint-plugin-react-compiler": "19.0.0-beta-63b359f-20241101",
|
|
170
170
|
"eslint-plugin-react-hooks": "^5.0.0",
|
|
171
|
-
"eslint-plugin-storybook": "^0.
|
|
171
|
+
"eslint-plugin-storybook": "^0.11.0",
|
|
172
172
|
"http-server": "^14.1.1",
|
|
173
173
|
"husky": "^8.0.3",
|
|
174
174
|
"jest": "^29.7.0",
|
|
@@ -185,7 +185,7 @@
|
|
|
185
185
|
"rimraf": "^5.0.5",
|
|
186
186
|
"semantic-release": "^24.0.0",
|
|
187
187
|
"start-server-and-test": "^2.0.5",
|
|
188
|
-
"storybook": "^8.4.
|
|
188
|
+
"storybook": "^8.4.2",
|
|
189
189
|
"styled-components": "^6.1.13",
|
|
190
190
|
"tsconfig-paths": "^4.2.0",
|
|
191
191
|
"typescript": "5.6.3",
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
useMemo,
|
|
18
18
|
useReducer,
|
|
19
19
|
useRef,
|
|
20
|
+
useState,
|
|
20
21
|
} from 'react'
|
|
21
22
|
import {EMPTY_ARRAY, EMPTY_RECORD} from '../../constants'
|
|
22
23
|
import {_hasFocus, _raf, focusFirstDescendant} from '../../helpers'
|
|
@@ -184,21 +185,22 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
184
185
|
typeof filterOptionProp === 'function' ? filterOptionProp : DEFAULT_FILTER_OPTION
|
|
185
186
|
|
|
186
187
|
// Element refs
|
|
187
|
-
const
|
|
188
|
-
const
|
|
189
|
-
const
|
|
188
|
+
const [rootElement, setRootElement] = useState<HTMLDivElement | null>(null)
|
|
189
|
+
const [resultsPopoverElement, setResultsPopoverElement] = useState<HTMLDivElement | null>(null)
|
|
190
|
+
const [inputElement, setInputElement] = useState<HTMLInputElement | null>(null)
|
|
190
191
|
const listBoxElementRef = useRef<HTMLDivElement | null>(null)
|
|
191
192
|
|
|
192
193
|
// Value refs
|
|
193
194
|
const listFocusedRef = useRef(false)
|
|
194
195
|
const valueRef = useRef(value)
|
|
195
196
|
const valuePropRef = useRef(valueProp)
|
|
196
|
-
const
|
|
197
|
+
const [popoverMouseWithin, setPopoverMouseWithin] = useState(false)
|
|
197
198
|
|
|
198
199
|
// Forward ref to parent
|
|
199
200
|
useImperativeHandle<HTMLInputElement | null, HTMLInputElement | null>(
|
|
200
201
|
forwardedRef,
|
|
201
|
-
() =>
|
|
202
|
+
() => inputElement,
|
|
203
|
+
[inputElement],
|
|
202
204
|
)
|
|
203
205
|
|
|
204
206
|
const listBoxId = `${id}-listbox`
|
|
@@ -222,13 +224,13 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
222
224
|
// NOTE: This is a workaround for a bug that may happen in Chrome (clicking the scrollbar
|
|
223
225
|
// closes the results in certain situations):
|
|
224
226
|
// - Do not handle blur if the mouse is within the popover
|
|
225
|
-
if (
|
|
227
|
+
if (popoverMouseWithin) {
|
|
226
228
|
return
|
|
227
229
|
}
|
|
228
230
|
|
|
229
231
|
const elements: HTMLElement[] = (relatedElements || []).concat(
|
|
230
|
-
|
|
231
|
-
|
|
232
|
+
rootElement ? [rootElement] : [],
|
|
233
|
+
resultsPopoverElement ? [resultsPopoverElement] : [],
|
|
232
234
|
)
|
|
233
235
|
|
|
234
236
|
let focusInside = false
|
|
@@ -244,13 +246,20 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
244
246
|
|
|
245
247
|
if (focusInside === false) {
|
|
246
248
|
dispatch({type: 'root/blur'})
|
|
247
|
-
|
|
249
|
+
setPopoverMouseWithin(false)
|
|
248
250
|
if (onQueryChange) onQueryChange(null)
|
|
249
251
|
if (onBlur) onBlur(event)
|
|
250
252
|
}
|
|
251
253
|
}, 0)
|
|
252
254
|
},
|
|
253
|
-
[
|
|
255
|
+
[
|
|
256
|
+
onBlur,
|
|
257
|
+
onQueryChange,
|
|
258
|
+
popoverMouseWithin,
|
|
259
|
+
relatedElements,
|
|
260
|
+
resultsPopoverElement,
|
|
261
|
+
rootElement,
|
|
262
|
+
],
|
|
254
263
|
)
|
|
255
264
|
|
|
256
265
|
const handleRootFocus = useCallback((event: FocusEvent<HTMLDivElement>) => {
|
|
@@ -269,7 +278,7 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
269
278
|
(v: string) => {
|
|
270
279
|
dispatch({type: 'value/change', value: v})
|
|
271
280
|
|
|
272
|
-
|
|
281
|
+
setPopoverMouseWithin(false)
|
|
273
282
|
|
|
274
283
|
if (onSelect) onSelect(v)
|
|
275
284
|
|
|
@@ -278,9 +287,9 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
278
287
|
if (onChange) onChange(v)
|
|
279
288
|
if (onQueryChange) onQueryChange(null)
|
|
280
289
|
|
|
281
|
-
|
|
290
|
+
inputElement?.focus()
|
|
282
291
|
},
|
|
283
|
-
[onChange,
|
|
292
|
+
[onSelect, onChange, onQueryChange, inputElement],
|
|
284
293
|
)
|
|
285
294
|
|
|
286
295
|
const handleRootKeyDown = useCallback(
|
|
@@ -324,9 +333,9 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
324
333
|
|
|
325
334
|
if (event.key === 'Escape') {
|
|
326
335
|
dispatch({type: 'root/escape'})
|
|
327
|
-
|
|
336
|
+
setPopoverMouseWithin(false)
|
|
328
337
|
if (onQueryChange) onQueryChange(null)
|
|
329
|
-
|
|
338
|
+
inputElement?.focus()
|
|
330
339
|
|
|
331
340
|
return
|
|
332
341
|
}
|
|
@@ -338,12 +347,12 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
338
347
|
(listEl === target || listEl?.contains(target)) &&
|
|
339
348
|
!AUTOCOMPLETE_LISTBOX_IGNORE_KEYS.includes(event.key)
|
|
340
349
|
) {
|
|
341
|
-
|
|
350
|
+
inputElement?.focus()
|
|
342
351
|
|
|
343
352
|
return
|
|
344
353
|
}
|
|
345
354
|
},
|
|
346
|
-
[activeValue, filteredOptions, filteredOptionsLen, onQueryChange],
|
|
355
|
+
[activeValue, filteredOptions, filteredOptionsLen, inputElement, onQueryChange],
|
|
347
356
|
)
|
|
348
357
|
|
|
349
358
|
const handleInputChange = useCallback(
|
|
@@ -377,11 +386,11 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
377
386
|
)
|
|
378
387
|
|
|
379
388
|
const handlePopoverMouseEnter = useCallback(() => {
|
|
380
|
-
|
|
389
|
+
setPopoverMouseWithin(true)
|
|
381
390
|
}, [])
|
|
382
391
|
|
|
383
392
|
const handlePopoverMouseLeave = useCallback(() => {
|
|
384
|
-
|
|
393
|
+
setPopoverMouseWithin(false)
|
|
385
394
|
}, [])
|
|
386
395
|
|
|
387
396
|
const handleClearButtonClick = useCallback(() => {
|
|
@@ -389,8 +398,8 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
389
398
|
valueRef.current = ''
|
|
390
399
|
if (onChange) onChange('')
|
|
391
400
|
if (onQueryChange) onQueryChange(null)
|
|
392
|
-
|
|
393
|
-
}, [onChange, onQueryChange])
|
|
401
|
+
inputElement?.focus()
|
|
402
|
+
}, [inputElement, onChange, onQueryChange])
|
|
394
403
|
|
|
395
404
|
const handleClearButtonFocus = useCallback(() => {
|
|
396
405
|
dispatch({type: 'input/focus'})
|
|
@@ -482,9 +491,9 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
482
491
|
|
|
483
492
|
if (openButtonProps.onClick) openButtonProps.onClick(event)
|
|
484
493
|
|
|
485
|
-
_raf(() =>
|
|
494
|
+
_raf(() => inputElement?.focus())
|
|
486
495
|
},
|
|
487
|
-
[openButtonProps,
|
|
496
|
+
[dispatchOpen, openButtonProps, inputElement],
|
|
488
497
|
)
|
|
489
498
|
|
|
490
499
|
const openButtonNode = useMemo(
|
|
@@ -554,7 +563,7 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
554
563
|
prefix={prefix}
|
|
555
564
|
radius={radius}
|
|
556
565
|
readOnly={readOnly}
|
|
557
|
-
ref={
|
|
566
|
+
ref={setInputElement}
|
|
558
567
|
role="combobox"
|
|
559
568
|
spellCheck={false}
|
|
560
569
|
suffix={suffix || openButtonNode}
|
|
@@ -566,10 +575,10 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
566
575
|
(event: KeyboardEvent<HTMLDivElement>) => {
|
|
567
576
|
// If the focus is currently in the list, move focus to the input element
|
|
568
577
|
if (event.key === 'Tab') {
|
|
569
|
-
if (listFocused)
|
|
578
|
+
if (listFocused) inputElement?.focus()
|
|
570
579
|
}
|
|
571
580
|
},
|
|
572
|
-
[listFocused],
|
|
581
|
+
[inputElement, listFocused],
|
|
573
582
|
)
|
|
574
583
|
|
|
575
584
|
const content = useMemo(() => {
|
|
@@ -635,11 +644,11 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
635
644
|
{
|
|
636
645
|
content,
|
|
637
646
|
hidden: !expanded,
|
|
638
|
-
inputElement
|
|
647
|
+
inputElement,
|
|
639
648
|
onMouseEnter: handlePopoverMouseEnter,
|
|
640
649
|
onMouseLeave: handlePopoverMouseLeave,
|
|
641
650
|
},
|
|
642
|
-
|
|
651
|
+
{current: resultsPopoverElement},
|
|
643
652
|
)
|
|
644
653
|
}
|
|
645
654
|
|
|
@@ -661,8 +670,8 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
661
670
|
placement={AUTOCOMPLETE_POPOVER_PLACEMENT}
|
|
662
671
|
portal
|
|
663
672
|
radius={radius}
|
|
664
|
-
ref={
|
|
665
|
-
referenceElement={
|
|
673
|
+
ref={setResultsPopoverElement}
|
|
674
|
+
referenceElement={inputElement}
|
|
666
675
|
{...popover}
|
|
667
676
|
/>
|
|
668
677
|
)
|
|
@@ -672,9 +681,11 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
672
681
|
filteredOptionsLen,
|
|
673
682
|
handlePopoverMouseEnter,
|
|
674
683
|
handlePopoverMouseLeave,
|
|
684
|
+
inputElement,
|
|
675
685
|
popover,
|
|
676
686
|
radius,
|
|
677
687
|
renderPopover,
|
|
688
|
+
resultsPopoverElement,
|
|
678
689
|
])
|
|
679
690
|
|
|
680
691
|
return (
|
|
@@ -683,7 +694,7 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
683
694
|
onBlur={handleRootBlur}
|
|
684
695
|
onFocus={handleRootFocus}
|
|
685
696
|
onKeyDown={handleRootKeyDown}
|
|
686
|
-
ref={
|
|
697
|
+
ref={setRootElement}
|
|
687
698
|
>
|
|
688
699
|
{input}
|
|
689
700
|
{results}
|
|
@@ -14,19 +14,21 @@ export interface MenuProps extends ResponsivePaddingProps {
|
|
|
14
14
|
/**
|
|
15
15
|
* @deprecated Use `shouldFocus="first"` instead.
|
|
16
16
|
*/
|
|
17
|
-
focusFirst?: boolean
|
|
17
|
+
'focusFirst'?: boolean
|
|
18
18
|
/**
|
|
19
19
|
* @deprecated Use `shouldFocus="last"` instead.
|
|
20
20
|
*/
|
|
21
|
-
focusLast?: boolean
|
|
22
|
-
onClickOutside?: (event: MouseEvent) => void
|
|
23
|
-
onEscape?: () => void
|
|
24
|
-
onItemClick?: () => void
|
|
25
|
-
onItemSelect?: (index: number) => void
|
|
26
|
-
originElement?: HTMLElement | null
|
|
27
|
-
registerElement?: (el: HTMLElement) => () => void
|
|
28
|
-
shouldFocus?: 'first' | 'last' | null
|
|
29
|
-
space?: number | number[]
|
|
21
|
+
'focusLast'?: boolean
|
|
22
|
+
'onClickOutside'?: (event: MouseEvent) => void
|
|
23
|
+
'onEscape'?: () => void
|
|
24
|
+
'onItemClick'?: () => void
|
|
25
|
+
'onItemSelect'?: (index: number) => void
|
|
26
|
+
'originElement'?: HTMLElement | null
|
|
27
|
+
'registerElement'?: (el: HTMLElement) => () => void
|
|
28
|
+
'shouldFocus'?: 'first' | 'last' | null
|
|
29
|
+
'space'?: number | number[]
|
|
30
|
+
'aria-labelledby'?: string
|
|
31
|
+
'onBlurCapture'?: (event: FocusEvent) => void
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
const Root = styled(Box)`
|
|
@@ -169,7 +169,7 @@ export const MenuButton = forwardRef(function MenuButton(
|
|
|
169
169
|
}, [buttonElement, disableRestoreFocusOnClose])
|
|
170
170
|
|
|
171
171
|
const handleBlur = useCallback(
|
|
172
|
-
(event:
|
|
172
|
+
(event: FocusEvent) => {
|
|
173
173
|
const target = event.relatedTarget
|
|
174
174
|
|
|
175
175
|
if (!(target instanceof Node)) {
|
|
@@ -199,32 +199,19 @@ export const MenuButton = forwardRef(function MenuButton(
|
|
|
199
199
|
return () => setChildMenuElements((els) => els.filter((_el) => _el !== el))
|
|
200
200
|
}, [])
|
|
201
201
|
|
|
202
|
-
const menuProps: MenuProps =
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}),
|
|
213
|
-
[
|
|
214
|
-
buttonElement,
|
|
215
|
-
handleMenuClickOutside,
|
|
216
|
-
handleMenuEscape,
|
|
217
|
-
handleItemClick,
|
|
218
|
-
id,
|
|
219
|
-
handleBlur,
|
|
220
|
-
registerElement,
|
|
221
|
-
shouldFocus,
|
|
222
|
-
],
|
|
223
|
-
)
|
|
202
|
+
const menuProps: MenuProps = {
|
|
203
|
+
'aria-labelledby': id,
|
|
204
|
+
'onBlurCapture': handleBlur,
|
|
205
|
+
'onClickOutside': handleMenuClickOutside,
|
|
206
|
+
'onEscape': handleMenuEscape,
|
|
207
|
+
'onItemClick': handleItemClick,
|
|
208
|
+
'originElement': buttonElement,
|
|
209
|
+
registerElement,
|
|
210
|
+
shouldFocus,
|
|
211
|
+
}
|
|
224
212
|
|
|
225
213
|
const menu = menuProp && cloneElement(menuProp, menuProps)
|
|
226
214
|
|
|
227
|
-
const ref = useRef<HTMLButtonElement | null>(null)
|
|
228
215
|
const button = useMemo(
|
|
229
216
|
() =>
|
|
230
217
|
buttonProp &&
|
|
@@ -236,7 +223,7 @@ export const MenuButton = forwardRef(function MenuButton(
|
|
|
236
223
|
'onMouseDown': handleMouseDown,
|
|
237
224
|
'aria-haspopup': true,
|
|
238
225
|
'aria-expanded': open,
|
|
239
|
-
'ref':
|
|
226
|
+
'ref': setButtonElement,
|
|
240
227
|
'selected': buttonProp.props.selected ?? open,
|
|
241
228
|
}),
|
|
242
229
|
[buttonProp, handleButtonClick, handleButtonKeyDown, handleMouseDown, id, open],
|
|
@@ -245,19 +232,10 @@ export const MenuButton = forwardRef(function MenuButton(
|
|
|
245
232
|
// Forward button ref to parent
|
|
246
233
|
useImperativeHandle<HTMLButtonElement | null, HTMLButtonElement | null>(
|
|
247
234
|
forwardedRef,
|
|
248
|
-
() =>
|
|
235
|
+
() => buttonElement,
|
|
236
|
+
[buttonElement],
|
|
249
237
|
)
|
|
250
238
|
|
|
251
|
-
// If there's a button then we need to set the reference element to the cloned button ref
|
|
252
|
-
// and if button changes we make sure to update or remove the reference element.
|
|
253
|
-
useEffect(() => {
|
|
254
|
-
if (!button) return undefined
|
|
255
|
-
|
|
256
|
-
setButtonElement(ref.current)
|
|
257
|
-
|
|
258
|
-
return () => setButtonElement(null)
|
|
259
|
-
}, [button])
|
|
260
|
-
|
|
261
239
|
const popoverProps: MenuButtonProps['popover'] = useMemo(
|
|
262
240
|
() => ({
|
|
263
241
|
boundaryElement: deprecated_boundaryElement,
|
|
@@ -8,6 +8,7 @@ import {Box} from '../../primitives'
|
|
|
8
8
|
import {Layer} from '../../utils'
|
|
9
9
|
import {Toast} from './toast'
|
|
10
10
|
import {ToastContext} from './toastContext'
|
|
11
|
+
import {generateToastId} from './toastState'
|
|
11
12
|
import {ToastContextValue, ToastParams} from './types'
|
|
12
13
|
|
|
13
14
|
type ToastState = {
|
|
@@ -45,8 +46,6 @@ const ToastContainer = styled.div`
|
|
|
45
46
|
width: 100%;
|
|
46
47
|
`
|
|
47
48
|
|
|
48
|
-
let toastId = 0
|
|
49
|
-
|
|
50
49
|
/**
|
|
51
50
|
* @public
|
|
52
51
|
*/
|
|
@@ -60,20 +59,20 @@ export function ToastProvider(props: ToastProviderProps): React.ReactElement {
|
|
|
60
59
|
() => ({
|
|
61
60
|
initial: {
|
|
62
61
|
opacity: 0,
|
|
63
|
-
[POPOVER_MOTION_CONTENT_OPACITY_PROPERTY
|
|
62
|
+
[POPOVER_MOTION_CONTENT_OPACITY_PROPERTY]: 0,
|
|
64
63
|
y: 32,
|
|
65
64
|
scale: 0.25,
|
|
66
65
|
willChange: 'transform',
|
|
67
66
|
},
|
|
68
67
|
animate: {
|
|
69
68
|
opacity: [0, 1, 1],
|
|
70
|
-
[POPOVER_MOTION_CONTENT_OPACITY_PROPERTY
|
|
69
|
+
[POPOVER_MOTION_CONTENT_OPACITY_PROPERTY]: [0, 0, 1],
|
|
71
70
|
y: 0,
|
|
72
71
|
scale: 1,
|
|
73
72
|
},
|
|
74
73
|
exit: {
|
|
75
74
|
opacity: [1, 1, 0],
|
|
76
|
-
[POPOVER_MOTION_CONTENT_OPACITY_PROPERTY
|
|
75
|
+
[POPOVER_MOTION_CONTENT_OPACITY_PROPERTY]: [1, 0, 0],
|
|
77
76
|
scale: 0.5,
|
|
78
77
|
transition: prefersReducedMotion ? {duration: 0} : {duration: 0.2},
|
|
79
78
|
},
|
|
@@ -86,7 +85,7 @@ export function ToastProvider(props: ToastProviderProps): React.ReactElement {
|
|
|
86
85
|
// Wrap setState in startTransition to allow React to give input state updates higher priority
|
|
87
86
|
const setState: typeof _setState = (state) => startTransition(() => _setState(state))
|
|
88
87
|
|
|
89
|
-
const id = params.id ||
|
|
88
|
+
const id = params.id || generateToastId()
|
|
90
89
|
const duration = params.duration || 5000
|
|
91
90
|
|
|
92
91
|
const dismiss = () => {
|
package/src/core/constants.ts
CHANGED
|
@@ -13,7 +13,7 @@ export const EMPTY_RECORD: Record<string, never> = {}
|
|
|
13
13
|
/**
|
|
14
14
|
* @internal
|
|
15
15
|
*/
|
|
16
|
-
export const POPOVER_MOTION_CONTENT_OPACITY_PROPERTY = '--motion-content-opacity'
|
|
16
|
+
export const POPOVER_MOTION_CONTENT_OPACITY_PROPERTY = '--motion-content-opacity' as string
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Shared `framer-motion` variants used by `Popover` and `Tooltip` components.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {useState} from 'react'
|
|
2
2
|
import {_getArrayProp} from '../styles'
|
|
3
3
|
|
|
4
4
|
/** @beta */
|
|
@@ -12,14 +12,19 @@ export function useArrayProp<T extends ArrayPropPrimitive = ArrayPropPrimitive>(
|
|
|
12
12
|
val: T | T[] | undefined,
|
|
13
13
|
defaultVal?: T[],
|
|
14
14
|
): T[] {
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
const [[cachedVal, cachedHash], setCache] = useState<[T[], string]>(() => [
|
|
16
|
+
_getArrayProp(val, defaultVal),
|
|
17
|
+
JSON.stringify(val ?? defaultVal),
|
|
18
|
+
])
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
() => _getArrayProp(val, defaultVal),
|
|
20
|
+
const hash = JSON.stringify(val ?? defaultVal)
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
if (hash !== cachedHash) {
|
|
23
|
+
// If the cached hash has changed, update the cache right away.
|
|
24
|
+
// Calling setState during render is fine in this case, and preferred over a useEffect loop
|
|
25
|
+
// https://19.react.dev/learn/you-might-not-need-an-effect#adjusting-some-state-when-a-prop-changes
|
|
26
|
+
setCache([_getArrayProp(val, defaultVal), hash])
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return cachedVal
|
|
25
30
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {getTheme_v2} from '@sanity/ui/theme'
|
|
2
|
-
import {Children, cloneElement, forwardRef, isValidElement
|
|
2
|
+
import {Children, cloneElement, forwardRef, isValidElement} from 'react'
|
|
3
3
|
import {styled, css} from 'styled-components'
|
|
4
4
|
import {EMPTY_RECORD} from '../../constants'
|
|
5
5
|
import {useArrayProp} from '../../hooks'
|
|
@@ -65,10 +65,7 @@ export const AvatarStack = forwardRef(function AvatarStack(
|
|
|
65
65
|
size: sizeProp = 1,
|
|
66
66
|
...restProps
|
|
67
67
|
} = props
|
|
68
|
-
const children
|
|
69
|
-
() => Children.toArray(childrenProp).filter(isValidElement),
|
|
70
|
-
[childrenProp],
|
|
71
|
-
)
|
|
68
|
+
const children: React.ReactElement[] = Children.toArray(childrenProp).filter(isValidElement)
|
|
72
69
|
const maxLength = Math.max(maxLengthProp, 0)
|
|
73
70
|
const size = useArrayProp(sizeProp)
|
|
74
71
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {forwardRef,
|
|
1
|
+
import {forwardRef, useImperativeHandle, useMemo, useState} from 'react'
|
|
2
2
|
import {useElementSize} from '../../hooks'
|
|
3
3
|
import {useTheme_v2} from '../../theme'
|
|
4
4
|
import {findMaxBreakpoints, findMinBreakpoints} from './helpers'
|
|
@@ -21,31 +21,30 @@ export const ElementQuery = forwardRef(function ElementQuery(
|
|
|
21
21
|
forwardedRef: React.ForwardedRef<HTMLDivElement>,
|
|
22
22
|
) {
|
|
23
23
|
const theme = useTheme_v2()
|
|
24
|
-
const {children, media
|
|
24
|
+
const {children, media: _media, ...restProps} = props
|
|
25
|
+
const media = _media ?? theme.media
|
|
25
26
|
|
|
26
|
-
const ref = useRef<HTMLDivElement | null>(null)
|
|
27
27
|
const [element, setElement] = useState<HTMLDivElement | null>(null)
|
|
28
28
|
const elementSize = useElementSize(element)
|
|
29
29
|
const width = useMemo(() => elementSize?.border.width ?? window.innerWidth, [elementSize])
|
|
30
30
|
|
|
31
|
-
const max = useMemo(() =>
|
|
32
|
-
|
|
31
|
+
const max = useMemo(() => {
|
|
32
|
+
const eq = findMaxBreakpoints(media, width)
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
return eq.length ? eq.join(' ') : undefined
|
|
35
|
+
}, [media, width])
|
|
36
|
+
const min = useMemo(() => {
|
|
37
|
+
const eq = findMinBreakpoints(media, width)
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
return eq.length ? eq.join(' ') : undefined
|
|
40
|
+
}, [media, width])
|
|
41
|
+
|
|
42
|
+
useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(forwardedRef, () => element, [
|
|
43
|
+
element,
|
|
44
|
+
])
|
|
40
45
|
|
|
41
46
|
return (
|
|
42
|
-
<div
|
|
43
|
-
data-ui="ElementQuery"
|
|
44
|
-
{...restProps}
|
|
45
|
-
data-eq-max={max.length ? max.join(' ') : undefined}
|
|
46
|
-
data-eq-min={min.length ? min.join(' ') : undefined}
|
|
47
|
-
ref={setRef}
|
|
48
|
-
>
|
|
47
|
+
<div data-ui="ElementQuery" {...restProps} data-eq-max={max} data-eq-min={min} ref={setElement}>
|
|
49
48
|
{children}
|
|
50
49
|
</div>
|
|
51
50
|
)
|