mirror of https://github.com/buster-so/buster.git
debebounce sidebar
This commit is contained in:
parent
60d59ff313
commit
f8f6bc7a8c
|
@ -49,13 +49,15 @@ export function useDebounceFn<T extends noop>(fn: T, options?: DebounceOptions)
|
||||||
flush: debounced.flush
|
flush: debounced.flush
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useDebounce<T>(value: T, options: DebounceOptions = {}) {
|
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 [debouncedValue, setDebouncedValue] = useState<T>(value);
|
||||||
const timeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
|
const timeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
|
||||||
const maxTimeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
|
const maxTimeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
|
||||||
const lastUpdateTime = useRef<number>(Date.now());
|
const lastUpdateTime = useRef<number>(Date.now());
|
||||||
const valueRef = useRef<T>(value);
|
const valueRef = useRef<T>(value);
|
||||||
|
const isFirstCallRef = useRef<boolean>(true);
|
||||||
|
|
||||||
// Keep latest value in ref
|
// Keep latest value in ref
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -70,17 +72,29 @@ export function useDebounce<T>(value: T, options: DebounceOptions = {}) {
|
||||||
if (maxTimeoutRef.current) {
|
if (maxTimeoutRef.current) {
|
||||||
clearTimeout(maxTimeoutRef.current);
|
clearTimeout(maxTimeoutRef.current);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isFirstCallRef.current = true;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
clearTimeouts();
|
clearTimeouts();
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
// Set up the regular debounce timeout
|
// Handle leading edge
|
||||||
timeoutRef.current = setTimeout(() => {
|
if (leading && isFirstCallRef.current) {
|
||||||
setDebouncedValue(valueRef.current);
|
setDebouncedValue(value);
|
||||||
lastUpdateTime.current = Date.now();
|
lastUpdateTime.current = now;
|
||||||
}, wait);
|
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
|
// Handle maxWait
|
||||||
if (maxWait) {
|
if (maxWait) {
|
||||||
|
@ -96,8 +110,9 @@ export function useDebounce<T>(value: T, options: DebounceOptions = {}) {
|
||||||
}, maxWaitTimeRemaining);
|
}, maxWaitTimeRemaining);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isFirstCallRef.current = false;
|
||||||
return () => clearTimeouts();
|
return () => clearTimeouts();
|
||||||
}, [value, wait, maxWait, clearTimeouts]);
|
}, [value, wait, maxWait, leading, trailing, clearTimeouts]);
|
||||||
|
|
||||||
return debouncedValue;
|
return debouncedValue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,13 +44,6 @@ export const useSelectedFile = ({
|
||||||
return false;
|
return false;
|
||||||
}, [selectedFile?.type, metricVersionNumber, dashboardVersionNumber]);
|
}, [selectedFile?.type, metricVersionNumber, dashboardVersionNumber]);
|
||||||
|
|
||||||
console.log(
|
|
||||||
'isVersionHistoryMode',
|
|
||||||
isVersionHistoryMode,
|
|
||||||
metricVersionNumber,
|
|
||||||
dashboardVersionNumber
|
|
||||||
);
|
|
||||||
|
|
||||||
const [renderViewLayoutKey, setRenderViewLayoutKey] = useState<ChatLayoutView>(
|
const [renderViewLayoutKey, setRenderViewLayoutKey] = useState<ChatLayoutView>(
|
||||||
selectedLayout || 'chat'
|
selectedLayout || 'chat'
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,7 +6,8 @@ import { AppPageLayout, AppSplitter, AppSplitterRef } from '@/components/ui/layo
|
||||||
import { useChatLayoutContextSelector } from '../ChatLayoutContext';
|
import { useChatLayoutContextSelector } from '../ChatLayoutContext';
|
||||||
import { createAutoSaveId } from '@/components/ui/layouts/AppSplitter/helper';
|
import { createAutoSaveId } from '@/components/ui/layouts/AppSplitter/helper';
|
||||||
import Cookies from 'js-cookie';
|
import Cookies from 'js-cookie';
|
||||||
import { useMemoizedFn, useUpdateLayoutEffect } from '@/hooks';
|
import { useDebounce, useMemoizedFn, useUpdateLayoutEffect } from '@/hooks';
|
||||||
|
import { FileContainerSecondary } from './FileContainerSecondary';
|
||||||
|
|
||||||
interface FileContainerProps {
|
interface FileContainerProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
@ -18,15 +19,22 @@ const autoSaveId = 'file-container-splitter';
|
||||||
|
|
||||||
export const FileContainer: React.FC<FileContainerProps> = ({ children }) => {
|
export const FileContainer: React.FC<FileContainerProps> = ({ children }) => {
|
||||||
const appSplitterRef = useRef<AppSplitterRef>(null);
|
const appSplitterRef = useRef<AppSplitterRef>(null);
|
||||||
|
const selectedFile = useChatLayoutContextSelector((x) => x.selectedFile);
|
||||||
const selectedFileViewSecondary = useChatLayoutContextSelector(
|
const selectedFileViewSecondary = useChatLayoutContextSelector(
|
||||||
(x) => x.selectedFileViewSecondary
|
(x) => x.selectedFileViewSecondary
|
||||||
);
|
);
|
||||||
const selectedFileViewRenderSecondary = useChatLayoutContextSelector(
|
const selectedFileViewRenderSecondary = useChatLayoutContextSelector(
|
||||||
(x) => x.selectedFileViewRenderSecondary
|
(x) => x.selectedFileViewRenderSecondary
|
||||||
);
|
);
|
||||||
|
|
||||||
const isOpenSecondary = 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 secondaryLayoutDimensions: [string, string] = useMemo(() => {
|
||||||
const cookieKey = createAutoSaveId(autoSaveId);
|
const cookieKey = createAutoSaveId(autoSaveId);
|
||||||
const cookieValue = Cookies.get(cookieKey);
|
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(() => {
|
useUpdateLayoutEffect(() => {
|
||||||
animateOpenSplitter(isOpenSecondary ? 'open' : 'closed');
|
animateOpenSplitter(isOpenSecondary ? 'open' : 'closed');
|
||||||
}, [isOpenSecondary]);
|
}, [isOpenSecondary]);
|
||||||
|
@ -70,7 +87,7 @@ export const FileContainer: React.FC<FileContainerProps> = ({ children }) => {
|
||||||
defaultLayout={defaultLayout}
|
defaultLayout={defaultLayout}
|
||||||
initialReady={false}
|
initialReady={false}
|
||||||
leftChildren={children}
|
leftChildren={children}
|
||||||
rightChildren={<div>Right {selectedFileViewSecondary}</div>}
|
rightChildren={rightChildren}
|
||||||
allowResize={selectedFileViewRenderSecondary}
|
allowResize={selectedFileViewRenderSecondary}
|
||||||
preserveSide={'right'}
|
preserveSide={'right'}
|
||||||
rightPanelMinSize={250}
|
rightPanelMinSize={250}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
import { Button } from '@/components/ui/buttons';
|
import { Button } from '@/components/ui/buttons';
|
||||||
import { ArrowLeft, History } from '@/components/ui/icons';
|
import { ArrowLeft, History } from '@/components/ui/icons';
|
||||||
import React, { useMemo, useTransition } from 'react';
|
import React, { useMemo, useTransition } from 'react';
|
||||||
|
@ -8,6 +10,7 @@ import { useGetDashboard } from '@/api/buster_rest/dashboards';
|
||||||
import { useAppLayoutContextSelector } from '@/context/BusterAppLayout';
|
import { useAppLayoutContextSelector } from '@/context/BusterAppLayout';
|
||||||
import { useMemoizedFn } from '@/hooks';
|
import { useMemoizedFn } from '@/hooks';
|
||||||
import { timeout } from '@/lib';
|
import { timeout } from '@/lib';
|
||||||
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
|
|
||||||
export const FileContainerVersionHistory = React.memo(() => {
|
export const FileContainerVersionHistory = React.memo(() => {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,31 +1,29 @@
|
||||||
import type { FileType } from '@/api/asset_interfaces/chat';
|
|
||||||
import type { FileContainerSecondaryProps } from './interfaces';
|
import type { FileContainerSecondaryProps } from './interfaces';
|
||||||
import type {
|
import { SelectedFileSecondaryRecord } from './secondaryPanelsConfig';
|
||||||
DashboardFileViewSecondary,
|
import { useMemo } from 'react';
|
||||||
MetricFileViewSecondary
|
|
||||||
} from '../../ChatLayoutContext/useLayoutConfig/interfaces';
|
|
||||||
|
|
||||||
const MetricSecondaryRecord: Record<
|
export const FileContainerSecondary: React.FC<FileContainerSecondaryProps> = ({
|
||||||
MetricFileViewSecondary,
|
selectedFile,
|
||||||
React.FC<FileContainerSecondaryProps>
|
selectedFileViewSecondary
|
||||||
> = {
|
}) => {
|
||||||
'chart-edit': () => null,
|
const Component = useMemo(() => {
|
||||||
'sql-edit': () => null,
|
if (!selectedFile || !selectedFileViewSecondary) return null;
|
||||||
'version-history': () => null
|
|
||||||
};
|
|
||||||
|
|
||||||
const DashboardSecondaryRecord: Record<
|
const assosciatedType = SelectedFileSecondaryRecord[selectedFile?.type];
|
||||||
DashboardFileViewSecondary,
|
|
||||||
React.FC<FileContainerSecondaryProps>
|
|
||||||
> = {
|
|
||||||
'version-history': () => null
|
|
||||||
};
|
|
||||||
|
|
||||||
const SelectedFileSecondaryRecord: Record<
|
if (!assosciatedType) return null;
|
||||||
FileType,
|
|
||||||
Record<string, React.FC<FileContainerSecondaryProps>>
|
return assosciatedType[selectedFileViewSecondary];
|
||||||
> = {
|
}, [selectedFileViewSecondary, selectedFile?.id, selectedFile?.type]);
|
||||||
metric: MetricSecondaryRecord,
|
|
||||||
dashboard: DashboardSecondaryRecord,
|
return (
|
||||||
reasoning: {}
|
<>
|
||||||
|
{Component && (
|
||||||
|
<Component
|
||||||
|
selectedFile={selectedFile}
|
||||||
|
selectedFileViewSecondary={selectedFileViewSecondary}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import type { FileViewSecondary } from '@/layouts/ChatLayout/ChatLayoutContext/useLayoutConfig';
|
import type { FileViewSecondary } from '@/layouts/ChatLayout/ChatLayoutContext/useLayoutConfig';
|
||||||
|
import { SelectedFile } from '../../interfaces';
|
||||||
|
|
||||||
export type FileContainerSecondaryProps = {
|
export type FileContainerSecondaryProps = {
|
||||||
selectedFileId: string | undefined;
|
|
||||||
selectedFileViewSecondary: FileViewSecondary | undefined;
|
selectedFileViewSecondary: FileViewSecondary | undefined;
|
||||||
|
selectedFile: SelectedFile | null;
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,7 +15,7 @@ export const DashboardSecondaryRecord: Record<
|
||||||
DashboardFileViewSecondary,
|
DashboardFileViewSecondary,
|
||||||
React.FC<FileContainerSecondaryProps>
|
React.FC<FileContainerSecondaryProps>
|
||||||
> = {
|
> = {
|
||||||
'version-history': ({ selectedFileId }) => (
|
'version-history': ({ selectedFile }) => (
|
||||||
<VersionHistoryPanel assetId={selectedFileId || ''} type="dashboard" />
|
<VersionHistoryPanel assetId={selectedFile?.id || ''} type="dashboard" />
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { CircleSpinnerLoaderContainer } from '@/components/ui/loaders/CircleSpin
|
||||||
|
|
||||||
export const loading = () => {
|
export const loading = () => {
|
||||||
return (
|
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 />
|
<CircleSpinnerLoaderContainer />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -26,9 +26,9 @@ export const MetricSecondaryRecord: Record<
|
||||||
MetricFileViewSecondary,
|
MetricFileViewSecondary,
|
||||||
React.FC<FileContainerSecondaryProps>
|
React.FC<FileContainerSecondaryProps>
|
||||||
> = {
|
> = {
|
||||||
'chart-edit': ({ selectedFileId }) => <MetricEditController metricId={selectedFileId || ''} />,
|
'chart-edit': ({ selectedFile }) => <MetricEditController metricId={selectedFile?.id || ''} />,
|
||||||
'sql-edit': ({ selectedFileId }) => <MetricViewResults metricId={selectedFileId || ''} />,
|
'sql-edit': ({ selectedFile }) => <MetricViewResults metricId={selectedFile?.id || ''} />,
|
||||||
'version-history': ({ selectedFileId }) => (
|
'version-history': ({ selectedFile }) => (
|
||||||
<VersionHistoryPanel assetId={selectedFileId || ''} type="metric" />
|
<VersionHistoryPanel assetId={selectedFile?.id || ''} type="metric" />
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue