ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

brick-design 的 BrickTree 组件深度解析:与画布实时同步的组件树、拖拽排序与页面结构编辑

brick-design 的 BrickTree 组件深度解析:与画布实时同步的组件树、拖拽排序与页面结构编辑 低代码前端【免费下载链接】brick-design低代码框架支持流式布局与自由布局拖拽编排可视化拖拽、随意嵌套组合、实时渲染、实时辅助线展示、自由布局支持辅助对齐、支持自动吸附、实时组件间距展示、实时拖拽排序、状态域管理可视化属性配置、可视化样式配置、多设备适配展示支持逻辑渲染、模板字符变量、表达式、自定义方法、自定义状态项目地址https://gitcode.com/gh_mirrors/br/brick-design点击查看免费下载BrickTree 是 brick-design 低代码编辑器中的组件树面板它以树形结构实时呈现当前页面的pageConfig层级并支持选中联动、复制、删除以及跨组件拖拽改序等操作。本文以官方文档 BrickTree.md 的 API 说明为主体结合packages/components与packages/core中的真实源码完整拆解它的渲染结构、与 BrickDesign 画布的同步机制以及“增、删、改 拖拽排序”背后的 Action/Reducer 调用链。读完本文你将掌握如何在BrickProvider体系内正确接入 BrickTree并理解每一个树上操作如何最终改写pageConfig状态。一、BrickTree 的定位画布的“结构镜像”官方文档对 BrickTree 的介绍可以概括为三点它是 brick design 编辑器的组件树同时也是页面结构的可视化展示当直接编辑画布不方便时可以通过修改组件树结构来实现想要的效果——即树面板是画布编辑之外的另一条操控入口BrickTree 与 BrickDesign 实时同步并且同样支持增、删、改支持跨组件拖拽更改顺序。在完整的编辑器外壳中BrickTree 并不单独出现。从源码 Layers 面板 可以看到它被放置在名为 Layers 的可拖拽调整宽度的侧栏DragResizeBar中与页面列表Pages上下组合DragResizeBar title{Layers} minWidth{212} icon{layersIcon} key{Layers} Pages onDelete{onDelete} / BrickTree / Tips icon{warnIcon} tip{删除不可逆} tipTitle{注意} ... / /DragResizeBar其中Pages负责页面Layer的增删改名BrickTree负责当前页面内组件树的展示与编辑Tips组件则承载“删除不可逆”的确认弹窗。也就是说BrickTree 的编辑对象是当前页面的组件结构而页面级的切换由同级的Pages完成。二、安装与使用方式按 docs/react/getting-started.md 的说明首先安装 React 集成包yarn add brickd/react或者npm install brickd/react然后在BrickProvider内部与BrickDesign并列挂载BrickTreeimport { createElement } from react; import { BrickDesign, BrickTree, BrickProvider, useSelector, createActions, } from brickd/react; const plugins [(vDom, componentConfig) vDom]; const customReducer (state, action) { const { type, payload } action; switch (type) { case customReducer: return { ...state }; default: return state; } }; const App () { const { componentConfigs } useSelector([componentConfigs]); return ( BrickProvider initState{{}} customReducer{customReducer} config{{ /* 你的配置 */ }} div onClick{() createActions({ type: customReducer, payload: { /* ... */ } })} 触发 action /div BrickDesign / BrickTree / /BrickProvider ); };这与 packages/components/README.md 中的用法示例一致BrickTree必须处于BrickProvider的上下文中与BrickDesign画板/页面编辑组件共享同一份状态。如果你使用完整编辑器外壳示例工程 examples/react-example/src/App.tsx 直接使用了封装好的BrickEdifice其内部BrickEdifice/index.tsx同样以BrickProviderBrickDesignCanvas组合呈现各面板Layers 面板含 BrickTree由Menus按需激活。属性说明className文档明确 BrickTree 目前只有一个属性className它作用在组件树的最外层 div 上。从当前源码结构看BrickTree/index.tsx组件树的根容器渲染为div onMouseLeave{onMouseLeave} onWheel{(event) event.stopPropagation()} className{styles[sort-container]} SortTree disabled childNodes{[ROOT]} specialProps{{ key: ROOT, domTreeKeys: [], parentKey: }} componentName{} / /div这里有两个实现细节值得注意onWheel中调用event.stopPropagation()阻止页面滚动事件冒泡避免树面板内部滚动时干扰外层画布交互onMouseLeave中调用setOperateState({ operateHoverKey: null, hoverNode: null })当鼠标离开整个树面板时清除画布上残留的 hover 高亮。样式层面.sort-container使用 flex 布局并允许自身滚动index.less。如果需要对树面板做布局调整例如固定高度、宽度按文档说明将className作用在最外层 div 上即可。三、树的数据来源与递归渲染结构3.1 数据源pageConfig[ROOT]BrickTree 入口 通过useSelector订阅STATE_PROPS域中的pageConfig其比较函数只在根节点从无到有或从有到无时才触发刷新const { pageConfig } useSelector( [pageConfig], (prevState, nextState) { const { pageConfig } nextState; return !!(!prevState.pageConfig[ROOT] pageConfig[ROOT]); } ); if (!pageConfig[ROOT]) return null;这解释了 BrickTree 的渲染前提页面必须存在根节点ROOT树面板才会渲染一旦整页被清空见后文deleteComponent对 ROOT 的处理树面板即返回null。树从childNodes{[ROOT]}开始递归展开specialProps携带key、domTreeKeys、parentKey等定位信息供深层节点识别自己的位置。3.2 递归渲染SortTree → SortItem树的主体由 SortTree.tsx 与 SortItem.tsx 两个组件递归构成SortTree把一组childNodes渲染为可排序容器ReactSortableSortItem列表SortItem表示一个节点其内部通过renderSortTree决定如何展开子级SortItem.tsx L33-L70若childNodes是数组普通容器继续渲染一个SortTree若childNodes不是数组且存在nodePropsConfig则按nodePropsConfig的每个属性名渲染“属性子树”——也就是说组件上配置了nodeProps的字段如 Form 的 items、List 的数据源等也会作为可嵌套子树展示propChildNodes取get(childNodes, propName, [])。每个SortItem通过useSelector订阅selectedInfo与pageConfigSortItem.tsx L103-L112从pageConfig[key]读出节点的componentName与childNodes。此外每个节点 DOM 上写入了三类数据属性SortItem.tsx L159-L165div className{styles[sort-item]} id{key} >onMouseOver{() { !isSelected setOperateState({ hoverNode: getSelectedNode(key), operateHoverKey: key, }); }}鼠标悬停树上节点时通过getSelectedNode(key)拿到画布中对应的真实 DOM 节点写入operateHoverKey与hoverNode画布侧组件据此显示 hover 轮廓onMouseLeave时再清空。Header还通过useOperate()的setSubscribe(changeStatus)订阅操作状态变化从而响应画布侧发起的 hover即画布 → 树方向的反向高亮。2树 → 画布点击选中 滚动定位onClick{(event) { event.stopPropagation(); if (isSelected) { clearSelectedStatus(); setOperateState({ selectedNode: null, operateSelectedKey: null }); } else { const selectedNode getSelectedNode(key); selectComponent({ ...specialProps, propName }); nodeScrollIntoView(selectedNode); setOperateState({ selectedNode, operateSelectedKey: key }); } }}点击节点会派发selectComponentaction定义见 produceActions/selectedComponent.ts更新全局selectedInfonodeScrollIntoView负责把画布中对应节点滚动进可视区域再次点击已选中节点则取消选中。由于 BrickDesign 画布同样订阅selectedInfo来渲染选中边框树与画布的选中态因此始终保持一致。3画布占位预览placeholderBridgeStore桥这是“实时同步”中最有特色的一环。当你在树上拖拽节点时画布侧会实时渲染一个占位框GuidePlaceholder预览组件将被放入的位置。其实现链路是dragSort.ts L14-L23 定义了全局桥对象export const placeholderBridgeStore { prevRects: null, renderPlaceholder: null, changePosition(rects: PlaceholderPositionType) { if (!isEqual(this.prevRects, rects)) { this.prevRects rects; this.renderPlaceholder this.renderPlaceholder(rects); } }, };画布侧 GuidePlaceholder 组件 把placeholderBridgeStore.renderPlaceholder注册为占位框渲染函数树上拖拽过程中SortTree的onChange调用 onDragBrickTree下文详述实时计算node1/node2/node3三段占位矩形并changePosition推给画布拖拽结束或无变化时changePosition({})清空占位框参见 Container.ts 中对placeholderBridgeStore的清空调用。因此即使落点在画布深处你也能在拖到一半时就直观看到“松手后会插在哪里”。五、增、删、改树上操作的 Action/Reducer 实现文档所说的“支持增、删、改支持跨组件拖拽更改顺序”在 Header.tsx 中落为三个按钮行为对应的 action 与 reducer 均在brickd/core包内。5.1 复制copyComponent树节点 hover 时显示复制图标非属性节点、非 ROOT 节点才显示点击后执行copyComponent({ key, parentKey, parentPropName })。其 reducer 实现于 handleComponentSchema.ts L104-L136未选中节点且未传key时warn(Please select the node you want to copy)并返回原 state禁止复制根节点selectedKey或key为ROOT时给出Prohibit copying root node警告合法复制时先undo.push({ pageConfig })、清空redo再通过getNewKey(pageConfig)生成新键用 immer 的produce update把newKey追加到父节点childNodes并用copyConfig深拷贝原节点配置——这就是文档所述“增”的来源复制出的节点会即时出现在树与画布中配合 3.3 节的自动展开。5.2 删除deleteComponent同样在 handleComponentSchema.ts L184-L224未选中且无key时警告Please select the components you want to delete若删除的是ROOT直接返回空pageConfig即删除整个页面树面板随之整体不渲染否则从父节点childNodes中移除引用deleteChildNodesKey若该节点自身还有childNodes则递归deleteChildNodes清掉所有子节点最后delete oldState[key]并重置selectedInfo: null。删除会先压入undo支持撤销而页面级的删除Layers 面板的deleteLayers则在 Layers/index.tsx 中通过Tips弹窗做“删除不可逆”的二次确认。5.3 排序与跨组件移动onLayoutSortChange这是 BrickTree 的核心能力。SortTree的onSortEnd回调SortTree.tsx L81-L99拿到排序后的sortKeys与拖拽事件后const layoutSortChange useCallback(function (sortKeys: string[], evt) { const dragInfo JSON.parse(evt.clone.dataset.special); // {key, parentPropName, parentKey} onLayoutSortChange({ sortKeys, parentKey: key, parentPropName: propName!, dragInfo }); setOperateState({ dropNode: null, isDropAble: false }); setNewDragKey(null); }, []);对应的 reducerhandleComponentSchema.ts L144-L177做了两件事本层重排把父节点位置的childNodes直接替换为sortKeys跨组件移动清理若dragInfo存在且其parentKey/parentPropName与当前落点不同说明节点从别的父容器中被拖出于是再对原父节点执行deleteChildNodesKey(childNodes, key, parentPropName)删除旧引用。源码注释也明确写道“dragInfo 有值说明为跨组件排序需要删除拖拽组件原先父组件中的引用”。两次update在同一个produce内完成保证pageConfig原子更新随后画布按新结构重新渲染——这就是文档中“支持跨组件拖拽更改顺序”的完整闭环。六、拖拽排序的实现细节ReactSortable 与落点规则6.1 拖拽引擎封装树的可排序容器基于 ReactSortable它是对sortablejs的forwardRef封装useEffect中为onChoose/onAdd/onUpdate/onChange/onEnd五个事件统一注入包装逻辑——onChoose记录nextSibling与当前容器onAdd/onUpdate触发时先把evt.item插回nextSibling之前回滚 DOM 乐观更新再取sortable.toArray()得到完整键序并回调onSortEnd(items, evt)由状态层真正改写pageConfigonChange把实时键序回调给onChange(items, evt)用于拖拽过程中的画布占位预览。6.2 Sortable 配置与落点校验SortTree.tsx L117-L137 给出了关键的optionsReactSortable className{styles[sort-able]} options{{ group: { name: nested, put: (a, b, c) putItem(c, props) }, animation: 200, disabled, dataIdAttr: id, ghostClass: styles[item-background], swapThreshold: 0.5, }} onSortEnd{layoutSortChange} onChange{onChange} onEnd{onEnd} group.name: nestedput: putItem所有层级的 SortTree 同属nested组因此节点天然可以跨层拖动而putItemSortTree.tsx L50-L64是落点守门人const putItem (c: any, props: SortTreePropsType) { const { childNodes [], nodeProps, componentName } props; const fatherNodesRule c.dataset.farules; const dragName c.dataset.name; if (fatherNodesRule !JSON.parse(fatherNodesRule).include(componentName)) return false; if (nodeProps) { if (nodeProps.isOnlyNode childNodes.length 1) return false; if (nodeProps.childNodesRule) { return nodeProps.childNodesRule.includes(dragName); } } return true; };落点合法性由组件 Schema 决定fatherNodesRule声明“哪些父级可以容纳我”childNodesRule声明“我可放入哪些子级”isOnlyNode约束“容器内只能有一个节点”时禁止再插入第二个。这使砖块设计器可以把组件的嵌套约束完全配置化。dataIdAttr: id与SortItem的id{key}配合保证toArray()返回的就是pageConfig键序列animation: 200、ghostClass灰色幽灵占位、swapThreshold: 0.5控制拖拽手感disabled由上层传入根层SortTree传disabled是因为 ROOT 本身不可被拖走复制/删除也禁止操作 ROOT见 5.1、5.2。6.3 拖拽过程画布占位框的实时计算拖拽过程中onChange会持续触发 onDragBrickTreeexport const onDragBrickTree (dragKey, sortKeys, parentKey, setOperateState, propName) { let parentNode getSelectedNode(parentKey); let childNodes get(getVNode(parentKey), childNodes, []); if (!Array.isArray(childNodes) propName) { childNodes get(childNodes, propName, []); // 属性子树 } if (isEqual(childNodes, sortKeys)) return placeholderBridgeStore.changePosition(placeholderPosition); // ...计算 node1/node2/node3 三段占位矩形 placeholderBridgeStore.changePosition(placeholderPosition); };它会解析属性子树propName情形、把落点 DOM 写入dropNode并置isDropAble: true供画布侧渲染投放提示、根据拖拽键在排序序列中的位置首/中/尾分别调用getPlaceholder1Rect/getPlaceholder3Rect计算三段式占位矩形最后推送给placeholderBridgeStore。若排序结果与原childNodes完全一致isEqual直接清空占位——这解释了为什么“原地微动”不会在画布上留下多余占位框。七、使用注意事项小结结合文档与源码使用 BrickTree 时需记住必须在BrickProvider内使用且与BrickDesign共享状态页面pageConfig中没有ROOT时树面板不渲染index.tsx L27当前对外属性只有className作用于组件树最外层 div用于布局与样式覆盖ROOT 节点是保护对象不可复制、不可拖拽删除 ROOT 等于清空整页会触发undo可撤销但页面级deleteLayers不可逆嵌套约束由组件配置驱动fatherNodesRule、childNodesRule、isOnlyNode决定拖拽能否落点配置不当会出现“拖不进去”的现象排查时优先检查对应组件的NodeProps配置树操作全部走 actioncopyComponent/deleteComponent/onLayoutSortChange/selectComponent见 actionTypes.ts 与 reducers/index.ts并自动维护undo/redo因此撤销/重做按钮MidBottomButtons对树操作同样生效性能上做了针对性收敛BrickTree、SortTree、SortItem、Header均为memo组件useSelector的自定义比较函数限制了根级重渲染滚动/悬停事件也做了stopPropagation隔离这些是树与画布能“实时同步”而不互相拖累的前提。至此BrickTree 从文档描述的“组件树 实时同步 增删改 跨组件拖拽”到pageConfig数据流、placeholderBridgeStore占位桥、nested组拖拽规则与produce原子更新的完整实现路径均已闭环。若需进一步阅读建议依次查看 BrickTree 面板源码、handleComponentSchema.ts 与 dragSort.ts它们分别对应树渲染、状态改写与拖拽预览三个核心环节。赞分享低代码前端【免费下载链接】brick-design低代码框架支持流式布局与自由布局拖拽编排可视化拖拽、随意嵌套组合、实时渲染、实时辅助线展示、自由布局支持辅助对齐、支持自动吸附、实时组件间距展示、实时拖拽排序、状态域管理可视化属性配置、可视化样式配置、多设备适配展示支持逻辑渲染、模板字符变量、表达式、自定义方法、自定义状态项目地址https://gitcode.com/gh_mirrors/br/brick-design点击查看免费下载相关推荐Brick Design拖拽编排进阶跨组件嵌套与排序技巧Brick Design拖拽编排进阶跨组件嵌套与排序技巧 Brick Design 是一款强大的 低代码可视化拖拽编排框架 专为前端开发者和设计师打造。通过低代码前端Ant Design Blazor Tree 组件拖拽排序实战Draggable 节点拖放与数据同步解析Ant Design Blazor Tree 组件拖拽排序实战Draggable 节点拖放与数据同步解析 Ant Design Blazor 的 Tree 组前端UI组件设计系统Brick Design核心组件详解BrickProvider、BrickDesign、BrickTreeBrick Design核心组件详解BrickProvider、BrickDesign、BrickTree Brick Design是一款强大的低代码可视化拖低代码前端上一篇猫抓cat-catch5分钟快速上手的浏览器视频下载终极指南下一篇Quartz.NET 裁剪发布与 Native AOT 实战指南从 IsAotCompatible 声明到可运行的持久化 JobStore创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表