Skip to content

Commit

Permalink
feat: make SplitPane semi-controlled (#457)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: renamed `initialSize` and `initialClosed` props to `size` and `closed`
  • Loading branch information
hamed-musallam authored Mar 13, 2023
1 parent 993f5a1 commit 7374555
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 24 deletions.
39 changes: 27 additions & 12 deletions src/components/split-pane/SplitPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
useState,
RefObject,
useReducer,
useEffect,
} from 'react';
import useResizeObserver from 'use-resize-observer';

Expand Down Expand Up @@ -37,10 +38,10 @@ export interface SplitPaneProps {
*/
controlledSide?: SplitPaneSide;
/**
* Initial size of the controlled side. Unit can be either '%' or 'px'.
* size of the controlled side. Unit can be either '%' or 'px'.
* @default '50%'
*/
initialSize?: SplitPaneSize;
size?: SplitPaneSize;
/**
* Defines whether the pane is initially closed.
* A value of `true` means the pane is always initially closed.
Expand All @@ -52,7 +53,7 @@ export interface SplitPaneProps {
* no longer open or close automatically.
* @default false
*/
initialClosed?: boolean | number;
closed?: boolean | number;
/**
* Called whenever the user finishes resizing the pane.
*/
Expand All @@ -78,25 +79,39 @@ export function SplitPane(props: SplitPaneProps) {
const {
direction = 'horizontal',
controlledSide = 'start',
initialSize = '50%',
initialClosed = false,
size = '50%',
closed = false,
onResize,
onToggle,
children,
} = props;

const minimumSize = typeof initialClosed === 'number' ? initialClosed : null;
const minimumSize = typeof closed === 'number' ? closed : null;

// Whether the pane is explicitly closed. If the value is `false`, the pane
// may still be currently closed because it is smaller than the minimum size.
const [isPaneClosed, closePane, openPane] = useOnOff(
typeof initialClosed === 'boolean' ? initialClosed : false,
typeof closed === 'boolean' ? closed : false,
);

// Whether the user has already interacted with the pane.
const [hasTouched, touch] = useReducer(() => true, false);

const [[size, sizeType], setSize] = useState(() => parseSize(initialSize));
const [[splitSize, sizeType], setSize] = useState(() => parseSize(size));

useEffect(() => {
setSize(parseSize(size));
}, [size]);

useEffect(() => {
if (typeof closed === 'boolean') {
if (closed) {
closePane();
} else {
openPane();
}
}
}, [closePane, closed, openPane]);

const splitterRef = useRef<HTMLDivElement>(null);
const { onMouseDown } = useSplitPaneSize({
Expand Down Expand Up @@ -148,7 +163,7 @@ export function SplitPane(props: SplitPaneProps) {
isFinalClosed,
controlledSide === side,
direction,
size,
splitSize,
sizeType,
);
}
Expand Down Expand Up @@ -214,10 +229,10 @@ function SplitSide(props: SplitSideProps) {
return <div style={style}>{children}</div>;
}

function parseSize(initialSize: string): [number, SplitPaneType] {
const value = Number.parseFloat(initialSize);
function parseSize(size: string): [number, SplitPaneType] {
const value = Number.parseFloat(size);
// remove numbers and dots from the string
const type = initialSize.replace(/[\d .]/g, '') as SplitPaneType;
const type = size.replace(/[\d .]/g, '') as SplitPaneType;

return [value, type];
}
Expand Down
2 changes: 1 addition & 1 deletion src/pages/demo/MainLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export default function MainLayout() {
flex: 1,
}}
>
<SplitPane initialSize="400px" initialClosed={500} controlledSide="end">
<SplitPane size="400px" closed={500} controlledSide="end">
<ErrorBoundary
FallbackComponent={ErrorFallback}
onReset={() => {
Expand Down
2 changes: 1 addition & 1 deletion stories/components/accordion.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ export function WithToggle() {
height: '300px',
}}
>
<SplitPane initialSize="35%">
<SplitPane size="35%">
<div style={{ padding: 5, display: 'flex', gap: 5, height: 40 }}>
<button
type="button"
Expand Down
2 changes: 1 addition & 1 deletion stories/components/modal.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ function DemoPage(props: { openModal: () => void }) {
height: '300px',
}}
>
<SplitPane initialSize="35%">
<SplitPane size="35%">
<div style={{ padding: 5 }}>
<Button
onClick={props.openModal}
Expand Down
18 changes: 9 additions & 9 deletions stories/components/split-pane.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ export function Control(props: Omit<SplitPaneProps, 'children'>) {
}

Control.args = {
initialSize: '50%',
initialClosed: false,
size: '50%',
closed: false,
};

Control.argTypes = {
Expand All @@ -57,7 +57,7 @@ Control.argTypes = {
export function Vertical() {
return (
<div style={{ backgroundColor: 'rgba(165, 180, 252)', height: 400 }}>
<SplitPane direction="vertical" initialSize="200px">
<SplitPane direction="vertical" size="200px">
<div>A</div>
<div>B</div>
</SplitPane>
Expand All @@ -68,7 +68,7 @@ export function Vertical() {
export function Horizontal() {
return (
<div style={{ backgroundColor: 'rgba(147, 197, 253)', height: 200 }}>
<SplitPane direction="horizontal" initialSize="30%">
<SplitPane direction="horizontal" size="30%">
<div>A</div>
<div>B</div>
</SplitPane>
Expand Down Expand Up @@ -133,7 +133,7 @@ export function WithEvilChild() {
height: 300,
}}
>
<SplitPane direction="horizontal" initialSize="300px">
<SplitPane direction="horizontal" size="300px">
<div>I am a good child. 😊</div>
<div
style={{
Expand All @@ -151,8 +151,8 @@ export function WithEvilChild() {
}

export function WithMinimalSize(props: Omit<SplitPaneProps, 'children'>) {
const { controlledSide, initialClosed } = props;
const nbPx = String(initialClosed);
const { controlledSide, closed } = props;
const nbPx = String(closed);
return (
<div
style={{
Expand All @@ -175,8 +175,8 @@ export function WithMinimalSize(props: Omit<SplitPaneProps, 'children'>) {
}

WithMinimalSize.args = {
initialSize: '500px',
initialClosed: 600,
size: '500px',
closed: 600,
};

WithMinimalSize.argTypes = {
Expand Down

0 comments on commit 7374555

Please sign in to comment.