collapse file click looking good 👼

This commit is contained in:
Nate Kelley 2025-03-27 15:58:17 -06:00
parent 2fde9f27a1
commit df1a685dde
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
10 changed files with 71 additions and 66 deletions

View File

@ -85,7 +85,7 @@ export const DropdownBase = <T,>({
closeOnSelect = true, closeOnSelect = true,
onSelect, onSelect,
children, children,
align = 'center', align = 'start',
side = 'bottom', side = 'bottom',
open, open,
onOpenChange, onOpenChange,

View File

@ -2,7 +2,7 @@ import React, { useEffect, useMemo } from 'react';
import type { MetricViewProps } from '../config'; import type { MetricViewProps } from '../config';
import { useMemoizedFn, useUnmount } from '@/hooks'; import { useMemoizedFn, useUnmount } from '@/hooks';
import { IDataResult } from '@/api/asset_interfaces'; import { IDataResult } from '@/api/asset_interfaces';
import { useMetricLayout } from '../useMetricLayout'; import { useMetricResultsLayout } from './useMetricResultsLayout';
import { useChatLayoutContextSelector } from '@/layouts/ChatLayout/ChatLayoutContext'; import { useChatLayoutContextSelector } from '@/layouts/ChatLayout/ChatLayoutContext';
import { AppSplitterRef } from '@/components/ui/layouts'; import { AppSplitterRef } from '@/components/ui/layouts';
import { AppVerticalCodeSplitter } from '@/components/features/layouts/AppVerticalCodeSplitter'; import { AppVerticalCodeSplitter } from '@/components/features/layouts/AppVerticalCodeSplitter';
@ -67,11 +67,10 @@ export const MetricViewResults: React.FC<MetricViewProps> = React.memo(({ metric
}); });
}); });
const { defaultLayout, renderSecondary } = useMetricLayout({ const { defaultLayout, renderSecondary } = useMetricResultsLayout({
selectedFileViewSecondary, selectedFileViewSecondary,
appSplitterRef, appSplitterRef,
autoSaveId, autoSaveId
type: 'sql'
}); });
useEffect(() => { useEffect(() => {

View File

@ -4,29 +4,23 @@ import { type AppSplitterRef } from '@/components/ui/layouts';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import { createAutoSaveId } from '@/components/ui/layouts/AppSplitter/helper'; import { createAutoSaveId } from '@/components/ui/layouts/AppSplitter/helper';
const defaultChartOpenLayout = ['auto', '310px'];
const defaultSqlOpenLayout = ['30%', 'auto']; const defaultSqlOpenLayout = ['30%', 'auto'];
const defaultChartLayout = ['auto', '0px'];
const defaultSqlLayout = ['0px', 'auto']; const defaultSqlLayout = ['0px', 'auto'];
export const useMetricLayout = ({ export const useMetricResultsLayout = ({
selectedFileViewSecondary, selectedFileViewSecondary,
appSplitterRef, appSplitterRef,
autoSaveId, autoSaveId
type
}: { }: {
selectedFileViewSecondary: null | string; selectedFileViewSecondary: null | string;
appSplitterRef: React.RefObject<AppSplitterRef | null>; appSplitterRef: React.RefObject<AppSplitterRef | null>;
autoSaveId: string; autoSaveId: string;
type: 'chart' | 'sql';
}) => { }) => {
const [renderSecondary, setRenderSecondary] = useState<boolean>(!!selectedFileViewSecondary); const [renderSecondary, setRenderSecondary] = useState<boolean>(!!selectedFileViewSecondary);
const isOpenSecondary = !!selectedFileViewSecondary; const isOpenSecondary = !!selectedFileViewSecondary;
const isChart = type === 'chart'; const defaultOpenLayout = defaultSqlOpenLayout;
const defaultOpenLayout = isChart ? defaultChartOpenLayout : defaultSqlOpenLayout; const defaultOriginalLayout = defaultSqlLayout;
const defaultOriginalLayout = isChart ? defaultChartLayout : defaultSqlLayout;
const secondaryLayoutDimensions: [string, string] = useMemo(() => { const secondaryLayoutDimensions: [string, string] = useMemo(() => {
const cookieKey = createAutoSaveId(autoSaveId); const cookieKey = createAutoSaveId(autoSaveId);
@ -54,20 +48,10 @@ export const useMetricLayout = ({
const animateOpenSplitter = useMemoizedFn((side: 'metric' | 'both') => { const animateOpenSplitter = useMemoizedFn((side: 'metric' | 'both') => {
if (!appSplitterRef.current) return; if (!appSplitterRef.current) return;
if (type === 'chart') { if (side === 'metric') {
if (side === 'metric') { appSplitterRef.current.animateWidth('0px', 'left');
appSplitterRef.current.animateWidth('100%', 'left'); } else if (side === 'both') {
} else if (side === 'both') { appSplitterRef.current.animateWidth('40%', 'left');
appSplitterRef.current.animateWidth('310px', 'right');
}
}
if (type === 'sql') {
if (side === 'metric') {
appSplitterRef.current.animateWidth('0px', 'left');
} else if (side === 'both') {
appSplitterRef.current.animateWidth('40%', 'left');
}
} }
}); });

View File

@ -26,8 +26,6 @@ export const ChatLayout: React.FC<ChatSplitterProps> = ({ children }) => {
return ['380px', 'auto']; return ['380px', 'auto'];
}, [selectedLayout]); }, [selectedLayout]);
console.log(selectedLayout);
return ( return (
<ChatLayoutContextProvider chatLayoutProps={chatLayoutProps}> <ChatLayoutContextProvider chatLayoutProps={chatLayoutProps}>
<ChatContextProvider <ChatContextProvider

View File

@ -37,12 +37,11 @@ export const useChatLayoutContext = ({ appSplitterRef }: UseLayoutConfigProps) =
} }
}); });
const { onCollapseFileClick, selectedFile, onSetSelectedFile, isVersionHistoryMode } = const { selectedFile, onSetSelectedFile, isVersionHistoryMode } = useSelectedFile({
useSelectedFile({ animateOpenSplitter,
animateOpenSplitter, chatParams,
chatParams, appSplitterRef
appSplitterRef });
});
const { const {
selectedFileView, selectedFileView,
@ -50,11 +49,14 @@ export const useChatLayoutContext = ({ appSplitterRef }: UseLayoutConfigProps) =
selectedFileViewSecondary, selectedFileViewSecondary,
onSetFileView, onSetFileView,
closeSecondaryView, closeSecondaryView,
selectedLayout selectedLayout,
onCollapseFileClick
} = useLayoutConfig({ } = useLayoutConfig({
selectedFile, selectedFile,
isVersionHistoryMode, isVersionHistoryMode,
chatId: chatParams.chatId chatId: chatParams.chatId,
animateOpenSplitter,
onSetSelectedFile
}); });
return { return {

View File

@ -7,15 +7,20 @@ import { useMemoizedFn } from '@/hooks';
import { create } from 'mutative'; import { create } from 'mutative';
import { ChatLayoutView } from '../../interfaces'; import { ChatLayoutView } from '../../interfaces';
import type { SelectedFile } from '../../interfaces'; import type { SelectedFile } from '../../interfaces';
import { timeout } from '@/lib';
export const useLayoutConfig = ({ export const useLayoutConfig = ({
selectedFile, selectedFile,
isVersionHistoryMode, isVersionHistoryMode,
chatId chatId,
onSetSelectedFile,
animateOpenSplitter
}: { }: {
selectedFile: SelectedFile | null; selectedFile: SelectedFile | null;
isVersionHistoryMode: boolean; isVersionHistoryMode: boolean;
chatId: string | undefined; chatId: string | undefined;
animateOpenSplitter: (side: 'left' | 'right' | 'both') => void;
onSetSelectedFile: (file: SelectedFile | null) => void;
}) => { }) => {
const [fileViews, setFileViews] = useState<Record<string, FileConfig>>({}); const [fileViews, setFileViews] = useState<Record<string, FileConfig>>({});
@ -48,7 +53,7 @@ export const useLayoutConfig = ({
}, [selectedFileViewConfig]); }, [selectedFileViewConfig]);
const onSetFileView = useMemoizedFn( const onSetFileView = useMemoizedFn(
({ async ({
fileView, fileView,
fileId: fileIdProp, fileId: fileIdProp,
secondaryView, secondaryView,
@ -61,6 +66,14 @@ export const useLayoutConfig = ({
}) => { }) => {
const fileId = fileIdProp ?? selectedFileId; const fileId = fileIdProp ?? selectedFileId;
if (!fileId) return; if (!fileId) return;
if (secondaryView) {
animateOpenSplitter('right');
await timeout(250); //wait for splitter to close before opening secondary view
} else {
animateOpenSplitter('both');
}
setFileViews((prev) => { setFileViews((prev) => {
return create(prev, (draft) => { return create(prev, (draft) => {
if (!draft[fileId]) { if (!draft[fileId]) {
@ -105,6 +118,11 @@ export const useLayoutConfig = ({
}); });
}); });
const onCollapseFileClick = useMemoizedFn(() => {
onSetSelectedFile(null);
closeSecondaryView();
});
const selectedLayout: ChatLayoutView = useMemo(() => { const selectedLayout: ChatLayoutView = useMemo(() => {
if (chatId) { if (chatId) {
if (selectedFileId) return 'both'; if (selectedFileId) return 'both';
@ -128,14 +146,26 @@ export const useLayoutConfig = ({
} }
}, [isVersionHistoryMode, selectedFileId]); }, [isVersionHistoryMode, selectedFileId]);
return { return useMemo(
selectedLayout, () => ({
selectedFileView, selectedLayout,
selectedFileViewSecondary, selectedFileView,
selectedFileViewRenderSecondary, selectedFileViewSecondary,
onSetFileView, selectedFileViewRenderSecondary,
closeSecondaryView onSetFileView,
}; closeSecondaryView,
onCollapseFileClick
}),
[
selectedLayout,
selectedFileView,
selectedFileViewSecondary,
selectedFileViewRenderSecondary,
onSetFileView,
closeSecondaryView,
onCollapseFileClick
]
);
}; };
const DEFAULT_FILE_VIEW: Record<FileType, FileView> = { const DEFAULT_FILE_VIEW: Record<FileType, FileView> = {

View File

@ -44,16 +44,11 @@ export const useSelectedFile = ({
animateOpenSplitter('both'); animateOpenSplitter('both');
}); });
const onCollapseFileClick = useMemoizedFn((close?: boolean) => {
onSetSelectedFile(null);
});
return useMemo( return useMemo(
() => ({ () => ({
isVersionHistoryMode, isVersionHistoryMode,
onSetSelectedFile, onSetSelectedFile,
selectedFile, selectedFile
onCollapseFileClick
}), }),
[onSetSelectedFile, isVersionHistoryMode, selectedFile] [onSetSelectedFile, isVersionHistoryMode, selectedFile]
); );

View File

@ -78,7 +78,7 @@ export const FileContainer: React.FC<FileContainerProps> = ({ children }) => {
useLayoutEffect(() => { useLayoutEffect(() => {
setTimeout(() => { setTimeout(() => {
animateOpenSplitter(isOpenSecondary ? 'open' : 'closed'); animateOpenSplitter(isOpenSecondary ? 'open' : 'closed');
}, 25); }, 20);
}, [isOpenSecondary]); }, [isOpenSecondary]);
return ( return (

View File

@ -2,18 +2,14 @@ import { DoubleChevronRight } from '@/components/ui/icons';
import { Button } from '@/components/ui/buttons'; import { Button } from '@/components/ui/buttons';
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { AnimatePresence, motion } from 'framer-motion'; import { AnimatePresence, motion } from 'framer-motion';
import { useMemoizedFn } from '@/hooks'; import { AppTooltip } from '@/components/ui/tooltip';
export const CollapseFileButton: React.FC<{ export const CollapseFileButton: React.FC<{
showCollapseButton: boolean; showCollapseButton: boolean;
onCollapseFileClick: (value?: boolean) => void; onCollapseFileClick: () => void;
}> = React.memo(({ showCollapseButton, onCollapseFileClick }) => { }> = React.memo(({ showCollapseButton, onCollapseFileClick }) => {
const icon = <DoubleChevronRight />; const icon = <DoubleChevronRight />;
const onClick = useMemoizedFn(() => {
onCollapseFileClick();
});
const animation = useMemo(() => { const animation = useMemo(() => {
return { return {
initial: { opacity: 0 }, initial: { opacity: 0 },
@ -26,7 +22,9 @@ export const CollapseFileButton: React.FC<{
<AnimatePresence mode="wait" initial={false}> <AnimatePresence mode="wait" initial={false}>
{showCollapseButton && ( {showCollapseButton && (
<motion.div variants={animation}> <motion.div variants={animation}>
<Button onClick={onClick} variant="ghost" prefix={icon}></Button> <AppTooltip title="Collapse file" delayDuration={350}>
<Button onClick={onCollapseFileClick} variant="ghost" prefix={icon}></Button>
</AppTooltip>
</motion.div> </motion.div>
)} )}
</AnimatePresence> </AnimatePresence>

View File

@ -7,10 +7,9 @@ import { useMemoizedFn } from '@/hooks';
import { AppTooltip } from '@/components/ui/tooltip'; import { AppTooltip } from '@/components/ui/tooltip';
export const CreateChatButton = React.memo(() => { export const CreateChatButton = React.memo(() => {
const onCollapseFileClick = useChatLayoutContextSelector((x) => x.onCollapseFileClick);
const onCollapseFileClickPreflight = useMemoizedFn(() => { const onCollapseFileClickPreflight = useMemoizedFn(() => {
onCollapseFileClick(false); // onCollapseFileClick(false);
alert('TODO');
}); });
useHotkeys('e', onCollapseFileClickPreflight, { preventDefault: true }); useHotkeys('e', onCollapseFileClickPreflight, { preventDefault: true });