Skip to content

Commit f5d2fe9

Browse files
authored
extract resize handle in a dedicated component
1 parent 281fda6 commit f5d2fe9

File tree

3 files changed

+95
-72
lines changed

3 files changed

+95
-72
lines changed

src/PageLayout/PageLayout.stories.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -701,7 +701,11 @@ export const ResizablePane: Story = args => (
701701
regular: args['Pane.position.regular'],
702702
wide: args['Pane.position.wide']
703703
}}
704-
divider="line"
704+
divider={{
705+
narrow: args['Pane.divider.narrow'],
706+
regular: args['Pane.divider.regular'],
707+
wide: args['Pane.divider.wide']
708+
}}
705709
canResizePane={true}
706710
paneWidthStorageKey="primer-react.pane-width"
707711
>

src/PageLayout/PageLayout.tsx

Lines changed: 78 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,68 @@ const Root: React.FC<React.PropsWithChildren<PageLayoutProps>> = ({
104104
Root.displayName = 'PageLayout'
105105

106106
// ----------------------------------------------------------------------------
107-
// Divider (internal)
107+
// ResizeHandle (internal)
108108

109-
type DividerProps = {
110-
variant?: 'none' | 'line' | 'filled' | ResponsiveValue<'none' | 'line' | 'filled'>
111-
canResize?: boolean
109+
type ResizeHandleProps = {
112110
isResizing?: boolean
113111
onClick?: (e: React.MouseEvent) => void
114112
onMouseDown?: (e: React.MouseEvent) => void
113+
}
114+
115+
const ResizeHandle: React.FC<React.PropsWithChildren<ResizeHandleProps>> = ({isResizing, onClick, onMouseDown}) => {
116+
return (
117+
<Box
118+
sx={{
119+
position: 'absolute',
120+
top: 0,
121+
bottom: 0
122+
}}
123+
>
124+
<Box
125+
onMouseDown={onMouseDown}
126+
onClick={onClick}
127+
sx={{
128+
width: '16px',
129+
height: '100%',
130+
display: 'flex',
131+
justifyContent: 'center',
132+
position: 'absolute',
133+
transform: 'translateX(50%)',
134+
right: 0,
135+
opacity: isResizing ? 1 : 0,
136+
cursor: 'col-resize',
137+
'&:hover': {
138+
animation: isResizing ? 'none' : 'resizer-appear 80ms 300ms both',
139+
140+
'@keyframes resizer-appear': {
141+
from: {
142+
opacity: 0
143+
},
144+
145+
to: {
146+
opacity: 1
147+
}
148+
}
149+
}
150+
}}
151+
>
152+
<Box
153+
sx={{
154+
backgroundColor: 'accent.fg',
155+
width: '1px',
156+
height: '100%'
157+
}}
158+
/>
159+
</Box>
160+
</Box>
161+
)
162+
}
163+
164+
// ----------------------------------------------------------------------------
165+
// Divider (internal)
166+
167+
type DividerProps = {
168+
variant?: 'none' | 'line' | 'filled' | ResponsiveValue<'none' | 'line' | 'filled'>
115169
} & SxProp
116170

117171
const horizontalDividerVariants = {
@@ -184,65 +238,20 @@ const verticalDividerVariants = {
184238
}
185239
}
186240

187-
const VerticalDivider: React.FC<React.PropsWithChildren<DividerProps>> = ({
188-
variant = 'none',
189-
canResize,
190-
isResizing,
191-
onClick,
192-
onMouseDown,
193-
sx = {}
194-
}) => {
241+
const VerticalDivider: React.FC<React.PropsWithChildren<DividerProps>> = ({variant = 'none', sx = {}}) => {
195242
const responsiveVariant = useResponsiveValue(variant, 'none')
196243
return (
197244
<Box
198245
sx={merge<BetterSystemStyleObject>(
199246
{
200-
height: '100%',
201-
position: 'relative',
247+
position: 'absolute',
248+
top: 0,
249+
bottom: 0,
202250
...verticalDividerVariants[responsiveVariant]
203251
},
204252
sx
205253
)}
206-
>
207-
{canResize && (
208-
<Box
209-
onMouseDown={onMouseDown}
210-
onClick={onClick}
211-
sx={{
212-
width: '16px',
213-
height: '100%',
214-
display: 'flex',
215-
justifyContent: 'center',
216-
position: 'absolute',
217-
transform: 'translateX(50%)',
218-
right: 0,
219-
opacity: isResizing ? 1 : 0,
220-
cursor: 'col-resize',
221-
'&:hover': {
222-
animation: isResizing ? 'none' : 'resizer-appear 80ms 300ms both',
223-
224-
'@keyframes resizer-appear': {
225-
from: {
226-
opacity: 0
227-
},
228-
229-
to: {
230-
opacity: 1
231-
}
232-
}
233-
}
234-
}}
235-
>
236-
<Box
237-
sx={{
238-
backgroundColor: 'accent.fg',
239-
width: '1px',
240-
height: '100%'
241-
}}
242-
/>
243-
</Box>
244-
)}
245-
</Box>
254+
/>
246255
)
247256
}
248257

@@ -506,18 +515,22 @@ const Pane = React.forwardRef<HTMLDivElement, React.PropsWithChildren<PageLayout
506515
}
507516
}, [sticky, enableStickyPane, disableStickyPane, offsetHeader])
508517

518+
const containerRef = React.useRef<HTMLDivElement>(null)
519+
509520
const paneRef = React.useRef<HTMLDivElement>(null)
510521
useRefObjectAsForwardedRef(forwardRef, paneRef)
511522

512523
const {onMouseDown, onClick, isResizing, paneWidth} = useHorizontalResize(
513524
canResizePane,
514525
position,
515526
paneRef,
527+
containerRef,
516528
paneWidthStorageKey
517529
)
518530

519531
return (
520532
<Box
533+
ref={containerRef}
521534
// eslint-disable-next-line @typescript-eslint/no-explicit-any
522535
sx={(theme: any) =>
523536
merge<BetterSystemStyleObject>(
@@ -559,14 +572,17 @@ const Pane = React.forwardRef<HTMLDivElement, React.PropsWithChildren<PageLayout
559572
variant={{narrow: dividerVariant, regular: 'none'}}
560573
sx={{[position === 'end' ? 'marginBottom' : 'marginTop']: SPACING_MAP[rowGap]}}
561574
/>
562-
<VerticalDivider
563-
variant={{narrow: 'none', regular: dividerVariant}}
564-
canResize={canResizePane}
565-
isResizing={isResizing}
566-
onClick={onClick}
567-
onMouseDown={onMouseDown}
568-
sx={{[position === 'end' ? 'marginRight' : 'marginLeft']: SPACING_MAP[columnGap]}}
569-
/>
575+
576+
<Box
577+
sx={{
578+
position: 'relative',
579+
height: '100%',
580+
[position === 'end' ? 'marginRight' : 'marginLeft']: SPACING_MAP[columnGap]
581+
}}
582+
>
583+
<VerticalDivider variant={{narrow: 'none', regular: dividerVariant}} />
584+
{canResizePane && <ResizeHandle isResizing={isResizing} onClick={onClick} onMouseDown={onMouseDown} />}
585+
</Box>
570586
<Box
571587
ref={paneRef}
572588
sx={(theme: Theme) => ({

src/PageLayout/useHorizontalResize.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export function useHorizontalResize(
44
enabled: boolean,
55
panePosition: 'start' | 'end',
66
paneRef: RefObject<HTMLDivElement>,
7+
containerRef: RefObject<HTMLDivElement>,
78
storageKey?: string
89
) {
910
const [isDragging, setIsDragging] = useState(false)
@@ -47,26 +48,28 @@ export function useHorizontalResize(
4748

4849
const onMouseMove = useCallback(
4950
(e: MouseEvent) => {
50-
if (isDragging && paneRef.current) {
51+
if (isDragging && paneRef.current && containerRef.current) {
5152
setIsResizing(true)
5253

53-
const rect = paneRef.current.getBoundingClientRect()
54-
const clientLeft = rect.left
55-
const clientRight = rect.right
56-
const paneCurrentWidth = rect.width
57-
const marginRight = clientRight - paneCurrentWidth - clientLeft
54+
const paneRect = paneRef.current.getBoundingClientRect()
55+
const containerRect = containerRef.current.getBoundingClientRect()
56+
57+
const paneCurrentWidth = paneRect.width
58+
59+
const marginLeft = paneRect.left
60+
const marginRight = containerRect.width - paneCurrentWidth
5861

5962
if (panePosition === 'start') {
60-
const newSize = e.clientX - clientLeft - marginRight
63+
const newSize = e.clientX - marginLeft - marginRight
6164
setPaneWidth(newSize)
6265
} else {
63-
const newSize = clientRight - e.clientX
66+
const newSize = paneRect.right - e.clientX - marginRight
6467
setPaneWidth(newSize)
6568
}
6669
e.preventDefault()
6770
}
6871
},
69-
[isDragging, panePosition, paneRef]
72+
[isDragging, panePosition, paneRef, containerRef]
7073
)
7174

7275
const onMouseUp = useCallback(() => {

0 commit comments

Comments
 (0)