mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-06-29 06:11:22 +08:00
🐛 fix(data-grid): 修复 DDL 视图常驻与侧栏交互
记忆 DDL 底部/侧栏布局并支持切表常驻刷新 修复侧栏关闭、拖拽预览和首次加载布局抖动 优化 DDL 行号 gutter,并保留文本选择时的横向视角
This commit is contained in:
@@ -1322,6 +1322,7 @@ const DataGrid: React.FC<DataGridProps> = ({
|
||||
handleViewModeChange,
|
||||
handleDdlSidebarResizeStart,
|
||||
resetDdlViewState,
|
||||
closeDdlView,
|
||||
} = useDataGridDdlView({
|
||||
canViewDdl,
|
||||
currentConnConfig,
|
||||
@@ -1329,6 +1330,7 @@ const DataGrid: React.FC<DataGridProps> = ({
|
||||
dbType,
|
||||
tableName,
|
||||
isV2Ui,
|
||||
isActive,
|
||||
cellEditMode,
|
||||
selectedRowKeys,
|
||||
mergedDisplayDataRef,
|
||||
@@ -1833,8 +1835,19 @@ const DataGrid: React.FC<DataGridProps> = ({
|
||||
}, [displayData, modifiedRows, deletedRowKeys]);
|
||||
mergedDisplayDataRef.current = mergedDisplayData;
|
||||
|
||||
const dataSourceContextKey = useMemo(
|
||||
() => `${connectionId || ''}\u0001${dbName || ''}\u0001${tableName || ''}`,
|
||||
[connectionId, dbName, tableName],
|
||||
);
|
||||
const previousDataSourceContextKeyRef = useRef<string | null>(null);
|
||||
|
||||
// Reset local state when data source likely changes (e.g. tableName change)
|
||||
useEffect(() => {
|
||||
const previousContextKey = previousDataSourceContextKeyRef.current;
|
||||
const contextChanged = previousContextKey !== dataSourceContextKey;
|
||||
previousDataSourceContextKeyRef.current = dataSourceContextKey;
|
||||
if (!contextChanged) return;
|
||||
|
||||
setAddedRows([]);
|
||||
setModifiedRows({});
|
||||
setDeletedRowKeys(new Set());
|
||||
@@ -1843,11 +1856,30 @@ const DataGrid: React.FC<DataGridProps> = ({
|
||||
setCopiedCellPatch(null);
|
||||
setCopiedRowsForPaste([]);
|
||||
closeRowEditor();
|
||||
resetDdlViewState();
|
||||
const shouldKeepOpenV2DdlView = previousContextKey !== null
|
||||
&& isV2Ui
|
||||
&& viewMode === 'ddl'
|
||||
&& canViewDdl
|
||||
&& !!currentConnConfig
|
||||
&& !!tableName;
|
||||
if (!shouldKeepOpenV2DdlView && previousContextKey !== null) {
|
||||
resetDdlViewState();
|
||||
}
|
||||
closeVirtualInlineEditor();
|
||||
closeCellEditor();
|
||||
formRef.current.resetFields();
|
||||
}, [tableName, dbName, connectionId, closeRowEditor, resetDdlViewState, closeVirtualInlineEditor, closeCellEditor]); // Reset on context change
|
||||
}, [
|
||||
canViewDdl,
|
||||
closeCellEditor,
|
||||
closeRowEditor,
|
||||
closeVirtualInlineEditor,
|
||||
currentConnConfig,
|
||||
dataSourceContextKey,
|
||||
isV2Ui,
|
||||
resetDdlViewState,
|
||||
tableName,
|
||||
viewMode,
|
||||
]); // Reset on context change
|
||||
|
||||
useEffect(() => {
|
||||
const next = new Map<string, Item>();
|
||||
@@ -4260,6 +4292,7 @@ const DataGrid: React.FC<DataGridProps> = ({
|
||||
handleCopyUpdate,
|
||||
handleDataPanelFormatJson,
|
||||
handleDataPanelSave,
|
||||
closeDdlView,
|
||||
handleDdlSidebarResizeStart,
|
||||
handleDeleteSelected,
|
||||
handleDragEnd,
|
||||
|
||||
@@ -83,6 +83,7 @@ const DataGridShell: React.FC<DataGridShellProps> = (props) => {
|
||||
closeBatchEditModal,
|
||||
closeCellEditMode,
|
||||
closeCellEditor,
|
||||
closeDdlView,
|
||||
closeJsonEditor,
|
||||
closeRowEditor,
|
||||
closestCenter,
|
||||
@@ -755,6 +756,7 @@ const renderDataTableView = () => (
|
||||
ddlText={ddlText}
|
||||
darkMode={darkMode}
|
||||
onDdlViewLayoutChange={setDdlViewLayout}
|
||||
onClose={closeDdlView}
|
||||
onReload={() => {
|
||||
void handleOpenTableDdl({ asView: true });
|
||||
}}
|
||||
@@ -773,6 +775,7 @@ const renderDataTableView = () => (
|
||||
ddlText={ddlText}
|
||||
darkMode={darkMode}
|
||||
onDdlViewLayoutChange={setDdlViewLayout}
|
||||
onClose={closeDdlView}
|
||||
onReload={() => {
|
||||
void handleOpenTableDdl({ asView: true });
|
||||
}}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Button, Segmented } from 'antd';
|
||||
import { CopyOutlined } from '@ant-design/icons';
|
||||
import Editor from './MonacoEditor';
|
||||
import Editor, { type OnMount } from './MonacoEditor';
|
||||
import { t as defaultTranslate, type I18nParams } from '../i18n';
|
||||
|
||||
type DdlViewLayoutMode = 'bottom' | 'side';
|
||||
@@ -16,10 +16,82 @@ export interface DataGridV2DdlViewProps {
|
||||
ddlText: string;
|
||||
darkMode: boolean;
|
||||
onDdlViewLayoutChange: (layout: DdlViewLayoutMode) => void;
|
||||
onClose?: () => void;
|
||||
onReload: () => void;
|
||||
onCopy: () => void;
|
||||
}
|
||||
|
||||
const handleReadOnlyDdlEditorMount: OnMount = (editor, monaco) => {
|
||||
const contentMouseTargetTypes = new Set([
|
||||
monaco.editor.MouseTargetType.CONTENT_TEXT,
|
||||
monaco.editor.MouseTargetType.CONTENT_EMPTY,
|
||||
]);
|
||||
let pendingContentInteraction:
|
||||
| { x: number; y: number; scrollLeft: number }
|
||||
| null = null;
|
||||
|
||||
const getMousePoint = (event: any) => ({
|
||||
x: Number.isFinite(Number(event?.posx)) ? Number(event.posx) : Number(event?.browserEvent?.clientX ?? 0),
|
||||
y: Number.isFinite(Number(event?.posy)) ? Number(event.posy) : Number(event?.browserEvent?.clientY ?? 0),
|
||||
});
|
||||
|
||||
const restoreScrollLeft = (scrollLeft: number) => {
|
||||
const apply = () => {
|
||||
if (typeof editor.getScrollLeft === 'function' && editor.getScrollLeft() === scrollLeft) return;
|
||||
editor.setScrollLeft?.(scrollLeft);
|
||||
};
|
||||
apply();
|
||||
if (typeof requestAnimationFrame === 'function') {
|
||||
requestAnimationFrame(() => {
|
||||
apply();
|
||||
requestAnimationFrame(apply);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
editor.onDidScrollChange?.((event: any) => {
|
||||
if (!pendingContentInteraction) return;
|
||||
if (event?.scrollLeftChanged === false) return;
|
||||
restoreScrollLeft(pendingContentInteraction.scrollLeft);
|
||||
});
|
||||
|
||||
editor.onMouseDown((event: any) => {
|
||||
pendingContentInteraction = null;
|
||||
if (!contentMouseTargetTypes.has(event.target?.type)) return;
|
||||
const mouseEvent = event.event;
|
||||
if (mouseEvent?.browserEvent && mouseEvent.browserEvent.button !== 0) return;
|
||||
if (mouseEvent?.leftButton === false) return;
|
||||
const point = getMousePoint(mouseEvent);
|
||||
pendingContentInteraction = {
|
||||
...point,
|
||||
scrollLeft: typeof editor.getScrollLeft === 'function' ? editor.getScrollLeft() : 0,
|
||||
};
|
||||
});
|
||||
|
||||
editor.onMouseUp((event: any) => {
|
||||
const interaction = pendingContentInteraction;
|
||||
if (!interaction) return;
|
||||
const point = getMousePoint(event.event);
|
||||
const moved = Math.abs(point.x - interaction.x) > 3 || Math.abs(point.y - interaction.y) > 3;
|
||||
restoreScrollLeft(interaction.scrollLeft);
|
||||
if (!moved) {
|
||||
pendingContentInteraction = null;
|
||||
return;
|
||||
}
|
||||
if (typeof requestAnimationFrame === 'function') {
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
if (pendingContentInteraction === interaction) {
|
||||
pendingContentInteraction = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
pendingContentInteraction = null;
|
||||
});
|
||||
};
|
||||
|
||||
export const DataGridV2DdlView: React.FC<DataGridV2DdlViewProps> = ({
|
||||
layout,
|
||||
translate = defaultTranslate,
|
||||
@@ -29,16 +101,17 @@ export const DataGridV2DdlView: React.FC<DataGridV2DdlViewProps> = ({
|
||||
ddlText,
|
||||
darkMode,
|
||||
onDdlViewLayoutChange,
|
||||
onClose,
|
||||
onReload,
|
||||
onCopy,
|
||||
}) => (
|
||||
<div data-grid-ddl-view={layout} className={`gn-v2-data-grid-ddl-view${layout === 'side' ? ' is-side' : ''}`}>
|
||||
<div className="gn-v2-data-grid-alt-toolbar">
|
||||
<div>
|
||||
<div className="gn-v2-data-grid-ddl-title">
|
||||
<span>DDL</span>
|
||||
<strong>{tableName ? `DDL - ${tableName}` : 'DDL'}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<div className="gn-v2-data-grid-ddl-actions">
|
||||
<Segmented
|
||||
size="small"
|
||||
value={ddlViewLayout}
|
||||
@@ -55,7 +128,7 @@ export const DataGridV2DdlView: React.FC<DataGridV2DdlViewProps> = ({
|
||||
{translate('data_grid.ddl.copy')}
|
||||
</Button>
|
||||
{layout === 'side' && (
|
||||
<Button size="small" onClick={() => onDdlViewLayoutChange('bottom')}>
|
||||
<Button size="small" onClick={onClose}>
|
||||
{translate('common.close')}
|
||||
</Button>
|
||||
)}
|
||||
@@ -68,13 +141,21 @@ export const DataGridV2DdlView: React.FC<DataGridV2DdlViewProps> = ({
|
||||
language="sql"
|
||||
theme={darkMode ? 'transparent-dark' : 'transparent-light'}
|
||||
value={ddlLoading ? translate('data_grid.ddl.loading') : ddlText}
|
||||
onMount={handleReadOnlyDdlEditorMount}
|
||||
options={{
|
||||
readOnly: true,
|
||||
domReadOnly: true,
|
||||
minimap: { enabled: false },
|
||||
scrollBeyondLastLine: false,
|
||||
wordWrap: 'off',
|
||||
tabSize: 2,
|
||||
automaticLayout: true,
|
||||
mouseStyle: 'default',
|
||||
renderLineHighlight: 'none',
|
||||
glyphMargin: false,
|
||||
folding: false,
|
||||
lineDecorationsWidth: 8,
|
||||
lineNumbersMinChars: 2,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,70 @@ import { formatDdlForDisplay } from '../utils/ddlFormat';
|
||||
type GridViewMode = 'table' | 'json' | 'text' | 'fields' | 'ddl' | 'er' | 'sqlLog';
|
||||
type DdlViewLayoutMode = 'bottom' | 'side';
|
||||
type TranslateParams = Record<string, string | number | boolean | null | undefined>;
|
||||
const DDL_VIEW_LAYOUT_STORAGE_KEY = 'gonavi.dataGrid.ddlViewLayout';
|
||||
let sharedDdlViewOpen = false;
|
||||
let sharedDdlViewLayout: DdlViewLayoutMode | null = null;
|
||||
const ddlViewLayoutListeners = new Set<(layout: DdlViewLayoutMode) => void>();
|
||||
|
||||
const sanitizeDdlViewLayout = (value: unknown): DdlViewLayoutMode => (
|
||||
value === 'side' ? 'side' : 'bottom'
|
||||
);
|
||||
|
||||
const readPersistedDdlViewLayout = (): DdlViewLayoutMode => {
|
||||
try {
|
||||
const storage = typeof window !== 'undefined' ? window.localStorage : undefined;
|
||||
return sanitizeDdlViewLayout(storage?.getItem(DDL_VIEW_LAYOUT_STORAGE_KEY));
|
||||
} catch {
|
||||
return 'bottom';
|
||||
}
|
||||
};
|
||||
|
||||
const persistDdlViewLayout = (layout: DdlViewLayoutMode) => {
|
||||
try {
|
||||
if (typeof window !== 'undefined') {
|
||||
window.localStorage?.setItem(DDL_VIEW_LAYOUT_STORAGE_KEY, layout);
|
||||
}
|
||||
} catch {
|
||||
// Ignore storage failures in restricted runtimes.
|
||||
}
|
||||
};
|
||||
|
||||
const readSharedDdlViewLayout = (): DdlViewLayoutMode => {
|
||||
if (!sharedDdlViewLayout) {
|
||||
sharedDdlViewLayout = readPersistedDdlViewLayout();
|
||||
}
|
||||
return sharedDdlViewLayout;
|
||||
};
|
||||
|
||||
const setSharedDdlViewLayout = (layout: DdlViewLayoutMode) => {
|
||||
sharedDdlViewLayout = layout;
|
||||
persistDdlViewLayout(layout);
|
||||
ddlViewLayoutListeners.forEach((listener) => listener(layout));
|
||||
};
|
||||
|
||||
const setSharedDdlViewOpen = (open: boolean) => {
|
||||
sharedDdlViewOpen = open;
|
||||
};
|
||||
|
||||
const shouldRestoreSharedDdlView = () => sharedDdlViewOpen;
|
||||
|
||||
export const resetDataGridDdlViewSharedStateForTests = () => {
|
||||
sharedDdlViewOpen = false;
|
||||
sharedDdlViewLayout = null;
|
||||
ddlViewLayoutListeners.clear();
|
||||
};
|
||||
|
||||
const buildDdlContextKey = (currentConnConfig: unknown, dbName?: string, tableName?: string) => {
|
||||
const config = (currentConnConfig || {}) as Record<string, unknown>;
|
||||
return [
|
||||
String(config.type || ''),
|
||||
String(config.host || ''),
|
||||
String(config.port || ''),
|
||||
String(config.database || ''),
|
||||
dbName || '',
|
||||
tableName || '',
|
||||
].join('\u0001');
|
||||
};
|
||||
|
||||
interface UseDataGridDdlViewParams {
|
||||
canViewDdl: boolean;
|
||||
@@ -14,6 +78,7 @@ interface UseDataGridDdlViewParams {
|
||||
dbName?: string;
|
||||
tableName?: string;
|
||||
isV2Ui: boolean;
|
||||
isActive?: boolean;
|
||||
cellEditMode: boolean;
|
||||
selectedRowKeys: React.Key[];
|
||||
mergedDisplayDataRef: React.MutableRefObject<any[]>;
|
||||
@@ -44,6 +109,7 @@ export interface UseDataGridDdlViewResult {
|
||||
handleViewModeChange: (nextMode: GridViewMode) => void;
|
||||
handleDdlSidebarResizeStart: (event: React.MouseEvent<HTMLDivElement>) => void;
|
||||
resetDdlViewState: () => void;
|
||||
closeDdlView: () => void;
|
||||
}
|
||||
|
||||
export const useDataGridDdlView = ({
|
||||
@@ -52,6 +118,7 @@ export const useDataGridDdlView = ({
|
||||
dbName,
|
||||
tableName,
|
||||
isV2Ui,
|
||||
isActive = true,
|
||||
cellEditMode,
|
||||
selectedRowKeys,
|
||||
mergedDisplayDataRef,
|
||||
@@ -62,28 +129,71 @@ export const useDataGridDdlView = ({
|
||||
dbType,
|
||||
translate,
|
||||
}: UseDataGridDdlViewParams): UseDataGridDdlViewResult => {
|
||||
const [viewMode, setViewMode] = React.useState<GridViewMode>('table');
|
||||
const canRestoreSharedDdlView = isV2Ui && canViewDdl && !!currentConnConfig && !!tableName && shouldRestoreSharedDdlView();
|
||||
const shouldStartWithSharedDdlView = isActive && canRestoreSharedDdlView;
|
||||
const [viewMode, setViewMode] = React.useState<GridViewMode>(() => (shouldStartWithSharedDdlView ? 'ddl' : 'table'));
|
||||
const [ddlModalOpen, setDdlModalOpen] = React.useState(false);
|
||||
const [ddlLoading, setDdlLoading] = React.useState(false);
|
||||
const [ddlLoading, setDdlLoading] = React.useState(shouldStartWithSharedDdlView);
|
||||
const [ddlText, setDdlText] = React.useState('');
|
||||
const [ddlViewLayout, setDdlViewLayout] = React.useState<DdlViewLayoutMode>('bottom');
|
||||
const [ddlViewLayoutState, setDdlViewLayoutState] = React.useState<DdlViewLayoutMode>(readSharedDdlViewLayout);
|
||||
const [ddlSidebarWidth, setDdlSidebarWidth] = React.useState(420);
|
||||
const [ddlSidebarResizePreviewX, setDdlSidebarResizePreviewX] = React.useState<number | null>(null);
|
||||
const ddlSidebarResizeRef = React.useRef<{
|
||||
startX: number;
|
||||
startWidth: number;
|
||||
previewWidth: number;
|
||||
previewX: number | null;
|
||||
previewElement?: HTMLElement | null;
|
||||
previewFrame?: number | null;
|
||||
moveHandler?: (event: MouseEvent) => void;
|
||||
upHandler?: () => void;
|
||||
} | null>(null);
|
||||
const ddlRequestSeqRef = React.useRef(0);
|
||||
const ddlRequestedContextKeyRef = React.useRef<string | null>(null);
|
||||
const ddlContextKey = React.useMemo(
|
||||
() => buildDdlContextKey(currentConnConfig, dbName, tableName),
|
||||
[currentConnConfig, dbName, tableName],
|
||||
);
|
||||
|
||||
const isTableSurfaceActive = viewMode === 'table' || (isV2Ui && viewMode === 'ddl' && ddlViewLayout === 'side');
|
||||
const ddlViewLayout = ddlViewLayoutState;
|
||||
const resolvedViewMode: GridViewMode = isActive
|
||||
&& canRestoreSharedDdlView
|
||||
&& (viewMode === 'table' || viewMode === 'ddl')
|
||||
? 'ddl'
|
||||
: viewMode;
|
||||
const isDdlContextPending = resolvedViewMode === 'ddl'
|
||||
&& isActive
|
||||
&& canRestoreSharedDdlView
|
||||
&& ddlRequestedContextKeyRef.current !== ddlContextKey;
|
||||
const resolvedDdlLoading = ddlLoading || isDdlContextPending;
|
||||
const resolvedDdlText = isDdlContextPending ? '' : ddlText;
|
||||
const isTableSurfaceActive = resolvedViewMode === 'table' || (isV2Ui && resolvedViewMode === 'ddl' && ddlViewLayout === 'side');
|
||||
|
||||
const translateMessage = React.useCallback((key: string, params?: TranslateParams) => {
|
||||
return translate ? translate(key, params) : catalogTranslate('zh-CN', key, params);
|
||||
}, [translate]);
|
||||
|
||||
const setDdlViewLayout: React.Dispatch<React.SetStateAction<DdlViewLayoutMode>> = React.useCallback((nextLayout) => {
|
||||
const currentLayout = readSharedDdlViewLayout();
|
||||
const resolvedLayout = sanitizeDdlViewLayout(
|
||||
typeof nextLayout === 'function' ? nextLayout(currentLayout) : nextLayout,
|
||||
);
|
||||
setSharedDdlViewLayout(resolvedLayout);
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
const handleSharedDdlViewLayoutChange = (nextLayout: DdlViewLayoutMode) => {
|
||||
setDdlViewLayoutState((currentLayout) => (
|
||||
currentLayout === nextLayout ? currentLayout : nextLayout
|
||||
));
|
||||
};
|
||||
ddlViewLayoutListeners.add(handleSharedDdlViewLayoutChange);
|
||||
handleSharedDdlViewLayoutChange(readSharedDdlViewLayout());
|
||||
return () => {
|
||||
ddlViewLayoutListeners.delete(handleSharedDdlViewLayoutChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleOpenTableDdl = React.useCallback(async (options?: { asView?: boolean }) => {
|
||||
if (!canViewDdl || !currentConnConfig || !tableName) {
|
||||
messageApi.error(translateMessage('data_grid.message.ddl_missing_context'));
|
||||
@@ -91,7 +201,9 @@ export const useDataGridDdlView = ({
|
||||
}
|
||||
const asView = options?.asView === true && isV2Ui;
|
||||
const requestSeq = ++ddlRequestSeqRef.current;
|
||||
ddlRequestedContextKeyRef.current = ddlContextKey;
|
||||
if (asView) {
|
||||
setSharedDdlViewOpen(true);
|
||||
setViewMode('ddl');
|
||||
setDdlModalOpen(false);
|
||||
} else {
|
||||
@@ -115,27 +227,51 @@ export const useDataGridDdlView = ({
|
||||
setDdlLoading(false);
|
||||
}
|
||||
}
|
||||
}, [canViewDdl, currentConnConfig, dbName, dbType, isV2Ui, messageApi, tableName, translateMessage]);
|
||||
}, [canViewDdl, currentConnConfig, dbName, dbType, ddlContextKey, isV2Ui, messageApi, tableName, translateMessage]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isV2Ui || (viewMode !== 'fields' && viewMode !== 'ddl' && viewMode !== 'er' && viewMode !== 'sqlLog')) return;
|
||||
setViewMode('table');
|
||||
}, [isV2Ui, viewMode]);
|
||||
|
||||
const closeDdlView = React.useCallback(() => {
|
||||
setSharedDdlViewOpen(false);
|
||||
ddlRequestedContextKeyRef.current = null;
|
||||
ddlRequestSeqRef.current += 1;
|
||||
setViewMode('table');
|
||||
setDdlModalOpen(false);
|
||||
setDdlLoading(false);
|
||||
setDdlText('');
|
||||
setDdlSidebarResizePreviewX(null);
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!isActive || !isV2Ui || !shouldRestoreSharedDdlView()) return;
|
||||
if (!canViewDdl || !currentConnConfig || !tableName) return;
|
||||
if (ddlRequestedContextKeyRef.current === ddlContextKey) return;
|
||||
void handleOpenTableDdl({ asView: true });
|
||||
}, [canViewDdl, currentConnConfig, ddlContextKey, handleOpenTableDdl, isActive, isV2Ui, tableName]);
|
||||
|
||||
const handleViewModeChange = React.useCallback((nextMode: GridViewMode) => {
|
||||
if ((nextMode === 'fields' || nextMode === 'ddl' || nextMode === 'er' || nextMode === 'sqlLog') && !isV2Ui) {
|
||||
setSharedDdlViewOpen(false);
|
||||
setViewMode('table');
|
||||
return;
|
||||
}
|
||||
if (nextMode === 'sqlLog') {
|
||||
setSharedDdlViewOpen(false);
|
||||
setViewMode('sqlLog');
|
||||
return;
|
||||
}
|
||||
if (nextMode === 'ddl') {
|
||||
if (isV2Ui && resolvedViewMode === 'ddl') {
|
||||
closeDdlView();
|
||||
return;
|
||||
}
|
||||
void handleOpenTableDdl({ asView: true });
|
||||
setViewMode('ddl');
|
||||
return;
|
||||
}
|
||||
setSharedDdlViewOpen(false);
|
||||
if (nextMode === 'json' && cellEditMode) {
|
||||
closeCellEditModeRef.current();
|
||||
}
|
||||
@@ -151,22 +287,69 @@ export const useDataGridDdlView = ({
|
||||
}
|
||||
|
||||
setViewMode(nextMode);
|
||||
}, [cellEditMode, closeCellEditModeRef, handleOpenTableDdl, isV2Ui, mergedDisplayDataRef, rowKeyStr, selectedRowKeys, setTextRecordIndex]);
|
||||
}, [cellEditMode, closeCellEditModeRef, closeDdlView, handleOpenTableDdl, isV2Ui, mergedDisplayDataRef, resolvedViewMode, rowKeyStr, selectedRowKeys, setTextRecordIndex]);
|
||||
|
||||
const handleDdlSidebarResizeStart = React.useCallback((event: React.MouseEvent<HTMLDivElement>) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const startX = event.clientX;
|
||||
const startWidth = ddlSidebarWidth;
|
||||
const resizerElement = event.currentTarget;
|
||||
const workspaceElement = resizerElement.parentElement;
|
||||
const previewElement = workspaceElement?.querySelector?.('[data-grid-ddl-resize-preview="true"]') as HTMLElement | null | undefined;
|
||||
const workspaceWidth = workspaceElement?.getBoundingClientRect?.().width;
|
||||
const resizerWidth = resizerElement.getBoundingClientRect?.().width || 8;
|
||||
const resolvePreviewX = (width: number, fallbackX: number) => (
|
||||
typeof workspaceWidth === 'number' && workspaceWidth > 0
|
||||
? Math.max(0, workspaceWidth - width - resizerWidth / 2)
|
||||
: fallbackX
|
||||
);
|
||||
const applyPreviewElementPosition = (x: number | null) => {
|
||||
if (!previewElement) return;
|
||||
if (x === null) {
|
||||
previewElement.style.opacity = '0';
|
||||
previewElement.style.transform = 'translate3d(0, 0, 0)';
|
||||
return;
|
||||
}
|
||||
previewElement.style.opacity = '1';
|
||||
previewElement.style.transform = `translateX(${x}px)`;
|
||||
};
|
||||
const schedulePreviewElementPosition = (x: number) => {
|
||||
const state = ddlSidebarResizeRef.current;
|
||||
if (!state?.previewElement) return false;
|
||||
state.previewX = x;
|
||||
if (state.previewFrame !== null && state.previewFrame !== undefined) return true;
|
||||
const run = () => {
|
||||
const current = ddlSidebarResizeRef.current;
|
||||
if (!current?.previewElement) return;
|
||||
current.previewFrame = null;
|
||||
applyPreviewElementPosition(current.previewX);
|
||||
};
|
||||
if (typeof requestAnimationFrame === 'function') {
|
||||
state.previewFrame = requestAnimationFrame(run);
|
||||
} else {
|
||||
run();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
const startPreviewX = resolvePreviewX(startWidth, startX);
|
||||
const moveHandler = (moveEvent: MouseEvent) => {
|
||||
const nextWidth = Math.max(320, Math.min(760, startWidth + (startX - moveEvent.clientX)));
|
||||
if (ddlSidebarResizeRef.current) {
|
||||
ddlSidebarResizeRef.current.previewWidth = nextWidth;
|
||||
}
|
||||
setDdlSidebarResizePreviewX(moveEvent.clientX);
|
||||
const nextPreviewX = resolvePreviewX(nextWidth, moveEvent.clientX);
|
||||
if (!schedulePreviewElementPosition(nextPreviewX)) {
|
||||
setDdlSidebarResizePreviewX(nextPreviewX);
|
||||
}
|
||||
};
|
||||
const upHandler = () => {
|
||||
const nextWidth = ddlSidebarResizeRef.current?.previewWidth ?? startWidth;
|
||||
const resizeState = ddlSidebarResizeRef.current;
|
||||
const nextWidth = resizeState?.previewWidth ?? startWidth;
|
||||
if (resizeState?.previewFrame !== null && resizeState?.previewFrame !== undefined && typeof cancelAnimationFrame === 'function') {
|
||||
cancelAnimationFrame(resizeState.previewFrame);
|
||||
}
|
||||
applyPreviewElementPosition(null);
|
||||
setDdlSidebarWidth(nextWidth);
|
||||
setDdlSidebarResizePreviewX(null);
|
||||
document.removeEventListener('mousemove', moveHandler);
|
||||
@@ -174,27 +357,41 @@ export const useDataGridDdlView = ({
|
||||
ddlSidebarResizeRef.current = null;
|
||||
};
|
||||
|
||||
ddlSidebarResizeRef.current = { startX, startWidth, previewWidth: startWidth, moveHandler, upHandler };
|
||||
ddlSidebarResizeRef.current = {
|
||||
startX,
|
||||
startWidth,
|
||||
previewWidth: startWidth,
|
||||
previewX: startPreviewX,
|
||||
previewElement,
|
||||
previewFrame: null,
|
||||
moveHandler,
|
||||
upHandler,
|
||||
};
|
||||
if (previewElement) {
|
||||
applyPreviewElementPosition(startPreviewX);
|
||||
} else {
|
||||
setDdlSidebarResizePreviewX(startPreviewX);
|
||||
}
|
||||
document.addEventListener('mousemove', moveHandler);
|
||||
document.addEventListener('mouseup', upHandler);
|
||||
}, [ddlSidebarWidth]);
|
||||
|
||||
const resetDdlViewState = React.useCallback(() => {
|
||||
ddlRequestedContextKeyRef.current = null;
|
||||
ddlRequestSeqRef.current += 1;
|
||||
setDdlModalOpen(false);
|
||||
setDdlLoading(false);
|
||||
setDdlText('');
|
||||
setDdlViewLayout('bottom');
|
||||
setDdlSidebarResizePreviewX(null);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
viewMode,
|
||||
viewMode: resolvedViewMode,
|
||||
setViewMode,
|
||||
ddlModalOpen,
|
||||
setDdlModalOpen,
|
||||
ddlLoading,
|
||||
ddlText,
|
||||
ddlLoading: resolvedDdlLoading,
|
||||
ddlText: resolvedDdlText,
|
||||
ddlViewLayout,
|
||||
setDdlViewLayout,
|
||||
ddlSidebarWidth,
|
||||
@@ -205,5 +402,6 @@ export const useDataGridDdlView = ({
|
||||
handleViewModeChange,
|
||||
handleDdlSidebarResizeStart,
|
||||
resetDdlViewState,
|
||||
closeDdlView,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -819,24 +819,35 @@ body[data-ui-version="v2"] .gn-v2-data-grid-ddl-view.is-side {
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-ddl-view.is-side .gn-v2-data-grid-alt-toolbar {
|
||||
min-height: 52px;
|
||||
align-items: flex-start;
|
||||
padding: 8px 10px;
|
||||
min-height: 46px;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 7px 10px;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-ddl-view.is-side .gn-v2-data-grid-alt-toolbar > div:first-child {
|
||||
flex: 1 1 auto;
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-ddl-view.is-side .gn-v2-data-grid-ddl-title {
|
||||
flex: 1 1 72px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-ddl-view.is-side .gn-v2-data-grid-alt-toolbar > div:last-child {
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-ddl-view.is-side .gn-v2-data-grid-ddl-actions {
|
||||
justify-content: flex-end;
|
||||
flex: 0 1 auto;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
flex: 0 0 auto;
|
||||
flex-wrap: nowrap;
|
||||
gap: 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-ddl-view.is-side .gn-v2-data-grid-ddl-actions .ant-btn-sm {
|
||||
padding-inline: 7px;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-ddl-view.is-side .gn-v2-data-grid-ddl-actions .ant-segmented-small .ant-segmented-item-label {
|
||||
padding-inline: 8px;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-ddl-view.is-side .gn-v2-data-grid-alt-toolbar strong {
|
||||
max-width: 210px;
|
||||
max-width: 150px;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-ddl-code {
|
||||
|
||||
Reference in New Issue
Block a user