Skip to main content

craft.js

  • prevwong/craft.js
    • MIT, React
    • drag and drop page editors
    • 编辑模式对现有组件侵入
  • @craftjs/core
    • 150kb - self 30kb + @craftjs/utils 13kb + lodash 96kb + immer + nanoid
  • prevwong/reka.js
    • 新的状态管理+序列化
    • mobx
caution
  • 对现有组件侵入
  • 暂不支持 Reaact 18 StrictMode #404
import React from "react";
import {Editor, Frame, Canvas, Selector} from "@craftjs/core";

const TextComponent = ({text}) => {
const { connectors: { connect, drag }, isClicked, actions: {setProp} } = useNode(
(state) => ({
isClicked: state.event.selected,
})
);

return (
<div ref={dom => connect(drag(dom))}>
<h2>{text}</h2>
{
isClicked ? (
<Modal>
<input
type="text"
value={text}
onChange={e => setProp(e.target.value)}
/>
</Modal>
)
}
</div>
)
}

const Container = () => {
const { actions: {add}, query: { createNode, node } } = useEditor();
const { id, connectors: {drag, connect} } = useNode();
return (
<div ref={dom => connect(drag(dom))}>
<a onClick={() => {
const { data: {type, props}} = node(id).get();
add(
createNode(React.createElement(type, props));
);
}}>
Make a copy of me
</a>
</div>
)
}

const SaveButton = () => {
const { query } = useEditor();
return <a onClick={() => console.log(query.serialize()) }>Get JSON</a>
}

const App = () => {
return (
<div>
<header>Editor</header>
<Editor>
<Frame resolver={{TextComponent, Container}}>
<Canvas>
<TextComponent text="I'm already rendered here" />
</Canvas>
</Frame>
<SaveButton/>
</Editor>
</div>
)
}

Internal

  • useMethos 基于 pelotom/use-methods 做了一定调整
    • 加了 QueryMethods - 用于查询状态
    • 加了状态订阅机制
    • 原始 userMethods 返回 [state, callbacks], 现在的返回 [actions, query, watcher, getState]
    • 理解 useMethos 对看懂 useEditor 和 useNode 非常有帮助
  • Editor
    • 配置项
      • onRender - 渲染 hoc
      • resolver - 组件列表
      • enabled - 是否启用编辑
      • indicator - 线条颜色 - success 和 error
    • 状态
      • nodes - 所有节点
      • events - 事件状态
      • options - 配置项
    • API 通过 useMethods 实现 - actions、queries
      • 主要是维护 node 的结构 - 增、删、改、查
  • Events
    • 事件处理 - "select" | "hover" | "drag" | "drop" | "create"
    • 维护节点上的事件状态
    • 维护 drop 的 标识动画
    • 逻辑相对复杂
  • NodeElement 会基于 id 对节点进行 memo
  • NodeHandlers 会从事件机制衍生出针对节点的事件处理
  • Canvas
    • 替代组件,相当于组件的 decorator、hoc
    • 初始化内部组件
    • 维护 ID 映射关系
    • 依赖 internalEditor 和 internalNode 维护状态
  • useInternalNode
    • 从 NodeContext 暴露额外信息
  • NodeContext
    • 包含 id, related, connectors
  • UserComponent
    • 附加额外配置信息的组件
    • name
    • rules - 控制拖拉行为
    • related
    • defaultProps
  • Node - 节点信息
    • id
    • data
    • events - selected、dragged、hovered
    • dom
    • related
    • rules - canDrag、canMoveIn、canMoveOut
  • NodeData - 节点数据
    • props
    • type
    • name
    • displayName
    • isCanvas
    • parent
    • index
    • nodes
    • hidden
    • custom
  • 序列化会排出默认的 type、props、isCanvas、name 然后序列化组件
  • https://craft.js.org/r/docs/acknowledgements/