@sanity/ui 3.0.0-static.11 → 3.0.0-static.13

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.
Files changed (251) hide show
  1. package/dist/_chunks-cjs/_visual-editing.js +1293 -1215
  2. package/dist/_chunks-cjs/_visual-editing.js.map +1 -1
  3. package/dist/_chunks-cjs/buildCommand.js +28 -10
  4. package/dist/_chunks-cjs/buildCommand.js.map +1 -1
  5. package/dist/_chunks-es/_visual-editing.mjs +1284 -1202
  6. package/dist/_chunks-es/_visual-editing.mjs.map +1 -1
  7. package/dist/_visual-editing.d.mts +528 -358
  8. package/dist/_visual-editing.d.ts +528 -358
  9. package/dist/cli.js +1 -1
  10. package/dist/css.d.mts +143 -73
  11. package/dist/css.d.ts +143 -73
  12. package/dist/css.js +329 -472
  13. package/dist/css.js.map +1 -1
  14. package/dist/css.mjs +330 -473
  15. package/dist/css.mjs.map +1 -1
  16. package/dist/index.d.mts +1362 -869
  17. package/dist/index.d.ts +1362 -869
  18. package/dist/index.js +383 -308
  19. package/dist/index.js.map +1 -1
  20. package/dist/index.mjs +387 -312
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/sanity-palette.css +105 -1
  23. package/dist/sanity-palette.min.css +1 -0
  24. package/dist/sanity-theme.css +4571 -1
  25. package/dist/sanity-theme.min.css +1 -0
  26. package/dist/system.css +22053 -21946
  27. package/dist/system.min.css +1 -0
  28. package/dist/theme.d.mts +51 -106
  29. package/dist/theme.d.ts +51 -106
  30. package/dist/theme.js +9 -9
  31. package/dist/theme.js.map +1 -1
  32. package/dist/theme.mjs +9 -9
  33. package/dist/theme.mjs.map +1 -1
  34. package/package.json +10 -5
  35. package/src/cli/buildCommand.ts +46 -19
  36. package/src/css/_comp.ts +2 -2
  37. package/src/css/{compile/compileStyle.ts → _compileStyle.ts} +7 -7
  38. package/src/css/{system.style.ts → _system.style.ts} +3 -2
  39. package/src/css/aspects/display/display.ts +1 -0
  40. package/src/css/aspects/display/types.ts +2 -3
  41. package/src/css/aspects/flex/types.ts +4 -12
  42. package/src/css/aspects/flexItem/types.ts +1 -3
  43. package/src/css/aspects/font/types.ts +1 -3
  44. package/src/css/aspects/grid/grid.ts +1 -0
  45. package/src/css/aspects/grid/types.ts +5 -9
  46. package/src/css/aspects/gridItem/gridItem.ts +1 -1
  47. package/src/css/aspects/gridItem/types.ts +7 -18
  48. package/src/css/aspects/height/height.ts +3 -4
  49. package/src/css/aspects/inset/inset.ts +1 -0
  50. package/src/css/aspects/margin/margin.ts +1 -0
  51. package/src/css/aspects/margin/types.ts +1 -0
  52. package/src/css/aspects/maxWidth/maxWidth.ts +1 -0
  53. package/src/css/aspects/maxWidth/types.ts +1 -0
  54. package/src/css/aspects/minWidth/minWidth.ts +1 -0
  55. package/src/css/aspects/minWidth/types.ts +2 -0
  56. package/src/css/aspects/overflow/overflow.ts +1 -0
  57. package/src/css/aspects/padding/padding.ts +1 -0
  58. package/src/css/aspects/pointerEvents/pointerEvents.ts +1 -0
  59. package/src/css/aspects/position/position.ts +1 -0
  60. package/src/css/aspects/shadow/shadow.ts +1 -0
  61. package/src/css/aspects/textOverflow/textOverflow.ts +1 -0
  62. package/src/css/aspects/textOverflow/types.ts +1 -0
  63. package/src/css/aspects/width/types.ts +2 -0
  64. package/src/css/aspects/width/width.ts +1 -0
  65. package/src/css/{compile/compilePalette.ts → compilePalette.ts} +14 -10
  66. package/src/css/compileSystem.ts +7 -0
  67. package/src/css/{compile/compileTheme.ts → compileTheme.ts} +1 -0
  68. package/src/css/{compile/compileTheme_v3.ts → compileTheme_v3.ts} +123 -104
  69. package/src/css/components/breadcrumbs/breadcrumbs.ts +1 -0
  70. package/src/css/components/dialog/dialog.ts +7 -0
  71. package/src/css/components/menu/menu.ts +2 -0
  72. package/src/css/components/skeleton/skeleton.ts +4 -0
  73. package/src/css/components/skeleton/types.ts +4 -0
  74. package/src/css/components/toast/toast.ts +5 -0
  75. package/src/css/components/tree/tree.ts +1 -0
  76. package/src/css/composeClassNames.ts +4 -2
  77. package/src/css/constants.ts +2 -0
  78. package/src/css/index.ts +3 -3
  79. package/src/css/primitives/_arrow/_arrow.ts +3 -0
  80. package/src/css/primitives/_input/input.ts +2 -0
  81. package/src/css/primitives/_selectable/_selectable.style.ts +1 -2
  82. package/src/css/primitives/_selectable/selectable.ts +1 -2
  83. package/src/css/primitives/avatar/avatar.style.ts +5 -8
  84. package/src/css/primitives/avatar/avatar.ts +6 -0
  85. package/src/css/primitives/avatar/types.ts +1 -0
  86. package/src/css/primitives/box/types.ts +1 -3
  87. package/src/css/primitives/button/button.ts +4 -21
  88. package/src/css/primitives/button/index.ts +1 -0
  89. package/src/css/primitives/button/types.ts +18 -0
  90. package/src/css/primitives/card/card.style.ts +3 -6
  91. package/src/css/primitives/card/card.ts +2 -2
  92. package/src/css/primitives/card/types.ts +2 -2
  93. package/src/css/primitives/checkbox/checkbox.ts +2 -0
  94. package/src/css/primitives/container/container.ts +1 -0
  95. package/src/css/primitives/container/types.ts +1 -0
  96. package/src/css/primitives/kbd/kbd.ts +1 -0
  97. package/src/css/primitives/label/types.ts +7 -2
  98. package/src/css/primitives/popover/popover.ts +2 -0
  99. package/src/css/primitives/radio/radio.ts +1 -0
  100. package/src/css/primitives/select/select.ts +2 -0
  101. package/src/css/primitives/spinner/spinner.ts +2 -0
  102. package/src/css/primitives/switch/switch.ts +5 -0
  103. package/src/css/primitives/text/types.ts +4 -0
  104. package/src/css/primitives/textArea/textArea.ts +1 -0
  105. package/src/css/primitives/textInput/textInput.ts +1 -0
  106. package/src/css/primitives/tooltip/tooltip.ts +2 -0
  107. package/src/css/scopeClassName.ts +1 -0
  108. package/src/css/types.ts +9 -12
  109. package/src/css/util.ts +5 -0
  110. package/src/css/utils/srOnly/srOnly.ts +3 -1
  111. package/src/css/varNames.ts +131 -141
  112. package/src/css/vars.ts +1 -6
  113. package/src/theme/palette/constants.ts +2 -0
  114. package/src/theme/palette/types.ts +3 -0
  115. package/src/theme/types.ts +2 -6
  116. package/src/theme/v0/focusRing.ts +1 -3
  117. package/src/theme/v0/font.ts +6 -18
  118. package/src/theme/v0/layer.ts +1 -3
  119. package/src/theme/v0/shadow.ts +2 -6
  120. package/src/theme/v2/avatar.ts +1 -3
  121. package/src/theme/v2/color/_system.ts +1 -3
  122. package/src/theme/v2/color/avatar.ts +2 -6
  123. package/src/theme/v2/color/badge.ts +2 -6
  124. package/src/theme/v2/color/button.ts +3 -9
  125. package/src/theme/v2/color/color.ts +3 -9
  126. package/src/theme/v2/color/input.ts +3 -9
  127. package/src/theme/v2/color/kbd.ts +1 -3
  128. package/src/theme/v2/color/selectable.ts +2 -6
  129. package/src/theme/v2/color/state.ts +1 -3
  130. package/src/theme/v2/color/syntax.ts +1 -3
  131. package/src/theme/v2/input.ts +1 -3
  132. package/src/theme/v2/theme.ts +2 -6
  133. package/src/theme/v3/buildTheme_v3.ts +1 -0
  134. package/src/theme/v3/color/color.ts +6 -4
  135. package/src/theme/v3/color/renderColor.ts +4 -1
  136. package/src/theme/v3/defaultTokens.ts +1 -0
  137. package/src/theme/v3/resolveTokens.ts +9 -9
  138. package/src/ui/RootClassNames.tsx +3 -0
  139. package/src/ui/StyleTags.tsx +3 -1
  140. package/src/ui/_compat/theme/themeColorProvider.tsx +2 -6
  141. package/src/ui/_compat/theme/themeProvider.tsx +2 -6
  142. package/src/ui/_compat/theme/types.ts +1 -3
  143. package/src/ui/_compat/theme/useRootTheme.ts +1 -3
  144. package/src/ui/_compat/theme/useTheme.ts +2 -6
  145. package/src/ui/components/autocomplete/__workshop__/constrainedHeight.tsx +2 -2
  146. package/src/ui/components/autocomplete/autocomplete.tsx +86 -114
  147. package/src/ui/components/autocomplete/autocompleteOption.tsx +2 -0
  148. package/src/ui/components/autocomplete/types.ts +3 -8
  149. package/src/ui/components/breadcrumbs/breadcrumbs.tsx +27 -21
  150. package/src/ui/components/dialog/dialog.tsx +89 -291
  151. package/src/ui/components/dialog/dialogCard.tsx +183 -0
  152. package/src/ui/components/dialog/isTargetWithinScope.ts +14 -0
  153. package/src/ui/components/hotkeys/hotkeys.tsx +33 -15
  154. package/src/ui/components/menu/__workshop__/asComponent.tsx +6 -6
  155. package/src/ui/components/menu/menu.tsx +27 -33
  156. package/src/ui/components/menu/menuButton.tsx +10 -14
  157. package/src/ui/components/menu/menuDivider.tsx +25 -13
  158. package/src/ui/components/menu/menuGroup.tsx +34 -26
  159. package/src/ui/components/menu/menuItem.tsx +51 -40
  160. package/src/ui/components/skeleton/codeSkeleton.tsx +53 -0
  161. package/src/ui/components/skeleton/headingSkeleton.tsx +52 -0
  162. package/src/ui/components/skeleton/index.ts +3 -0
  163. package/src/ui/components/skeleton/labelSkeleton.tsx +51 -0
  164. package/src/ui/components/skeleton/skeleton.tsx +39 -16
  165. package/src/ui/components/skeleton/textSkeleton.tsx +31 -110
  166. package/src/ui/components/tab/tab.tsx +23 -19
  167. package/src/ui/components/tab/tabList.tsx +34 -33
  168. package/src/ui/components/tab/tabPanel.tsx +25 -19
  169. package/src/ui/components/toast/toast.tsx +21 -16
  170. package/src/ui/components/toast/toastLayer.tsx +28 -10
  171. package/src/ui/components/toast/toastProvider.tsx +2 -6
  172. package/src/ui/components/toast/types.ts +2 -6
  173. package/src/ui/components/toast/useToast.ts +1 -3
  174. package/src/ui/components/tree/tree.tsx +194 -187
  175. package/src/ui/components/tree/treeGroup.tsx +25 -5
  176. package/src/ui/components/tree/treeItem.tsx +35 -23
  177. package/src/ui/hooks/useClickOutside.ts +2 -6
  178. package/src/ui/hooks/useClickOutsideEvent.ts +3 -9
  179. package/src/ui/primitives/_selectable/selectable.tsx +33 -20
  180. package/src/ui/primitives/avatar/avatar.tsx +28 -31
  181. package/src/ui/primitives/avatar/avatarCounter.tsx +33 -25
  182. package/src/ui/primitives/avatar/avatarStack.tsx +31 -24
  183. package/src/ui/primitives/avatar/types.ts +2 -0
  184. package/src/ui/primitives/badge/__workshop__/props.tsx +1 -9
  185. package/src/ui/primitives/badge/__workshop__/tones.tsx +1 -6
  186. package/src/ui/primitives/badge/badge.tsx +33 -32
  187. package/src/ui/primitives/badge/types.ts +2 -1
  188. package/src/ui/primitives/box/box.tsx +59 -29
  189. package/src/ui/primitives/button/__workshop__/sanityUploadButton.tsx +3 -3
  190. package/src/ui/primitives/button/__workshop__/uploadButton.tsx +2 -1
  191. package/src/ui/primitives/button/button.test.tsx +1 -15
  192. package/src/ui/primitives/button/button.tsx +64 -70
  193. package/src/ui/primitives/card/__workshop__/asComponent.tsx +3 -3
  194. package/src/ui/primitives/card/card.tsx +22 -22
  195. package/src/ui/primitives/card/cardProvider.tsx +2 -0
  196. package/src/ui/primitives/checkbox/checkbox.tsx +26 -15
  197. package/src/ui/primitives/code/code.tsx +24 -22
  198. package/src/ui/primitives/container/container.tsx +26 -15
  199. package/src/ui/primitives/flex/flex.tsx +21 -19
  200. package/src/ui/primitives/grid/grid.tsx +21 -16
  201. package/src/ui/primitives/heading/heading.tsx +42 -19
  202. package/src/ui/primitives/inline/inline.tsx +26 -23
  203. package/src/ui/primitives/kbd/kbd.tsx +25 -23
  204. package/src/ui/primitives/label/label.tsx +41 -26
  205. package/src/ui/primitives/popover/popover.tsx +281 -277
  206. package/src/ui/primitives/popover/popoverCard.tsx +119 -118
  207. package/src/ui/primitives/radio/radio.tsx +30 -14
  208. package/src/ui/primitives/select/select.tsx +24 -16
  209. package/src/ui/primitives/spinner/animatedSpinnerIcon.tsx +4 -2
  210. package/src/ui/primitives/spinner/spinner.tsx +24 -12
  211. package/src/ui/primitives/stack/stack.tsx +27 -18
  212. package/src/ui/primitives/switch/switch.tsx +31 -14
  213. package/src/ui/primitives/text/text.tsx +42 -24
  214. package/src/ui/primitives/textArea/textArea.tsx +33 -18
  215. package/src/ui/primitives/textInput/textInput.tsx +28 -40
  216. package/src/ui/primitives/tooltip/constants.ts +1 -1
  217. package/src/ui/primitives/tooltip/tooltip.tsx +76 -59
  218. package/src/ui/primitives/tooltip/tooltipCard.tsx +89 -82
  219. package/src/ui/primitives/types.ts +1 -3
  220. package/src/ui/types/avatar.ts +3 -6
  221. package/src/ui/types/badge.ts +1 -3
  222. package/src/ui/types/button.ts +1 -3
  223. package/src/ui/types/card.ts +1 -3
  224. package/src/ui/types/dialog.ts +1 -3
  225. package/src/ui/types/props.ts +20 -10
  226. package/src/ui/utils/arrow/arrow.tsx +40 -14
  227. package/src/ui/utils/boundaryElement/boundaryElementProvider.tsx +2 -6
  228. package/src/ui/utils/boundaryElement/types.ts +1 -3
  229. package/src/ui/utils/boundaryElement/useBoundaryElement.ts +1 -3
  230. package/src/ui/utils/elementQuery/elementQuery.tsx +36 -14
  231. package/src/ui/utils/errorBoundary.tsx +2 -0
  232. package/src/ui/utils/index.ts +0 -1
  233. package/src/ui/utils/layer/layer.tsx +23 -93
  234. package/src/ui/utils/layer/layerCard.tsx +92 -0
  235. package/src/ui/utils/layer/layerProvider.tsx +2 -6
  236. package/src/ui/utils/layer/useLayer.ts +1 -3
  237. package/src/ui/utils/portal/portal.ts +2 -6
  238. package/src/ui/utils/portal/portalProvider.tsx +2 -6
  239. package/src/ui/utils/portal/types.ts +1 -3
  240. package/src/ui/utils/portal/usePortal.ts +1 -3
  241. package/src/ui/utils/srOnly/srOnly.tsx +25 -20
  242. package/src/ui/utils/virtualList/virtualList.tsx +36 -24
  243. package/src/css/compile/compileRule.ts +0 -97
  244. package/src/css/compile/compileRules.test.ts +0 -36
  245. package/src/css/compile/compileRules.ts +0 -43
  246. package/src/css/compile/compileSystem.ts +0 -7
  247. package/src/css/compile/types.ts +0 -6
  248. package/src/css/primitives/_selectable/_contants.ts +0 -11
  249. package/src/css/primitives/card/_constants.ts +0 -13
  250. package/src/ui/utils/conditionalWrapper/conditionalWrapper.tsx +0 -23
  251. package/src/ui/utils/conditionalWrapper/index.ts +0 -1
