Skip to content

Commit df4d79a

Browse files
authoredMar 14, 2025··
fix(core): update releases navbar menu spacing and click area (#8928)
1 parent f4ea15b commit df4d79a

File tree

5 files changed

+69
-55
lines changed

5 files changed

+69
-55
lines changed
 

‎packages/sanity/src/core/perspective/ReleasesToolLink.tsx

+7-3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {Tooltip} from '../../ui-components/tooltip/Tooltip'
1111
import {RELEASES_TOOL_NAME} from '../releases/plugin'
1212
import {useReleasesStore} from '../releases/store/useReleasesStore'
1313
import {ToolLink} from '../studio/components/navbar/tools/ToolLink'
14+
import {oversizedButtonStyle} from './styles'
1415

1516
const Dot = styled.div({
1617
width: 4,
@@ -19,6 +20,10 @@ const Dot = styled.div({
1920
boxShadow: '0 0 0 1px var(--card-bg-color)',
2021
})
2122

23+
const OversizedButton = styled(ToolLink)`
24+
${oversizedButtonStyle}
25+
`
26+
2227
/**
2328
* represents the calendar icon for the releases tool.
2429
* It will be hidden if users have turned off releases.
@@ -39,13 +44,12 @@ export function ReleasesToolLink(): React.JSX.Element {
3944
return (
4045
<Tooltip content={t('release.navbar.tooltip')}>
4146
<Button
42-
as={ToolLink}
47+
as={OversizedButton}
4348
name={RELEASES_TOOL_NAME}
4449
data-as="a"
45-
className="p-menu-btn"
4650
icon={CalendarIcon}
4751
mode="bleed"
48-
padding={3}
52+
padding={2}
4953
radius="full"
5054
data-testid="releases-tool-link"
5155
selected={activeToolName === RELEASES_TOOL_NAME}

‎packages/sanity/src/core/perspective/navbar/GlobalPerspectiveMenu.tsx

+6-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {styled} from 'styled-components'
66

77
import {MenuButton} from '../../../ui-components'
88
import {CreateReleaseDialog} from '../../releases/components/dialog/CreateReleaseDialog'
9+
import {oversizedButtonStyle} from '../styles'
910
import {type ReleaseId} from '../types'
1011
import {ReleasesList} from './ReleasesList'
1112
import {useScrollIndicatorVisibility} from './useScrollIndicatorVisibility'
@@ -14,6 +15,9 @@ const StyledMenu = styled(Menu)`
1415
min-width: 200px;
1516
max-width: 320px;
1617
`
18+
const OversizedButton = styled(Button)`
19+
${oversizedButtonStyle}
20+
`
1721

1822
export function GlobalPerspectiveMenu({
1923
selectedReleaseId,
@@ -36,13 +40,12 @@ export function GlobalPerspectiveMenu({
3640
<>
3741
<MenuButton
3842
button={
39-
<Button
43+
<OversizedButton
4044
data-testid="global-perspective-menu-button"
4145
iconRight={ChevronDownIcon}
4246
mode="bleed"
43-
padding={3}
47+
padding={2}
4448
radius="full"
45-
className="p-menu-btn"
4649
/>
4750
}
4851
id="releases-menu"
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Card, Flex} from '@sanity/ui'
1+
import {Card} from '@sanity/ui'
22
import {styled} from 'styled-components'
33

44
import {usePerspective} from '../../perspective/usePerspective'
@@ -9,19 +9,13 @@ import {GlobalPerspectiveMenu} from './GlobalPerspectiveMenu'
99

1010
const ReleasesNavContainer = styled(Card)`
1111
position: relative;
12-
--p-bg-color: var(--card-bg-color);
13-
&::after {
14-
content: '';
15-
position: absolute;
16-
top: 0;
17-
left: 0;
18-
right: 0;
19-
bottom: 0;
20-
border: 1px solid var(--card-border-color);
21-
border-radius: 9999px;
22-
pointer-events: none;
23-
z-index: 3;
12+
display: flex;
13+
&:not([hidden]) {
14+
display: flex;
2415
}
16+
align-items: center;
17+
gap: 2px;
18+
padding: 2px;
2519
2620
// The children in button is rendered inside a span, we need to absolutely position the dot for the error.
2721
span:has(> [data-ui='error-status-icon']) {
@@ -31,40 +25,25 @@ const ReleasesNavContainer = styled(Card)`
3125
padding: 0;
3226
}
3327
34-
.p-menu-btn {
35-
margin: -1px;
36-
box-shadow: inset 0 0 0 4px var(--p-bg-color) !important;
37-
}
38-
39-
button.p-menu-btn:hover,
40-
a.p-menu-btn:hover {
28+
a:hover,
29+
button:hover {
4130
position: relative;
4231
z-index: 2;
4332
}
44-
45-
.p-menu-btn:nth-child(2) {
46-
margin-left: -6px;
47-
}
48-
49-
.p-menu-btn:nth-child(3) {
50-
margin-left: -6px;
51-
}
5233
`
5334
export function ReleasesNav(): React.JSX.Element {
5435
const areReleasesEnabled = !!useWorkspace().releases?.enabled
5536

5637
const {selectedPerspective, selectedReleaseId} = usePerspective()
5738

5839
return (
59-
<ReleasesNavContainer flex="none" tone="inherit" radius="full" data-ui="ReleasesNav">
60-
<Flex>
61-
{areReleasesEnabled && <ReleasesToolLink />}
62-
<CurrentGlobalPerspectiveLabel selectedPerspective={selectedPerspective} />
63-
<GlobalPerspectiveMenu
64-
selectedReleaseId={selectedReleaseId}
65-
areReleasesEnabled={areReleasesEnabled}
66-
/>
67-
</Flex>
40+
<ReleasesNavContainer flex="none" tone="inherit" radius="full" data-ui="ReleasesNav" border>
41+
{areReleasesEnabled && <ReleasesToolLink />}
42+
<CurrentGlobalPerspectiveLabel selectedPerspective={selectedPerspective} />
43+
<GlobalPerspectiveMenu
44+
selectedReleaseId={selectedReleaseId}
45+
areReleasesEnabled={areReleasesEnabled}
46+
/>
6847
</ReleasesNavContainer>
6948
)
7049
}

‎packages/sanity/src/core/perspective/navbar/currentGlobalPerspectiveLabel.tsx

+23-12
Original file line numberDiff line numberDiff line change
@@ -5,39 +5,56 @@ import {
55
type ForwardedRef,
66
forwardRef,
77
type ReactNode,
8+
useCallback,
89
useLayoutEffect,
910
useMemo,
1011
useRef,
1112
useState,
1213
} from 'react'
1314
import {IntentLink} from 'sanity/router'
15+
import {styled} from 'styled-components'
1416

1517
import {useTranslation} from '../../i18n/hooks/useTranslation'
1618
import {RELEASES_INTENT} from '../../releases/plugin'
1719
import {isReleaseDocument, type ReleaseDocument} from '../../releases/store/types'
1820
import {getReleaseIdFromReleaseDocumentId} from '../../releases/util/getReleaseIdFromReleaseDocumentId'
1921
import {isDraftPerspective, isPublishedPerspective} from '../../releases/util/util'
22+
import {oversizedButtonStyle} from '../styles'
2023
import {type SelectedPerspective} from '../types'
2124

25+
const OversizedButton = styled(IntentLink)`
26+
${oversizedButtonStyle}
27+
`
28+
2229
function AnimatedTextWidth({children, text}: {children: ReactNode; text: string}) {
2330
const textRef = useRef<HTMLDivElement>(null)
2431
const [containerWidth, setContainerWidth] = useState<null | number>(null) // in pixels
32+
const [isAnimating, setIsAnimating] = useState(false)
2533

2634
useLayoutEffect(() => {
2735
if (!textRef.current) return
2836
const newWidth = textRef.current.offsetWidth
2937
setContainerWidth(newWidth)
3038
}, [text])
3139

40+
const onAnimationStart = useCallback(() => {
41+
setIsAnimating(true)
42+
}, [])
43+
const onAnimationComplete = useCallback(() => {
44+
setIsAnimating(false)
45+
}, [])
46+
3247
return (
3348
<motion.div
3449
style={{
3550
display: 'inline-block',
36-
overflow: 'hidden',
3751
width: containerWidth === null ? 'auto' : containerWidth, // use auto on first render
52+
overflow: isAnimating ? 'hidden' : 'visible',
3853
}}
3954
animate={{width: containerWidth || 'auto'}}
4055
transition={{type: 'spring', bounce: 0, duration: 0.3}}
56+
onAnimationStart={onAnimationStart}
57+
onAnimationComplete={onAnimationComplete}
4158
>
4259
<div ref={textRef} style={{display: 'inline-block', whiteSpace: 'nowrap'}}>
4360
{children}
@@ -57,14 +74,14 @@ const ReleasesLink = ({selectedPerspective}: {selectedPerspective: ReleaseDocume
5774
linkRef: ForwardedRef<HTMLAnchorElement>,
5875
) {
5976
return (
60-
<IntentLink
77+
<OversizedButton
6178
{...intentProps}
6279
ref={linkRef}
6380
intent={RELEASES_INTENT}
6481
params={{id: getReleaseIdFromReleaseDocumentId(selectedPerspective._id)}}
6582
>
6683
{children}
67-
</IntentLink>
84+
</OversizedButton>
6885
)
6986
}),
7087
[selectedPerspective],
@@ -76,10 +93,9 @@ const ReleasesLink = ({selectedPerspective}: {selectedPerspective: ReleaseDocume
7693
data-as="a"
7794
rel="noopener noreferrer"
7895
mode="bleed"
79-
padding={3}
80-
className="p-menu-btn small"
96+
padding={2}
8197
radius="full"
82-
style={{maxWidth: '180px', overflow: 'hidden', textOverflow: 'ellipsis'}}
98+
style={{maxWidth: '180px', textOverflow: 'ellipsis'}}
8399
text={selectedPerspective.metadata?.title || t('release.placeholder-untitled-release')}
84100
/>
85101
)
@@ -97,12 +113,7 @@ export function CurrentGlobalPerspectiveLabel({
97113
text={isReleaseDocument(selectedPerspective) ? selectedPerspective._id : selectedPerspective}
98114
>
99115
{isPublishedPerspective(selectedPerspective) || isDraftPerspective(selectedPerspective) ? (
100-
<Card
101-
tone="inherit"
102-
padding={3}
103-
className="p-menu-btn"
104-
style={{userSelect: 'none', overflow: 'hidden'}}
105-
>
116+
<Card tone="inherit" padding={2} style={{userSelect: 'none', overflow: 'hidden'}}>
106117
<Text size={1} textOverflow="ellipsis" weight="medium">
107118
{isPublishedPerspective(selectedPerspective)
108119
? t('release.chip.published')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import {css} from 'styled-components'
2+
3+
/**
4+
* A CSS helper that extends the clickable area of a component by adding a pseudo-element.
5+
* This creates a larger hit area for better usability without affecting the visual size.
6+
*/
7+
export const oversizedButtonStyle = css`
8+
position: relative;
9+
cursor: default;
10+
&::before {
11+
content: '';
12+
position: absolute;
13+
display: block;
14+
inset: -4px;
15+
border-radius: 9999px;
16+
}
17+
`

0 commit comments

Comments
 (0)
Please sign in to comment.