debebounce sidebar

This commit is contained in:
Nate Kelley 2025-03-25 23:52:36 -06:00
parent 60d59ff313
commit f8f6bc7a8c
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
9 changed files with 78 additions and 51 deletions

View File

@ -49,13 +49,15 @@ export function useDebounceFn<T extends noop>(fn: T, options?: DebounceOptions)
flush: debounced.flush
};
}
export function useDebounce<T>(value: T, options: DebounceOptions = {}) {
const { wait = 1000, maxWait } = options;
const { wait = 1000, maxWait, leading = false, trailing = true } = options;
const [debouncedValue, setDebouncedValue] = useState<T>(value);
const timeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
const maxTimeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
const lastUpdateTime = useRef<number>(Date.now());
const valueRef = useRef<T>(value);
const isFirstCallRef = useRef<boolean>(true);
// Keep latest value in ref
useEffect(() => {
@ -70,17 +72,29 @@ export function useDebounce<T>(value: T, options: DebounceOptions = {}) {
if (maxTimeoutRef.current) {
clearTimeout(maxTimeoutRef.current);
}
isFirstCallRef.current = true;
}, []);
useEffect(() => {
clearTimeouts();
const now = Date.now();
// Set up the regular debounce timeout
timeoutRef.current = setTimeout(() => {
setDebouncedValue(valueRef.current);
lastUpdateTime.current = Date.now();
}, wait);
// Handle leading edge
if (leading && isFirstCallRef.current) {
setDebouncedValue(value);
lastUpdateTime.current = now;
isFirstCallRef.current = false;
return;
}
// Only set up trailing timeout if trailing is true
if (trailing) {
timeoutRef.current = setTimeout(() => {
setDebouncedValue(valueRef.current);
lastUpdateTime.current = Date.now();
}, wait);
}
// Handle maxWait
if (maxWait) {
@ -96,8 +110,9 @@ export function useDebounce<T>(value: T, options: DebounceOptions = {}) {
}, maxWaitTimeRemaining);
}
isFirstCallRef.current = false;
return () => clearTimeouts();
}, [value, wait, maxWait, clearTimeouts]);
}, [value, wait, maxWait, leading, trailing, clearTimeouts]);
return debouncedValue;
}

View File

@ -44,13 +44,6 @@ export const useSelectedFile = ({
return false;
}, [selectedFile?.type, metricVersionNumber, dashboardVersionNumber]);
console.log(
'isVersionHistoryMode',
isVersionHistoryMode,
metricVersionNumber,
dashboardVersionNumber
);
const [renderViewLayoutKey, setRenderViewLayoutKey] = useState<ChatLayoutView>(
selectedLayout || 'chat'
);

View File

