fix(editor): fix zoom and disableScrollLimit -> enableScrollLimit (#390)

* fix(core): flow-nodes-content-layer auto resize

* fix(editor): fix zoom and disableScrollLimit -> enableScrollLimit
This commit is contained in:
xiamidaxia 2025-06-18 21:58:07 +08:00 committed by GitHub
parent 7c376e3254
commit d21cdae3ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 74 additions and 50 deletions

View File

@ -6,7 +6,7 @@ import { Divider, Dropdown } from '@douyinfe/semi-ui';
import { SelectZoom } from './styles'; import { SelectZoom } from './styles';
export const ZoomSelect = () => { export const ZoomSelect = () => {
const tools = usePlaygroundTools(); const tools = usePlaygroundTools({ maxZoom: 2, minZoom: 0.25 });
const [dropDownVisible, openDropDown] = useState(false); const [dropDownVisible, openDropDown] = useState(false);
return ( return (
<Dropdown <Dropdown

View File

@ -184,6 +184,13 @@ export function useEditorProps(
onBind: ({ bind }) => { onBind: ({ bind }) => {
bind(CustomService).toSelf().inSingletonScope(); bind(CustomService).toSelf().inSingletonScope();
}, },
scroll: {
/**
*
* Limit scrolling so that none of the nodes can see it
*/
enableScrollLimit: true,
},
/** /**
* Playground init * Playground init
*/ */

View File

@ -6,7 +6,7 @@ import { Divider, Dropdown } from '@douyinfe/semi-ui';
import { SelectZoom } from './styles'; import { SelectZoom } from './styles';
export const ZoomSelect = () => { export const ZoomSelect = () => {
const tools = usePlaygroundTools(); const tools = usePlaygroundTools({ maxZoom: 2, minZoom: 0.25 });
const playground = usePlayground(); const playground = usePlayground();
const [dropDownVisible, openDropDown] = useState(false); const [dropDownVisible, openDropDown] = useState(false);
return ( return (

View File

@ -1,6 +1,6 @@
import { nanoid } from 'nanoid'; import { nanoid } from 'nanoid';
import { Field, FieldArray } from '@flowgram.ai/free-layout-editor'; import { Field, FieldArray } from '@flowgram.ai/free-layout-editor';
import { ConditionRow, ConditionRowValueType, VariableSelector } from '@flowgram.ai/form-materials'; import { ConditionRow, ConditionRowValueType } from '@flowgram.ai/form-materials';
import { Button } from '@douyinfe/semi-ui'; import { Button } from '@douyinfe/semi-ui';
import { IconPlus, IconCrossCircleStroked } from '@douyinfe/semi-icons'; import { IconPlus, IconCrossCircleStroked } from '@douyinfe/semi-icons';

View File

@ -101,7 +101,7 @@ export class PlaygroundConfigEntity extends ConfigEntity<PlaygroundConfigEntityD
originY: 0, originY: 0,
width: 0, width: 0,
height: 0, height: 0,
minZoom: 0.1, minZoom: 0.25,
maxZoom: 2, maxZoom: 2,
zoom: 1, zoom: 1,
clientX: 0, clientX: 0,

View File

@ -6,8 +6,6 @@ import {
FlowNodesTransformLayer, FlowNodesTransformLayer,
type FlowRendererContribution, type FlowRendererContribution,
type FlowRendererRegistry, type FlowRendererRegistry,
FlowScrollBarLayer,
FlowScrollLimitLayer,
} from '@flowgram.ai/renderer'; } from '@flowgram.ai/renderer';
import { import {
type FlowDocument, type FlowDocument,
@ -94,9 +92,7 @@ export class FlowRegisters
FlowNodesContentLayer, // 节点内容渲染 FlowNodesContentLayer, // 节点内容渲染
FlowLinesLayer, // 线条渲染 FlowLinesLayer, // 线条渲染
FlowLabelsLayer, // Label 渲染 FlowLabelsLayer, // Label 渲染
PlaygroundLayer, // 画布基础层,提供缩放、手势等能力 PlaygroundLayer // 画布基础层,提供缩放、手势等能力
FlowScrollLimitLayer, // 控制滚动范围
FlowScrollBarLayer // 滚动条
); );
} }

View File

@ -81,7 +81,7 @@ export class FlowNodesContentLayer extends Layer {
const PortalRenderer = this.getPortalRenderer(data!); const PortalRenderer = this.getPortalRenderer(data!);
function Portal(): JSX.Element { function Portal(): JSX.Element {
React.useLayoutEffect(() => { React.useEffect(() => {
// 第一次加载需要把宽高通知 // 第一次加载需要把宽高通知
if (!entity.getNodeMeta().autoResizeDisable && node.clientWidth && node.clientHeight) { if (!entity.getNodeMeta().autoResizeDisable && node.clientWidth && node.clientHeight) {
const transform = entity.getData<FlowNodeTransformData>(FlowNodeTransformData); const transform = entity.getData<FlowNodeTransformData>(FlowNodeTransformData);

View File

@ -87,7 +87,7 @@ export interface EditorProps<
reduxDevTool?: ReduxDevToolPluginOptions; reduxDevTool?: ReduxDevToolPluginOptions;
scroll?: { scroll?: {
disableScrollLimit?: boolean; // 关闭滚动限制 enableScrollLimit?: boolean; // 开启滚动限制
disableScrollBar?: boolean; // 关闭滚动条 disableScrollBar?: boolean; // 关闭滚动条
}; };

View File

@ -72,7 +72,7 @@ export interface PlaygroundTools {
} }
export function usePlaygroundTools(props?: PlaygroundToolsPropsType): PlaygroundTools { export function usePlaygroundTools(props?: PlaygroundToolsPropsType): PlaygroundTools {
const { maxZoom = 2, minZoom = 0.25, padding = 30 } = props || {}; const { maxZoom, minZoom, padding = 30 } = props || {};
const playground = usePlayground(); const playground = usePlayground();
const container = usePlaygroundContainer(); const container = usePlaygroundContainer();
const historyService = container.isBound(HistoryService) const historyService = container.isBound(HistoryService)
@ -85,12 +85,6 @@ export function usePlaygroundTools(props?: PlaygroundToolsPropsType): Playground
const [canUndo, setCanUndo] = useState(false); const [canUndo, setCanUndo] = useState(false);
const [canRedo, setCanRedo] = useState(false); const [canRedo, setCanRedo] = useState(false);
const fitViewOptions = {
maxZoom,
minZoom,
padding,
};
const changeLayout = useCallback( const changeLayout = useCallback(
(newLayout?: FlowLayoutDefault) => { (newLayout?: FlowLayoutDefault) => {
const allNodes = doc.getAllNodes(); const allNodes = doc.getAllNodes();
@ -105,8 +99,10 @@ export function usePlaygroundTools(props?: PlaygroundToolsPropsType): Playground
}); });
setTimeout(() => { setTimeout(() => {
fitView(doc, playground.config, { fitView(doc, playground.config, {
...fitViewOptions,
easingDuration: 300, easingDuration: 300,
padding,
maxZoom: playground.config.config.maxZoom,
minZoom: playground.config.config.minZoom,
}); });
}, 10); }, 10);
setTimeout(() => { setTimeout(() => {
@ -118,14 +114,11 @@ export function usePlaygroundTools(props?: PlaygroundToolsPropsType): Playground
doc.setLayout(newLayout); doc.setLayout(newLayout);
updateLayout(doc.layout); updateLayout(doc.layout);
}, },
[doc, playground] [doc, playground, padding]
); );
const handleZoomOut = useCallback( const handleZoomOut = useCallback(
(easing?: boolean) => { (easing?: boolean) => {
if (zoom < minZoom) {
return;
}
playground?.config.zoomout(easing); playground?.config.zoomout(easing);
}, },
[zoom, playground] [zoom, playground]
@ -133,9 +126,6 @@ export function usePlaygroundTools(props?: PlaygroundToolsPropsType): Playground
const handleZoomIn = useCallback( const handleZoomIn = useCallback(
(easing?: boolean) => { (easing?: boolean) => {
if (zoom > maxZoom) {
return;
}
playground?.config.zoomin(easing); playground?.config.zoomin(easing);
}, },
[zoom, playground] [zoom, playground]
@ -145,12 +135,14 @@ export function usePlaygroundTools(props?: PlaygroundToolsPropsType): Playground
const handleFitView = useCallback( const handleFitView = useCallback(
(easing?: boolean, easingDuration?: number) => { (easing?: boolean, easingDuration?: number) => {
fitView(doc, playground.config, { fitView(doc, playground.config, {
...fitViewOptions,
easing, easing,
easingDuration, easingDuration,
padding,
maxZoom: playground.config.config.maxZoom,
minZoom: playground.config.config.minZoom,
}); });
}, },
[doc, playground] [doc, playground, padding]
); );
const handleUpdateZoom = useCallback( const handleUpdateZoom = useCallback(
@ -179,6 +171,14 @@ export function usePlaygroundTools(props?: PlaygroundToolsPropsType): Playground
return () => dispose.dispose(); return () => dispose.dispose();
}, [playground, historyService]); }, [playground, historyService]);
useEffect(() => {
const config = playground.config.config;
playground.config.updateConfig({
maxZoom: maxZoom !== undefined ? maxZoom : config.maxZoom,
minZoom: minZoom !== undefined ? minZoom : config.minZoom,
});
}, [playground, maxZoom, minZoom]);
return { return {
zoomin: handleZoomIn, zoomin: handleZoomIn,
zoomout: handleZoomOut, zoomout: handleZoomOut,

View File

@ -141,7 +141,7 @@ export function createFixedLayoutPreset(
FlowNodesContentLayer, // 节点内容渲染 FlowNodesContentLayer, // 节点内容渲染
FlowNodesTransformLayer // 节点位置偏移计算 FlowNodesTransformLayer // 节点位置偏移计算
); );
if (!opts.scroll?.disableScrollLimit) { if (opts.scroll?.enableScrollLimit) {
// 控制滚动范围 // 控制滚动范围
ctx.playground.registerLayer(FlowScrollLimitLayer); ctx.playground.registerLayer(FlowScrollLimitLayer);
} }

View File

@ -41,5 +41,10 @@ export namespace FixedLayoutProps {
/** /**
* *
*/ */
export const DEFAULT: FixedLayoutProps = EditorProps.DEFAULT as FixedLayoutProps; export const DEFAULT: FixedLayoutProps = {
...EditorProps.DEFAULT,
scroll: {
enableScrollLimit: true,
},
} as FixedLayoutProps;
} }

View File

@ -44,7 +44,19 @@ export interface PlaygroundTools {
setMouseScrollDelta: (mouseScrollDelta: number | ((zoom: number) => number)) => void; setMouseScrollDelta: (mouseScrollDelta: number | ((zoom: number) => number)) => void;
} }
export function usePlaygroundTools(): PlaygroundTools { export interface PlaygroundToolsPropsType {
/**
* 2
*/
maxZoom?: number;
/**
* 0.25
*/
minZoom?: number;
}
export function usePlaygroundTools(props?: PlaygroundToolsPropsType): PlaygroundTools {
const { maxZoom, minZoom } = props || {};
const playground = usePlayground(); const playground = usePlayground();
const doc = useService<WorkflowDocument>(WorkflowDocument); const doc = useService<WorkflowDocument>(WorkflowDocument);
@ -55,9 +67,6 @@ export function usePlaygroundTools(): PlaygroundTools {
const handleZoomOut = useCallback( const handleZoomOut = useCallback(
(easing?: boolean) => { (easing?: boolean) => {
if (zoom < 0.1) {
return;
}
playground?.config.zoomout(easing); playground?.config.zoomout(easing);
}, },
[zoom, playground] [zoom, playground]
@ -65,9 +74,6 @@ export function usePlaygroundTools(): PlaygroundTools {
const handleZoomIn = useCallback( const handleZoomIn = useCallback(
(easing?: boolean) => { (easing?: boolean) => {
if (zoom > 1.9) {
return;
}
playground?.config.zoomin(easing); playground?.config.zoomin(easing);
}, },
[zoom, playground] [zoom, playground]
@ -171,6 +177,14 @@ export function usePlaygroundTools(): PlaygroundTools {
}); });
} }
useEffect(() => {
const config = playground.config.config;
playground.config.updateConfig({
maxZoom: maxZoom !== undefined ? maxZoom : config.maxZoom,
minZoom: minZoom !== undefined ? minZoom : config.minZoom,
});
}, [playground, maxZoom, minZoom]);
return { return {
zoomin: handleZoomIn, zoomin: handleZoomIn,
zoomout: handleZoomOut, zoomout: handleZoomOut,

View File

@ -181,7 +181,7 @@ export function createFreeLayoutPreset(
} }
}, },
}); });
if (!opts.scroll?.disableScrollLimit) { if (opts.scroll?.enableScrollLimit) {
// 控制滚动范围 // 控制滚动范围
ctx.playground.registerLayer(FlowScrollLimitLayer); ctx.playground.registerLayer(FlowScrollLimitLayer);
} }

View File

@ -1,5 +1,6 @@
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { DisposableCollection } from '@flowgram.ai/utils';
import { import {
EditorState, EditorState,
EditorStateConfigEntity, EditorStateConfigEntity,
@ -7,7 +8,6 @@ import {
useConfigEntity, useConfigEntity,
usePlayground, usePlayground,
} from '@flowgram.ai/core'; } from '@flowgram.ai/core';
import { DisposableCollection } from '@flowgram.ai/utils';
export interface PlaygroundToolsPropsType { export interface PlaygroundToolsPropsType {
/** /**
@ -49,7 +49,7 @@ export interface PlaygroundTools {
} }
export function usePlaygroundTools(props?: PlaygroundToolsPropsType): PlaygroundTools { export function usePlaygroundTools(props?: PlaygroundToolsPropsType): PlaygroundTools {
const { maxZoom = 2, minZoom = 0.25 } = props || {}; const { maxZoom, minZoom } = props || {};
const playground = usePlayground(); const playground = usePlayground();
const editorState = useConfigEntity(EditorStateConfigEntity, true); const editorState = useConfigEntity(EditorStateConfigEntity, true);
@ -57,29 +57,23 @@ export function usePlaygroundTools(props?: PlaygroundToolsPropsType): Playground
const handleZoomOut = useCallback( const handleZoomOut = useCallback(
(easing?: boolean) => { (easing?: boolean) => {
if (zoom < minZoom) {
return;
}
playground.config.zoomout(easing); playground.config.zoomout(easing);
}, },
[zoom, playground, minZoom], [playground]
); );
const handleZoomIn = useCallback( const handleZoomIn = useCallback(
(easing?: boolean) => { (easing?: boolean) => {
if (zoom > maxZoom) {
return;
}
playground.config.zoomin(easing); playground.config.zoomin(easing);
}, },
[zoom, playground, maxZoom], [playground]
); );
const handleUpdateZoom = useCallback( const handleUpdateZoom = useCallback(
(value: number, easing?: boolean, easingDuration?: number) => { (value: number, easing?: boolean, easingDuration?: number) => {
playground.config.updateZoom(value, easing, easingDuration); playground.config.updateZoom(value, easing, easingDuration);
}, },
[playground], [playground]
); );
const handleToggleIneractiveType = useCallback(() => { const handleToggleIneractiveType = useCallback(() => {
@ -92,10 +86,18 @@ export function usePlaygroundTools(props?: PlaygroundToolsPropsType): Playground
useEffect(() => { useEffect(() => {
const dispose = new DisposableCollection(); const dispose = new DisposableCollection();
dispose.push(playground.onZoom(z => setZoom(z))); dispose.push(playground.onZoom((z) => setZoom(z)));
return () => dispose.dispose(); return () => dispose.dispose();
}, [playground]); }, [playground]);
useEffect(() => {
const config = playground.config.config;
playground.config.updateConfig({
maxZoom: maxZoom !== undefined ? maxZoom : config.maxZoom,
minZoom: minZoom !== undefined ? minZoom : config.minZoom,
});
}, [playground, maxZoom, minZoom]);
return { return {
zoomin: handleZoomIn, zoomin: handleZoomIn,
zoomout: handleZoomOut, zoomout: handleZoomOut,