Skip to content

Commit

Permalink
Renamed "gutter" to "hitAreaMargins" and split coarse and fine input …
Browse files Browse the repository at this point in the history
…types
  • Loading branch information
bvaughn committed Jan 30, 2024
1 parent 6fb837b commit 0700cd1
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 154 deletions.
44 changes: 22 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,26 @@ Supported input methods include mouse, touch, and keyboard (via [Window Splitter

No. Pixel-based constraints [added significant complexity](https://github.com/bvaughn/react-resizable-panels/pull/176) to the initialization and validation logic and so I've decided not to support them. You may be able to implement a version of this yourself following [a pattern like this](https://github.com/bvaughn/react-resizable-panels/issues/46#issuecomment-1368108416) but it is not officially supported by this library.

### How can I fix layout/sizing problems with conditionally rendered panels?

The `Panel` API doesn't _require_ `id` and `order` props because they aren't necessary for static layouts. When panels are conditionally rendered though, it's best to supply these values.

```tsx
<PanelGroup direction="horizontal">
{renderSideBar && (
<>
<Panel id="sidebar" minSize={25} order={1}>
<Sidebar />
</Panel>
<PanelResizeHandle />
</>
)}
<Panel minSize={25} order={2}>
<Main />
</Panel>
</PanelGroup>
```

### Can a attach a ref to the DOM elements?

No. I think exposing two refs (one for the component's imperative API and one for a DOM element) would be awkward. This library does export several utility methods for accessing the underlying DOM elements though. For example:
Expand Down Expand Up @@ -74,31 +94,11 @@ This likely means that you haven't applied any CSS to style the resize handles.
<PanelResizeHandle className="w-2 bg-blue-800" />
```

### How can I fix layout/sizing problems with conditionally rendered panels?

The `Panel` API doesn't _require_ `id` and `order` props because they aren't necessary for static layouts. When panels are conditionally rendered though, it's best to supply these values.

```tsx
<PanelGroup direction="horizontal">
{renderSideBar && (
<>
<Panel id="sidebar" minSize={25} order={1}>
<Sidebar />
</Panel>
<PanelResizeHandle />
</>
)}
<Panel minSize={25} order={2}>
<Main />
</Panel>
</PanelGroup>
```

### How can I use persistent layouts with SSR?

By default, this library uses `localStorage` to persist layouts. With server rendering, this can cause a flicker when the default layout (rendered on the server) is replaced with the persisted layout (in `localStorage`). The way to avoid this flicker is to also persist the layout with a cookie like so:

##### Server component
#### Server component

```tsx
import ResizablePanels from "@/app/ResizablePanels";
Expand All @@ -116,7 +116,7 @@ export function ServerComponent() {
}
```

##### Client component
#### Client component

```tsx
"use client";
Expand Down
5 changes: 5 additions & 0 deletions packages/react-resizable-panels/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## 2.0.0

- Support resizing multiple (intersecting) panels at once (#274)
This behavior can be customized using a new `hitAreaMargins` prop; defaults to a 15 pixel margin for _coarse_ inputs and a 5 pixel margin for _fine_ inputs.

## 1.0.10

- Fixed edge case constraints check bug that could cause a collapsed panel to re-expand unnecessarily (#273)
Expand Down
133 changes: 29 additions & 104 deletions packages/react-resizable-panels/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
</PanelGroup>;
```

### If you like this project, 🎉 [become a sponsor](https://github.com/sponsors/bvaughn/) or ☕ [buy me a coffee](http://givebrian.coffee/)
## If you like this project, 🎉 [become a sponsor](https://github.com/sponsors/bvaughn/) or ☕ [buy me a coffee](http://givebrian.coffee/)

## Props

Expand All @@ -44,11 +44,11 @@ import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
- `setItem: (name: string, value: string) => void`

`PanelGroup` components also expose an imperative API for manual resizing:
| method | description
| :-------------------------------- | :---
| `getId(): string` | Gets the panel group's ID.
| `getLayout(): number[]` | Gets the panel group's current _layout_ (`[1 - 100, ...]`).
| `setLayout(layout: number[])` | Resize panel group to the specified _layout_ (`[1 - 100, ...]`).
| method | description |
| :---------------------------- | :--------------------------------------------------------------- |
| `getId(): string` | Gets the panel group's ID. |
| `getLayout(): number[]` | Gets the panel group's current _layout_ (`[1 - 100, ...]`). |
| `setLayout(layout: number[])` | Resize panel group to the specified _layout_ (`[1 - 100, ...]`). |

### `Panel`

Expand All @@ -63,7 +63,7 @@ import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
| `maxSize` | `?number = 100` | Maximum allowable size of panel (numeric value between 1-100); defaults to `100` |
| `minSize` | `?number = 10` | Minimum allowable size of panel (numeric value between 1-100); defaults to `10` |
| `onCollapse` | `?() => void` | Called when panel is collapsed |
| `onExpand` | `?() => void` | Called when panel is expanded |
| `onExpand` | `?() => void` | Called when panel is expanded |
| `onResize` | `?(size: number) => void` | Called when panel is resized; `size` parameter is a numeric value between 1-100. <sup>1</sup> |
| `order` | `?number` | Order of panel within group; required for groups with conditionally rendered panels |
| `style` | `?CSSProperties` | CSS style to attach to root element |
Expand All @@ -72,28 +72,29 @@ import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
<sup>1</sup>: If any `Panel` has an `onResize` callback, the `order` prop should be provided for all `Panel`s.

`Panel` components also expose an imperative API for manual resizing:
| method | description
| :--------------------------- | :---
| `collapse()` | If panel is `collapsible`, collapse it fully.
| `expand()` | If panel is currently _collapsed_, expand it to its most recent size.
| `getId(): string` | Gets the ID of the panel.
| `getSize(): number` | Gets the current size of the panel as a percentage (`1 - 100`).
| `isCollapsed(): boolean` | Returns `true` if the panel is currently _collapsed_ (`size === 0`).
| `isExpanded(): boolean` | Returns `true` if the panel is currently _not collapsed_ (`!isCollapsed()`).
| `getSize(): number` | Returns the most recently commited size of the panel as a percentage (`1 - 100`).
| `resize(size: number)` | Resize panel to the specified _percentage_ (`1 - 100`).
| method | description |
| :----------------------- | :--------------------------------------------------------------------------------- |
| `collapse()`v | If panel is `collapsible`, collapse it fully. |
| `expand()` | If panel is currently _collapsed_, expand it to its most recent size. |
| `getId(): string` | Gets the ID of the panel. |
| `getSize(): number` | Gets the current size of the panel as a percentage (`1 - 100`). |
| `isCollapsed(): boolean` | Returns `true` if the panel is currently _collapsed_ (`size === 0`). |
| `isExpanded(): boolean` | Returns `true` if the panel is currently _not collapsed_ (`!isCollapsed()`). |
| `getSize(): number` | Returns the most recently committed size of the panel as a percentage (`1 - 100`). |
| `resize(size: number)` | Resize panel to the specified _percentage_ (`1 - 100`). |

### `PanelResizeHandle`

| prop | type | description |
| :----------- | :------------------------------- | :------------------------------------------------------------------------------ |
| `children` | `?ReactNode` | Custom drag UI; can be any arbitrary React element(s) |
| `className` | `?string` | Class name to attach to root element |
| `disabled` | `?boolean` | Disable drag handle |
| `id` | `?string` | Resize handle id (unique within group); falls back to `useId` when not provided |
| `onDragging` | `?(isDragging: boolean) => void` | Called when group layout changes |
| `style` | `?CSSProperties` | CSS style to attach to root element |
| `tagName` | `?string = "div"` | HTML element tag name for root element |
| prop | type | description |
| :--------------- | :-------------------------------------------- | :------------------------------------------------------------------------------ |
| `children` | `?ReactNode` | Custom drag UI; can be any arbitrary React element(s) |
| `className` | `?string` | Class name to attach to root element |
| `hitAreaMargins` | `?{ coarse: number = 15; fine: number = 5; }` | Allow this much margin when determining resizable handle hit detection |
| `disabled` | `?boolean` | Disable drag handle |
| `id` | `?string` | Resize handle id (unique within group); falls back to `useId` when not provided |
| `onDragging` | `?(isDragging: boolean) => void` | Called when group layout changes |
| `style` | `?CSSProperties` | CSS style to attach to root element |
| `tagName` | `?string = "div"` | HTML element tag name for root element |

---

Expand Down Expand Up @@ -123,62 +124,6 @@ The `Panel` API doesn't _require_ `id` and `order` props because they aren't nec
</PanelGroup>
```

### How can I use persistent layouts with SSR?

By default, this library uses `localStorage` to persist layouts. With server rendering, this can cause a flicker when the default layout (rendered on the server) is replaced with the persisted layout (in `localStorage`). The way to avoid this flicker is to also persist the layout with a cookie like so:

##### Server component

```tsx
import ResizablePanels from "@/app/ResizablePanels";
import { cookies } from "next/headers";

export function ServerComponent() {
const layout = cookies().get("react-resizable-panels:layout");

let defaultLayout;
if (layout) {
defaultLayout = JSON.parse(layout.value);
}

return <ClientComponent defaultLayout={defaultLayout} />;
}
```

##### Client component

```tsx
"use client";

import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";

export function ClientComponent({
defaultLayout = [33, 67],
}: {
defaultLayout: number[] | undefined;
}) {
const onLayout = (sizes: number[]) => {
document.cookie = `react-resizable-panels:layout=${JSON.stringify(sizes)}`;
};

return (
<PanelGroup direction="horizontal" onLayout={onLayout}>
<Panel defaultSize={defaultLayout[0]}>{/* ... */}</Panel>
<PanelResizeHandle className="w-2 bg-blue-800" />
<Panel defaultSize={defaultLayout[1]}>{/* ... */}</Panel>
</PanelGroup>
);
}
```

---

## FAQ

### Can panel sizes be specified in pixels?

No. Pixel-based constraints [added significant complexity](https://github.com/bvaughn/react-resizable-panels/pull/176) to the initialization and validation logic and so I've decided not to support them. You may be able to implement a version of this yourself following [a pattern like this](https://github.com/bvaughn/react-resizable-panels/issues/46#issuecomment-1368108416) but it is not officially supported by this library.

### Can a attach a ref to the DOM elements?

No. I think exposing two refs (one for the component's imperative API and one for a DOM element) would be awkward. This library does export several utility methods for accessing the underlying DOM elements though. For example:
Expand Down Expand Up @@ -230,31 +175,11 @@ This likely means that you haven't applied any CSS to style the resize handles.
<PanelResizeHandle className="w-2 bg-blue-800" />
```

### How can I fix layout/sizing problems with conditionally rendered panels?

The `Panel` API doesn't _require_ `id` and `order` props because they aren't necessary for static layouts. When panels are conditionally rendered though, it's best to supply these values.

```tsx
<PanelGroup direction="horizontal">
{renderSideBar && (
<>
<Panel id="sidebar" minSize={25} order={1}>
<Sidebar />
</Panel>
<PanelResizeHandle />
</>
)}
<Panel minSize={25} order={2}>
<Main />
</Panel>
</PanelGroup>
```

### How can I use persistent layouts with SSR?

By default, this library uses `localStorage` to persist layouts. With server rendering, this can cause a flicker when the default layout (rendered on the server) is replaced with the persisted layout (in `localStorage`). The way to avoid this flicker is to also persist the layout with a cookie like so:

##### Server component
#### Server component

```tsx
import ResizablePanels from "@/app/ResizablePanels";
Expand All @@ -272,7 +197,7 @@ export function ServerComponent() {
}
```

##### Client component
#### Client component

```tsx
"use client";
Expand Down
14 changes: 10 additions & 4 deletions packages/react-resizable-panels/src/PanelResizeHandle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
ResizeHandler,
} from "./PanelGroupContext";
import {
PointerHitAreaMargins,
registerResizeHandle,
ResizeHandlerAction,
ResizeHandlerState,
Expand All @@ -33,7 +34,7 @@ export type PanelResizeHandleProps = Omit<
PropsWithChildren<{
className?: string;
disabled?: boolean;
gutter?: number;
hitAreaMargins?: PointerHitAreaMargins;
id?: string | null;
onDragging?: PanelResizeHandleOnDragging;
style?: CSSProperties;
Expand All @@ -45,7 +46,12 @@ export function PanelResizeHandle({
children = null,
className: classNameFromProps = "",
disabled = false,
gutter = 5,
hitAreaMargins = {
// Coarse inputs (e.g. finger/touch)
coarse: 15,
// Fine inputs (e.g. mouse)
fine: 5,
},
id: idFromProps,
onDragging,
style: styleFromProps = {},
Expand Down Expand Up @@ -136,13 +142,13 @@ export function PanelResizeHandle({
resizeHandleId,
element,
direction,
gutter,
hitAreaMargins,
setResizeHandlerState
);
}, [
direction,
disabled,
gutter,
hitAreaMargins,
registerResizeHandleWithParentGroup,
resizeHandleId,
resizeHandler,
Expand Down
Loading

0 comments on commit 0700cd1

Please sign in to comment.