@ -6,7 +6,8 @@ import { AppPageLayout, AppSplitter, AppSplitterRef } from '@/components/ui/layo
import { useChatLayoutContextSelector } from '../ChatLayoutContext';
import { createAutoSaveId } from '@/components/ui/layouts/AppSplitter/helper';
import Cookies from 'js-cookie';
import { useMemoizedFn, useUpdateLayoutEffect } from '@/hooks';
import { useDebounce, useMemoizedFn, useUpdateLayoutEffect } from '@/hooks';
import { FileContainerSecondary } from './FileContainerSecondary';
interface FileContainerProps {
children: React.ReactNode;
@ -18,15 +19,22 @@ const autoSaveId = 'file-container-splitter';
export const FileContainer: React.FC<FileContainerProps> = ({ children }) => {
const appSplitterRef = useRef<AppSplitterRef>(null);
const selectedFile = useChatLayoutContextSelector((x) => x.selectedFile);
const selectedFileViewSecondary = useChatLayoutContextSelector(
(x) => x.selectedFileViewSecondary
);
const selectedFileViewRenderSecondary = useChatLayoutContextSelector(
(x) => x.selectedFileViewRenderSecondary
);
const isOpenSecondary = selectedFileViewRenderSecondary;
//we need to debounce the selectedFileViewSecondary to avoid flickering
const debouncedSelectedFileViewSecondary = useDebounce(selectedFileViewSecondary, {
wait: 350,
leading: selectedFileViewRenderSecondary
});
const secondaryLayoutDimensions: [string, string] = useMemo(() => {
const cookieKey = createAutoSaveId(autoSaveId);
const cookieValue = Cookies.get(cookieKey);
@ -58,6 +66,15 @@ export const FileContainer: React.FC<FileContainerProps> = ({ children }) => {
}
});
const rightChildren = useMemo(() => {
return (
<FileContainerSecondary
selectedFile={selectedFile}
selectedFileViewSecondary={debouncedSelectedFileViewSecondary}
/>
);
}, [debouncedSelectedFileViewSecondary, selectedFile?.id, selectedFile?.type]);
useUpdateLayoutEffect(() => {
animateOpenSplitter(isOpenSecondary ? 'open' : 'closed');
}, [isOpenSecondary]);
@ -70,7 +87,7 @@ export const FileContainer: React.FC<FileContainerProps> = ({ children }) => {
defaultLayout={defaultLayout}
initialReady={false}
leftChildren={children}
rightChildren={<div>Right {selectedFileViewSecondary}</div>}
rightChildren={rightChildren}
allowResize={selectedFileViewRenderSecondary}
preserveSide={'right'}
rightPanelMinSize={250}

View File

@ -1,3 +1,5 @@
'use client';
import { Button } from '@/components/ui/buttons';
import { ArrowLeft, History } from '@/components/ui/icons';
import React, { useMemo, useTransition } from 'react';
@ -8,6 +10,7 @@ import { useGetDashboard } from '@/api/buster_rest/dashboards';
import { useAppLayoutContextSelector } from '@/context/BusterAppLayout';
import { useMemoizedFn } from '@/hooks';
import { timeout } from '@/lib';
import { AnimatePresence, motion } from 'framer-motion';
export const FileContainerVersionHistory = React.memo(() => {
return (

View File

@ -1,31 +1,29 @@
import type { FileType } from '@/api/asset_interfaces/chat';
import type { FileContainerSecondaryProps } from './interfaces';
import type {
DashboardFileViewSecondary,
MetricFileViewSecondary
} from '../../ChatLayoutContext/useLayoutConfig/interfaces';
import { SelectedFileSecondaryRecord } from './secondaryPanelsConfig';
import { useMemo } from 'react';
const MetricSecondaryRecord: Record<
MetricFileViewSecondary,
React.FC<FileContainerSecondaryProps>
> = {
'chart-edit': () => null,
'sql-edit': () => null,
'version-history': () => null
};
export const FileContainerSecondary: React.FC<FileContainerSecondaryProps> = ({
selectedFile,
selectedFileViewSecondary
}) => {
const Component = useMemo(() => {
if (!selectedFile || !selectedFileViewSecondary) return null;
const DashboardSecondaryRecord: Record<
DashboardFileViewSecondary,
React.FC<FileContainerSecondaryProps>
> = {
'version-history': () => null
};
const assosciatedType = SelectedFileSecondaryRecord[selectedFile?.type];
const SelectedFileSecondaryRecord: Record<
FileType,
Record<string, React.FC<FileContainerSecondaryProps>>
> = {
metric: MetricSecondaryRecord,
dashboard: DashboardSecondaryRecord,
reasoning: {}
if (!assosciatedType) return null;
return assosciatedType[selectedFileViewSecondary];
}, [selectedFileViewSecondary, selectedFile?.id, selectedFile?.type]);
return (
<>
{Component && (
<Component
selectedFile={selectedFile}
selectedFileViewSecondary={selectedFileViewSecondary}
/>
)}
</>
);
};

View File

@ -1,6 +1,7 @@
import type { FileViewSecondary } from '@/layouts/ChatLayout/ChatLayoutContext/useLayoutConfig';
import { SelectedFile } from '../../interfaces';
export type FileContainerSecondaryProps = {
selectedFileId: string | undefined;
selectedFileViewSecondary: FileViewSecondary | undefined;
selectedFile: SelectedFile | null;
};

View File

@ -15,7 +15,7 @@ export const DashboardSecondaryRecord: Record<
DashboardFileViewSecondary,
React.FC<FileContainerSecondaryProps>
> = {
'version-history': ({ selectedFileId }) => (
<VersionHistoryPanel assetId={selectedFileId || ''} type="dashboard" />
'version-history': ({ selectedFile }) => (
<VersionHistoryPanel assetId={selectedFile?.id || ''} type="dashboard" />
)
};

View File

@ -2,7 +2,7 @@ import { CircleSpinnerLoaderContainer } from '@/components/ui/loaders/CircleSpin
export const loading = () => {
return (
<div className="flex h-full w-full items-center justify-center">
<div className="flex h-full w-full items-center justify-center overflow-hidden">
<CircleSpinnerLoaderContainer />
</div>
);

View File

@ -26,9 +26,9 @@ export const MetricSecondaryRecord: Record<
MetricFileViewSecondary,
React.FC<FileContainerSecondaryProps>
> = {
'chart-edit': ({ selectedFileId }) => <MetricEditController metricId={selectedFileId || ''} />,
'sql-edit': ({ selectedFileId }) => <MetricViewResults metricId={selectedFileId || ''} />,
'version-history': ({ selectedFileId }) => (
<VersionHistoryPanel assetId={selectedFileId || ''} type="metric" />
'chart-edit': ({ selectedFile }) => <MetricEditController metricId={selectedFile?.id || ''} />,
'sql-edit': ({ selectedFile }) => <MetricViewResults metricId={selectedFile?.id || ''} />,
'version-history': ({ selectedFile }) => (
<VersionHistoryPanel assetId={selectedFile?.id || ''} type="metric" />
)
};