瀏覽代碼

Merge branch 'bentiss-infinite-scrolling' into edge

Lauri Ojansivu 6 年之前
父節點
當前提交
d08bee6817
共有 3 個文件被更改,包括 95 次插入1 次删除
  1. 3 0
      client/components/lists/list.styl
  2. 11 1
      client/components/lists/listBody.jade
  3. 81 0
      client/components/lists/listBody.js

+ 3 - 0
client/components/lists/list.styl

@@ -211,6 +211,9 @@
   max-height: 250px
   overflow: hidden
 
+.sk-spinner-list
+  margin-top: unset !important
+
 list-header-color(background, color...)
   border-bottom: 6px solid background
 

+ 11 - 1
client/components/lists/listBody.jade

@@ -4,7 +4,7 @@ template(name="listBody")
       if cards.count
         +inlinedForm(autoclose=false position="top")
           +addCardForm(listId=_id position="top")
-      each (cards (idOrNull ../../_id))
+      each (cardsWithLimit (idOrNull ../../_id))
         a.minicard-wrapper.js-minicard(href=absoluteUrl
           class="{{#if cardIsSelected}}is-selected{{/if}}"
           class="{{#if MultiSelection.isSelected _id}}is-checked{{/if}}")
@@ -12,6 +12,16 @@ template(name="listBody")
             .materialCheckBox.multi-selection-checkbox.js-toggle-multi-selection(
               class="{{#if MultiSelection.isSelected _id}}is-checked{{/if}}")
           +minicard(this)
+      if (showSpinner (idOrNull ../../_id))
+        .sk-spinner.sk-spinner-wave.sk-spinner-list(
+          class=currentBoard.colorClass
+          id="showMoreResults")
+          .sk-rect1
+          .sk-rect2
+          .sk-rect3
+          .sk-rect4
+          .sk-rect5
+
       if canSeeAddCard
         +inlinedForm(autoclose=false position="bottom")
           +addCardForm(listId=_id position="bottom")

+ 81 - 0
client/components/lists/listBody.js

@@ -1,6 +1,34 @@
 const subManager = new SubsManager();
+const InfiniteScrollIter = 10;
 
 BlazeComponent.extendComponent({
+  onCreated() {
+    // for infinite scrolling
+    this.cardlimit = new ReactiveVar(InfiniteScrollIter);
+  },
+
+  onRendered() {
+    const domElement = this.find('.js-perfect-scrollbar');
+
+    this.$(domElement).on('scroll', () => this.updateList(domElement));
+    $(window).on(`resize.${this.data().listId}`, () => this.updateList(domElement));
+
+    // we add a Mutation Observer to allow propagations of cardlimit
+    // when the spinner stays in the current view (infinite scrolling)
+    this.mutationObserver = new MutationObserver(() => this.updateList(domElement));
+
+    this.mutationObserver.observe(domElement, {
+      childList: true,
+    });
+
+    this.updateList(domElement);
+  },
+
+  onDestroyed() {
+    $(window).off(`resize.${this.data().listId}`);
+    this.mutationObserver.disconnect();
+  },
+
   mixins() {
     return [Mixins.PerfectScrollbar];
   },
@@ -60,6 +88,13 @@ BlazeComponent.extendComponent({
         type: 'cardType-card',
       });
 
+      // if the displayed card count is less than the total cards in the list,
+      // we need to increment the displayed card count to prevent the spinner
+      // to appear
+      const cardCount = this.data().cards(this.idOrNull(swimlaneId)).count();
+      if (this.cardlimit.get() < cardCount) {
+        this.cardlimit.set(this.cardlimit.get() + InfiniteScrollIter);
+      }
 
       // In case the filter is active we need to add the newly inserted card in
       // the list of exceptions -- cards that are not filtered. Otherwise the
@@ -119,6 +154,52 @@ BlazeComponent.extendComponent({
     return undefined;
   },
 
+  cardsWithLimit(swimlaneId) {
+    const limit = this.cardlimit.get();
+    const selector = {
+      listId: this.currentData()._id,
+      archived: false,
+    };
+    if (swimlaneId)
+      selector.swimlaneId = swimlaneId;
+    return Cards.find(Filter.mongoSelector(selector), {
+      sort: ['sort'],
+      limit,
+    });
+  },
+
+  spinnerInView(container) {
+    const parentViewHeight = container.clientHeight;
+    const bottomViewPosition = container.scrollTop + parentViewHeight;
+
+    const spinner = this.find('.sk-spinner-list');
+
+    const threshold = spinner.offsetTop;
+
+    return bottomViewPosition > threshold;
+  },
+
+  showSpinner(swimlaneId) {
+    const list = Template.currentData();
+    return list.cards(swimlaneId).count() > this.cardlimit.get();
+  },
+
+  updateList(container) {
+    // first, if the spinner is not rendered, we have reached the end of
+    // the list of cards, so skip and disable firing the events
+    const target = this.find('.sk-spinner-list');
+    if (!target) {
+      this.$(container).off('scroll');
+      $(window).off(`resize.${this.data().listId}`);
+      return;
+    }
+
+    if (this.spinnerInView(container)) {
+      this.cardlimit.set(this.cardlimit.get() + InfiniteScrollIter);
+      Ps.update(container);
+    }
+  },
+
   canSeeAddCard() {
     return !this.reachedWipLimit() && Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
   },