From 77c6f578468d2e5816f4aebb9f8d0a3e1ef012ed Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Thu, 23 Jan 2025 16:15:42 -0700 Subject: [PATCH] can pass in pixels instead of percentage to app splitter animate --- .../_components/ChatSplitter/ChatSplitter.tsx | 2 - .../ChatSplitterContext/useChatSplitter.ts | 8 +- .../layout/AppSplitter/AppSplitter.tsx | 91 +++++++++++++++---- 3 files changed, 81 insertions(+), 20 deletions(-) diff --git a/web/src/app/app/_components/ChatSplitter/ChatSplitter.tsx b/web/src/app/app/_components/ChatSplitter/ChatSplitter.tsx index 83dee31f7..919979279 100644 --- a/web/src/app/app/_components/ChatSplitter/ChatSplitter.tsx +++ b/web/src/app/app/_components/ChatSplitter/ChatSplitter.tsx @@ -28,8 +28,6 @@ export const ChatSplitter: React.FC = React.memo( const useChatSplitterProps = useChatSplitter({ defaultSelectedFile, appSplitterRef }); const { selectedFile, hasFile } = useChatSplitterProps; - console.log('hasFile', hasFile); - return ( { if (appSplitterRef.current) { setSelectedFile(defaultSelectedFile); - appSplitterRef.current.animateWidth('50%', 'right'); + setTimeout(() => { + appSplitterRef.current?.animateWidth('50%', 'right'); + }, 1000); + + // appSplitterRef.current.animateWidth('200px', 'left'); + + appSplitterRef.current?.animateWidth('100px', 'right'); } }, [defaultSelectedFile]); diff --git a/web/src/components/layout/AppSplitter/AppSplitter.tsx b/web/src/components/layout/AppSplitter/AppSplitter.tsx index 78954a7c1..ca42ab2b7 100644 --- a/web/src/components/layout/AppSplitter/AppSplitter.tsx +++ b/web/src/components/layout/AppSplitter/AppSplitter.tsx @@ -1,7 +1,14 @@ 'use client'; import { useMemoizedFn } from 'ahooks'; -import React, { useEffect, useMemo, useState, forwardRef, useImperativeHandle } from 'react'; +import React, { + useEffect, + useMemo, + useState, + forwardRef, + useImperativeHandle, + useRef +} from 'react'; import SplitPane, { Pane } from './SplitPane'; import { createAutoSaveId } from './helper'; import Cookies from 'js-cookie'; @@ -58,6 +65,7 @@ export const AppSplitter = React.memo( }, ref ) => { + const containerRef = useRef(null); const [isDragging, setIsDragging] = useState(false); const [sizes, setSizes] = useState<(number | string)[]>(defaultLayout); const hasHidden = useMemo(() => leftHidden || rightHidden, [leftHidden, rightHidden]); @@ -143,18 +151,27 @@ export const AppSplitter = React.memo( const animateWidth = useMemoizedFn( async (width: string, side: 'left' | 'right', duration = 0.25) => { + const { value: targetValue, unit: targetUnit } = parseWidthValue(width); + const container = containerRef.current; + if (!container) return; + + const containerWidth = container.getBoundingClientRect().width; + let targetPercentage: number; + + // Convert target width to percentage + if (targetUnit === 'px') { + targetPercentage = convertPxToPercentage(targetValue, containerWidth); + } else { + targetPercentage = targetValue; + } + const leftPanelSize = _sizes[0]; const rightPanelSize = _sizes[1]; const currentSize = side === 'left' ? leftPanelSize : rightPanelSize; - const isNumeric = typeof leftPanelSize === 'number'; + const otherSize = side === 'left' ? rightPanelSize : leftPanelSize; - // Convert current size to number - const currentSizeNumber = isNumeric - ? ((currentSize as number) / (Number(leftPanelSize) + Number(rightPanelSize))) * 100 - : parseFloat(String(currentSize).replace('%', '')); - - // Convert target width to number (always in percentage scale) - const targetSizeNumber = parseFloat(width.replace('%', '')); + // Calculate current percentage considering 'auto' cases + const currentSizeNumber = getCurrentSizePercentage(currentSize, otherSize, container); const NUMBER_OF_STEPS = 24; const stepDuration = (duration * 1000) / NUMBER_OF_STEPS; @@ -165,13 +182,11 @@ export const AppSplitter = React.memo( const progress = i / NUMBER_OF_STEPS; const easedProgress = easeInOutCubic(progress); const newSizeNumber = - currentSizeNumber + (targetSizeNumber - currentSizeNumber) * easedProgress; + currentSizeNumber + (targetPercentage - currentSizeNumber) * easedProgress; - // Always use percentage strings const newSize = `${newSizeNumber}%`; const otherSize = `${100 - newSizeNumber}%`; - // Update both sides const newSizes = side === 'left' ? [newSize, otherSize] : [otherSize, newSize]; setSplitSizes(newSizes); @@ -193,7 +208,7 @@ export const AppSplitter = React.memo( useImperativeHandle(ref, imperativeHandleMethods); return ( -
+
{ - return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2; -}; - const useStyles = createStyles(({ css, token }) => ({ splitter: css` background: ${token.colorPrimary}; @@ -282,3 +293,49 @@ const useStyles = createStyles(({ css, token }) => ({ } ` })); + +const easeInOutCubic = (t: number): number => { + return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2; +}; + +const parseWidthValue = (width: string): { value: number; unit: 'px' | '%' } => { + const match = width.match(/^(\d+(?:\.\d+)?)(px|%)$/); + if (!match) throw new Error('Invalid width format. Must be in px or %'); + return { + value: parseFloat(match[1]), + unit: match[2] as 'px' | '%' + }; +}; + +const convertPxToPercentage = (px: number, containerWidth: number): number => { + return (px / containerWidth) * 100; +}; + +const getCurrentSizePercentage = ( + size: string | number, + otherSize: string | number, + container: HTMLElement +): number => { + if (size === 'auto') { + // If this side is auto, calculate based on the other side + const otherPercentage = getCurrentSizePercentage(otherSize, size, container); + return 100 - otherPercentage; + } + + if (typeof size === 'number') { + return size; + } + + // Handle percentage + if (size.endsWith('%')) { + return parseFloat(size); + } + + // Handle pixel values + if (size.endsWith('px')) { + const pixels = parseFloat(size); + return convertPxToPercentage(pixels, container.getBoundingClientRect().width); + } + + return 0; +};