Skip to content

Commit

Permalink
Re-compute split if child nodes are inserted dynamically; Fix bug tha…
Browse files Browse the repository at this point in the history
…t caused overriding of user defined gutter classes
  • Loading branch information
mlejva committed Dec 15, 2021
1 parent b82e390 commit 850bff8
Show file tree
Hide file tree
Showing 12 changed files with 166 additions and 36 deletions.
8 changes: 7 additions & 1 deletion example/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import InitialSizes from './InitialSizes';
import ScrollableChildren from './ScrollableChildren';
import OnDidResizeSplit from './OnDidResize';
import DynamicParentSize from './DynamicParentSize';
import DynamicChildren from './DynamicChildren';

enum Page {
SingleHorizontal,
Expand All @@ -26,6 +27,7 @@ enum Page {
ScrollableChildren,
OnDidResize,
DynamicParent,
DynamicChildren,
}

function App() {
Expand All @@ -45,6 +47,7 @@ function App() {
<button onClick={() => setPage(Page.ScrollableChildren)}>Scrollable tiles</button>
<button onClick={() => setPage(Page.OnDidResize)}>On size change</button>
<button onClick={() => setPage(Page.DynamicParent)}>Dynamic parent size</button>
<button onClick={() => setPage(Page.DynamicChildren)}>Dynamic children</button>
</div>

<div className="splits">
Expand Down Expand Up @@ -87,6 +90,13 @@ function App() {
<DynamicParentSize />
</>
}
{
page === Page.DynamicChildren &&
<>
<p>Additional child nodes will be inserted after 5s.</p>
<DynamicChildren />
</>
}
</div>
</div>
);
Expand Down
33 changes: 33 additions & 0 deletions example/src/DynamicChildren/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useEffect, useState } from 'react'
import ReactSplit, { SplitDirection } from '@devbookhq/splitter';
import Tile from '../Tile';

function HorizontalSplit() {
const [insertChildren, setInsertChildren] = useState(false)

useEffect(() => {
setTimeout(() => {
setInsertChildren(true)
}, 5000)
}, [])

return (
<ReactSplit>
<Tile>Initial child node</Tile>
{insertChildren &&
<>
<Tile>Dynamically inserted child 1</Tile>
<ReactSplit
direction={SplitDirection.Vertical}
>
<Tile>Dynamically inserted child 2</Tile>
<Tile>Dynamically inserted child 3</Tile>
</ReactSplit>
</>
}
</ReactSplit>
);
}

export default HorizontalSplit;

4 changes: 2 additions & 2 deletions example/src/DynamicParentSize/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react'
import styled from 'styled-components';
import Tile from '../Tile';
import ReactSplit, { SplitDirection } from '@devbookhq/splitter';
import Tile from '../Tile';

const Zero = styled.div<{ isReady: boolean }>`
height: 100%;
Expand All @@ -26,7 +26,7 @@ function HorizontalSplit() {
<Tile/>
<Tile/>
</ReactSplit>
</Zero>
</Zero>
);
}

Expand Down
2 changes: 1 addition & 1 deletion example/src/InitialSizes/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Tile from '../Tile';
import ReactSplit, { SplitDirection } from '@devbookhq/splitter';
import Tile from '../Tile';

function InitialSizes() {
return (
Expand Down
6 changes: 3 additions & 3 deletions example/src/OnDidResize/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import React, { useState } from 'react';
import styled from 'styled-components';
import Tile from '../Tile';
import ReactSplit, { SplitDirection } from '@devbookhq/splitter';
import Tile from '../Tile';

function OnDidResizeSplit() {
const [sizes, setSizes] = useState([1/3, 1/3, 1/3]);
const [sizes, setSizes] = useState([1/3 * 100, 1/3 * 100, 1/3 * 100]);

function handleResize(gutterIdx: number, allSizes: number[]) {
console.log('gutterIdx', gutterIdx);
Expand All @@ -16,6 +15,7 @@ function OnDidResizeSplit() {
<ReactSplit
direction={SplitDirection.Horizontal}
onResizeFinished={handleResize}
initialSizes={sizes}
>
<Tile>Takes {sizes[0]}%</Tile>
<Tile>Takes {sizes[1]}%</Tile>
Expand Down
36 changes: 34 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@types/node": "^12.20.4",
"@types/react": "^17.0.20",
"@types/react-dom": "^17.0.9",
"@types/react-is": "^17.0.3",
"autoprefixer": "^10.2.5",
"postcss": "^8.2.7",
"rollup": "^2.56.2",
Expand All @@ -46,5 +47,8 @@
"react",
"resize",
"split"
]
],
"dependencies": {
"react-is": "^17.0.2"
}
}
10 changes: 5 additions & 5 deletions src/Gutter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { SplitDirection, GutterTheme } from './index';

interface GutterProps {
className?: string;
theme?: GutterTheme;
theme: GutterTheme;
draggerClassName?: string;
direction?: SplitDirection;
onMouseDown?: (e: any) => void;
Expand All @@ -12,17 +12,17 @@ interface GutterProps {
const Gutter = React.forwardRef<HTMLDivElement, GutterProps>((
{
className,
theme = GutterTheme.Dark,
theme,
draggerClassName,
direction = SplitDirection.Vertical,
onMouseDown,
},
ref,
) => {
const containerClass = `__dbk__gutter ${direction} ${theme} ${className || ''}`;
const draggerClass = `__dbk__dragger ${direction} ${theme} ${draggerClassName || ''}`;
const containerClass = `__dbk__gutter ${direction} ${className || theme}`;
const draggerClass = `__dbk__dragger ${direction} ${draggerClassName || theme}`;

return (
return (
<div
className={containerClass}
ref={ref}
Expand Down
42 changes: 21 additions & 21 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Gutter from './Gutter';
import { ActionType } from './state/reducer.actions';
import reducer, { State } from './state/reducer';
import getGutterSizes from './utils/getGutterSize';
import flattenChildren from './utils/flattenChildren';

export enum SplitDirection {
Horizontal = 'Horizontal',
Expand Down Expand Up @@ -70,11 +71,14 @@ function Split({
gutterTheme = GutterTheme.Dark,
gutterClassName,
draggerClassName,
children,
children: reactChildren,
onResizeStarted,
onResizeFinished,
classes = [],
}: SplitProps) {
const children = flattenChildren(reactChildren)
console.log('children', children)

const [state, dispatch] = useReducer(reducer, initialState);

const containerRef = useRef<HTMLDivElement>(null)
Expand All @@ -85,7 +89,7 @@ function Split({
gutterRefs.current = [];

// Helper dispatch functions.
const setIsRenderToCompute = React.useCallback((isReady: boolean) => {
const setIsReadyToCompute = React.useCallback((isReady: boolean) => {
dispatch({
type: ActionType.SetIsReadyToCompute,
payload: { isReady },
Expand Down Expand Up @@ -325,7 +329,7 @@ function Split({
const style = getComputedStyle(el)
const size = direction === SplitDirection.Horizontal ? el.clientWidth : el.clientHeight
const isReady = !!style && !!size
setIsRenderToCompute(isReady)
setIsReadyToCompute(isReady)
})
observer.observe(el)

Expand All @@ -340,21 +344,24 @@ function Split({
// Initial setup, runs every time the child views change.
useEffect(function initialSetup() {
if (!state.isReady) return

if (children === undefined) throw new Error(`Cannot initialize split - 'children' is undefined`);
// Handle gracefully is user specified only one child elment.
if (!Array.isArray(children) || children.length <= 1) {
return
}
// throw new Error(`Cannot initialize split - the 'children' array has 1 or less elements. Provide at least 2 child views for Splitter`);
// No work to do if there's only one child.
if (children.length <= 1) return

// By the time first useEffect runs refs should be already set, unless something really bad happened.
if (!childRefs.current || !gutterRefs.current)
if (!childRefs.current || !gutterRefs.current) {
throw new Error(`Cannot create pairs - either variable 'childRefs' or 'gutterRefs' is undefined`);
}

setInitialSizes(direction, childRefs.current, gutterRefs.current, initialSizes);
createPairs(direction, childRefs.current, gutterRefs.current);
}, [state.isReady, direction, setInitialSizes, createPairs, initialSizes]);
}, [
reactChildren,
state.isReady,
direction,
setInitialSizes,
createPairs,
initialSizes,
]);

function addRef(refs: typeof childRefs | typeof gutterRefs, el: any) {
if (!refs.current) throw new Error(`Can't add element to ref object - ref isn't initialized`);
Expand All @@ -368,14 +375,7 @@ function Split({
className={'__dbk__container ' + `${direction}`}
ref={containerRef}
>
{/* User passed only a single child node. */}
{children && !Array.isArray(children) && (
<>
{children}
</>
)}

{state.isReady && children && Array.isArray(children) && children.map((c, idx) => (
{state.isReady && children.map((c, idx) => (
<React.Fragment key={idx}>
<div
ref={el => addRef(childRefs, el)}
Expand All @@ -384,7 +384,7 @@ function Split({
</div>

{/* Gutter is between each two child views. */}
{idx < children.length - 1 &&
{idx < (children as React.ReactNodeArray).length - 1 &&
<Gutter
ref={el => addRef(gutterRefs, el)}
className={gutterClassName}
Expand Down
Loading

0 comments on commit 850bff8

Please sign in to comment.