@sanity/ui 3.0.9 → 3.0.11-canary.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/_chunks-cjs/_visual-editing.js +82 -64
- package/dist/_chunks-cjs/_visual-editing.js.map +1 -1
- package/dist/_chunks-es/_visual-editing.mjs +82 -64
- package/dist/_chunks-es/_visual-editing.mjs.map +1 -1
- package/dist/index.js +308 -98
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +310 -100
- package/dist/index.mjs.map +1 -1
- package/package.json +31 -45
- package/src/core/components/autocomplete/autocomplete.tsx +79 -47
- package/src/core/components/menu/menu.test.tsx +11 -14
- package/src/core/components/menu/menu.tsx +13 -18
- package/src/core/components/menu/menuContext.ts +3 -14
- package/src/core/components/menu/menuGroup.tsx +1 -1
- package/src/core/components/menu/menuItem.tsx +2 -2
- package/src/core/components/menu/useMenu.ts +2 -2
- package/src/core/components/menu/useMenuController.ts +5 -5
- package/src/core/components/tree/treeItem.tsx +17 -15
- package/src/core/hooks/useClickOutside.test.tsx +1 -1
- package/src/core/lib/createGlobalScopedContext.ts +3 -1
- package/src/core/primitives/avatar/avatar.tsx +1 -0
- package/src/core/primitives/card/card.tsx +1 -0
- package/src/core/primitives/popover/types.ts +2 -1
- package/src/core/styles/font/responsiveFont.ts +2 -0
- package/src/core/theme/__workshop__/canvas.tsx +1 -0
- package/src/core/utils/portal/portalProvider.tsx +2 -34
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/ui",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.11-canary.0",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"sanity",
|
|
6
6
|
"ui",
|
|
@@ -60,32 +60,6 @@
|
|
|
60
60
|
"exports",
|
|
61
61
|
"src"
|
|
62
62
|
],
|
|
63
|
-
"scripts": {
|
|
64
|
-
"build": "run-s clean pkg:build pkg:check figma:pkg:build",
|
|
65
|
-
"clean": "rimraf .workshop dist",
|
|
66
|
-
"commit": "cz",
|
|
67
|
-
"cypress:dev": "run-p dev cypress:open",
|
|
68
|
-
"cypress:open": "cypress open",
|
|
69
|
-
"cypress:run": "cypress run",
|
|
70
|
-
"dev": "run-p storybook:dev workshop:dev",
|
|
71
|
-
"figma:pkg:build": "cd figma && pnpm build",
|
|
72
|
-
"format": "prettier --write --cache --ignore-unknown .",
|
|
73
|
-
"lint": "eslint .",
|
|
74
|
-
"prepack": "pnpm build",
|
|
75
|
-
"pkg:build": "pkg build --strict",
|
|
76
|
-
"pkg:check": "pkg --strict",
|
|
77
|
-
"prepare": "husky install",
|
|
78
|
-
"release": "semantic-release",
|
|
79
|
-
"storybook:build": "storybook build",
|
|
80
|
-
"storybook:dev": "storybook dev -p 6006",
|
|
81
|
-
"test": "jest",
|
|
82
|
-
"test:browser": "start-server-and-test 'run-s workshop:build workshop:start' http://localhost:1337 'run-s cypress:run'",
|
|
83
|
-
"ts:check": "tsc",
|
|
84
|
-
"watch": "pkg watch --strict",
|
|
85
|
-
"workshop:build": "workshop build",
|
|
86
|
-
"workshop:dev": "workshop dev",
|
|
87
|
-
"workshop:start": "http-server -a localhost -c-0 -p 1337 -s -P http://localhost:1337/index.html? dist"
|
|
88
|
-
},
|
|
89
63
|
"commitlint": {
|
|
90
64
|
"extends": [
|
|
91
65
|
"@commitlint/config-conventional"
|
|
@@ -156,7 +130,7 @@
|
|
|
156
130
|
"@testing-library/user-event": "^14.6.1",
|
|
157
131
|
"@types/jest": "^30.0.0",
|
|
158
132
|
"@types/jest-axe": "^3.5.9",
|
|
159
|
-
"@types/node": "^22.18.
|
|
133
|
+
"@types/node": "^22.18.1",
|
|
160
134
|
"@types/react": "^19.1.11",
|
|
161
135
|
"@types/react-dom": "^19.1.8",
|
|
162
136
|
"@types/react-is": "^19.0.0",
|
|
@@ -169,13 +143,13 @@
|
|
|
169
143
|
"cz-conventional-changelog": "^3.3.0",
|
|
170
144
|
"eslint": "^9.34.0",
|
|
171
145
|
"eslint-config-prettier": "^10.1.8",
|
|
172
|
-
"eslint-formatter-
|
|
146
|
+
"eslint-formatter-gha": "^1.6.0",
|
|
173
147
|
"eslint-plugin-boundaries": "^5.0.1",
|
|
174
148
|
"eslint-plugin-import": "^2.32.0",
|
|
175
149
|
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
176
150
|
"eslint-plugin-react": "^7.37.5",
|
|
177
|
-
"eslint-plugin-react-
|
|
178
|
-
"eslint-plugin-react-hooks": "0.0.0-experimental-edac0dde-20250723",
|
|
151
|
+
"eslint-plugin-react-hooks": "6.0.0-rc.2",
|
|
152
|
+
"eslint-plugin-react-hooks-with-use-effect-event": "npm:eslint-plugin-react-hooks@0.0.0-experimental-edac0dde-20250723",
|
|
179
153
|
"eslint-plugin-react-refresh": "^0.4.20",
|
|
180
154
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
181
155
|
"eslint-plugin-storybook": "^0.12.0",
|
|
@@ -197,7 +171,7 @@
|
|
|
197
171
|
"semantic-release": "^24.2.7",
|
|
198
172
|
"start-server-and-test": "^2.0.13",
|
|
199
173
|
"storybook": "^8.6.14",
|
|
200
|
-
"styled-components": "
|
|
174
|
+
"styled-components": "npm:@sanity/styled-components@^6.1.22",
|
|
201
175
|
"tsconfig-paths": "^4.2.0",
|
|
202
176
|
"typescript": "5.9.2",
|
|
203
177
|
"typescript-eslint": "^8.41.0",
|
|
@@ -210,25 +184,37 @@
|
|
|
210
184
|
"react-is": "^18 || >=19.0.0-0",
|
|
211
185
|
"styled-components": "^5.2 || ^6"
|
|
212
186
|
},
|
|
213
|
-
"packageManager": "pnpm@9.15.9",
|
|
214
187
|
"engines": {
|
|
215
188
|
"node": ">=20.19 <22 || >=22.12"
|
|
216
189
|
},
|
|
217
190
|
"publishConfig": {
|
|
218
191
|
"access": "public"
|
|
219
192
|
},
|
|
220
|
-
"pnpm": {
|
|
221
|
-
"overrides": {
|
|
222
|
-
"@types/react": "$@types/react",
|
|
223
|
-
"@types/react-dom": "$@types/react-dom",
|
|
224
|
-
"@types/react-is": "$@types/react-is",
|
|
225
|
-
"conventional-changelog-conventionalcommits": ">= 8.0.0",
|
|
226
|
-
"react": "$react",
|
|
227
|
-
"react-dom": "$react-dom",
|
|
228
|
-
"react-is": "$react-is"
|
|
229
|
-
}
|
|
230
|
-
},
|
|
231
193
|
"esm.sh": {
|
|
232
194
|
"bundle": false
|
|
195
|
+
},
|
|
196
|
+
"scripts": {
|
|
197
|
+
"build": "run-s clean pkg:build pkg:check figma:pkg:build",
|
|
198
|
+
"clean": "rimraf .workshop dist",
|
|
199
|
+
"commit": "cz",
|
|
200
|
+
"cypress:dev": "run-p dev cypress:open",
|
|
201
|
+
"cypress:open": "cypress open",
|
|
202
|
+
"cypress:run": "cypress run",
|
|
203
|
+
"dev": "run-p storybook:dev workshop:dev",
|
|
204
|
+
"figma:pkg:build": "cd figma && pnpm build",
|
|
205
|
+
"format": "prettier --write --cache --ignore-unknown .",
|
|
206
|
+
"lint": "eslint --cache .",
|
|
207
|
+
"pkg:build": "pkg build --strict",
|
|
208
|
+
"pkg:check": "pkg --strict",
|
|
209
|
+
"release": "semantic-release",
|
|
210
|
+
"storybook:build": "storybook build",
|
|
211
|
+
"storybook:dev": "storybook dev -p 6006",
|
|
212
|
+
"test": "jest",
|
|
213
|
+
"test:browser": "start-server-and-test 'run-s workshop:build workshop:start' http://localhost:1337 'run-s cypress:run'",
|
|
214
|
+
"ts:check": "tsc",
|
|
215
|
+
"watch": "pkg watch --strict",
|
|
216
|
+
"workshop:build": "workshop build",
|
|
217
|
+
"workshop:dev": "workshop dev",
|
|
218
|
+
"workshop:start": "http-server -a localhost -c-0 -p 1337 -s -P http://localhost:1337/index.html? dist"
|
|
233
219
|
}
|
|
234
|
-
}
|
|
220
|
+
}
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
useMemo,
|
|
17
17
|
useReducer,
|
|
18
18
|
useRef,
|
|
19
|
+
useState,
|
|
19
20
|
} from 'react'
|
|
20
21
|
|
|
21
22
|
import {EMPTY_ARRAY, EMPTY_RECORD} from '../../constants'
|
|
@@ -189,6 +190,8 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
189
190
|
const resultsPopoverElementRef = useRef<HTMLDivElement | null>(null)
|
|
190
191
|
const inputElementRef = useRef<HTMLInputElement | null>(null)
|
|
191
192
|
const listBoxElementRef = useRef<HTMLDivElement | null>(null)
|
|
193
|
+
// Element refs that need to be accessed during render
|
|
194
|
+
const [inputElement, setInputElement] = useState<HTMLInputElement | null>(null)
|
|
192
195
|
|
|
193
196
|
// Value refs
|
|
194
197
|
const listFocusedRef = useRef(false)
|
|
@@ -196,10 +199,17 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
196
199
|
const valuePropRef = useRef(valueProp)
|
|
197
200
|
const popoverMouseWithinRef = useRef(false)
|
|
198
201
|
|
|
199
|
-
// Forward
|
|
202
|
+
// Forward inputElement state to inputElementRef
|
|
203
|
+
useImperativeHandle<HTMLInputElement | null, HTMLInputElement | null>(
|
|
204
|
+
inputElementRef,
|
|
205
|
+
() => inputElement,
|
|
206
|
+
[inputElement],
|
|
207
|
+
)
|
|
208
|
+
// Forward inputElement to parent
|
|
200
209
|
useImperativeHandle<HTMLInputElement | null, HTMLInputElement | null>(
|
|
201
210
|
forwardedRef,
|
|
202
|
-
() =>
|
|
211
|
+
() => inputElement,
|
|
212
|
+
[inputElement],
|
|
203
213
|
)
|
|
204
214
|
|
|
205
215
|
const listBoxId = `${id}-listbox`
|
|
@@ -529,40 +539,6 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
529
539
|
return query
|
|
530
540
|
}, [currentOption, query, renderValue, value])
|
|
531
541
|
|
|
532
|
-
const input = (
|
|
533
|
-
<TextInput
|
|
534
|
-
{...restProps}
|
|
535
|
-
aria-activedescendant={activeItemId}
|
|
536
|
-
aria-autocomplete="list"
|
|
537
|
-
aria-expanded={expanded}
|
|
538
|
-
aria-owns={listBoxId}
|
|
539
|
-
autoCapitalize="off"
|
|
540
|
-
autoComplete="off"
|
|
541
|
-
autoCorrect="off"
|
|
542
|
-
border={border}
|
|
543
|
-
clearButton={clearButton}
|
|
544
|
-
customValidity={customValidity}
|
|
545
|
-
disabled={disabled}
|
|
546
|
-
fontSize={fontSize}
|
|
547
|
-
icon={icon}
|
|
548
|
-
iconRight={loading && AnimatedSpinnerIcon}
|
|
549
|
-
id={id}
|
|
550
|
-
inputMode="search"
|
|
551
|
-
onChange={handleInputChange}
|
|
552
|
-
onClear={handleClearButtonClick}
|
|
553
|
-
onFocus={handleInputFocus}
|
|
554
|
-
padding={padding}
|
|
555
|
-
prefix={prefix}
|
|
556
|
-
radius={radius}
|
|
557
|
-
readOnly={readOnly}
|
|
558
|
-
ref={inputElementRef}
|
|
559
|
-
role="combobox"
|
|
560
|
-
spellCheck={false}
|
|
561
|
-
suffix={suffix || openButtonNode}
|
|
562
|
-
value={inputValue}
|
|
563
|
-
/>
|
|
564
|
-
)
|
|
565
|
-
|
|
566
542
|
const handleListBoxKeyDown = useCallback(
|
|
567
543
|
(event: KeyboardEvent<HTMLDivElement>) => {
|
|
568
544
|
// If the focus is currently in the list, move focus to the input element
|
|
@@ -632,15 +608,16 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
632
608
|
|
|
633
609
|
const results = useMemo(() => {
|
|
634
610
|
if (renderPopover) {
|
|
635
|
-
return
|
|
636
|
-
|
|
637
|
-
content
|
|
638
|
-
hidden
|
|
639
|
-
inputElement
|
|
640
|
-
onMouseEnter
|
|
641
|
-
onMouseLeave
|
|
642
|
-
|
|
643
|
-
|
|
611
|
+
return (
|
|
612
|
+
<RenderPopover
|
|
613
|
+
content={content}
|
|
614
|
+
hidden={!expanded}
|
|
615
|
+
inputElement={inputElement}
|
|
616
|
+
onMouseEnter={handlePopoverMouseEnter}
|
|
617
|
+
onMouseLeave={handlePopoverMouseLeave}
|
|
618
|
+
resultsPopoverElementRef={resultsPopoverElementRef}
|
|
619
|
+
renderPopover={renderPopover}
|
|
620
|
+
/>
|
|
644
621
|
)
|
|
645
622
|
}
|
|
646
623
|
|
|
@@ -663,7 +640,7 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
663
640
|
portal
|
|
664
641
|
radius={radius}
|
|
665
642
|
ref={resultsPopoverElementRef}
|
|
666
|
-
referenceElement={
|
|
643
|
+
referenceElement={inputElement}
|
|
667
644
|
{...popover}
|
|
668
645
|
/>
|
|
669
646
|
)
|
|
@@ -673,6 +650,7 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
673
650
|
filteredOptionsLen,
|
|
674
651
|
handlePopoverMouseEnter,
|
|
675
652
|
handlePopoverMouseLeave,
|
|
653
|
+
inputElement,
|
|
676
654
|
popover,
|
|
677
655
|
radius,
|
|
678
656
|
renderPopover,
|
|
@@ -686,12 +664,66 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
686
664
|
onKeyDown={handleRootKeyDown}
|
|
687
665
|
ref={rootElementRef}
|
|
688
666
|
>
|
|
689
|
-
|
|
667
|
+
<TextInput
|
|
668
|
+
{...restProps}
|
|
669
|
+
aria-activedescendant={activeItemId}
|
|
670
|
+
aria-autocomplete="list"
|
|
671
|
+
aria-expanded={expanded}
|
|
672
|
+
aria-owns={listBoxId}
|
|
673
|
+
autoCapitalize="off"
|
|
674
|
+
autoComplete="off"
|
|
675
|
+
autoCorrect="off"
|
|
676
|
+
border={border}
|
|
677
|
+
clearButton={clearButton}
|
|
678
|
+
customValidity={customValidity}
|
|
679
|
+
disabled={disabled}
|
|
680
|
+
fontSize={fontSize}
|
|
681
|
+
icon={icon}
|
|
682
|
+
iconRight={loading && AnimatedSpinnerIcon}
|
|
683
|
+
id={id}
|
|
684
|
+
inputMode="search"
|
|
685
|
+
onChange={handleInputChange}
|
|
686
|
+
onClear={handleClearButtonClick}
|
|
687
|
+
onFocus={handleInputFocus}
|
|
688
|
+
padding={padding}
|
|
689
|
+
prefix={prefix}
|
|
690
|
+
radius={radius}
|
|
691
|
+
readOnly={readOnly}
|
|
692
|
+
ref={setInputElement}
|
|
693
|
+
role="combobox"
|
|
694
|
+
spellCheck={false}
|
|
695
|
+
suffix={suffix || openButtonNode}
|
|
696
|
+
value={inputValue}
|
|
697
|
+
/>
|
|
690
698
|
{results}
|
|
691
699
|
</StyledAutocomplete>
|
|
692
700
|
)
|
|
693
701
|
})
|
|
694
702
|
|
|
703
|
+
function RenderPopover({
|
|
704
|
+
renderPopover,
|
|
705
|
+
content,
|
|
706
|
+
hidden,
|
|
707
|
+
inputElement,
|
|
708
|
+
onMouseEnter,
|
|
709
|
+
onMouseLeave,
|
|
710
|
+
resultsPopoverElementRef,
|
|
711
|
+
}: {
|
|
712
|
+
renderPopover: Exclude<AutocompleteProps['renderPopover'], undefined>
|
|
713
|
+
resultsPopoverElementRef: Parameters<Exclude<AutocompleteProps['renderPopover'], undefined>>[1]
|
|
714
|
+
} & Parameters<Exclude<AutocompleteProps['renderPopover'], undefined>>[0]) {
|
|
715
|
+
return renderPopover(
|
|
716
|
+
{
|
|
717
|
+
content,
|
|
718
|
+
hidden,
|
|
719
|
+
inputElement,
|
|
720
|
+
onMouseEnter,
|
|
721
|
+
onMouseLeave,
|
|
722
|
+
},
|
|
723
|
+
resultsPopoverElementRef,
|
|
724
|
+
)
|
|
725
|
+
}
|
|
726
|
+
|
|
695
727
|
InnerAutocomplete.displayName = 'ForwardRef(Autocomplete)'
|
|
696
728
|
|
|
697
729
|
/**
|
|
@@ -32,18 +32,16 @@ describe('components/menu', () => {
|
|
|
32
32
|
[],
|
|
33
33
|
)
|
|
34
34
|
|
|
35
|
-
const value
|
|
36
|
-
() =>
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
onMouseLeave: handleItemMouseLeave,
|
|
46
|
-
}),
|
|
35
|
+
const value = useMemo(
|
|
36
|
+
() =>
|
|
37
|
+
({
|
|
38
|
+
version: 2,
|
|
39
|
+
activeElement: null,
|
|
40
|
+
mount: (element: HTMLElement | null) => () => console.log(element),
|
|
41
|
+
onItemClick: () => undefined,
|
|
42
|
+
onItemMouseEnter: handleItemMouseEnter,
|
|
43
|
+
onItemMouseLeave: handleItemMouseLeave,
|
|
44
|
+
}) satisfies MenuContextValue,
|
|
47
45
|
[handleItemMouseEnter, handleItemMouseLeave],
|
|
48
46
|
)
|
|
49
47
|
|
|
@@ -58,8 +56,7 @@ describe('components/menu', () => {
|
|
|
58
56
|
|
|
59
57
|
const contextValue = log.mock.calls[0][0]
|
|
60
58
|
|
|
61
|
-
expect(contextValue.version).toBe(
|
|
62
|
-
expect(contextValue.activeIndex).toBe(0)
|
|
59
|
+
expect(contextValue.version).toBe(2)
|
|
63
60
|
expect(typeof contextValue.mount).toBe('function')
|
|
64
61
|
expect(typeof contextValue.onItemClick).toBe('function')
|
|
65
62
|
expect(typeof contextValue.onItemMouseEnter).toBe('function')
|
|
@@ -128,26 +128,21 @@ export const Menu = forwardRef(function Menu(
|
|
|
128
128
|
),
|
|
129
129
|
)
|
|
130
130
|
|
|
131
|
-
const value
|
|
132
|
-
() =>
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
// deprecated
|
|
145
|
-
onMouseEnter: handleItemMouseEnter,
|
|
146
|
-
onMouseLeave: handleItemMouseLeave,
|
|
147
|
-
}),
|
|
131
|
+
const value = useMemo(
|
|
132
|
+
() =>
|
|
133
|
+
({
|
|
134
|
+
version: 2,
|
|
135
|
+
activeElement,
|
|
136
|
+
mount,
|
|
137
|
+
onClickOutside,
|
|
138
|
+
onEscape,
|
|
139
|
+
onItemClick,
|
|
140
|
+
onItemMouseEnter: handleItemMouseEnter,
|
|
141
|
+
onItemMouseLeave: handleItemMouseLeave,
|
|
142
|
+
registerElement,
|
|
143
|
+
}) satisfies MenuContextValue,
|
|
148
144
|
[
|
|
149
145
|
activeElement,
|
|
150
|
-
activeIndex,
|
|
151
146
|
mount,
|
|
152
147
|
handleItemMouseEnter,
|
|
153
148
|
handleItemMouseLeave,
|
|
@@ -1,26 +1,15 @@
|
|
|
1
1
|
import {createGlobalScopedContext} from '../../lib/createGlobalScopedContext'
|
|
2
2
|
|
|
3
3
|
export interface MenuContextValue {
|
|
4
|
-
version:
|
|
4
|
+
version: 2
|
|
5
5
|
activeElement: HTMLElement | null
|
|
6
|
-
activeIndex: number
|
|
7
6
|
mount: (element: HTMLElement | null, selected?: boolean) => () => void
|
|
8
7
|
onClickOutside?: (event: MouseEvent) => void
|
|
9
8
|
onEscape?: () => void
|
|
10
9
|
onItemClick?: () => void
|
|
11
|
-
onItemMouseEnter
|
|
12
|
-
onItemMouseLeave
|
|
10
|
+
onItemMouseEnter: (event: React.MouseEvent<HTMLElement>) => void
|
|
11
|
+
onItemMouseLeave: (event: React.MouseEvent<HTMLElement>) => void
|
|
13
12
|
registerElement?: (el: HTMLElement) => () => void
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @deprecated Use `onItemMouseEnter` instead
|
|
17
|
-
*/
|
|
18
|
-
onMouseEnter: (event: React.MouseEvent<HTMLElement>) => void
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* @deprecated Use `onItemMouseLeave` instead
|
|
22
|
-
*/
|
|
23
|
-
onMouseLeave: (event: React.MouseEvent<HTMLElement>) => void
|
|
24
13
|
}
|
|
25
14
|
|
|
26
15
|
export const MenuContext = createGlobalScopedContext<MenuContextValue | null>(
|
|
@@ -69,7 +69,7 @@ export function MenuGroup(
|
|
|
69
69
|
onItemMouseEnter: _onItemMouseEnter,
|
|
70
70
|
registerElement,
|
|
71
71
|
} = menu
|
|
72
|
-
const onItemMouseEnter = _onItemMouseEnter ?? menu.
|
|
72
|
+
const onItemMouseEnter = _onItemMouseEnter ?? menu.onItemMouseEnter
|
|
73
73
|
const [rootElement, setRootElement] = useState<HTMLButtonElement | HTMLDivElement | null>(null)
|
|
74
74
|
const [open, setOpen] = useState(false)
|
|
75
75
|
const [shouldFocus, setShouldFocus] = useState<'first' | 'last' | null>(null)
|
|
@@ -76,8 +76,8 @@ export const MenuItem = forwardRef(function MenuItem(
|
|
|
76
76
|
onItemMouseEnter: _onItemMouseEnter,
|
|
77
77
|
onItemMouseLeave: _onItemMouseLeave,
|
|
78
78
|
} = menu
|
|
79
|
-
const onItemMouseEnter = _onItemMouseEnter ?? menu.
|
|
80
|
-
const onItemMouseLeave = _onItemMouseLeave ?? menu.
|
|
79
|
+
const onItemMouseEnter = _onItemMouseEnter ?? menu.onItemMouseEnter
|
|
80
|
+
const onItemMouseLeave = _onItemMouseLeave ?? menu.onItemMouseLeave
|
|
81
81
|
const [rootElement, setRootElement] = useState<HTMLDivElement | null>(null)
|
|
82
82
|
const active = Boolean(activeElement) && activeElement === rootElement
|
|
83
83
|
const ref = useRef<HTMLDivElement | null>(null)
|
|
@@ -12,8 +12,8 @@ export function useMenu(): MenuContextValue {
|
|
|
12
12
|
|
|
13
13
|
// NOTE: This check is for future-compatiblity
|
|
14
14
|
// - If the value is not an object, it’s not compatible with the current version
|
|
15
|
-
// - If the value is an object, but doesn’t have `version:
|
|
16
|
-
if (!isRecord(value) || value.version !==
|
|
15
|
+
// - If the value is an object, but doesn’t have `version: 2`, it’s not compatible with the current version
|
|
16
|
+
if (!isRecord(value) || value.version !== 2) {
|
|
17
17
|
throw new Error('useMenu(): the context value is not compatible')
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {useCallback, useEffect,
|
|
1
|
+
import {useCallback, useEffect, useRef, useState} from 'react'
|
|
2
2
|
|
|
3
3
|
import {_getFocusableElements, _sortElements} from './helpers'
|
|
4
4
|
|
|
@@ -29,12 +29,12 @@ export function useMenuController(props: {
|
|
|
29
29
|
const elementsRef = useRef<HTMLElement[]>([])
|
|
30
30
|
const [activeIndex, _setActiveIndex] = useState(-1)
|
|
31
31
|
const activeIndexRef = useRef(activeIndex)
|
|
32
|
-
const activeElement =
|
|
33
|
-
const mounted = Boolean(rootElementRef.current)
|
|
32
|
+
const [activeElement, setActiveElement] = useState<HTMLElement | null>(null)
|
|
34
33
|
|
|
35
34
|
const setActiveIndex = useCallback((nextActiveIndex: number) => {
|
|
36
35
|
_setActiveIndex(nextActiveIndex)
|
|
37
36
|
activeIndexRef.current = nextActiveIndex
|
|
37
|
+
setActiveElement(elementsRef.current[nextActiveIndex] || null)
|
|
38
38
|
}, [])
|
|
39
39
|
|
|
40
40
|
const mount = useCallback(
|
|
@@ -183,7 +183,7 @@ export function useMenuController(props: {
|
|
|
183
183
|
|
|
184
184
|
// Set focus on the currently active element
|
|
185
185
|
useEffect(() => {
|
|
186
|
-
if (!
|
|
186
|
+
if (!rootElementRef.current) return
|
|
187
187
|
|
|
188
188
|
const rafId = requestAnimationFrame(() => {
|
|
189
189
|
if (activeIndex === -1) {
|
|
@@ -218,7 +218,7 @@ export function useMenuController(props: {
|
|
|
218
218
|
})
|
|
219
219
|
|
|
220
220
|
return () => cancelAnimationFrame(rafId)
|
|
221
|
-
}, [activeIndex,
|
|
221
|
+
}, [activeIndex, rootElementRef, setActiveIndex, shouldFocus])
|
|
222
222
|
|
|
223
223
|
return {
|
|
224
224
|
activeElement,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {ToggleArrowRightIcon} from '@sanity/icons'
|
|
2
2
|
import {ThemeFontWeightKey} from '@sanity/ui/theme'
|
|
3
|
-
import {memo, useCallback, useEffect, useId, useMemo, useRef} from 'react'
|
|
3
|
+
import {memo, useCallback, useEffect, useId, useMemo, useRef, useState} from 'react'
|
|
4
4
|
import {styled} from 'styled-components'
|
|
5
5
|
|
|
6
6
|
import {Box, BoxProps, Flex, Text} from '../../primitives'
|
|
@@ -65,18 +65,20 @@ export const TreeItem = memo(function TreeItem(
|
|
|
65
65
|
weight,
|
|
66
66
|
...restProps
|
|
67
67
|
} = props
|
|
68
|
-
const
|
|
68
|
+
const [rootElement, setRootElement] = useState<HTMLLIElement | null>(null)
|
|
69
69
|
const treeitemRef = useRef<HTMLDivElement | null>(null)
|
|
70
70
|
const tree = useTree()
|
|
71
71
|
const {path, registerItem, setExpanded, setFocusedElement} = tree
|
|
72
72
|
const _id = useId()
|
|
73
73
|
const id = idProp || _id
|
|
74
|
-
const itemPath = useMemo(() =>
|
|
75
|
-
|
|
74
|
+
const [itemPath, itemKey] = useMemo(() => {
|
|
75
|
+
const itemPath = path.concat([id || ''])
|
|
76
|
+
return [itemPath, itemPath.join('/')]
|
|
77
|
+
}, [id, path])
|
|
76
78
|
const itemState = tree.state[itemKey]
|
|
77
|
-
const focused = tree.focusedElement ===
|
|
79
|
+
const focused = tree.focusedElement === rootElement
|
|
78
80
|
const expanded = itemState?.expanded === undefined ? expandedProp : itemState?.expanded || false
|
|
79
|
-
const tabIndex = tree.focusedElement && tree.focusedElement ===
|
|
81
|
+
const tabIndex = tree.focusedElement && tree.focusedElement === rootElement ? 0 : -1
|
|
80
82
|
const contextValue = useMemo(
|
|
81
83
|
() => ({...tree, level: tree.level + 1, path: itemPath}),
|
|
82
84
|
[itemPath, tree],
|
|
@@ -95,28 +97,28 @@ export const TreeItem = memo(function TreeItem(
|
|
|
95
97
|
) {
|
|
96
98
|
event.stopPropagation()
|
|
97
99
|
setExpanded(itemKey, !expanded)
|
|
98
|
-
setFocusedElement(
|
|
100
|
+
setFocusedElement(rootElement)
|
|
99
101
|
}
|
|
100
102
|
},
|
|
101
|
-
[expanded, itemKey, onClick, setExpanded, setFocusedElement],
|
|
103
|
+
[expanded, itemKey, onClick, rootElement, setExpanded, setFocusedElement],
|
|
102
104
|
)
|
|
103
105
|
|
|
104
106
|
const handleKeyDown = useCallback(
|
|
105
107
|
(event: React.KeyboardEvent<HTMLElement>) => {
|
|
106
108
|
if (focused && event.key === 'Enter') {
|
|
107
|
-
const el = treeitemRef.current ||
|
|
109
|
+
const el = treeitemRef.current || rootElement
|
|
108
110
|
|
|
109
111
|
el?.click()
|
|
110
112
|
}
|
|
111
113
|
},
|
|
112
|
-
[focused],
|
|
114
|
+
[focused, rootElement],
|
|
113
115
|
)
|
|
114
116
|
|
|
115
117
|
useEffect(() => {
|
|
116
|
-
if (!
|
|
118
|
+
if (!rootElement) return
|
|
117
119
|
|
|
118
|
-
return registerItem(
|
|
119
|
-
}, [expanded,
|
|
120
|
+
return registerItem(rootElement, itemKey, expanded, selected)
|
|
121
|
+
}, [expanded, itemKey, registerItem, rootElement, selected])
|
|
120
122
|
|
|
121
123
|
const content = (
|
|
122
124
|
<Flex padding={padding}>
|
|
@@ -155,7 +157,7 @@ export const TreeItem = memo(function TreeItem(
|
|
|
155
157
|
data-ui="TreeItem"
|
|
156
158
|
{...restProps}
|
|
157
159
|
onClick={handleClick}
|
|
158
|
-
ref={
|
|
160
|
+
ref={setRootElement}
|
|
159
161
|
role="none"
|
|
160
162
|
>
|
|
161
163
|
<TreeItemBox
|
|
@@ -188,7 +190,7 @@ export const TreeItem = memo(function TreeItem(
|
|
|
188
190
|
aria-expanded={expanded}
|
|
189
191
|
onClick={handleClick}
|
|
190
192
|
onKeyDown={handleKeyDown}
|
|
191
|
-
ref={
|
|
193
|
+
ref={setRootElement}
|
|
192
194
|
role="treeitem"
|
|
193
195
|
tabIndex={tabIndex}
|
|
194
196
|
>
|
|
@@ -441,7 +441,7 @@ describe('useClickOutside', () => {
|
|
|
441
441
|
const useBoundaryElementFromRef = (ref: React.MutableRefObject<HTMLElement | null>) => {
|
|
442
442
|
const [element, setElement] = useState(() => ref.current)
|
|
443
443
|
|
|
444
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
444
|
+
// eslint-disable-next-line react-hooks-with-use-effect-event/exhaustive-deps
|
|
445
445
|
useEffect(() => {
|
|
446
446
|
// If the ref has mutated since the last render, update the state and schedule a re-render
|
|
447
447
|
if (ref.current !== element) {
|
|
@@ -25,7 +25,9 @@ export function createGlobalScopedContext<ContextType, const T extends ContextTy
|
|
|
25
25
|
* Prevent errors about re-renders on React SSR on Next.js App Router
|
|
26
26
|
*/
|
|
27
27
|
if (typeof document === 'undefined') {
|
|
28
|
-
|
|
28
|
+
const context = createContext<ContextType>(defaultValue)
|
|
29
|
+
context.displayName = key
|
|
30
|
+
return context
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
globalScope[symbol] = globalScope[symbol] || createContext<T>(defaultValue)
|
|
@@ -77,6 +77,7 @@ export const Avatar = forwardRef(function Avatar(
|
|
|
77
77
|
const as = ReactIs.isValidElementType(asProp) ? asProp : 'div'
|
|
78
78
|
const size = useArrayProp(sizeProp)
|
|
79
79
|
|
|
80
|
+
// eslint-disable-next-line no-warning-comments
|
|
80
81
|
// @todo: remove this
|
|
81
82
|
const avatarSize = avatar.sizes[size[0]] || avatar.sizes[0]
|
|
82
83
|
const _sizeRem = avatarSize.size
|
|
@@ -83,6 +83,7 @@ export const Card = forwardRef(function Card(
|
|
|
83
83
|
const rootTheme = useRootTheme()
|
|
84
84
|
const tone = toneProp === 'inherit' ? rootTheme.tone : toneProp
|
|
85
85
|
|
|
86
|
+
// eslint-disable-next-line no-warning-comments
|
|
86
87
|
// todo: Consider adding the wrapper approach for nested cards in which the tones are not changing, avoid unnecessary ThemeColorProvider
|
|
87
88
|
return (
|
|
88
89
|
<ThemeColorProvider scheme={scheme} tone={tone}>
|
|
@@ -17,6 +17,7 @@ export function responsiveFont(
|
|
|
17
17
|
const {family, sizes, weights} = font[fontKey]
|
|
18
18
|
const fontWeight = ($weight && weights[$weight]) || weights.regular
|
|
19
19
|
|
|
20
|
+
// eslint-disable-next-line no-warning-comments
|
|
20
21
|
// @todo: make this configurable
|
|
21
22
|
const defaultSize = sizes[2]
|
|
22
23
|
|
|
@@ -48,6 +49,7 @@ export function responsiveFont(
|
|
|
48
49
|
},
|
|
49
50
|
}
|
|
50
51
|
|
|
52
|
+
// eslint-disable-next-line no-warning-comments
|
|
51
53
|
// @TODO fix the real condition that is causing $size to be undefined sometimes
|
|
52
54
|
if (!$size) {
|
|
53
55
|
// @ts-expect-error: `warned` isn't typed, the underlying issue should be solved rather than typing it
|
|
@@ -124,6 +124,7 @@ function Color(props: {color: ThemeColorCard_v2; tone: CardTone}) {
|
|
|
124
124
|
|
|
125
125
|
{/* {features.muted && <ColorMuted color={color.muted} />} */}
|
|
126
126
|
|
|
127
|
+
{/* eslint-disable-next-line no-warning-comments */}
|
|
127
128
|
{/* @todo: remove use of `muted` here */}
|
|
128
129
|
{features.selectable && <ColorSelectable color={color.selectable || color.muted} />}
|
|
129
130
|
|