bade-mind-react
bade-mind
React 框架封装库
Live demo
Installation
npm install bade-mind-react
Simple demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">
<title>bade-mind-react demo</title>
<style>
body,#root{
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
#root .mind__lines{
stroke: #474b4c;
stroke-width: 3px;
}
.node{
font-size: 16px;
color: white;
padding: 8px 12px;
background: #1f2623;
border-radius: 8px;
box-shadow: 2px 2px 8px #666;
position: relative;
}
.fold{
position: absolute;
width: 8px;
height: 8px;
background: red;
right: 0;
top: 50%;
}
</style>
</head>
<body>
<div id="root"></div>
<script src="./simple-usage.tsx" type="module"></script>
</body>
</html>
import * as React from 'react'
import { useCallback, useState } from 'react'
import ReactDom from 'react-dom'
import { Mind, MindReact } from 'bade-mind-react'
const root: MindReact.Root = {
negative: [
{
attachData: 'negative',
id: 'n-1-l'
}
],
node: {
attachData: 'root',
id: 'root'
},
positive: [
{
attachData: 'positive',
id: 'p-1-l'
}
]
}
const generateChildren = () => {
const result: MindReact.Node[] = []
const num = Math.ceil(3 * Math.random())
for (let counter = 0; counter < num; counter++) {
result.push({
attachData: Math.random().toFixed(6),
draggable: true,
id: Math.random().toString()
})
}
return result
}
const Render = (props: {
node: MindReact.Node
onClick: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void
onFold: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void
}) => {
const { node, onClick, onFold } = props
return (
<div className={'node'} onClick={onClick}>
{node.attachData}
<div
onClick={(e) => {
onFold(e)
e.stopPropagation()
}}
className={'fold'}
/>
</div>
)
}
const options: Mind.Options = {
childAlignMode: Mind.ChildAlignMode.structured,
lineStyle: Mind.LinkStyle.bezier,
nodeSeparate: 80,
rankSeparate: 100
}
const Demo = () => {
const [data, setData] = useState<MindReact.Root>(root)
const [anchor, setAnchor] = useState<string | undefined>()
const onDragEnd = useCallback<MindReact.DragEndEvent>(
(event) => {
const { attach, node, original } = event
if (attach && attach.index >= 0) {
let children: MindReact.Node[] = []
if (attach.parent.id === data.node.id) {
if (attach.orientation === Mind.Orientation.positive) {
children = data.positive = data.positive || []
} else {
children = data.negative = data.negative || []
}
} else {
children = attach.parent.children = attach.parent.children || []
}
children.splice(attach.index, 0, {
...node
})
let originalPlaceNodes = []
if (original.parent.id === data.node.id) {
originalPlaceNodes =
original.orientation === Mind.Orientation.positive ? data.positive : data.negative
} else {
originalPlaceNodes = original.parent.children
}
const dragNodeIndex = originalPlaceNodes.indexOf(node)
if (dragNodeIndex >= 0) {
originalPlaceNodes.splice(dragNodeIndex, 1)
}
setAnchor(attach.parent.id)
setData((pre) => ({ ...pre }))
}
},
[data]
)
return (
<MindReact.View
data={data}
anchor={anchor}
scrollbar={true}
onDragEnd={onDragEnd}
render={(node) => (
<Render
node={node}
onClick={(e) => {
setAnchor(node.id)
if (node.id !== root.node.id) {
if (!node.children) {
node.children = []
}
node.children.push(...generateChildren())
setData((pre) => {
return { ...pre }
})
}
}}
onFold={() => {
setAnchor(node.id)
if (node.id === data.node.id) {
node.fold = []
} else {
node.fold = !node.fold
}
setData((pre) => {
return { ...pre }
})
}}
/>
)}
options={options}
/>
)
}
ReactDom.render(<Demo />, document.getElementById('root'))
API
Mind
组件依赖的bade-mind
库导出
MindReact
组件内部已自动实现sizeof
函数,故而不需要用户再次处理
组件对Mind
原有类型进行了一定的修改、拓展
MindReact.View
脑图可视化组件
Props
参数 | 类型 | 默认值 | 必填 | 说明 |
---|
options | Mind.Options | undefined | 否 | mind配置项 |
data | Root | 无 | | 数据源 |
render | Render | 无 | 是 | 节点渲染器 |
anchor | string | undefined | 否 | 渲染锚点数据 |
scrollbar | boolean | false | 否 | 是否展示滚动条 |
wheelMoveSpeed | number | 0.5 | 否 | 滚轮移动速度 |
className | string | undefined | 否 | 注入到根上的 class |
style | React.CSSProperties | undefined | 否 | 注入到根上的 style |
options、data、render
-
仅做浅比较
-
改变则会引起 布局重计算
-
改变会引起 节点规则内尺寸刷新
-
node.size
存在,则直接使用其数据
-
如果node.disableSizeCache=true
,则,刷新节点尺寸
-
如果外部设置node.needUpdateSize=true
,则,本次节点尺寸会被刷新
-
如上述条件都不符合,并且节点之前已存在,则,节点不刷新其自身尺寸
Events
事件名 | 事件类型 | 必填 | 说明 |
---|
onUpdated | (mind: Mind.Graphic) => void | 否 | 图形更新完成 - 由 data 和 options 改变所引起,脑图控制对象内部状态刷新 - 即,此时,脑图所有的状态以及渲染已经完成 |
onDragStart | DragStartEvent | 否 | 拖拽开始事件 |
onDrag | DragEvent | 否 | 拖拽中事件 |
onDragEnd | DragEndEvent | 否 | 拖拽结束事件 |
Node
所有的节点数据改变之后,都需要更新data
的引用,以通知组件
interface Node extends Omit<Mind.Node, 'sizeof' | 'children'> {
size?: Mind.Size
needUpdateSize?: boolean
beProtected?: boolean
alwaysVisible?: boolean
disableSizeCache?: boolean
children?: Node[]
draggable?: boolean
droppable?: boolean
}
Root
interface Root {
node: Node
positive?: Node[]
negative?: Node[]
}
Render
节点渲染器
type Render = (data: Node, mirror: boolean) => React.ReactNode
DragStartEvent
拖拽节点开始事件
type DragStartEvent = (event: { node: Node }) => void
DragEvent
节点拖拽中事件
type DragEvent = (event: {
node: Node
attach:
| {
parent: Node
orientation: Mind.Orientation
}
| undefined
mirrorPosition: Mind.Coordinate
}) => void
- @param
event.node
拖拽的节点 - @param
event.attach
拖拽节点关联的节点(关联父级),可能为空 - @param
event.position
拖拽节点镜像中心当前位置 - @param
event.orientation
拖拽节点当前位于哪个区域(位于根节点区域时为空,此时无法附着在任何一个节点上)
DragEndEvent
拖拽结束事件
type DragEndEvent = (event: {
node: Node
attach:
| {
parent: Node
orientation: Mind.Orientation
index: number
}
| undefined
original: {
parent: Node
orientation: Mind.Orientation
}
}) => void
- @param
event.node
拖拽的节点 - @param
event.attach
拖拽节点最终关联的节点(关联父级),可能为空 - @param
event.orientation
拖拽节点当前最终位于哪个区域(位于根节点区域时为空,此时无法附着在任何一个节点上) - @param
event.id
拖拽节点位于最终关联节点子代(关联父级)中的目标位置(-1代表无需改变位置)(需要注意的是,关联的父节点可能仍然是拖动节点自身的父节点)
Tips
渲染初始定位
可使用onUpdated
在渲染完成之后,改变位移缩放等
- 谨记,
onUpdated
会在每一次脑图渲染更新完成之后调用
onUpdated={(mind) => {
mind.nodeTranslateTo({
diff: {
x: 0,
y: 0
},
id: 'root',
relative: {
x: Mind.RelativeX.middle,
y: Mind.RelativeY.top
}
})
}MindReact.Node