Skip to content

Commit

Permalink
feat: add visible prop to Pane (#137)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnwalley committed Feb 8, 2022
1 parent bf2834a commit 3b0542c
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 7 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ Minimum size of this pane. Overrides `minSize` set on parent component.

Enable snap to zero for this pane. Overrides `snap` set on parent component.

### visible

Whether the pane should be visible.

## Styling

Allotment uses [CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) for styling.
Expand Down
27 changes: 25 additions & 2 deletions src/allotment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ function isPane(item: React.ReactNode): item is typeof Pane {
return (item as any).type.displayName === "Allotment.Pane";
}

function isPaneProps(props: AllotmentProps | PaneProps): props is PaneProps {
return (props as PaneProps).visible !== undefined;
}

export interface CommonProps {
/** Maximum size of each element */
maxSize?: number;
Expand All @@ -30,6 +34,7 @@ export interface CommonProps {

export type PaneProps = {
children: React.ReactNode;
visible?: boolean;
} & CommonProps;

export const Pane = forwardRef<HTMLDivElement, PaneProps>(
Expand Down Expand Up @@ -85,7 +90,9 @@ const Allotment = forwardRef<AllotmentHandle, AllotmentProps>(
) => {
const containerRef = useRef<HTMLDivElement>(null!);
const previousKeys = useRef<string[]>([]);
const splitViewPropsRef = useRef(new Map<React.Key, CommonProps>());
const splitViewPropsRef = useRef(
new Map<React.Key, AllotmentProps | PaneProps>()
);
const splitViewRef = useRef<SplitView | null>(null);
const splitViewViewRef = useRef(new Map<React.Key, HTMLElement>());

Expand Down Expand Up @@ -173,12 +180,13 @@ const Allotment = forwardRef<AllotmentHandle, AllotmentProps>(
}, []);

/**
* Add or remove views as number of children changes
* Add, remove or update views as children change
*/
useEffect(() => {
const keys = childrenArray.map((child) => child.key as string);

const enter = keys.filter((key) => !previousKeys.current.includes(key));
const update = keys.filter((key) => previousKeys.current.includes(key));
const exit = previousKeys.current.map((key) => !keys.includes(key));

exit.forEach((flag, index) => {
Expand All @@ -204,6 +212,21 @@ const Allotment = forwardRef<AllotmentHandle, AllotmentProps>(
);
}

for (const updateKey of update) {
const props = splitViewPropsRef.current.get(updateKey);
const index = keys.findIndex((key) => key === updateKey);

if (props && isPaneProps(props)) {
if (props.visible !== undefined) {
if (splitViewRef.current?.isViewVisible(index) === props.visible) {
return;
}

splitViewRef.current?.setViewVisible(index, props.visible);
}
}
}

if (enter.length > 0 || exit.length > 0) {
previousKeys.current = keys;
}
Expand Down
12 changes: 12 additions & 0 deletions src/helpers/range.ts → src/helpers/array.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
/**
* Pushes an element to the end of the array, if found.
*/
export function pushToEnd<T>(arr: T[], value: T): void {
const index = arr.indexOf(value);

if (index > -1) {
arr.splice(index, 1);
arr.push(value);
}
}

/**
* Returns an array containing an arithmetic progression.
*
Expand Down
45 changes: 41 additions & 4 deletions src/split-view/split-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import EventEmitter from "eventemitter3";
import clamp from "lodash.clamp";

import styles from "../allotment.module.css";
import { pushToEnd, range } from "../helpers/array";
import { Disposable } from "../helpers/disposable";
import { range } from "../helpers/range";
import {
Orientation,
Sash,
Expand Down Expand Up @@ -190,7 +190,7 @@ abstract class ViewItem {
return typeof this._cachedVisibleSize === "undefined";
}

setVisible(visible: boolean, size?: number): void {
public setVisible(visible: boolean, size?: number): void {
if (visible === this.visible) {
return;
}
Expand Down Expand Up @@ -607,6 +607,39 @@ export class SplitView extends EventEmitter implements Disposable {
return this.viewItems[index].size;
}

/**
* Returns whether the {@link View view} is visible.
*
* @param index The {@link View view} index.
*/
isViewVisible(index: number): boolean {
if (index < 0 || index >= this.viewItems.length) {
throw new Error("Index out of bounds");
}

const viewItem = this.viewItems[index];
return viewItem.visible;
}

/**
* Set a {@link View view}'s visibility.
*
* @param index The {@link View view} index.
* @param visible Whether the {@link View view} should be visible.
*/
setViewVisible(index: number, visible: boolean): void {
if (index < 0 || index >= this.viewItems.length) {
throw new Error("Index out of bounds");
}

const viewItem = this.viewItems[index];
viewItem.setVisible(visible);

this.distributeEmptySpace(index);
this.layoutViews();
this.saveProportions();
}

/**
* Distribute the entire {@link SplitView} size among all {@link View views}.
*/
Expand Down Expand Up @@ -884,11 +917,15 @@ export class SplitView extends EventEmitter implements Disposable {
return delta;
}

private distributeEmptySpace(): void {
private distributeEmptySpace(lowPriorityIndex?: number): void {
const contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
let emptyDelta = this.size - contentSize;

const indexes = range(0, this.viewItems.length);
const indexes = range(this.viewItems.length - 1, -1, -1);

if (typeof lowPriorityIndex === "number") {
pushToEnd(indexes, lowPriorityIndex);
}

for (let i = 0; emptyDelta !== 0 && i < indexes.length; i++) {
const item = this.viewItems[indexes[i]];
Expand Down
29 changes: 28 additions & 1 deletion stories/allotment.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
AllotmentProps,
setSashSize,
} from "../src";
import { range } from "../src/helpers/range";
import { range } from "../src/helpers/array";
import styles from "./allotment.stories.module.css";
import { Content } from "./content";

Expand Down Expand Up @@ -272,6 +272,33 @@ ConfigureSash.args = {
sashSize: 4,
};

export const Visible: Story<AllotmentProps> = (args) => {
const [visible, setVisible] = useState(true);

return (
<div>
<button
className={styles.button}
type="button"
onClick={() => {
setVisible((visible) => !visible);
}}
>
{visible ? "Hide" : "Show"}
</button>
<div className={styles.container}>
<Allotment {...args}>
<Content />
<Allotment.Pane visible={visible}>
<Content />
</Allotment.Pane>
</Allotment>
</div>
</div>
);
};
Visible.args = {};

export const OnReset: Story = (args) => {
const ref = useRef<AllotmentHandle>(null!);

Expand Down
1 change: 1 addition & 0 deletions website/docs/pane.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ title: Pane
| `maxSize` | `number` | | Maximum size of this pane. Overrides `maxSize` set on parent component. |
| `minSize` | `number` | | Minimum size of this pane. Overrides `minSize` set on parent component. |
| `snap` | `boolean` | `false` | Enable snap to zero for this pane. Overrides `snap` set on parent component. |
| `visible` | `boolean` | `true` | Whether this pane should be visible. |

0 comments on commit 3b0542c

Please sign in to comment.