@rxflow/base
Version:
BaseFlow - 核心 Flow 组件库
231 lines (230 loc) • 10.4 kB
JavaScript
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
/*
* @author: yanxianliang
* @date: 2025-06-09 09:45
* @desc: 画布控制器
*
* Copyright (c) 2025 by yanxianliang, All Rights Reserved.
*/
import FullscreenExitOutlined from '@ant-design/icons/FullscreenExitOutlined';
import FullscreenOutlined from '@ant-design/icons/FullscreenOutlined';
import ZoomInOutlined from "@ant-design/icons/lib/icons/ZoomInOutlined";
import ZoomOutOutlined from "@ant-design/icons/lib/icons/ZoomOutOutlined";
import { ControlButton, Panel, useReactFlow, useStore, useStoreApi } from "@xyflow/react";
import { useFullscreen } from "ahooks";
import { Tooltip } from 'antd';
import cc from 'classcat';
import React, { memo, useMemo } from "react";
import { Fragment } from "react/jsx-runtime";
import { shallow } from "zustand/shallow";
import { FitViewIcon, ForceLayout, LockIcon, Minimap, UnlockIcon, UnMinimap } from "./icons";
import { jsx as _jsx } from "react/jsx-runtime";
import { jsxs as _jsxs } from "react/jsx-runtime";
var selector = function selector(s) {
return {
isInteractive: s.nodesDraggable || s.nodesConnectable || s.elementsSelectable,
minZoomReached: s.transform[2] <= s.minZoom,
maxZoomReached: s.transform[2] >= s.maxZoom
};
};
function Controls(_ref) {
var style = _ref.style,
_ref$showZoom = _ref.showZoom,
showZoom = _ref$showZoom === void 0 ? true : _ref$showZoom,
_ref$showFitView = _ref.showFitView,
showFitView = _ref$showFitView === void 0 ? true : _ref$showFitView,
_ref$showInteractive = _ref.showInteractive,
showInteractive = _ref$showInteractive === void 0 ? false : _ref$showInteractive,
_ref$showFullscreen = _ref.showFullscreen,
showFullscreen = _ref$showFullscreen === void 0 ? true : _ref$showFullscreen,
showMiniMap = _ref.showMiniMap,
containerRef = _ref.containerRef,
fitViewOptions = _ref.fitViewOptions,
onZoomIn = _ref.onZoomIn,
onZoomOut = _ref.onZoomOut,
onFitView = _ref.onFitView,
onInteractiveChange = _ref.onInteractiveChange,
className = _ref.className,
children = _ref.children,
_ref$position = _ref.position,
position = _ref$position === void 0 ? 'bottom-left' : _ref$position,
_ref$orientation = _ref.orientation,
orientation = _ref$orientation === void 0 ? 'vertical' : _ref$orientation,
_ref$ariaLabel = _ref['aria-label'],
ariaLabel = _ref$ariaLabel === void 0 ? 'React Flow controls' : _ref$ariaLabel,
minimapVisible = _ref.minimapVisible,
setMinimapVisible = _ref.setMinimapVisible,
showForceLayout = _ref.showForceLayout;
var _useFullscreen = useFullscreen(containerRef),
_useFullscreen2 = _slicedToArray(_useFullscreen, 2),
isFullscreen = _useFullscreen2[0],
toggleFullscreen = _useFullscreen2[1].toggleFullscreen;
var tooltipPlacement = useMemo(function () {
switch (position) {
case 'top-left':
case 'center-left':
case 'top-center':
if (orientation === 'horizontal') {
return 'bottom';
}
return 'right';
case 'top-right':
case 'center-right':
if (orientation === 'horizontal') {
return 'bottom';
}
return 'left';
case 'bottom-left':
case 'bottom-center':
if (orientation === 'horizontal') {
return 'top';
}
return 'right';
case 'bottom-right':
if (orientation === 'horizontal') {
return 'top';
}
return 'left';
default:
return 'top';
}
}, []);
var store = useStoreApi();
var _useStore = useStore(selector, shallow),
isInteractive = _useStore.isInteractive,
minZoomReached = _useStore.minZoomReached,
maxZoomReached = _useStore.maxZoomReached;
var _useReactFlow = useReactFlow(),
zoomIn = _useReactFlow.zoomIn,
zoomOut = _useReactFlow.zoomOut,
fitView = _useReactFlow.fitView;
var onZoomInHandler = function onZoomInHandler() {
zoomIn();
onZoomIn === null || onZoomIn === void 0 || onZoomIn();
};
var onZoomOutHandler = function onZoomOutHandler() {
zoomOut();
onZoomOut === null || onZoomOut === void 0 || onZoomOut();
};
var onFitViewHandler = function onFitViewHandler() {
fitView(fitViewOptions);
onFitView === null || onFitView === void 0 || onFitView();
};
var onToggleInteractivity = function onToggleInteractivity() {
store.setState({
nodesDraggable: !isInteractive,
nodesConnectable: !isInteractive,
elementsSelectable: !isInteractive
});
onInteractiveChange === null || onInteractiveChange === void 0 || onInteractiveChange(!isInteractive);
};
var onForceLayout = function onForceLayout() {
// TODO 美化布局
};
var onToggleMiniMap = function onToggleMiniMap() {
setMinimapVisible(!minimapVisible);
};
var orientationClass = orientation === 'horizontal' ? 'horizontal' : 'vertical';
return /*#__PURE__*/_jsxs(Panel, {
className: cc(['react-flow__controls', orientationClass, className]),
position: position,
style: style,
"data-testid": "rf__controls",
"aria-label": ariaLabel,
children: [showZoom && /*#__PURE__*/_jsxs(Fragment, {
children: [/*#__PURE__*/_jsx(Tooltip, {
title: '放大',
placement: tooltipPlacement,
children: /*#__PURE__*/_jsx("span", {
children: /*#__PURE__*/_jsx(ControlButton, {
onClick: onZoomInHandler,
className: "react-flow__controls-zoomin",
title: '放大',
"aria-label": 'zoom in',
disabled: maxZoomReached,
children: /*#__PURE__*/_jsx(ZoomInOutlined, {})
})
})
}), /*#__PURE__*/_jsx(Tooltip, {
title: '缩小',
placement: tooltipPlacement,
children: /*#__PURE__*/_jsx("span", {
children: /*#__PURE__*/_jsx(ControlButton, {
onClick: onZoomOutHandler,
className: "react-flow__controls-zoomout",
title: '缩小',
"aria-label": 'zoom out',
disabled: minZoomReached,
children: /*#__PURE__*/_jsx(ZoomOutOutlined, {})
})
})
})]
}), showFitView && /*#__PURE__*/_jsx(Tooltip, {
title: '自适应视图',
placement: tooltipPlacement,
children: /*#__PURE__*/_jsx("span", {
children: /*#__PURE__*/_jsx(ControlButton, {
className: "react-flow__controls-fitview",
onClick: onFitViewHandler,
title: '自适应视图',
"aria-label": 'fit view',
children: /*#__PURE__*/_jsx(FitViewIcon, {})
})
})
}), showFullscreen && /*#__PURE__*/_jsx(Tooltip, {
title: isFullscreen ? '取消全屏' : '全屏',
placement: tooltipPlacement,
children: /*#__PURE__*/_jsx("span", {
children: /*#__PURE__*/_jsx(ControlButton, {
className: "react-flow__controls-fullscreen",
onClick: toggleFullscreen,
title: isFullscreen ? '取消全屏' : '全屏',
"aria-label": "toggle fullscreen",
children: isFullscreen ? /*#__PURE__*/_jsx(FullscreenExitOutlined, {}) : /*#__PURE__*/_jsx(FullscreenOutlined, {})
})
})
}), showMiniMap && /*#__PURE__*/_jsx(Tooltip, {
title: minimapVisible ? '关闭缩略图' : '开启缩略图',
placement: tooltipPlacement,
children: /*#__PURE__*/_jsx("span", {
children: /*#__PURE__*/_jsx(ControlButton, {
className: "react-flow__controls-interactive",
onClick: onToggleMiniMap,
title: minimapVisible ? '关闭缩略图' : '开启缩略图',
"aria-label": "toggle minimap",
children: minimapVisible ? /*#__PURE__*/_jsx(UnMinimap, {}) : /*#__PURE__*/_jsx(Minimap, {})
})
})
}), showInteractive && /*#__PURE__*/_jsx(Tooltip, {
title: isInteractive ? '解锁' : '锁定',
placement: tooltipPlacement,
children: /*#__PURE__*/_jsx("span", {
children: /*#__PURE__*/_jsx(ControlButton, {
className: "react-flow__controls-interactive",
onClick: onToggleInteractivity,
title: isInteractive ? '解锁' : '锁定',
"aria-label": "toggle interactivity",
children: isInteractive ? /*#__PURE__*/_jsx(UnlockIcon, {}) : /*#__PURE__*/_jsx(LockIcon, {})
})
})
}), showForceLayout && /*#__PURE__*/_jsx(Tooltip, {
title: '美化布局',
placement: tooltipPlacement,
children: /*#__PURE__*/_jsx("span", {
children: /*#__PURE__*/_jsx(ControlButton, {
className: "react-flow__controls-layout",
onClick: onForceLayout,
title: '美化布局',
"aria-label": "force layout",
children: /*#__PURE__*/_jsx(ForceLayout, {})
})
})
}), children]
});
}
export default /*#__PURE__*/memo(Controls);