UNPKG

@rxflow/base

Version:

BaseFlow - 核心 Flow 组件库

215 lines (207 loc) 7.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Scrollbar = void 0; var _react = require("@xyflow/react"); var _react2 = require("react"); require("./index.less"); var _useScrollerOptions = require("./hooks/useScrollerOptions"); var _ahooks = require("ahooks"); var _overlayscrollbars = require("overlayscrollbars"); var _overlayscrollbarsReact = require("overlayscrollbars-react"); require("overlayscrollbars/overlayscrollbars.css"); var _classcat = _interopRequireDefault(require("classcat")); var _jsxRuntime = require("react/jsx-runtime"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * @author: yanxianliang * @date: 2025-08-09 21:05 * @desc: 滚动条插件 * * 适配 reactflow 无限延伸特性,在 wheel 事件中自动调整滚动区域尺寸,操作滚动条时锁定尺寸 * Copyright (c) 2025 by yanxianliang, All Rights Reserved. */ _overlayscrollbars.OverlayScrollbars.plugin(_overlayscrollbars.ClickScrollPlugin); // 取向下的最小倍数 const getFloorNumber = (current, splitSize) => { const ratio = Math.floor(current / splitSize); return ratio * splitSize; }; // 取向上的最小倍数 const getCeilNumber = (current, splitSize) => { const ratio = Math.ceil(current / splitSize); return ratio * splitSize; }; const padding = 50; const Scrollbar = props => { const { width, height, pageWidth, pageHeight, pageBreak, pageVisible } = props; const scrollerContainerRef = (0, _react2.useRef)(null); const scrollbarRef = (0, _react2.useRef)(null); const scrollOptionsRef = (0, _react2.useRef)(null); // 锁定 viewport,不自动增长画布 const lockAutoViewportRef = (0, _react2.useRef)(false); // 部分正常的 onScroll 也会被拦截 const refInit = (0, _react2.useCallback)(ref => { scrollerContainerRef.current = ref ? ref.getElement() : null; }, []); const onInitial = (0, _react2.useCallback)(instance => { scrollbarRef.current = instance; // 监听事件 bindUserActions(instance); updateScrollBar(); }, []); const onPointerDown = (0, _react2.useCallback)(() => { lockAutoViewportRef.current = true; }, []); const onPointerUp = (0, _react2.useCallback)(() => { lockAutoViewportRef.current = false; }, []); const bindUserActions = (0, _react2.useCallback)(instance => { const { scrollbarHorizontal, scrollbarVertical } = instance.elements(); scrollbarHorizontal.scrollbar.addEventListener('pointerdown', onPointerDown); scrollbarVertical.scrollbar.addEventListener('pointerdown', onPointerDown); document.addEventListener('pointerup', onPointerUp); }, []); const unBindUserActions = (0, _react2.useCallback)(instance => { if (instance) { const { scrollbarHorizontal, scrollbarVertical } = instance.elements(); scrollbarHorizontal.scrollbar.removeEventListener('pointerdown', onPointerDown); scrollbarVertical.scrollbar.removeEventListener('pointerdown', onPointerDown); document.removeEventListener('pointerup', onPointerUp); } }, []); (0, _react2.useEffect)(() => { return () => { unBindUserActions(scrollbarRef.current); }; }, []); const options = (0, _useScrollerOptions.useScrollerOptions)({ width, height, pageWidth, pageHeight, pageBreak, pageVisible, scrollerContainer: scrollerContainerRef }); const flowInstance = (0, _react.useReactFlow)(); const nodes = (0, _react.useNodes)(); const viewport = (0, _react.useViewport)(); const bounds = (0, _react2.useMemo)(() => { return flowInstance.getNodesBounds(nodes); // 获取可能有问题,nodeLookup为什么获取不到???应该是创建了才对。 }, [nodes]); const scrollOptions = (0, _react2.useMemo)(() => { if (lockAutoViewportRef.current) { return scrollOptionsRef.current; // 不更新 } const { pageHeight, pageWidth, clientHeight, clientWidth } = options; // 页码宽和高 if (!pageHeight || !pageWidth) { return undefined; } // 根据 bounds 计算最小的 rect 范围 const boundLeft = bounds.x - pageWidth; const boundTop = bounds.y - pageHeight; const boundRight = bounds.x + bounds.width + pageWidth; const boundBottom = bounds.y + bounds.height + pageHeight; // 根据 viewport 调整对应的上下左右 rect 范围, viewport 是不是也自动叠加 0.5 宽高 const { x = 0, y = 0, zoom } = viewport; const viewportLeft = getFloorNumber(-x - padding, pageWidth / 2); const viewportTop = getFloorNumber(-y - padding, pageHeight / 2); const viewportRight = getCeilNumber(-x + zoom * clientWidth + padding, pageWidth / 2); const viewportBottom = getCeilNumber(-y + zoom * clientHeight + padding, pageHeight / 2); const left = Math.min(viewportLeft, boundLeft); const top = Math.min(viewportTop, boundTop); const bottom = Math.max(viewportBottom, boundBottom); const right = Math.max(viewportRight, boundRight); scrollOptionsRef.current = { left, top, scrollLeft: Math.ceil(-x - left), // 滚动条 必须要动起来,不然拖动滚动条的时候变化非常大。 scrollTop: Math.ceil(-y - top), width: Math.ceil(right - left), height: Math.ceil(bottom - top) }; return scrollOptionsRef.current; }, [options, viewport, bounds.x, bounds.y, bounds.width, bounds.height]); const updateScrollBar = (0, _ahooks.useMemoizedFn)(() => { if (scrollerContainerRef.current && scrollOptions && scrollbarRef.current) { const { scrollOffsetElement } = scrollbarRef.current.elements(); scrollOffsetElement.scrollTo({ // behavior: 'smooth', left: scrollOptions.scrollLeft, top: scrollOptions.scrollTop }); } }); (0, _react2.useLayoutEffect)(() => { updateScrollBar(); }, [scrollOptions]); // 从 viewport 触发的滚动事件不应该是 const onScroll = (0, _ahooks.useMemoizedFn)((scroller, ev) => { if (scrollOptions) { const { scrollTop, scrollLeft } = ev.target; const x = -(scrollLeft + scrollOptions.left); const y = -(scrollTop + scrollOptions.top); // 计算的 viewport 不对 const _viewport = flowInstance.getViewport(); if (Math.abs(_viewport.x - x) >= 1 || Math.abs(_viewport.y - y) >= 1) { flowInstance.setViewport({ x, y, zoom: _viewport.zoom }); } } }); return /*#__PURE__*/(0, _jsxRuntime.jsx)(_overlayscrollbarsReact.OverlayScrollbarsComponent, { className: (0, _classcat.default)(["overlayscrollbars-react", 'rxflow-plugin-scrollbar']), ref: refInit, options: { scrollbars: { clickScroll: true, // autoHide: 'leave', theme: 'os-theme-dark' } }, events: { scroll: onScroll, initialized: onInitial }, defer: true, children: scrollOptions ? /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { style: { width: scrollOptions.width, height: scrollOptions.height } }) : null }); }; exports.Scrollbar = Scrollbar;