@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
@@ -0,0 +1,183 @@
1
+ import {CloseIcon} from '@sanity/icons'
2
+ import {
3
+ dialogCardRoot,
4
+ dialogContent,
5
+ dialogFooter,
6
+ dialogHeader,
7
+ dialogLayout,
8
+ } from '@sanity/ui/css'
9
+ import {ForwardedRef, ReactNode, useCallback, useEffect, useImperativeHandle, useRef} from 'react'
10
+
11
+ import {focusFirstDescendant} from '../../helpers'
12
+ import {useClickOutsideEvent, useGlobalKeyDown} from '../../hooks'
13
+ import {Box, Button, Card, CardElementType, CardOwnProps, Flex, Text} from '../../primitives'
14
+ import {Props} from '../../types'
15
+ import {useBoundaryElement, useLayer, usePortal} from '../../utils'
16
+ import {isTargetWithinScope} from './isTargetWithinScope'
17
+
18
+ /** @internal */
19
+ export const DEFAULT_DIALOG_CARD_ELEMENT = 'div'
20
+
21
+ /** @internal */
22
+ export type DialogCardOwnProps = CardOwnProps & {
23
+ /**
24
+ * @beta
25
+ */
26
+ __unstable_autoFocus: boolean
27
+ /**
28
+ * @beta
29
+ */
30
+ __unstable_hideCloseButton: boolean
31
+ children: ReactNode
32
+ contentRef?: ForwardedRef<HTMLDivElement>
33
+ footer: ReactNode
34
+ header: ReactNode
35
+ id: string
36
+ onClickOutside?: () => void
37
+ onClose?: () => void
38
+ portal?: string
39
+ }
40
+
41
+ /** @internal */
42
+ export type DialogCardElementType = CardElementType
43
+
44
+ /** @internal */
45
+ export type DialogCardProps<E extends DialogCardElementType = DialogCardElementType> = Props<
46
+ DialogCardOwnProps,
47
+ E
48
+ >
49
+
50
+ /** @internal */
51
+
52
+ export function DialogCard<E extends DialogCardElementType = typeof DEFAULT_DIALOG_CARD_ELEMENT>(
53
+ props: DialogCardProps<E>,
54
+ ) {
55
+ const {
56
+ __unstable_autoFocus: autoFocus,
57
+ __unstable_hideCloseButton: hideCloseButton,
58
+ as = DEFAULT_DIALOG_CARD_ELEMENT,
59
+ children,
60
+ contentRef: forwardedContentRef,
61
+ footer,
62
+ header,
63
+ id,
64
+ onClickOutside,
65
+ onClose,
66
+ portal: portalProp,
67
+ ref: forwardedRef,
68
+ ...rest
69
+ } = props as DialogCardProps<typeof DEFAULT_DIALOG_CARD_ELEMENT>
70
+
71
+ const portal = usePortal()
72
+ const portalElement = portalProp ? portal.elements?.[portalProp] || null : portal.element
73
+ const boundaryElement = useBoundaryElement().element
74
+ const ref = useRef<HTMLDivElement | null>(null)
75
+ const contentRef = useRef<HTMLDivElement | null>(null)
76
+ const layer = useLayer()
77
+ const {isTopLayer} = layer
78
+ const labelId = `${id}_label`
79
+ const showCloseButton = Boolean(onClose) && hideCloseButton === false
80
+ const showHeader = Boolean(header) || showCloseButton
81
+
82
+ useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(forwardedRef, () => ref.current)
83
+ useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(
84
+ forwardedContentRef,
85
+ () => contentRef.current,
86
+ )
87
+
88
+ useEffect(() => {
89
+ if (!autoFocus) return
90
+
91
+ // On mount: focus the first focusable element
92
+ if (ref.current) {
93
+ focusFirstDescendant(ref.current)
94
+ }
95
+ }, [autoFocus, ref])
96
+
97
+ useGlobalKeyDown(
98
+ useCallback(
99
+ (event: KeyboardEvent) => {
100
+ if (!isTopLayer || !onClose) return
101
+
102
+ const target = document.activeElement
103
+
104
+ if (target && !isTargetWithinScope(boundaryElement, portalElement, target)) {
105
+ // Ignore key presses when the focused element is outside of scope
106
+ return
107
+ }
108
+
109
+ if (event.key === 'Escape') {
110
+ event.preventDefault()
111
+ event.stopPropagation()
112
+ onClose()
113
+ }
114
+ },
115
+ [boundaryElement, isTopLayer, onClose, portalElement],
116
+ ),
117
+ )
118
+
119
+ useClickOutsideEvent(
120
+ isTopLayer &&
121
+ onClickOutside &&
122
+ ((event) => {
123
+ const target = event.target as Node | null
124
+
125
+ if (target && !isTargetWithinScope(boundaryElement, portalElement, target)) {
126
+ // Ignore clicks outside of the scope
127
+ return
128
+ }
129
+
130
+ onClickOutside()
131
+ }),
132
+ () => [ref.current],
133
+ )
134
+
135
+ return (
136
+ <Card {...rest} as={as} className={dialogCardRoot()} display="flex" ref={ref}>
137
+ <Flex className={dialogLayout()} direction="column" flex={1}>
138
+ {showHeader && (
139
+ <Box className={dialogHeader()} position="relative">
140
+ <Flex align="flex-start" padding={3}>
141
+ <Box flex={1} padding={2}>
142
+ {header && (
143
+ <Text id={labelId} size={1} weight="semibold">
144
+ {header}
145
+ </Text>
146
+ )}
147
+ </Box>
148
+ {showCloseButton && (
149
+ <Box flex="none">
150
+ <Button
151
+ aria-label="Close dialog"
152
+ disabled={!onClose}
153
+ icon={CloseIcon}
154
+ mode="bleed"
155
+ onClick={onClose}
156
+ padding={2}
157
+ />
158
+ </Box>
159
+ )}
160
+ </Flex>
161
+ </Box>
162
+ )}
163
+ <Box
164
+ className={dialogContent()}
165
+ flex={1}
166
+ overflow="auto"
167
+ position="relative"
168
+ ref={contentRef}
169
+ tabIndex={-1}
170
+ >
171
+ {children}
172
+ </Box>
173
+ {footer && (
174
+ <Box className={dialogFooter()} position="relative">
175
+ {footer}
176
+ </Box>
177
+ )}
178
+ </Flex>
179
+ </Card>
180
+ )
181
+ }
182
+
183
+ DialogCard.displayName = 'DialogCard'
@@ -0,0 +1,14 @@
1
+ import {containsOrEqualsElement} from '../../helpers'
2
+
3
+ export function isTargetWithinScope(
4
+ boundaryElement: HTMLElement | null,
5
+ portalElement: HTMLElement | null,
6
+ target: Node,
7
+ ): boolean {
8
+ if (!boundaryElement || !portalElement) return true
9
+
10
+ return (
11
+ containsOrEqualsElement(boundaryElement, target) ||
12
+ containsOrEqualsElement(portalElement, target)
13
+ )
14
+ }
@@ -1,14 +1,14 @@
1
1
  import {GapStyleProps, RadiusStyleProps, ResponsiveProp} from '@sanity/ui/css'
