All Downloads are FREE. Search and download functionalities are using the official Maven repository.

META-INF.resources.frontend.ironListConnector.js Maven / Gradle / Ivy

There is a newer version: 23.5.5
Show newest version
import { Debouncer } from "@polymer/polymer/lib/utils/debounce.js";
import { timeOut } from "@polymer/polymer/lib/utils/async.js";

window.Vaadin.Flow.ironListConnector = {
  initLazy: function(list) {
    // Check whether the connector was already initialized for the Iron list
    if (list.$connector) {
      return;
    }

    const extraItemsBuffer = 20;

    let lastRequestedRange = [0, 0];

    list.$connector = {};
    list.$connector.placeholderItem = { __placeholder: true };

    const updateRequestedItem = function() {
      /*
       * TODO Iron list seems to do a small index adjustment after scrolling
       * has stopped. This causes a redundant request to be sent to make a
       * corresponding minimal change to the buffer. We should avoid these
       * requests by making the logic skip doing a request if the available
       * buffer is within some tolerance compared to the requested buffer.
       */
      let firstNeededItem = list._virtualStart;
      let lastNeededItem = list._virtualEnd;

      let first = Math.max(0, firstNeededItem - extraItemsBuffer);
      let last = Math.min(lastNeededItem + extraItemsBuffer, list.items.length);

      if (lastRequestedRange[0] != first || lastRequestedRange[1] != last) {
        lastRequestedRange = [first, last];
        const count = 1 + last - first;
        list.$server.setRequestedRange(first, count);
      }
    };

    let requestDebounce;
    const scheduleUpdateRequest = function() {
      requestDebounce = Debouncer.debounce(
        requestDebounce,
        timeOut.after(10),
        updateRequestedItem
      );
    };

    /*
     * Ensure all items that iron list will be looking at are actually defined.
     * If this is not done, the component will keep looking ahead through the
     * array until finding enough present items to render. In our case, that's
     * a really slow way of achieving nothing since the rest of the array is
     * empty.
     */
    const originalAssign = list._assignModels;
    list._assignModels = function() {
      const tempItems = [];
      const start = list._virtualStart;
      const count = Math.min(list.items.length, list._physicalCount);
      for (let i = 0; i < count; i++) {
        if (list.items[start + i] === undefined) {
          tempItems.push(i);
          list.items[start + i] = list.$connector.placeholderItem;
        }
      }

      originalAssign.apply(list, arguments);

      /*
       * TODO: Keep track of placeholder items in the "active" range and
       * avoid deleting them so that the next pass will be faster. Instead,
       * the end of each pass should only delete placeholders that are no
       * longer needed.
       */
      for (let i = 0; i < tempItems.length; i++) {
        delete list.items[start + tempItems[i]];
      }

      /*
       * Check if we need to do anything once things have settled down.
       * This method is called multiple times in sequence for the same user
       * action, but we only want to do the check once.
       */
      scheduleUpdateRequest();
    };

    list.items = [];

    list.$connector.set = function(index, items) {
      for (let i = 0; i < items.length; i++) {
        const itemsIndex = index + i;
        list.items[itemsIndex] = items[i];
      }
      // Do a full render since dirty detection for splices is broken
      list._render();
    };

    list.$connector.updateData = function(items) {
      // Find the items by key inside the list update them
      const oldItems = list.items;
      const mapByKey = {};
      let leftToUpdate = items.length;

      for (let i = 0; i < items.length; i++) {
        const item = items[i];
        mapByKey[item.key] = item;
      }

      for (let i = 0; i < oldItems.length; i++) {
        const oldItem = oldItems[i];
        const newItem = mapByKey[oldItem.key];
        if (newItem) {
          list.items[i] = newItem;
          list.notifyPath("items." + i);
          leftToUpdate--;
          if (leftToUpdate == 0) {
            break;
          }
        }
      }
    };

    list.$connector.clear = function(index, length) {
      for (let i = 0; i < length; i++) {
        const itemsIndex = index + i;
        delete list.items[itemsIndex];

        // Most likely a no-op since the affected index isn't in view
        list.notifyPath("items." + itemsIndex);
      }
    };

    list.$connector.updateSize = function(newSize) {
      const delta = newSize - list.items.length;
      if (delta > 0) {
        list.items.length = newSize;

        list.notifySplices("items", [
          {
            index: newSize - delta,
            removed: [],
            addedCount: delta,
            object: list.items,
            type: "splice"
          }
        ]);
      } else if (delta < 0) {
        const removed = list.items.slice(newSize, list.items.length);
        list.items.splice(newSize);
        list.notifySplices("items", [
          {
            index: newSize,
            removed: removed,
            addedCount: 0,
            object: list.items,
            type: "splice"
          }
        ]);
      }
    };

    list.$connector.setPlaceholderItem = function(placeholderItem) {
      if (!placeholderItem) {
        placeholderItem = {};
      }
      placeholderItem.__placeholder = true;
      list.$connector.placeholderItem = placeholderItem;
    };
  }
};




© 2015 - 2024 Weber Informatics LLC | Privacy Policy