Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

设置了 custom_node_render 后 jmexpander 位置计算错误被遮蔽 #589

Closed
hqm19 opened this issue Mar 23, 2024 · 7 comments
Closed

Comments

@hqm19
Copy link

hqm19 commented Mar 23, 2024

版本 0.8.3,复现代码:

import React, { useEffect, useRef } from "react"
import ReactDOM from "react-dom"
import jsMind from "jsmind"
import "jsmind/style/jsmind.css"

const MindmapTest = () => {
  const jmContainer = useRef(null)
  const jm = useRef(null)

  const nodeRenderTest = (jm, element, node) => {
    if (node.isroot || node.id === "2") {
      return false
    }
    ReactDOM.render(<span>{node.topic}</span>, element)
    return true
  }

  useEffect(() => {
    if (!jmContainer.current) {
      return
    }
    jm.current = new jsMind({
      container: jmContainer.current,
      editable: true,
      theme: "orange", //"greensea", //
      shortcut: {
        enable: true, // 是否启用快捷键
      },
      view: {
        custom_node_render: nodeRenderTest, // 使用自定义渲染函数
      },
    })

    jm.current.show({
      meta: { name: "", author: "", version: "" },
      format: "node_array",
      data: [
        { id: "root", topic: "前端库", isroot: true },
        { id: "1", topic: "脑图(custom_node_render展开按钮被遮蔽了)", parentid: "root", expanded: false },
        { id: "11", topic: "JSMind", parentid: "1" },
        { id: "2", topic: "流程图", parentid: "root", expanded: false },
        { id: "21", topic: "PlantUML", parentid: "2" },
      ],
    })

    jm.current.resize()
  })

  return <div ref={jmContainer} style={{ width: "100%", height: "100%" }} />
}

export { MindmapTest }

产生的dom结构:

<div style="width: 100%; height: 100%;">
  <div class="jsmind-inner jmnode-overflow-hidden" tabindex="1">
    <canvas class="jsmind" width="1512" height="410"></canvas>
    <jmnodes class="theme-orange" style="width: 1512px; height: 410px;">
      <jmexpander nodeid="1" style="visibility: visible; left: 765px; top: 169px;">+</jmexpander>
      <jmnode nodeid="1" class="" style="visibility: visible; left: 745px; top: 166px;">
        <span>脑图(custom_node_render展开按钮被遮蔽了)</span>
      </jmnode>
      <jmexpander nodeid="2" style="visibility: visible; left: 813px; top: 218px;">-</jmexpander>
      <jmnode nodeid="2" style="visibility: visible; left: 745px; top: 206px;">
        流程图
      </jmnode>
      <jmexpander nodeid="11" style="visibility: hidden; display: none;">-</jmexpander>
      <jmnode nodeid="11" style="visibility: hidden; display: none;">
        <span>JSMind</span>
      </jmnode>
      <jmexpander nodeid="21" style="visibility: hidden; display: none;">-</jmexpander>
      <jmnode nodeid="21" style="visibility: visible; left: 856px; top: 215px;">
        <span>PlantUML</span>
      </jmnode>
      <jmnode class="root selected" nodeid="root" style="visibility: visible; left: 623px; top: 181.5px;">
        前端库
      </jmnode>
    </jmnodes>
  </div>
</div>

页面效果:

image.png

注意上面第一个子节点,看不到展开按钮。实际因 left top 设值错误,被节点本身遮住了。并且被 custom_node_render 渲染出的节点,上下的 margin 也消失了,挤在一起。

@hizzgdev
Copy link
Owner

你在脑图…那个节点再加一个下级节点看看,看一下下一级节点的位置是不是正常的。
如果正常的话,这个节点可能是受页面上的其他样式的影响才这样的。
我稍后也试试看能不能复现这个现象。谢谢反馈!

@hqm19
Copy link
Author

hqm19 commented Mar 23, 2024

哇,您回复好快。

看我例子的数据里面,是加了子节点的。

用开发者工具修改 css 让展开图标露出来,点击后的子节点位置也不对:

image.png

@hizzgdev
Copy link
Owner

我想这个问题可能的原因是 ReactDOM.render(<span>{node.topic}</span>, element) 这一句,按我对 React 粗浅的理解,它并不是实时把 html element 给 render 出来的。jsMind 会在 custom_node_render 后尝试读取这个 node 的尺寸以进行排版,但是在 React 的环境下,读取到的 size 可能是个 0,由此产生了这个布局问题。

要验证这个原因,你可以把ReactDOM.render 这句直接改成 html dom 的写法,像 document.createElement 这种实验一下。

抱歉 @hqm19 , 我对 React 并不是很在行,在我本地配置了一个 react 18 的项目,但加上你上面帖的代码后一直跑不起来,所以也没能复现这个情况。你能否把你项目的 package.json 帖出来?或者在github/gitee上搭建一个小的项目演示这个问题,我也好可以运行起来试一下?

@hqm19
Copy link
Author

hqm19 commented Mar 25, 2024

抱歉忘记说明 react 的版本了,我用的是 react 16 目前比较稳定。 react 18 + jsmind 还没调通。 我提交了一个工程把完整的复现代码放上去了: https://github.com/hqm19/jsmind-react

@hizzgdev
Copy link
Owner

Hi @hqm19 我提交了一个 PR hqm19/jsmind-react#1 你可以看看。

@hqm19
Copy link
Author

hqm19 commented Mar 26, 2024

react 整体都是异步渲染的,把 react 改成同步没什么便捷的办法。 问了 ChatGPT ,综合后,下面的方案可以试试:

  1. 修改 jsmind 内部实现,允许将 view.custom_node_render 设置为一个返回 Promise 的函数。 jsmind 判断返回值,如果是 Promise 对象,则将后续动作放在, Promise 的 than 中执行。例如
const obj = custom_node_render_func(jm, element, node)
if (!!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'){
  obj.then((result) => {
      // 原来对返回 true 的处理   (label x)
   }
}else if(obj){
    // 原来对返回 true 的处理
}else{
   // 原来对返回 false 的处理
}
  1. custom_node_render 设置为返回 一个 Promise 的函数:
view.custom_node_render = (jm, element, node) => {
  // 函数返回一个Promise
  return new Promise((resolve, reject) => {
      ReactDOM.render(<span>{node.topic}</span>, element, () => {
        //  ReactDOM.render在异步渲染 element 完成后,会执行这个回调函数,
         resolve(true)  // 解决Promise后, 执行(label x)的地方
      })
   })
}

@hizzgdev
Copy link
Owner

hizzgdev commented May 31, 2024

原因总结:

  1. jsMind 使用同步模式获取 html 元素尺寸并计算位置
  2. 本用例在 custom_node_render 里使用 React 框架异步填充 html 元素
  3. jsMind 在获取 html 元素尺寸时,该元素尚未 render 出来
  4. 导致位置的计算结果不符合预期

解决办法:
方法1: 在 custom_node_render 的实现里,使用同步模式填充 html 元素 (推荐)
方法2: 修改 jsMind 以支持异步方式生成节点。这种方法需要对 jsMind 进行较大规模重构,而且无法保证目前文档中 api 的兼容性,因此考虑暂不支持,后续或考虑以新项目的方式支持异步模型。

感谢 @hqm19 带来的建设性的讨论,以及非常有价值的尝试 #594
相关的 PR 将会被关闭,此 issue 将会移至 discussion 区。

Repository owner locked and limited conversation to collaborators May 31, 2024
@hizzgdev hizzgdev converted this issue into discussion #607 May 31, 2024

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants