Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@ The `SplitterLayout` component supports the following props.
If this prop is not defined, `SplitterLayout` tries to split the layout with equal sizes.
(Note: equal size may not apply when there are nested layouts.)

* `secondaryHidden: PropTypes.bool`

Determine whether the secondary pane is visible or not. The default value is `false`.

Use this prop for hiding the secondary pane in favour of conditional rendering to avoid re-renders.

* `onDragStart: PropTypes.func`

Called when dragging is started.
Expand Down
67 changes: 50 additions & 17 deletions example/javascripts/components/TogglableSidebarLayout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,48 +6,81 @@ export default class TogglableSidebarLayout extends React.Component {
constructor(props) {
super(props);
this.toggleSidebar = this.toggleSidebar.bind(this);
this.toggleUseSecondaryHidden = this.toggleUseSecondaryHidden.bind(this);
this.state = {
sidebarVisible: true
sidebarVisible: true,
useSecondaryHidden: false
};
}

toggleSidebar() {
this.setState(state => ({ sidebarVisible: !state.sidebarVisible }));
}

toggleUseSecondaryHidden() {
this.setState(state => ({ useSecondaryHidden: !state.useSecondaryHidden }));
}

render() {
return (
<SplitterLayout percentage secondaryInitialSize={25}>
<SplitterLayout
percentage
secondaryInitialSize={25}
secondaryHidden={this.state.useSecondaryHidden ? !this.state.sidebarVisible : undefined}
>
<div className="my-pane">
<h2>1st Pane</h2>
<p>This is the 1st pane, and this is the primary pane by default.</p>
<button type="button" onClick={this.toggleSidebar}>
{this.state.sidebarVisible && 'Hide Sidebar'}
{!this.state.sidebarVisible && 'Show Sidebar'}
</button>
<pre>
&lt;SplitterLayout primaryIndex={'{0}'}&gt;{'\n'}
&nbsp;&nbsp;<strong>&lt;div&gt;1st&lt;/div&gt;</strong>{'\n'}
{this.state.sidebarVisible && ' <div>2nd</div>\n'}
&lt;/SplitterLayout&gt;
</pre>
<button type="button" style={{ marginLeft: 16 }} onClick={this.toggleUseSecondaryHidden}>
{this.state.useSecondaryHidden && 'Use secondaryHidden prop to hide secondary pane'}
{!this.state.useSecondaryHidden && 'Use conditional rendering to hide secondary pane'}
</button>
{!this.state.useSecondaryHidden && (
<pre>
&lt;SplitterLayout primaryIndex={'{0}'}&gt;{'\n'}
&nbsp;&nbsp;<strong>&lt;div&gt;1st&lt;/div&gt;</strong>{'\n'}
{this.state.sidebarVisible && ' <div>2nd</div>\n'}
&lt;/SplitterLayout&gt;
</pre>
)}
{this.state.useSecondaryHidden && (
<pre>
&lt;SplitterLayout primaryIndex={'{0}'} secondaryHidden={`{${!this.state.sidebarVisible}}`}&gt;{'\n'}
&nbsp;&nbsp;<strong>&lt;div&gt;1st&lt;/div&gt;</strong>{'\n'}
{' <div>2nd</div>\n'}
&lt;/SplitterLayout&gt;
</pre>
)}

<Lorem title="1st Pane" />
</div>
{this.state.sidebarVisible &&
(
<div className="my-pane">
<h2>2nd Pane</h2>
<p>This is the 2nd pane, considered as a sidebar.</p>
{(this.state.sidebarVisible || this.state.useSecondaryHidden) && (
<div className="my-pane">
<h2>2nd Pane</h2>
<p>This is the 2nd pane, considered as a sidebar.</p>
{!this.state.useSecondaryHidden && (
<pre>
&lt;SplitterLayout primaryIndex={'{0}'}&gt;{'\n'}
&nbsp;&nbsp;&lt;div&gt;1st&lt;/div&gt;{'\n'}
&nbsp;&nbsp;<strong>&lt;div&gt;2nd&lt;/div&gt;</strong>{'\n'}
&lt;/SplitterLayout&gt;
</pre>
<Lorem title="2nd Pane" />
</div>
)
}
)}
{this.state.useSecondaryHidden && (
<pre>
&lt;SplitterLayout primaryIndex={'{0}'} secondaryHidden={`{${!this.state.sidebarVisible}}`}&gt;{'\n'}
&nbsp;&nbsp;&lt;div&gt;1st&lt;/div&gt;{'\n'}
&nbsp;&nbsp;<strong>&lt;div&gt;2nd&lt;/div&gt;</strong>{'\n'}
&lt;/SplitterLayout&gt;
</pre>
)}
<Lorem title="2nd Pane" />
</div>
)}
</SplitterLayout>
);
}
Expand Down
7 changes: 6 additions & 1 deletion src/components/Pane.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ import PropTypes from 'prop-types';
function Pane(props) {
const size = props.size || 0;
const unit = props.percentage ? '%' : 'px';
const hidden = props.hidden || false;
let classes = 'layout-pane';
const style = {};
if (!props.primary) {
if (props.vertical) {
if (hidden) {
style.display = 'none';
} else if (props.vertical) {
style.height = `${size}${unit}`;
} else {
style.width = `${size}${unit}`;
Expand All @@ -24,6 +27,7 @@ Pane.propTypes = {
vertical: PropTypes.bool,
primary: PropTypes.bool,
size: PropTypes.number,
hidden: PropTypes.bool,
percentage: PropTypes.bool,
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
Expand All @@ -35,6 +39,7 @@ Pane.defaultProps = {
vertical: false,
primary: false,
size: 0,
hidden: false,
percentage: false,
children: []
};
Expand Down
16 changes: 13 additions & 3 deletions src/components/SplitterLayout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -190,12 +190,20 @@ class SplitterLayout extends React.Component {
for (let i = 0; i < children.length; ++i) {
let primary = true;
let size = null;
let hidden = false;
if (children.length > 1 && i !== primaryIndex) {
primary = false;
size = this.state.secondaryPaneSize;
hidden = this.props.secondaryHidden === true;
}
wrappedChildren.push(
<Pane vertical={this.props.vertical} percentage={this.props.percentage} primary={primary} size={size}>
<Pane
vertical={this.props.vertical}
percentage={this.props.percentage}
primary={primary}
size={size}
hidden={hidden}
>
{children[i]}
</Pane>
);
Expand All @@ -204,7 +212,7 @@ class SplitterLayout extends React.Component {
return (
<div className={containerClasses} ref={(c) => { this.container = c; }}>
{wrappedChildren[0]}
{wrappedChildren.length > 1 &&
{!this.props.secondaryHidden && wrappedChildren.length > 1 &&
(
<div
role="separator"
Expand All @@ -215,7 +223,7 @@ class SplitterLayout extends React.Component {
/>
)
}
{wrappedChildren.length > 1 && wrappedChildren[1]}
{wrappedChildren[1] || false}
</div>
);
}
Expand All @@ -229,6 +237,7 @@ SplitterLayout.propTypes = {
primaryMinSize: PropTypes.number,
secondaryInitialSize: PropTypes.number,
secondaryMinSize: PropTypes.number,
secondaryHidden: PropTypes.bool,
onDragStart: PropTypes.func,
onDragEnd: PropTypes.func,
onSecondaryPaneSizeChange: PropTypes.func,
Expand All @@ -243,6 +252,7 @@ SplitterLayout.defaultProps = {
primaryMinSize: 0,
secondaryInitialSize: undefined,
secondaryMinSize: 0,
secondaryHidden: false,
onDragStart: null,
onDragEnd: null,
onSecondaryPaneSizeChange: null,
Expand Down
23 changes: 23 additions & 0 deletions test/SplitterLayout.spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,24 @@ describe('SplitterLayout', () => {
expect(output.props.children[2].props.primary).toBe(false);
expect(output.props.children[2].props.percentage).toBe(false);
});

it('should render 2 children when 2 children provided and secondaryHidden is true', () => {
const output = render(2, { secondaryHidden: true });
expect(output.type).toBe('div');
expect(output.props.className).toBe('splitter-layout');
expect(output.props.children.length).toBe(3);
expect(output.props.children[0].type).toBe(Pane);
expect(output.props.children[0].props.vertical).toBe(false);
expect(output.props.children[0].props.primary).toBe(true);
expect(output.props.children[0].props.percentage).toBe(false);
expect(output.props.children[0].props.hidden).toBe(false);
expect(output.props.children[1]).toBe(false);
expect(output.props.children[2].type).toBe(Pane);
expect(output.props.children[2].props.vertical).toBe(false);
expect(output.props.children[2].props.primary).toBe(false);
expect(output.props.children[2].props.percentage).toBe(false);
expect(output.props.children[2].props.hidden).toBe(true);
});
});

describe('sizing', () => {
Expand Down Expand Up @@ -550,5 +568,10 @@ describe('SplitterLayout', () => {
const component = renderIntoDocument(2, { secondaryInitialSize: 20, vertical: true });
expect(component.state.secondaryPaneSize).toBe(20);
});

it('should set secondary size to zero if secondary is hidden', () => {
const component = renderIntoDocument(2, { secondaryHidden: true });
expect(component.state.secondaryPaneSize).toBe(-4);
});
});
});