@@ -1,12 +1,8 @@
1
- import {GapStyleProps, ResponsiveProp} from '@sanity/ui/css'
1
+ import {ResponsiveProp} from '@sanity/ui/css'
2
2
  import {Space} from '@sanity/ui/theme'
3
3
  import {
4
4
  FocusEvent,
5
- ForwardedRef,
6
- forwardRef,
7
5
  KeyboardEvent,
8
- memo,
9
- ReactElement,
10
6
  useCallback,
11
7
  useEffect,
12
8
  useImperativeHandle,
@@ -16,229 +12,240 @@ import {
16
12
  } from 'react'
17
13
 
18
14
  import {Stack} from '../../primitives'
19
- import {Props} from '../../types'
15
+ import {ComponentType, Props} from '../../types'
20
16
  import {_findNextItemElement, _findPrevItemElement, _focusItemElement} from './helpers'
21
17
  import {TreeContext} from './treeContext'
22
18
  import {TreeContextValue, TreeState} from './types'
23
19
 
20
+ /** @beta */
21
+ export const DEFAULT_TREE_ELEMENT = 'div'
22
+
24
23
  /**
25
24
  * This API might change. DO NOT USE IN PRODUCTION.
26
25
  * @beta
27
26
  */
28
- export interface TreeProps extends Pick<GapStyleProps, 'gap'> {
27
+ export type TreeOwnProps = {
28
+ gap?: ResponsiveProp<Space>
29
29
  /** @deprecated Use `gap` instead. */
30
30
  space?: ResponsiveProp<Space>
31
31
  }
32
32
 
33
+ /** @beta */
34
+ export type TreeElementType = 'div' | ComponentType
35
+
36
+ /** @beta */
37
+ export type TreeProps<E extends TreeElementType = TreeElementType> = Props<TreeOwnProps, E>
38
+
33
39
  /**
34
40
  * This API might change. DO NOT USE IN PRODUCTION.
35
41
  * @beta
36
42
  */
37
- export const Tree = memo(
38
- forwardRef(function Tree(
39
- props: Props<TreeProps, 'div'>,
40
- forwardedRef: ForwardedRef<HTMLDivElement>,
41
- ): ReactElement {
42
- const {children, gap, space = 1, onFocus, ...restProps} = props
43
- const ref = useRef<HTMLDivElement | null>(null)
44
- const [focusedElement, setFocusedElement] = useState<HTMLElement | null>(null)
45
- const focusedElementRef = useRef(focusedElement)
46
- const path: string[] = useMemo(() => [], [])
47
- const [itemElements, setItemElements] = useState<HTMLElement[]>([])
48
- const [state, setState] = useState<TreeState>({})
49
- const stateRef = useRef(state)
50
-
51
- useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(
52
- forwardedRef,
53
- () => ref.current,
54
- )
55
-
56
- useEffect(() => {
57
- focusedElementRef.current = focusedElement
58
- }, [focusedElement])
59
-
60
- useEffect(() => {
61
- stateRef.current = state
62
- }, [state])
63
-
64
- const registerItem = useCallback(
65
- (element: HTMLElement, path: string, expanded: boolean, selected: boolean) => {
66
- setState((s) => ({...s, [path]: {element, expanded}}))
67
-
68
- if (selected) {
69
- setFocusedElement(element)
43
+ export function Tree<E extends TreeElementType = typeof DEFAULT_TREE_ELEMENT>(props: TreeProps<E>) {
44
+ const {
45
+ children,
46
+ gap,
47
+ space = 1,
48
+ onFocus,
49
+ ref: forwardedRef,
50
+ ...rest
51
+ } = props as TreeProps<typeof DEFAULT_TREE_ELEMENT>
52
+
53
+ const ref = useRef<HTMLDivElement | null>(null)
54
+ const [focusedElement, setFocusedElement] = useState<HTMLElement | null>(null)
55
+ const focusedElementRef = useRef(focusedElement)
56
+ const path: string[] = useMemo(() => [], [])
57
+ const [itemElements, setItemElements] = useState<HTMLElement[]>([])
58
+ const [state, setState] = useState<TreeState>({})
59
+ const stateRef = useRef(state)
60
+
61
+ useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(forwardedRef, () => ref.current)
62
+
63
+ useEffect(() => {
64
+ focusedElementRef.current = focusedElement
65
+ }, [focusedElement])
66
+
67
+ useEffect(() => {
68
+ stateRef.current = state
69
+ }, [state])
70
+
71
+ const registerItem = useCallback(
72
+ (element: HTMLElement, path: string, expanded: boolean, selected: boolean) => {
73
+ setState((s) => ({...s, [path]: {element, expanded}}))
74
+
75
+ if (selected) {
76
+ setFocusedElement(element)
77
+ }
78
+
79
+ return () => {
80
+ setState((s) => {
81
+ const newState = {...s}
82
+
83
+ delete newState[path]
84
+
85
+ return newState
86
+ })
87
+ }
88
+ },
89
+ [],
90
+ )
91
+
92
+ const setExpanded = useCallback((path: string, expanded: boolean) => {
93
+ setState((s) => {
94
+ const itemState = s[path]
95
+
96
+ if (!itemState) return s
97
+
98
+ return {...s, [path]: {...itemState, expanded}}
99
+ })
100
+ }, [])
101
+
102
+ const contextValue: TreeContextValue = useMemo(
103
+ () => ({
104
+ version: 0.0,
105
+ focusedElement: focusedElement || itemElements[0] || null,
106
+ level: 0,
107
+ path,
108
+ registerItem,
109
+ setExpanded,
110
+ setFocusedElement,
111
+ space,
112
+ state,
113
+ }),
114
+ [focusedElement, itemElements, path, registerItem, setExpanded, space, state],
115
+ )
116
+
117
+ const handleKeyDown = useCallback(
118
+ (event: KeyboardEvent<HTMLDivElement>) => {
119
+ if (!focusedElementRef.current) return
120
+
121
+ if (event.key === 'ArrowDown') {
122
+ event.preventDefault()
123
+
124
+ const nextEl = _findNextItemElement(
125
+ stateRef.current,
126
+ itemElements,
127
+ focusedElementRef.current,
128
+ )
129
+
130
+ if (nextEl) {
131
+ _focusItemElement(nextEl)
132
+ setFocusedElement(nextEl)
70
133
  }
71
134
 
72
- return () => {
73
- setState((s) => {
74
- const newState = {...s}
135
+ return
136
+ }
75
137
 
76
- delete newState[path]
138
+ if (event.key === 'ArrowUp') {
139
+ event.preventDefault()
77
140
 
78
- return newState
79
- })
80
- }
81
- },
82
- [],
83
- )
84
-
85
- const setExpanded = useCallback((path: string, expanded: boolean) => {
86
- setState((s) => {
87
- const itemState = s[path]
88
-
89
- if (!itemState) return s
90
-
91
- return {...s, [path]: {...itemState, expanded}}
92
- })
93
- }, [])
94
-
95
- const contextValue: TreeContextValue = useMemo(
96
- () => ({
97
- version: 0.0,
98
- focusedElement: focusedElement || itemElements[0] || null,
99
- level: 0,
100
- path,
101
- registerItem,
102
- setExpanded,
103
- setFocusedElement,
104
- space,
105
- state,
106
- }),
107
- [focusedElement, itemElements, path, registerItem, setExpanded, space, state],
108
- )
109
-
110
- const handleKeyDown = useCallback(
111
- (event: KeyboardEvent<HTMLDivElement>) => {
112
- if (!focusedElementRef.current) return
113
-
114
- if (event.key === 'ArrowDown') {
115
- event.preventDefault()
116
-
117
- const nextEl = _findNextItemElement(
118
- stateRef.current,
119
- itemElements,
120
- focusedElementRef.current,
121
- )
122
-
123
- if (nextEl) {
124
- _focusItemElement(nextEl)
125
- setFocusedElement(nextEl)
126
- }
141
+ const prevEl = _findPrevItemElement(
142
+ stateRef.current,
143
+ itemElements,
144
+ focusedElementRef.current,
145
+ )
127
146
 
128
- return
147
+ if (prevEl) {
148
+ _focusItemElement(prevEl)
149
+ setFocusedElement(prevEl)
129
150
  }
130
151
 
131
- if (event.key === 'ArrowUp') {
132
- event.preventDefault()
133
-
134
- const prevEl = _findPrevItemElement(
135
- stateRef.current,
136
- itemElements,
137
- focusedElementRef.current,
138
- )
152
+ return
153
+ }
139
154
 
140
- if (prevEl) {
141
- _focusItemElement(prevEl)
142
- setFocusedElement(prevEl)
143
- }
144
-
145
- return
146
- }
155
+ if (event.key === 'ArrowLeft') {
156
+ event.preventDefault()
147
157
 
148
- if (event.key === 'ArrowLeft') {
149
- event.preventDefault()
158
+ const itemKey = focusedElementRef.current.getAttribute('data-tree-key')
150
159
 
151
- const itemKey = focusedElementRef.current.getAttribute('data-tree-key')
160
+ if (!itemKey) return
152
161
 
153
- if (!itemKey) return
162
+ const itemState = stateRef.current[itemKey]
154
163
 
155
- const itemState = stateRef.current[itemKey]
164
+ if (!itemState) return
156
165
 
157
- if (!itemState) return
158
-
159
- if (itemState.expanded) {
160
- setState((s) => {
161
- const itemState = s[itemKey]
166
+ if (itemState.expanded) {
167
+ setState((s) => {
168
+ const itemState = s[itemKey]
162
169
 
163
- if (!itemState) return s
170
+ if (!itemState) return s
164
171
 
165
- return {...s, [itemKey]: {...itemState, expanded: false}}
166
- })
167
- } else {
168
- const itemPath = itemKey.split('/')
172
+ return {...s, [itemKey]: {...itemState, expanded: false}}
173
+ })
174
+ } else {
175
+ const itemPath = itemKey.split('/')
169
176
 
170
- itemPath.pop()
177
+ itemPath.pop()
171
178
 
172
- const parentKey = itemPath.join('/')
173
- const parentState = parentKey && stateRef.current[parentKey]
179
+ const parentKey = itemPath.join('/')
180
+ const parentState = parentKey && stateRef.current[parentKey]
174
181
 
175
- if (parentState) {
176
- parentState.element.focus()
177
- setFocusedElement(parentState.element)
178
- }
182
+ if (parentState) {
183
+ parentState.element.focus()
184
+ setFocusedElement(parentState.element)
179
185
  }
180
-
181
- return
182
186
  }
183
187
 
184
- if (event.key === 'ArrowRight') {
185
- event.preventDefault()
188
+ return
189
+ }
186
190
 
187
- const focusedKey = focusedElementRef.current.getAttribute('data-tree-key')
191
+ if (event.key === 'ArrowRight') {
192
+ event.preventDefault()
188
193
 
189
- if (!focusedKey) return
194
+ const focusedKey = focusedElementRef.current.getAttribute('data-tree-key')
190
195
 
191
- if (!stateRef.current[focusedKey]?.expanded) {
192
- setState((s) => {
193
- const itemState = s[focusedKey]
196
+ if (!focusedKey) return
194
197
 
195
- if (!itemState) return s
198
+ if (!stateRef.current[focusedKey]?.expanded) {
199
+ setState((s) => {
200
+ const itemState = s[focusedKey]
196
201
 
197
- return {...s, [focusedKey]: {...itemState, expanded: true}}
198
- })
199
- }
202
+ if (!itemState) return s
200
203
 
201
- return
204
+ return {...s, [focusedKey]: {...itemState, expanded: true}}
205
+ })
202
206
  }
203
- },
204
- [itemElements],
205
- )
206
-
207
- const handleFocus = useCallback(
208
- (event: FocusEvent<HTMLDivElement>) => {
209
- setFocusedElement(event.target)
210
-
211
- // Call the element's `focus` handler
212
- onFocus?.(event)
213
- },
214
- [onFocus],
215
- )
216
-
217
- useEffect(() => {
218
- if (!ref.current) return
219
- const _itemElements = Array.from(
220
- ref.current.querySelectorAll('[data-ui="TreeItem"]'),
221
- ) as HTMLElement[]
222
-
223
- setItemElements(_itemElements)
224
- }, [children])
225
-
226
- return (
227
- <TreeContext.Provider value={contextValue}>
228
- <Stack
229
- as="ul"
230
- data-ui="Tree"
231
- {...restProps}
232
- gap={gap ?? space}
233
- onFocus={handleFocus}
234
- onKeyDown={handleKeyDown}
235
- ref={ref}
236
- role="tree"
237
- >
238
- {children}
239
- </Stack>
240
- </TreeContext.Provider>
241
- )
242
- }),
243
- )
244
- Tree.displayName = 'Memo(ForwardRef(Tree))'
207
+
208
+ return
209
+ }
210
+ },
211
+ [itemElements],
212
+ )
213
+
214
+ const handleFocus = useCallback(
215
+ (event: FocusEvent<HTMLDivElement>) => {
216
+ setFocusedElement(event.target)
217
+
218
+ // Call the element's `focus` handler
219
+ onFocus?.(event)
220
+ },
221
+ [onFocus],
222
+ )
223
+
224
+ useEffect(() => {
225
+ if (!ref.current) return
226
+ const _itemElements = Array.from(
227
+ ref.current.querySelectorAll('[data-ui="TreeItem"]'),
228
+ ) as HTMLElement[]
229
+
230
+ setItemElements(_itemElements)
231
+ }, [children])
232
+
233
+ return (
234
+ <TreeContext.Provider value={contextValue}>
235
+ <Stack
236
+ as="ul"
237
+ data-ui="Tree"
238
+ {...rest}
239
+ gap={gap ?? space}
240
+ onFocus={handleFocus}
241
+ onKeyDown={handleKeyDown}
242
+ ref={ref}
243
+ role="tree"
244
+ >
245
+ {children}
246
+ </Stack>
247
+ </TreeContext.Provider>
248
+ )
249
+ }
250
+
251
+ Tree.displayName = 'Tree'
@@ -1,30 +1,50 @@
1
1
  import {memo, ReactElement} from 'react'
2
2
 
3
3
  import {Stack} from '../../primitives'
4
- import {Props} from '../../types'
4
+ import {ComponentType, Props} from '../../types'
5
5
  import {useTree} from './useTree'
6
6
 
7
- export interface TreeGroupProps {
7
+ /** @public */
8
+ export const DEFAULT_TREE_GROUP_ELEMENT = 'div'
9
+
10
+ /** @public */
11
+ export type TreeGroupElementType = 'div' | ComponentType
12
+
13
+ /** @public */
14
+ export type TreeGroupOwnProps = {
8
15
  expanded?: boolean
9
16
  }
10
17
 
18
+ /** @public */
19
+ export type TreeGroupProps<E extends TreeGroupElementType = TreeGroupElementType> = Props<
20
+ TreeGroupOwnProps,
21
+ E
22
+ >
23
+
11
24
  export const TreeGroup = memo(function TreeGroup(
12
25
  props: Props<TreeGroupProps, 'div'>,
13
26
  ): ReactElement {
14
- const {children, expanded = false, ...restProps} = props
27
+ const {
28
+ children,
29
+ expanded = false,
30
+ ...rest
31
+ } = props as TreeGroupProps<typeof DEFAULT_TREE_GROUP_ELEMENT>
32
+
15
33
  const tree = useTree()
16
34
 
17
35
  return (
18
36
  <Stack
19
37
  as="ul"
20
38
  data-ui="TreeGroup"
21
- {...restProps}
39
+ {...rest}
22
40
  hidden={!expanded}
23
41
  marginTop={tree.space}
24
42
  role="group"
25
- space={tree.space}
43
+ gap={tree.space}
26
44
  >
27
45
  {children}
28
46
  </Stack>
29
47
  )
30
48
  })
49
+
50
+ TreeGroup.displayName = 'TreeGroup'
@@ -4,10 +4,10 @@ import {FontTextSize, Space, ThemeFontWeightKey} from '@sanity/ui/theme'
4
4
  import {
5
5
  ElementType,
6
6
  KeyboardEvent,
7
- memo,
8
7
  MouseEvent,
9
- ReactElement,
8
+ MouseEventHandler,
10
9
  ReactNode,
10
+ Ref,
11
11
  useCallback,
12
12
  useEffect,
13
13
  useId,
@@ -15,37 +15,48 @@ import {
15
15
  useRef,
16
16
  } from 'react'
17
17
 
18
- import {Box, CardProps, Flex, Text} from '../../primitives'
19
- import {Selectable} from '../../primitives/_selectable'
20
- import {Props} from '../../types'
18
+ import {Box, BoxProps, CardOwnProps, Flex, Text} from '../../primitives'
19
+ import {Selectable, SelectableElementType} from '../../primitives/_selectable'
20
+ import {ComponentType, Props} from '../../types'
21
21
  import {TreeContext} from './treeContext'
22
22
  import {TreeGroup} from './treeGroup'
23
23
  import {useTree} from './useTree'
24
24
 
25
- /**
26
- * @beta
27
- */
28
- export interface TreeItemProps extends CardProps {
25
+ /** @beta */
26
+ export const DEFAULT_TREE_ITEM_ELEMENT = 'a'
27
+
28
+ /** @beta */
29
+ export type TreeItemOwnProps = CardOwnProps & {
29
30
  expanded?: boolean
30
31
  fontSize?: ResponsiveProp<FontTextSize>
31
32
  icon?: ElementType
32
33
  /**
33
34
  * Allows passing a custom element type to the link component
34
35
  */
35
- linkAs?: ElementType
36
+ linkAs?: SelectableElementType
37
+ onClick?: MouseEventHandler<HTMLAnchorElement | HTMLLIElement>
36
38
  padding?: ResponsiveProp<Space>
37
39
  space?: ResponsiveProp<Space>
38
40
  text?: ReactNode
39
41
  weight?: ThemeFontWeightKey
40
42
  }
41
43
 
44
+ /** @beta */
45
+ export type TreeItemElementType = 'a' | 'li' | ComponentType
46
+
47
+ /** @beta */
48
+ export type TreeItemProps<E extends TreeItemElementType = TreeItemElementType> = Props<
49
+ TreeItemOwnProps,
50
+ E
51
+ >
52
+
42
53
  /**
43
54
  * This API might change. DO NOT USE IN PRODUCTION.
44
55
  * @beta
45
56
  */
46
- export const TreeItem = memo(function TreeItem(
47
- props: Omit<Props<TreeItemProps, 'div'>, 'as'>,
48
- ): ReactElement {
57
+ export function TreeItem<E extends TreeItemElementType = typeof DEFAULT_TREE_ITEM_ELEMENT>(
58
+ props: TreeItemProps<E>,
59
+ ) {
49
60
  const {
50
61
  children,
51
62
  className,
@@ -63,9 +74,10 @@ export const TreeItem = memo(function TreeItem(
63
74
  space = 2,
64
75
  text,
65
76
  weight,
66
- ...restProps
67
- } = props
68
- const rootRef = useRef<HTMLDivElement | null>(null)
77
+ ...rest
78
+ } = props as TreeItemProps<typeof DEFAULT_TREE_ITEM_ELEMENT>
79
+
80
+ const rootRef = useRef<HTMLElement | null>(null)
69
81
  const treeitemRef = useRef<HTMLDivElement | null>(null)
70
82
  const tree = useTree()
71
83
  const {path, registerItem, setExpanded, setFocusedElement} = tree
@@ -83,7 +95,7 @@ export const TreeItem = memo(function TreeItem(
83
95
  )
84
96
 
85
97
  const handleClick = useCallback(
86
- (event: MouseEvent<HTMLDivElement>) => {
98
+ (event: MouseEvent<HTMLAnchorElement> | MouseEvent<HTMLLIElement>) => {
87
99
  if (onClick) onClick(event)
88
100
 
89
101
  const target = event.target
@@ -152,11 +164,11 @@ export const TreeItem = memo(function TreeItem(
152
164
  data-tree-id={id}
153
165
  data-tree-key={itemKey}
154
166
  data-ui="TreeItem"
155
- {...restProps}
167
+ {...(rest as BoxProps<'li'>)}
156
168
  as="li"
157
169
  className={composeClassNames(className, treeItem())}
158
170
  onClick={handleClick}
159
- ref={rootRef}
171
+ ref={rootRef as Ref<HTMLLIElement>}
160
172
  role="none"
161
173
  >
162
174
  <Selectable
@@ -189,12 +201,12 @@ export const TreeItem = memo(function TreeItem(
189
201
  data-ui="TreeItem"
190
202
  data-tree-id={id}
191
203
  data-tree-key={itemKey}
192
- {...restProps}
204
+ {...(rest as BoxProps<'a'>)}
193
205
  aria-expanded={expanded}
194
206
  className={composeClassNames(className, treeItem())}
195
207
  onClick={handleClick}
196
208
  onKeyDown={handleKeyDown}
197
- ref={rootRef}
209
+ ref={rootRef as Ref<HTMLAnchorElement>}
198
210
  role="treeitem"
199
211
  tabIndex={tabIndex}
200
212
  >
@@ -215,6 +227,6 @@ export const TreeItem = memo(function TreeItem(
215
227
  </TreeContext.Provider>
216
228
  </Box>
217
229
  )
218
- })
230
+ }
219
231
 
220
- TreeItem.displayName = 'Memo(TreeItem)'
232
+ TreeItem.displayName = 'TreeItem'
@@ -2,14 +2,10 @@ import {useEffect, useRef, useState} from 'react'
2
2
 
3
3
  import {EMPTY_ARRAY} from '../constants'
4
4
 
5
- /**
6
- * @public
7
- */
5
+ /** @public */
8
6
  export type ClickOutsideListener = (event: MouseEvent) => void
9
7
 
10
- /**
11
- * @public
12
- */
8
+ /** @public */
13
9
  export type ClickOutsideElements = (HTMLElement | null | (HTMLElement | null)[])[]
14
10
 
15
11
  function _getElements(
@@ -3,19 +3,13 @@ import {useEffectEvent} from 'use-effect-event'
3
3
 
4
4
  import {EMPTY_ARRAY} from '../constants'
5
5
 
6
- /**
7
- * @public
8
- */
6
+ /** @public */
9
7
  export type ClickOutsideEventListener = (event: MouseEvent) => void
10
8
 
11
- /**
12
- * @public
13
- */
9
+ /** @public */
14
10
  export type ClickOutsideEventElements = (HTMLElement | null | (HTMLElement | null)[])[]
15
11
 
16
- /**
17
- * @public
18
- */
12
+ /** @public */
19
13
  export function useClickOutsideEvent(
20
14
  listener: ClickOutsideEventListener | false | undefined,
21
15
  elementsArg: () => ClickOutsideEventElements = () => EMPTY_ARRAY,