Fancytree 选择模式 3 和拖放挑战

Fancytree selectmode 3 and drag and drop challenges

我更喜欢使用 Fancytree 的 select 模式 3,因为我相信这会为用户带来最直观的体验(其中 selecting 一个 parent 节点自动 selects children)。但是,我注意到当我使用带有 dnd5 扩展名的模式时,拖放文件夹时文件夹的层次结构会丢失(有一个例外)。

我已经设置了一个 codepen 来演示这个问题。如果您 select 文件夹 4 旁边的复选框并将其拖到文件夹 3,当您放下它时,所有层次结构都会丢失。当使用select模式2时,情况就不是这样了。有趣的是,如果您不选中该框,而只是抓住文件夹 4 并移动它,层次结构仍然存在。

我查看了调试和控制台(特别是 data.otherNodeList),我观察到当(在 select 模式 3 中)如果我选中复选框,parent 文件夹节点和 children 节点都被视为单独的叶节点,事实上,如果您检查文件夹节点,它会说它没有 children。但是,如果(仍处于 select 模式 3)我只是在不选中复选框的情况下获取文件夹,则 child 节点嵌套在文件夹下方,并且文件夹指示它具有 children.

就是说,有没有人知道在移动的文件夹中维护层次结构的变通方法?我真的更喜欢使用 select 模式 3,因为它在我的代码的其他区域用于下载和删除。另外,我觉得它更 auto-selection 是最终用户所期望的。

 $("#tree").fancytree({
  checkbox: true,
  selectMode: 3,
  extensions: ["dnd5", "multi"],
  debugLevel: 0,
  source: [
    // Typically we would load using ajax instead...
    { title: "Node 1" },
    { title: "Node 2" },
    {
      title: "Folder 3",
      folder: true,
      expanded: true,
      children: [
        { title: "Node 3.1", key: "id3.1" },
        { title: "Node 3.2" }
      ]
    },
    {
      title: "Folder 4",
      folder: true,
      expanded: true,
      children: [{ title: "Node 4.1" }, { title: "Node 4.2" }]
    }
  ],
  activate: function(event, data) {
    $("#statusLine").text(event.type + ": " + data.node);
  },
  select: function(event, data) {
    $("#statusLine").text(
      event.type + ": " + data.node.isSelected() + " " + data.node
    );
  },
  dnd5: {
    preventVoidMoves: true, // Prevent moving nodes 'before self', etc.
    preventRecursion: true, // Prevent dropping nodes on own descendants
    preventSameParent: false, // Prevent dropping nodes under the same direct parent
    autoExpandMS: 1000,
    multiSource: true,  // drag all selected nodes (plus current node)
    // focusOnClick: true,
    // refreshPositions: true,
    dragStart: function(node, data) {
      // allow dragging `node`:
      data.effectAllowed = "all";
      data.dropEffect = data.dropEffectSuggested;  //"link";
      // data.dropEffect = "move";
      return true;
    },
    // dragDrag: function(node, data) {
    //   data.node.info("dragDrag", data);
    //   data.dropEffect = "copy";
    //   return true;
    // },
    dragEnter: function(node, data) {
      data.node.info("dragEnter", data);
      // data.dropEffect = "link";
      return true;
    },
    dragOver: function(node, data) {
      // data.node.info("dragOver", data);
      data.dropEffect = data.dropEffectSuggested;  //"link";
      return true;
    },
    dragEnd: function(node, data) {
      data.node.info("dragEnd", data);
    },
    dragDrop: function(node, data) {
      // This function MUST be defined to enable dropping of items on the tree.
      //
      // The source data is provided in several formats:
      //   `data.otherNode` (null if it's not a FancytreeNode from the same page)
      //   `data.otherNodeData` (Json object; null if it's not a FancytreeNode)
      //   `data.dataTransfer.getData()`
      //
      // We may access some meta data to decide what to do:
      //   `data.hitMode` ("before", "after", or "over").
      //   `data.dropEffect`, `.effectAllowed`
      //   `data.originalEvent.shiftKey`, ...
      //
      // Example:

      var sourceNodes = data.otherNodeList,
          copyMode = data.dropEffect !== "move";
      console.log("sourceNodes below");
      console.log(sourceNodes);

      if( data.hitMode === "after" ){
        // If node are inserted directly after tagrget node one-by-one,
        // this would reverse them. So we compensate:
        sourceNodes.reverse();
      }
      if( copyMode ) {
        $.each(sourceNodes, function(i, o){
          o.info("copy to " + node + ": " + data.hitMode);
          o.copyTo(node, data.hitMode, function(n){
            delete n.key;
            n.selected = false;
            n.title = "Copy of " + n.title;
          });
        });
      } else {
        $.each(sourceNodes, function(i, o){
          o.info("move to " + node + ": " + data.hitMode);
          o.moveTo(node, data.hitMode);
        });
      }
      node.debug("drop", data);
      node.setExpanded();
    }
  }
});

此代码将所有选定 个节点移动为目标节点的子节点:

var sourceNodes = data.otherNodeList;
...
$.each(sourceNodes, function(i, o){
  o.info("move to " + node + ": " + data.hitMode);
  o.moveTo(node, data.hitMode);
});

我想在你的情况下你只想移动拖动源,它是顶部节点 otherNode:

data.otherNode.moveTo(node, data.hitMode);

如果您需要移动超过一个顶级节点,另一种选择是实施 dnd5. multiSource 回调和 return 仅包含这些节点的修改列表。

对于那些可能正在寻找类似东西的人,这是我使用的回调。

multiSource: function(node, data) {
      var topMostSelected = data.tree.getSelectedNodes(true);
      return topMostSelected;
 }