Prechádzať zdrojové kódy

Improve card and list sortable drag

Use a custom build of jquery-ui with only the plugins we need (instead
of including everything).

Fix a tricky bug of conflict between Blaze reactive updates and
jquery-ui (which caused cards to sometimes disappear).
Maxime Quandalle 10 rokov pred
rodič
commit
fad4cba5e2

+ 1 - 1
.meteor/packages

@@ -47,9 +47,9 @@ underscore
 
 # UI components
 fortawesome:fontawesome
-linto:jquery-ui
 mousetrap:mousetrap
 mquandalle:jquery-textcomplete
+mquandalle:jquery-ui-drag-drop-sort
 peerlibrary:blaze-components
 perak:markdown
 reactive-var

+ 1 - 1
.meteor/versions

@@ -56,7 +56,6 @@ json@1.0.3
 kenton:accounts-sandstorm@0.1.3
 launch-screen@1.0.2
 less@1.0.14
-linto:jquery-ui@1.11.2
 livedata@1.0.13
 localstorage@1.0.3
 logging@1.0.7
@@ -76,6 +75,7 @@ mquandalle:bower@1.4.1
 mquandalle:jade@0.4.3
 mquandalle:jade-compiler@0.4.3
 mquandalle:jquery-textcomplete@0.3.9_1
+mquandalle:jquery-ui-drag-drop-sort@0.1.0
 mquandalle:moment@1.0.0
 mquandalle:stylus@1.1.1
 npm-bcrypt@0.7.8_2

+ 1 - 1
client/components/boards/boardBody.js

@@ -72,10 +72,10 @@ BlazeComponent.extendComponent({
 
     self.$(lists).sortable({
       tolerance: 'pointer',
-      appendTo: '.js-lists',
       helper: 'clone',
       items: '.js-list:not(.js-list-composer)',
       placeholder: 'list placeholder',
+      distance: 7,
       start: function(evt, ui) {
         ui.placeholder.height(ui.helper.height());
         Popup.close();

+ 0 - 1
client/components/boards/boardBody.styl

@@ -31,7 +31,6 @@ position()
 
     &.is-dragging-active
 
-      .list-composer,
       .open-minicard-composer,
       .minicard-wrapper.is-checked
         display: none

+ 1 - 0
client/components/cards/minicard.styl

@@ -14,6 +14,7 @@
     border-radius: 2px
 
   &.ui-sortable-helper
+    cursor: grabbing
     transform: rotate(4deg)
     display: block !important
 

+ 1 - 1
client/components/lists/body.jade

@@ -16,7 +16,7 @@ template(name="listBody")
         +inlinedForm(autoclose=false position="bottom")
           +addCardForm(listId=_id position="bottom")
         else
-          a.open-minicard-composer.js-open-inlined-form
+          a.open-minicard-composer.js-card-composer.js-open-inlined-form
             i.fa.fa-plus
             | {{_ 'add-card'}}
 

+ 21 - 13
client/components/lists/main.js

@@ -12,25 +12,25 @@ BlazeComponent.extendComponent({
     this.newCardFormIsVisible = new ReactiveVar(true);
   },
 
-  // XXX The jQuery UI sortable plugin is far from ideal here. First we include
-  // all jQuery components but only use one. Second, it modifies the DOM itself,
-  // resulting in Blaze abandoning reactive update of the nodes that have been
-  // moved which result in bugs if multiple users use the board in real time. I
-  // tried sortable:sortable but that was not better. And dragula is not
-  // powerful enough for our use casesShould we “simply” write the drag&drop
-  // code ourselves?
+  // The jquery UI sortable library is the best solution I've found so far. I
+  // tried sortable and dragula but they were not powerful enough four our use
+  // case. I also considered writing/forking a drag-and-drop + sortable library
+  // but it's probably too much work.
+  // By calling asking the sortable library to cancel its move on the `stop`
+  // callback, we basically solve all issues related to reactive updates. A
+  // comment below provides further details.
   onRendered: function() {
     var self = this;
     if (! Meteor.userId() || ! Meteor.user().isBoardMember())
       return;
 
     var boardComponent = self.componentParent();
-    var itemsSelector = '.js-minicard:not(.placeholder, .js-composer)';
+    var itemsSelector = '.js-minicard:not(.placeholder, .js-card-composer)';
     var $cards = self.$('.js-minicards');
     $cards.sortable({
       connectWith: '.js-minicards',
       tolerance: 'pointer',
-      appendTo: '.js-lists',
+      appendTo: '#surface',
       helper: function(evt, item) {
         var helper = item.clone();
         if (MultiSelection.isActive()) {
@@ -46,7 +46,9 @@ BlazeComponent.extendComponent({
         }
         return helper;
       },
+      distance: 7,
       items: itemsSelector,
+      scroll: false,
       placeholder: 'minicard-wrapper placeholder',
       start: function(evt, ui) {
         ui.placeholder.height(ui.helper.height());
@@ -54,7 +56,7 @@ BlazeComponent.extendComponent({
         boardComponent.setIsDragging(true);
       },
       stop: function(evt, ui) {
-        // To attribute the new index number, we need to get the dom element
+        // To attribute the new index number, we need to get the DOM element
         // of the previous and the following card -- if any.
         var prevCardDom = ui.item.prev('.js-minicard').get(0);
         var nextCardDom = ui.item.next('.js-minicard').get(0);
@@ -62,6 +64,15 @@ BlazeComponent.extendComponent({
         var sortIndex = Utils.calculateIndex(prevCardDom, nextCardDom, nCards);
         var listId = Blaze.getData(ui.item.parents('.list').get(0))._id;
 
+        // Normally the jquery-ui sortable library moves the dragged DOM element
+        // to its new position, which disrupts Blaze reactive updates mechanism
+        // (especially when we move the last card of a list, or when multiple
+        // users move some cards at the same time). To prevent these UX glitches
+        // we ask sortable to gracefully cancel the move, and to put back the
+        // DOM in its initial state. The card move is then handled reactively by
+        // Blaze with the below query.
+        $cards.sortable('cancel');
+
         if (MultiSelection.isActive()) {
           Cards.find(MultiSelection.getMongoSelector()).forEach(function(c, i) {
             Cards.update(c._id, {
@@ -77,9 +88,6 @@ BlazeComponent.extendComponent({
           Cards.update(cardId, {
             $set: {
               listId: listId,
-              // XXX Using the same sort index for multiple cards is
-              // unacceptable. Keep that only until we figure out if we want to
-              // refactor the whole sorting mecanism or do something more basic.
               sort: sortIndex.base
             }
           });