Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/lucky-rooms-dream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"react-flexy-panels": minor
---

support conditional rendering
2 changes: 2 additions & 0 deletions apps/docs/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
ExamplesPage,
StylingGuidePage,
HomePage,
SandboxPage,
} from "./pages";

const App = () => {
Expand All @@ -17,6 +18,7 @@ const App = () => {
<Route path="/api" element={<ApiReferencePage />} />
<Route path="/examples" element={<ExamplesPage />} />
<Route path="/styling" element={<StylingGuidePage />} />
<Route path="/sandbox" element={<SandboxPage />} />
</Routes>
</RootLayout>
);
Expand Down
44 changes: 44 additions & 0 deletions apps/docs/src/pages/SandboxPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { FlexyPanel, FlexyPanelGroup } from "react-flexy-panels";
import { FlexyHandle } from "../components";
import { Button } from "@rlx-widgets/button";
import { useState } from "react";

export const SandboxPage = () => {
const [panel2Open, setPanel2Open] = useState(true);
return (
<div>
<div className="h-full w-full border">
<FlexyPanelGroup direction="horizontal">
<FlexyPanel defaultSize={33}>
<div className="flex items-center justify-center h-100">
Panel 1
</div>
</FlexyPanel>
{panel2Open && (
<>
<FlexyHandle />
<FlexyPanel defaultSize={33}>
<div className="flex items-center justify-center h-100">
Panel 2
</div>
</FlexyPanel>
</>
)}
<FlexyHandle />
<FlexyPanel defaultSize={34}>
<div className="flex items-center justify-center h-100">
Panel 3
</div>
</FlexyPanel>
</FlexyPanelGroup>
</div>
<div className="flex justify-center mt-4">
<Button variant="outline" onClick={() => setPanel2Open(!panel2Open)}>
Toggle Panel 2
</Button>
</div>
</div>
);
};

export default SandboxPage;
1 change: 1 addition & 0 deletions apps/docs/src/pages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from "./GettingStartedPage";
export * from "./ApiReferencePage";
export * from "./ExamplesPage";
export * from "./StylingGuidePage";
export * from "./SandboxPage";
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"@nx/react": "22.1.1",
"@nx/vite": "22.1.1",
"@nx/web": "22.1.1",
"@rlx-ui/mcp": "^0.0.6",
"@rlx-ui/mcp": "^0.0.12",
"@swc-node/register": "~1.9.1",
"@swc/cli": "~0.6.0",
"@swc/core": "~1.5.7",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { attachSetSizeFunction } from "../../utils/attachSetSizeFunction";

export const FlexyPanel = forwardRef<FlexyPanelRef, FlexyPanelProps>(
({ children, defaultSize, defaultSizeUnit = "%", style, ...props }, ref) => {
const { addPanelRef } = useFlexyPanelsContext();
const size: CSSProperties =
defaultSize === "auto"
? { flex: "auto" }
Expand All @@ -27,9 +26,8 @@ export const FlexyPanel = forwardRef<FlexyPanelRef, FlexyPanelProps>(

const setRef = useCallback(
(node: HTMLDivElement | null) => {
// Call addPanelRef to register the element
// Attach setSize function to panel element
if (node) {
addPanelRef(node);
attachSetSizeFunction(node);
}
// Forward the ref if provided
Expand All @@ -40,7 +38,7 @@ export const FlexyPanel = forwardRef<FlexyPanelRef, FlexyPanelProps>(
currentRef.current = node as FlexyPanelRef;
}
},
[addPanelRef]
[]
);

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { useCallback, useMemo, useState } from "react";
import { useMemo } from "react";
import { FlexyPanelsContext } from "../../contexts";
import { FlexyPanelGroupProps } from "./types";

Expand All @@ -10,16 +10,7 @@ export const FlexyPanelGroup = ({
style,
...props
}: FlexyPanelGroupProps) => {
const [panelRefs, setPanelRefs] = useState<Array<HTMLDivElement>>([]);

const addPanelRef = useCallback((ref: HTMLDivElement) => {
setPanelRefs((prev) => [...prev, ref]);
}, []);

const value = useMemo(
() => ({ direction, panelRefs, addPanelRef }),
[direction, panelRefs, addPanelRef]
);
const value = useMemo(() => ({ direction }), [direction]);

return (
<div
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,66 +4,62 @@ import { useCallback, useId, forwardRef, useRef, useEffect } from "react";
import { useFlexyPanelsContext, usePanelDrag } from "../../hooks";
import { FlexyPanelHandleProps } from "./types";

export const FlexyPanelHandle = forwardRef<HTMLDivElement, FlexyPanelHandleProps>(
({ onMouseDown, onTouchStart, ...props }, ref) => {
const id = useId();
const { direction, panelRefs, addPanelRef } = useFlexyPanelsContext();
const { handleMouseDown: handleDragMouseDown, handleTouchStart: handleDragTouchStart } = usePanelDrag({
direction,
panelRefs,
handleId: id,
});
export const FlexyPanelHandle = forwardRef<
HTMLDivElement,
FlexyPanelHandleProps
>(({ onMouseDown, onTouchStart, ...props }, ref) => {
const id = useId();
const { direction } = useFlexyPanelsContext();
const {
handleMouseDown: handleDragMouseDown,
handleTouchStart: handleDragTouchStart,
} = usePanelDrag({
direction,
handleId: id,
});

const handleMouseDown = useCallback(
(e: React.MouseEvent<HTMLDivElement>) => {
handleDragMouseDown(e);
onMouseDown?.(e);
},
[handleDragMouseDown, onMouseDown]
);
const handleMouseDown = useCallback(
(e: React.MouseEvent<HTMLDivElement>) => {
handleDragMouseDown(e);
onMouseDown?.(e);
},
[handleDragMouseDown, onMouseDown]
);

const handleTouchStartEvent = useCallback(
(e: React.TouchEvent<HTMLDivElement>) => {
handleDragTouchStart(e);
onTouchStart?.(e);
},
[handleDragTouchStart, onTouchStart]
);
const handleTouchStartEvent = useCallback(
(e: React.TouchEvent<HTMLDivElement>) => {
handleDragTouchStart(e);
onTouchStart?.(e);
},
[handleDragTouchStart, onTouchStart]
);

// Store the forwarded ref in a ref to avoid dependency issues
const forwardedRef = useRef(ref);
useEffect(() => {
forwardedRef.current = ref;
}, [ref]);
// Store the forwarded ref in a ref to avoid dependency issues
const forwardedRef = useRef(ref);
useEffect(() => {
forwardedRef.current = ref;
}, [ref]);

const setRef = useCallback(
(node: HTMLDivElement | null) => {
// Call addPanelRef to register the element
if (node) {
addPanelRef(node);
}
// Forward the ref if provided
const currentRef = forwardedRef.current;
if (typeof currentRef === "function") {
currentRef(node);
} else if (currentRef) {
currentRef.current = node;
}
},
[addPanelRef]
);
const setRef = useCallback((node: HTMLDivElement | null) => {
// Forward the ref if provided
const currentRef = forwardedRef.current;
if (typeof currentRef === "function") {
currentRef(node);
} else if (currentRef) {
currentRef.current = node;
}
}, []);

return (
<div
id={id}
ref={setRef}
data-direction={direction}
onMouseDown={handleMouseDown}
onTouchStart={handleTouchStartEvent}
{...props}
/>
);
}
);
return (
<div
id={id}
ref={setRef}
data-direction={direction}
onMouseDown={handleMouseDown}
onTouchStart={handleTouchStartEvent}
{...props}
/>
);
});

FlexyPanelHandle.displayName = "FlexyPanelHandle";
6 changes: 0 additions & 6 deletions packages/react-flexy-panels/src/contexts/FlexyPanelContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@ import { Direction } from "../types";

export const FlexyPanelsContext = createContext<{
direction: Direction;
panelRefs: Array<HTMLDivElement>;
addPanelRef: (ref: HTMLDivElement) => void;
}>({
direction: "horizontal",
panelRefs: [],
addPanelRef: () => {
return;
},
});
39 changes: 24 additions & 15 deletions packages/react-flexy-panels/src/hooks/usePanelDrag.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,37 @@
import { Direction } from "../types";
import { updatePanelSizes } from "../utils";
import { findAdjacentPanels, updatePanelSizes } from "../utils";
import { useCallback, useEffect, useRef, useState } from "react";

type UsePanelDragOptions = {
direction: Direction;
panelRefs: Array<HTMLDivElement>;
handleId: string;
};

/**
* Custom hook to handle panel dragging logic
*/
export const usePanelDrag = ({
direction,
panelRefs,
handleId,
}: UsePanelDragOptions) => {
export const usePanelDrag = ({ direction, handleId }: UsePanelDragOptions) => {
const [isDragging, setIsDragging] = useState(false);
const dragStartRef = useRef<number>(0);
const handleElementRef = useRef<HTMLElement | null>(null);

const updateDragPosition = useCallback(
(clientX: number, clientY: number) => {
const dragCurrent = direction === "horizontal" ? clientX : clientY;
const dragDelta = dragCurrent - dragStartRef.current;

const handleIndex = panelRefs.findIndex((ref) => ref.id === handleId);
if (handleIndex === -1) {
// Find handle element if not cached
if (!handleElementRef.current) {
handleElementRef.current = document.getElementById(handleId);
}

if (!handleElementRef.current) {
return;
}

const panel1 = panelRefs[handleIndex - 1];
const panel2 = panelRefs[handleIndex + 1];
// Find adjacent panels using DOM traversal
const { panel1, panel2 } = findAdjacentPanels(handleElementRef.current);

if (!panel1 || !panel2) {
return;
}
Expand All @@ -43,7 +44,7 @@ export const usePanelDrag = ({
});
dragStartRef.current = dragCurrent;
},
[direction, handleId, panelRefs]
[direction, handleId]
);

const onDrag = useCallback(
Expand Down Expand Up @@ -71,6 +72,8 @@ export const usePanelDrag = ({

const handleMouseDown = useCallback(
(e: React.MouseEvent<HTMLDivElement>) => {
// Cache handle element reference on drag start
handleElementRef.current = e.currentTarget;
setIsDragging(true);
dragStartRef.current = direction === "horizontal" ? e.clientX : e.clientY;
},
Expand All @@ -80,9 +83,12 @@ export const usePanelDrag = ({
const handleTouchStart = useCallback(
(e: React.TouchEvent<HTMLDivElement>) => {
if (e.touches.length > 0) {
// Cache handle element reference on drag start
handleElementRef.current = e.currentTarget;
setIsDragging(true);
const touch = e.touches[0];
dragStartRef.current = direction === "horizontal" ? touch.clientX : touch.clientY;
dragStartRef.current =
direction === "horizontal" ? touch.clientX : touch.clientY;
}
},
[direction]
Expand All @@ -102,7 +108,7 @@ export const usePanelDrag = ({
const originalUserSelect = document.body.style.userSelect;
// Disable text selection during drag
document.body.style.userSelect = "none";

document.addEventListener("mousemove", onDrag);
document.addEventListener("mouseup", handleMouseUp);
document.addEventListener("touchmove", onTouchDrag, { passive: false });
Expand All @@ -112,7 +118,10 @@ export const usePanelDrag = ({
return () => {
// Restore original user-select value
document.body.style.userSelect = originalUserSelect;


// Clear handle element reference on drag end
handleElementRef.current = null;

document.removeEventListener("mousemove", onDrag);
document.removeEventListener("mouseup", handleMouseUp);
document.removeEventListener("touchmove", onTouchDrag);
Expand Down
Loading