2
2
  import {FontTextSize, Space} from '@sanity/ui/theme'
3
- import {ForwardedRef, forwardRef} from 'react'
4
3
 
5
4
  import {Box, KBD} from '../../primitives'
6
- import {Props} from '../../types'
5
+ import {ComponentType, Props} from '../../types'
7
6
 
8
- /**
9
- * @public
10
- */
11
- export interface HotkeysProps extends GapStyleProps, RadiusStyleProps {
7
+ /** @public */
8
+ export const DEFAULT_HOTKEYS_ELEMENT = 'kbd'
9
+
10
+ /** @public */
11
+ export interface HotkeysOwnProps extends GapStyleProps, RadiusStyleProps {
12
12
  fontSize?: ResponsiveProp<FontTextSize>
13
13
  padding?: ResponsiveProp<Space>
14
14
  /** @deprecated Use `gap` instead. */
@@ -16,16 +16,35 @@ export interface HotkeysProps extends GapStyleProps, RadiusStyleProps {
16
16
  keys?: string[]
17
17
  }
18
18
 
19
+ /** @public */
20
+ export type HotkeysElementType = 'kbd' | ComponentType
21
+
22
+ /** @public */
23
+ export type HotkeysProps<E extends HotkeysElementType = HotkeysElementType> = Props<
24
+ HotkeysOwnProps,
25
+ E
26
+ >
27
+
19
28
  /**
20
29
  * Represent hotkeys (a keyboard combination) with semantic `<kbd>` elements.
21
30
  *
22
31
  * @public
23
32
  */
24
- export const Hotkeys = forwardRef(function Hotkeys(
25
- props: Props<HotkeysProps, 'div'>,
26
- ref: ForwardedRef<HTMLDivElement>,
33
+ export function Hotkeys<E extends HotkeysElementType = typeof DEFAULT_HOTKEYS_ELEMENT>(
34
+ props: HotkeysProps<E>,
27
35
  ) {
28
- const {fontSize, gap, gapX, gapY, keys, padding, radius, space = 0.5, ...restProps} = props
36
+ const {
37
+ as = DEFAULT_HOTKEYS_ELEMENT,
38
+ fontSize,
39
+ gap,
40
+ gapX,
41
+ gapY,
42
+ keys,
43
+ padding,
44
+ radius,
45
+ space = 0.5,
46
+ ...rest
47
+ } = props as HotkeysProps<typeof DEFAULT_HOTKEYS_ELEMENT>
29
48
 
30
49
  if (!keys || keys.length === 0) {
31
50
  return undefined
@@ -33,14 +52,13 @@ export const Hotkeys = forwardRef(function Hotkeys(
33
52
 
34
53
  return (
35
54
  <Box
36
- as="kbd"
55
+ as={as}
37
56
  data-ui="Hotkeys"
38
- {...restProps}
57
+ {...rest}
39
58
  display="flex"
40
59
  gap={gap ?? space}
41
60
  gapX={gapX}
42
61
  gapY={gapY}
43
- ref={ref}
44
62
  >
45
63
  {keys.map((key, i) => (
46
64
  <KBD fontSize={fontSize} key={i} padding={padding} radius={radius}>
@@ -49,6 +67,6 @@ export const Hotkeys = forwardRef(function Hotkeys(
49
67
  ))}
50
68
  </Box>
51
69
  )
52
- })
70
+ }
53
71
 
54
- Hotkeys.displayName = 'ForwardRef(Hotkeys)'
72
+ Hotkeys.displayName = 'Hotkeys'
@@ -5,11 +5,11 @@ const CustomLink = forwardRef(function CustomLink(
5
5
  props: {req: string} & Omit<React.HTMLProps<HTMLAnchorElement>, 'as' | 'href'>,
6
6
  ref: React.ForwardedRef<HTMLAnchorElement>,
7
7
  ): React.JSX.Element {
8
- const {children, req, ...restProps} = props
8
+ const {children, req, ...rest} = props
9
9
 
10
10
  return (
11
11
  // eslint-disable-next-line jsx-a11y/anchor-is-valid
12
- <a data-required={req} {...restProps} href="#" ref={ref}>
12
+ <a data-required={req} {...rest} href="#" ref={ref}>
13
13
  {children}
14
14
  </a>
15
15
  )
@@ -25,16 +25,16 @@ export default function AsComponentStory() {
25
25
  id="test"
26
26
  menu={
27
27
  <Menu>
28
- <MenuItem as={CustomLink} data-as="a" {...props} padding={3}>
28
+ <MenuItem as={CustomLink} data-as="a" {...props} padding={3} req="1">
29
29
  <Text>Component 1</Text>
30
30
  </MenuItem>
31
- <MenuItem as={CustomLink} data-as="a" {...props} padding={3}>
31
+ <MenuItem as={CustomLink} data-as="a" {...props} padding={3} req="2">
32
32
  <Text>Component 2</Text>
33
33
  </MenuItem>
34
- <MenuItem as={CustomLink} data-as="a" {...props} padding={3}>
34
+ <MenuItem as={CustomLink} data-as="a" {...props} padding={3} req="3">
35
35
  <Text>Component 3</Text>
36
36
  </MenuItem>
37
- <MenuItem as={CustomLink} data-as="a" {...props} padding={3}>
37
+ <MenuItem as={CustomLink} data-as="a" {...props} padding={3} req="4">
38
38
  <Text>Component 3</Text>
39
39
  </MenuItem>
40
40
  </Menu>
@@ -1,32 +1,19 @@
1
- import {
2
- composeClassNames,
3
- GapStyleProps,
4
- menu,
5
- PaddingStyleProps,
6
- ResponsiveProp,
7
- } from '@sanity/ui/css'
8
- import {Space} from '@sanity/ui/theme'
9
- import {
10
- ForwardedRef,
11
- forwardRef,
12
- useCallback,
13
- useEffect,
14
- useImperativeHandle,
15
- useMemo,
16
- useRef,
17
- } from 'react'
1
+ import {composeClassNames, menu, type PaddingStyleProps, type ResponsiveProp} from '@sanity/ui/css'
2
+ import type {Space} from '@sanity/ui/theme'
3
+ import {useCallback, useEffect, useImperativeHandle, useMemo, useRef} from 'react'
18
4
 
19
5
  import {useClickOutsideEvent, useGlobalKeyDown} from '../../hooks'
20
6
  import {Box, Stack} from '../../primitives'
21
- import {Props} from '../../types'
7
+ import type {ComponentType, Props} from '../../types'
22
8
  import {useLayer} from '../../utils'
23
- import {MenuContext, MenuContextValue} from './menuContext'
9
+ import {MenuContext, type MenuContextValue} from './menuContext'
24
10
  import {useMenuController} from './useMenuController'
25
11
 
26
- /**
27
- * @public
28
- */
29
- export interface MenuProps extends PaddingStyleProps, Pick<GapStyleProps, 'gap'> {
12
+ /** @public */
13
+ export const DEFAULT_MENU_ELEMENT = 'div'
14
+
15
+ /** @public */
16
+ export type MenuOwnProps = PaddingStyleProps & {
30
17
  /**
31
18
  * @deprecated Use `shouldFocus="first"` instead.
32
19
  */
@@ -35,6 +22,7 @@ export interface MenuProps extends PaddingStyleProps, Pick<GapStyleProps, 'gap'>
35
22
  * @deprecated Use `shouldFocus="last"` instead.
36
23
  */
37
24
  'focusLast'?: boolean
25
+ 'gap'?: ResponsiveProp<Space>
38
26
  'onClickOutside'?: (event: MouseEvent) => void
39
27
  'onEscape'?: () => void
40
28
  'onItemClick'?: () => void
@@ -48,16 +36,20 @@ export interface MenuProps extends PaddingStyleProps, Pick<GapStyleProps, 'gap'>
48
36
  // 'onBlurCapture'?: (event: FocusEvent) => void
49
37
  }
50
38
 
39
+ /** @public */
40
+ export type MenuElementType = 'div' | 'span' | ComponentType
41
+
42
+ /** @public */
43
+ export type MenuProps<E extends MenuElementType = MenuElementType> = Props<MenuOwnProps, E>
44
+
51
45
  /**
52
46
  * The `Menu` component is a building block for application menus.
53
47
  *
54
48
  * @public
55
49
  */
56
- export const Menu = forwardRef(function Menu(
57
- props: Props<MenuProps, 'div'>,
58
- forwardedRef: ForwardedRef<HTMLDivElement>,
59
- ) {
50
+ export function Menu<E extends MenuElementType = typeof DEFAULT_MENU_ELEMENT>(props: MenuProps<E>) {
60
51
  const {
52
+ as = DEFAULT_MENU_ELEMENT,
61
53
  children,
62
54
  className,
63
55
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -65,7 +57,6 @@ export const Menu = forwardRef(function Menu(
65
57
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
66
58
  focusLast,
67
59
  gap,
68
- // onBlurCapture,
69
60
  onClickOutside,
70
61
  onEscape,
71
62
  onItemClick,
@@ -73,11 +64,13 @@ export const Menu = forwardRef(function Menu(
73
64
  onKeyDown,
74
65
  originElement,
75
66
  padding = 1,
67
+ ref: forwardedRef,
76
68
  registerElement,
77
69
  shouldFocus: _shouldFocus,
78
70
  space = 1,
79
- ...restProps
80
- } = props
71
+ ...rest
72
+ } = props as MenuProps<typeof DEFAULT_MENU_ELEMENT>
73
+
81
74
  const shouldFocus =
82
75
  _shouldFocus ?? ((props.focusFirst && 'first') || (props.focusLast && 'last') || null)
83
76
 
@@ -181,7 +174,8 @@ export const Menu = forwardRef(function Menu(
181
174
  <MenuContext.Provider value={value}>
182
175
  <Box
183
176
  data-ui="Menu"
184
- {...restProps}
177
+ {...rest}
178
+ as={as}
185
179
  className={composeClassNames(className, menu())}
186
180
  onKeyDown={handleKeyDown}
187
181
  overflow="auto"
@@ -195,6 +189,6 @@ export const Menu = forwardRef(function Menu(
195
189
  </Box>
196
190
  </MenuContext.Provider>
197
191
  )
198
- })
192
+ }
199
193
 
200
- Menu.displayName = 'ForwardRef(Menu)'
194
+ Menu.displayName = 'Menu'
@@ -3,7 +3,6 @@ import {
3
3
  cloneElement,
4
4
  FocusEvent,
5
5
  ForwardedRef,
6
- forwardRef,
7
6
  KeyboardEvent,
8
7
  MouseEvent as ReactMouseEvent,
9
8
  ReactElement,
@@ -19,10 +18,8 @@ import {ButtonProps, Popover, PopoverProps} from '../../primitives'
19
18
  import {Placement, Props, Radius} from '../../types'
20
19
  import {MenuProps} from './menu'
21
20
 
22
- /**
23
- * @public
24
- */
25
- export interface MenuButtonProps {
21
+ /** @public */
22
+ export type MenuButtonProps = {
26
23
  /**
27
24
  * @beta Do not use in production.
28
25
  */
@@ -31,7 +28,7 @@ export interface MenuButtonProps {
31
28
  * @deprecated Use `popover={{boundaryElement: element}}` instead.
32
29
  */
33
30
  boundaryElement?: HTMLElement
34
- button: ReactElement<Props<ButtonProps, 'button'>>
31
+ button: ReactElement<ButtonProps>
35
32
  id: string
36
33
  menu?: ReactElement
37
34
  onClose?: () => void
@@ -58,6 +55,7 @@ export interface MenuButtonProps {
58
55
  * @deprecated Use `popover={{preventOverflow: true}}` instead.
59
56
  */
60
57
  preventOverflow?: boolean
58
+ ref?: ForwardedRef<HTMLButtonElement | null>
61
59
  }
62
60
 
63
61
  /**
@@ -65,10 +63,7 @@ export interface MenuButtonProps {
65
63
  *
66
64
  * @public
67
65
  */
68
- export const MenuButton = forwardRef(function MenuButton(
69
- props: MenuButtonProps,
70
- forwardedRef: ForwardedRef<HTMLButtonElement | null>,
71
- ) {
66
+ export function MenuButton(props: MenuButtonProps) {
72
67
  const {
73
68
  __unstable_disableRestoreFocusOnClose: disableRestoreFocusOnClose = false,
74
69
  boundaryElement: deprecated_boundaryElement,
@@ -83,6 +78,7 @@ export const MenuButton = forwardRef(function MenuButton(
83
78
  popover,
84
79
  popoverRadius: deprecated_popoverRadius,
85
80
  preventOverflow: deprecated_preventOverflow,
81
+ ref: forwardedRef,
86
82
  } = props
87
83
  const [open, setOpen] = useState(false)
88
84
  const [shouldFocus, setShouldFocus] = useState<'first' | 'last' | null>(null)
@@ -221,7 +217,7 @@ export const MenuButton = forwardRef(function MenuButton(
221
217
  const button = useMemo(() => {
222
218
  if (!buttonProp) return null
223
219
 
224
- const buttonProps = {
220
+ const buttonProps: ButtonProps<'button'> = {
225
221
  'data-ui': 'MenuButton',
226
222
  id,
227
223
  'onClick': handleButtonClick,
@@ -231,7 +227,7 @@ export const MenuButton = forwardRef(function MenuButton(
231
227
  'aria-expanded': open,
232
228
  'ref': setButtonElement,
233
229
  'selected': buttonProp.props.selected ?? open,
234
- } satisfies Props<ButtonProps, 'button'> & {ref: ForwardedRef<HTMLButtonElement>}
230
+ }
235
231
 
236
232
  return cloneElement(buttonProp, buttonProps)
237
233
  }, [buttonProp, handleButtonClick, handleButtonKeyDown, handleMouseDown, id, open])
@@ -270,6 +266,6 @@ export const MenuButton = forwardRef(function MenuButton(
270
266
  {button || <></>}
271
267
  </Popover>
272
268
  )
273
- })
269
+ }
274
270
 
275
- MenuButton.displayName = 'ForwardRef(MenuButton)'
271
+ MenuButton.displayName = 'MenuButton'
@@ -1,22 +1,34 @@
1
1
  import {composeClassNames, menuDivider} from '@sanity/ui/css'
2
- import {ForwardedRef, forwardRef} from 'react'
3
2
 
4
- import {Props} from '../../types'
3
+ import {ComponentType, Props} from '../../types'
4
+
5
+ /** @public */
6
+ export const DEFAULT_MENU_DIVIDER_ELEMENT = 'hr'
5
7
 
6
8
  /** @public */
7
9
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
8
- export interface MenuDividerProps {}
9
-
10
- /**
11
- * @public
12
- */
13
- export const MenuDivider = forwardRef(function MenuDivider(
14
- props: Props<MenuDividerProps, 'div'>,
15
- ref: ForwardedRef<HTMLHRElement>,
10
+ export type MenuDividerOwnProps = {}
11
+
12
+ /** @public */
13
+ export type MenuDividerElementType = 'div' | 'hr' | 'span' | ComponentType
14
+
15
+ /** @public */
16
+ export type MenuDividerProps<E extends MenuDividerElementType = MenuDividerElementType> = Props<
17
+ MenuDividerOwnProps,
18
+ E
19
+ >
20
+
21
+ /** @public */
22
+ export function MenuDivider<E extends MenuDividerElementType = typeof DEFAULT_MENU_DIVIDER_ELEMENT>(
23
+ props: MenuDividerProps<E>,
16
24
  ) {
17
- const {className, ...restProps} = props
25
+ const {
26
+ as: Element = DEFAULT_MENU_DIVIDER_ELEMENT,
27
+ className,
28
+ ...rest
29
+ } = props as MenuDividerProps<typeof DEFAULT_MENU_DIVIDER_ELEMENT>
18
30
 
19
- return <hr {...restProps} className={composeClassNames(className, menuDivider())} ref={ref} />
20
- })
31
+ return <Element {...rest} className={composeClassNames(className, menuDivider())} />
32
+ }
21
33
 
22
34
  MenuDivider.displayName = 'MenuDivider'