1.1.3 • Published 2 years ago

bade-mind-react v1.1.3

Weekly downloads
-
License
ISC
Repository
github
Last release
2 years ago

bade-mind-react

bade-mind React 框架封装库

  • 实现了节点的sizeof函数,能够自动测量节点尺寸,并添加缓存功能 ,用户只需要负责渲染节点即可

  • 增加滚动条交互

  • 增加滚轮手势交互

  • 实现拖拽功能(内置布局只有structured支持拖拽)

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'
    }
  ],
  // negative: [],
  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

参数类型默认值必填说明
optionsMind.Optionsundefinedmind配置项
dataRoot数据源
renderRender节点渲染器
anchorstringundefined渲染锚点数据
scrollbarbooleanfalse是否展示滚动条
wheelMoveSpeednumber0.5滚轮移动速度
classNamestringundefined注入到根上的 class
styleReact.CSSPropertiesundefined注入到根上的 style

options、data、render

  • 仅做浅比较

  • 改变则引起 布局重计算

  • 改变引起 节点规则内尺寸刷新

    1. node.size 存在,则直接使用其数据

    2. 如果node.disableSizeCache=true,则,刷新节点尺寸

    3. 如果外部设置node.needUpdateSize=true,则,本次节点尺寸会被刷新

    4. 如上述条件都不符合,并且节点之前已存在,则,节点不刷新其自身尺寸

Events

事件名事件类型必填说明
onUpdated(mind: Mind.Graphic) => void图形更新完成- 由 data 和 options 改变所引起,脑图控制对象内部状态刷新- 即,此时,脑图所有的状态以及渲染已经完成
onDragStartDragStartEvent拖拽开始事件
onDragDragEvent拖拽中事件
onDragEndDragEndEvent拖拽结束事件

Node

所有的节点数据改变之后,都需要更新data的引用,以通知组件

interface Node extends Omit<Mind.Node, 'sizeof' | 'children'> {
    /**
     * 当设置 size 时,将会禁止自动测量,提升速率
     */
    size?: Mind.Size
    /**
     * 是否需要更新节点尺寸(设置为true之后会更新一次节点尺寸,后组件将自动设置此值为false)
     * @default false
     */
    needUpdateSize?: boolean
    /**
     * 节点是否受到保护(超出可视区域不会被销毁,但会设置`visible=hidden`)
     * @default false
     */
    beProtected?: boolean
    /**
     * 节点总是可见(超出可视区域不会被销毁也不会改变`visible`状态)
     * @default false
     */
    alwaysVisible?: boolean
    /**
     * 禁用尺寸缓存
     * @default false
     */
    disableSizeCache?: boolean
    /**
     * 子代节点
     */
    children?: Node[]
    /**
     * 节点是否可拖拽
     * - 需要当前布局算法支持拖拽
     * @default false
     */
    draggable?: boolean
    /**
     * 节点是否可被拖拽节点依附作为其子节点
     * @default true
     */
    droppable?: boolean
}

Root

interface Root {
    node: Node
    positive?: Node[]
    negative?: Node[]
}

Render

节点渲染器

type Render = (data: Node, mirror: boolean) => React.ReactNode
  • @param data 节点数据

  • @param mirror 是否为镜像节点(拖拽所产生的)渲染

DragStartEvent

拖拽节点开始事件

type DragStartEvent = (event: { node: Node }) => void
  • @param node 拖拽的节点

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) => {
    // 如果只需要初次渲染修改位移,则需要在此处做逻辑判断处理
    // 将root移动到viewport上中
    mind.nodeTranslateTo({
        diff: {
            x: 0,
            y: 0
        },
        id: 'root',
        relative: {
            x: Mind.RelativeX.middle,
            y: Mind.RelativeY.top
        }
    })
}MindReact.Node