[PATCH 18 of 18 seda] [seda tree / js] Reimplement rules telling which moves are allowed within the js tree

Sylvain Thenault sylvain.thenault at logilab.fr
Mon Dec 11 16:53:50 CET 2017


# HG changeset patch
# User Sylvain Thénault <sylvain.thenault at logilab.fr>
# Date 1513006588 -3600
#      Mon Dec 11 16:36:28 2017 +0100
# Node ID af99ec718f86487d6b1b32c4e7a7ca69874a120c
# Parent  14ca365f3b34f6b21c94daafd8c5e89ae5343c1f
[seda tree / js] Reimplement rules telling which moves are allowed within the js tree

And call the ajax API accordingly. We now allow on top of the earlier
"maybeParentOf" mecanism:

* to reorder binary / physical data object
* to reorder archive units.

To do so, we:

* turn 'inside' position while moving a node which is already a children node
  into 'before' + first children as target node,

* check if the position is correct, provided that we don't want to mix types:
  binary objects should be before physical objects which should be before
  archival units,

* call the ajax API by giving it the relative index above mentionned (i.e. index
  of the node within nodes of the same type).

diff --git a/cubicweb_seda/data/cubes.jqtree.js b/cubicweb_seda/data/cubes.jqtree.js
--- a/cubicweb_seda/data/cubes.jqtree.js
+++ b/cubicweb_seda/data/cubes.jqtree.js
@@ -16,10 +16,32 @@
 
 
 jqtree = {
     jqTree: function(domid, dragAndDrop, canMoveTo) {
         var $tree = cw.jqNode(domid);
+
+        function adjustPositionAndTarget(position, target_node, moved_node) {
+            if (position === 'inside' && target_node.children.some(e => e.id === moved_node.id)) {
+                position = 'before';
+                target_node = target_node.children[0];
+            }
+            return [position, target_node];
+        }
+
+        function relativeIndex(node) {
+            const children = node.parent.children;
+            let result = 0;
+            for (let idx=0; idx < children.length; idx++) {
+                const child = children[idx];
+                if (child.id === node.id) {
+                    return result;
+                } else if (child.type === node.type) {
+                    result++;
+                }
+            }
+        }
+
         // tree display and basic controls.
         $tree.tree({
             dragAndDrop: dragAndDrop,
             autoOpen: 0,  // only open level-0
             selectable: false,
@@ -28,10 +50,16 @@ jqtree = {
             openedIcon: $('<i class="glyphicon glyphicon-collapse-down"></i>'),
             onCanMove: function(node) {
                 return node.maybeMoved;
             },
             onCanMoveTo: function(moved_node, target_node, position) {
+                if ( target_node.id === undefined || position === 'none') {
+                    return false;
+                } else {
+                    const args = adjustPositionAndTarget(position, target_node, moved_node);
+                    position = args[0];
+                    target_node = args[1];
                     return canMoveTo(moved_node, target_node, position);
                 }
             },
             onCreateLi: function(node, $li) {
                 $li.find('.jqtree-title').addClass(node.type);
@@ -60,11 +88,35 @@ jqtree = {
         );
         $tree.bind(
             'tree.move',
             function(event) {
                 event.preventDefault();
-                asyncRemoteExec('jqtree_reparent', move.moved_node.id, target_node.id, 0);
+                const move = event.move_info;
+                function destinationIndex(position, target_node) {
+                    let idx = 0;
+                    if (position === 'after' && target_node.type === move.moved_node.type) {
+                        idx = relativeIndex(target_node) + 1;
+                    }
+                    return idx;
+                }
+
+                const args = adjustPositionAndTarget(move.position, move.target_node, move.moved_node),
+                      position = args[0],
+                      target_node = args[1];
+                if (position === 'inside') {
+                    // this is a reparenting
+                    asyncRemoteExec('jqtree_reparent', move.moved_node.id, target_node.id, 0);
+                } else if (move.moved_node.parent !== target_node.parent) {
+                    // this is a reparenting at specific index
+                    asyncRemoteExec('jqtree_reparent', move.moved_node.id, target_node.parent.id,
+                                    destinationIndex(position, target_node));
+                } else {
+                    // this is an ordering change into the same parent
+                    asyncRemoteExec('jqtree_reorder', move.moved_node.parent.id, move.moved_node.id,
+                                    destinationIndex(position, target_node));
+                }
+
                 // do the move after POSTing.
                 move.do_move();
             }
         );
     }
diff --git a/cubicweb_seda/data/cubes.seda.js b/cubicweb_seda/data/cubes.seda.js
--- a/cubicweb_seda/data/cubes.seda.js
+++ b/cubicweb_seda/data/cubes.seda.js
@@ -28,23 +28,45 @@ seda = {
         }
         $a.attr('class', classes.join(' '));
     },
 
     canMoveTo: function(moved_node, target_node, position){
-        if ( target_node.id === undefined ) {
-            return false;
-        } else if ( position != 'inside' ) {
-            // moving before/after is not supported
-            return false;
+        if (position === 'after' || position === 'before') {
+            // this is an ordering change into the same parent
+            const target_index = target_node.parent.children.indexOf(target_node);
+            if (moved_node.type === 'SEDAArchiveUnit') {
+                // archive unit move
+                // -> OK if before / after another archive unit
+                if (target_node.type === 'SEDAArchiveUnit') {
+                    return true;
+                } else if (target_node.type !== 'SEDAArchiveTransfer') {
+                    // -> OK if it's after the last binary/physical data object before archive unit
+                    if (target_index === target_node.parent.children.length - 1
+                        || target_node.parent.children[target_index + 1].type == 'SEDAArchiveUnit') {
+                        return true;
+                    }
+                }
+                return false;
+            } else {
+                // data object move
+                // -> OK if it's after a data object of the same type
+                //    or if we're moving a physical data object after the last of its kind
+                return (target_node.type === moved_node.type
+                       || (moved_node.type === 'SEDAPhysicalDataObject'
+                           && target_node.type === 'SEDABinaryDataObject'
+                           && target_node.parent.children[target_index + 1].type == 'SEDAPhysicalDataObject'));
+            }
         } else {
-            // avoid moving into the same parent
-            function isMovedNode(element, index, array) {
-                return element.id == moved_node.id;
-            }
-            if ( target_node.children.some(isMovedNode) ) {
+            // special case of moving an archive unit to root: disallow if there
+            // are some data objects since this is visually false, only allow
+            // moving before/after existing children
+            if (target_node.type === 'SEDAArchiveTransfer'
+                && moved_node.type === 'SEDAArchiveUnit'
+                && target_node.children[0].type != 'SEDAArchiveUnit') {
                 return false;
             }
+            // this is a reparenting
             // ensure the new parent target accept the moved node
             return target_node.maybeParentOf.indexOf(moved_node.type) !== -1;
         }
     }
 };
diff --git a/cubicweb_seda/views/sedatree.py b/cubicweb_seda/views/sedatree.py
--- a/cubicweb_seda/views/sedatree.py
+++ b/cubicweb_seda/views/sedatree.py
@@ -113,11 +113,11 @@ class SimplifiedArchiveUnitIJQTreeAdapte
 class DataObjectIJQTreeAdapter(SEDAIJQTreeAdapter):
     __select__ = (SEDAIJQTreeAdapter.__select__
                   & is_instance('SEDABinaryDataObject', 'SEDAPhysicalDataObject'))
 
     def maybe_moved(self):
-        return False
+        return True
 
 
 class SimplifiedBinaryDataObjectIJQTreeAdapter(SEDAIJQTreeAdapter):
     __select__ = (SEDAIJQTreeAdapter.__select__
                   & is_instance('SEDABinaryDataObject') & simplified_profile())


More information about the saem-devel mailing list