/**
* vue3-virtual-scroll-list v0.2.1
* open source under the MIT license
* https://github.com/reactjser/vue3-virtual-scroll-list#readme
*/
import { defineComponent, ref, createVNode, computed, onMounted, onUpdated, onUnmounted, watch, onBeforeMount, onActivated } from 'vue';

function ownKeys(object, enumerableOnly) {
  var keys = Object.keys(object);

  if (Object.getOwnPropertySymbols) {
    var symbols = Object.getOwnPropertySymbols(object);

    if (enumerableOnly) {
      symbols = symbols.filter(function (sym) {
        return Object.getOwnPropertyDescriptor(object, sym).enumerable;
      });
    }

    keys.push.apply(keys, symbols);
  }

  return keys;
}

function _objectSpread2(target) {
  for (var i = 1; i < arguments.length; i++) {
    var source = arguments[i] != null ? arguments[i] : {};

    if (i % 2) {
      ownKeys(Object(source), true).forEach(function (key) {
        _defineProperty(target, key, source[key]);
      });
    } else if (Object.getOwnPropertyDescriptors) {
      Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
    } else {
      ownKeys(Object(source)).forEach(function (key) {
        Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
      });
    }
  }

  return target;
}

function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

function _defineProperties(target, props) {
  for (var i = 0; i < props.length; i++) {
    var descriptor = props[i];
    descriptor.enumerable = descriptor.enumerable || false;
    descriptor.configurable = true;
    if ("value" in descriptor) descriptor.writable = true;
    Object.defineProperty(target, descriptor.key, descriptor);
  }
}

function _createClass(Constructor, protoProps, staticProps) {
  if (protoProps) _defineProperties(Constructor.prototype, protoProps);
  if (staticProps) _defineProperties(Constructor, staticProps);
  return Constructor;
}

function _defineProperty(obj, key, value) {
  if (key in obj) {
    Object.defineProperty(obj, key, {
      value: value,
      enumerable: true,
      configurable: true,
      writable: true
    });
  } else {
    obj[key] = value;
  }

  return obj;
}

function _toConsumableArray(arr) {
  return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
}

function _arrayWithoutHoles(arr) {
  if (Array.isArray(arr)) return _arrayLikeToArray(arr);
}

function _iterableToArray(iter) {
  if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
}

function _unsupportedIterableToArray(o, minLen) {
  if (!o) return;
  if (typeof o === "string") return _arrayLikeToArray(o, minLen);
  var n = Object.prototype.toString.call(o).slice(8, -1);
  if (n === "Object" && o.constructor) n = o.constructor.name;
  if (n === "Map" || n === "Set") return Array.from(o);
  if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}

function _arrayLikeToArray(arr, len) {
  if (len == null || len > arr.length) len = arr.length;

  for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];

  return arr2;
}

function _nonIterableSpread() {
  throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}

/**
 * virtual list core calculating center
 */
var DIRECTION_TYPE = {
  FRONT: 'FRONT',
  // scroll up or left
  BEHIND: 'BEHIND' // scroll down or right

};
var CALC_TYPE = {
  INIT: 'INIT',
  FIXED: 'FIXED',
  DYNAMIC: 'DYNAMIC'
};
var LEADING_BUFFER = 2;

var Virtual = /*#__PURE__*/function () {
  function Virtual(param, callUpdate) {
    _classCallCheck(this, Virtual);

    this.init(param, callUpdate);
  }

  _createClass(Virtual, [{
    key: "init",
    value: function init(param, callUpdate) {
      // param data
      this.param = param;
      this.callUpdate = callUpdate; // size data

      this.sizes = new Map();
      this.firstRangeTotalSize = 0;
      this.firstRangeAverageSize = 0;
      this.lastCalcIndex = 0;
      this.fixedSizeValue = 0;
      this.calcType = CALC_TYPE.INIT; // scroll data

      this.offset = 0;
      this.direction = ''; // range data

      this.range = Object.create(null);

      if (param) {
        this.checkRange(0, param.keeps - 1);
      } // benchmark test data
      // this.__bsearchCalls = 0
      // this.__getIndexOffsetCalls = 0

    }
  }, {
    key: "destroy",
    value: function destroy() {
      this.init(null, null);
    } // return current render range

  }, {
    key: "getRange",
    value: function getRange() {
      var range = Object.create(null);
      range.start = this.range.start;
      range.end = this.range.end;
      range.padFront = this.range.padFront;
      range.padBehind = this.range.padBehind;
      return range;
    }
  }, {
    key: "isBehind",
    value: function isBehind() {
      return this.direction === DIRECTION_TYPE.BEHIND;
    }
  }, {
    key: "isFront",
    value: function isFront() {
      return this.direction === DIRECTION_TYPE.FRONT;
    } // return start index offset

  }, {
    key: "getOffset",
    value: function getOffset(start) {
      return (start < 1 ? 0 : this.getIndexOffset(start)) + this.param.slotHeaderSize;
    }
  }, {
    key: "updateParam",
    value: function updateParam(key, value) {
      var _this = this;

      if (this.param && key in this.param) {
        // if uniqueIds change, find out deleted id and remove from size map
        if (key === 'uniqueIds') {
          this.sizes.forEach(function (v, key) {
            if (!value.includes(key)) {
              _this.sizes["delete"](key);
            }
          });
        }

        this.param[key] = value;
      }
    } // save each size map by id

  }, {
    key: "saveSize",
    value: function saveSize(id, size) {
      this.sizes.set(id, size); // we assume size type is fixed at the beginning and remember first size value
      // if there is no size value different from this at next comming saving
      // we think it's a fixed size list, otherwise is dynamic size list

      if (this.calcType === CALC_TYPE.INIT) {
        this.fixedSizeValue = size;
        this.calcType = CALC_TYPE.FIXED;
      } else if (this.calcType === CALC_TYPE.FIXED && this.fixedSizeValue !== size) {
        this.calcType = CALC_TYPE.DYNAMIC; // it's no use at all

        delete this.fixedSizeValue;
      } // calculate the average size only in the first range


      if (this.calcType !== CALC_TYPE.FIXED && typeof this.firstRangeTotalSize !== 'undefined') {
        if (this.sizes.size < Math.min(this.param.keeps, this.param.uniqueIds.length)) {
          this.firstRangeTotalSize = _toConsumableArray(this.sizes.values()).reduce(function (acc, val) {
            return acc + val;
          }, 0);
          this.firstRangeAverageSize = Math.round(this.firstRangeTotalSize / this.sizes.size);
        } else {
          // it's done using
          delete this.firstRangeTotalSize;
        }
      }
    } // in some special situation (e.g. length change) we need to update in a row
    // try goiong to render next range by a leading buffer according to current direction

  }, {
    key: "handleDataSourcesChange",
    value: function handleDataSourcesChange() {
      var start = this.range.start;

      if (this.isFront()) {
        start = start - LEADING_BUFFER;
      } else if (this.isBehind()) {
        start = start + LEADING_BUFFER;
      }

      start = Math.max(start, 0);
      this.updateRange(this.range.start, this.getEndByStart(start));
    } // when slot size change, we also need force update

  }, {
    key: "handleSlotSizeChange",
    value: function handleSlotSizeChange() {
      this.handleDataSourcesChange();
    } // calculating range on scroll

  }, {
    key: "handleScroll",
    value: function handleScroll(offset) {
      this.direction = offset < this.offset ? DIRECTION_TYPE.FRONT : DIRECTION_TYPE.BEHIND;
      this.offset = offset;

      if (!this.param) {
        return;
      }

      if (this.direction === DIRECTION_TYPE.FRONT) {
        this.handleFront();
      } else if (this.direction === DIRECTION_TYPE.BEHIND) {
        this.handleBehind();
      }
    } // ----------- public method end -----------

  }, {
    key: "handleFront",
    value: function handleFront() {
      var overs = this.getScrollOvers(); // should not change range if start doesn't exceed overs

      if (overs > this.range.start) {
        return;
      } // move up start by a buffer length, and make sure its safety


      var start = Math.max(overs - this.param.buffer, 0);
      this.checkRange(start, this.getEndByStart(start));
    }
  }, {
    key: "handleBehind",
    value: function handleBehind() {
      var overs = this.getScrollOvers(); // range should not change if scroll overs within buffer

      if (overs < this.range.start + this.param.buffer) {
        return;
      }

      this.checkRange(overs, this.getEndByStart(overs));
    } // return the pass overs according to current scroll offset

  }, {
    key: "getScrollOvers",
    value: function getScrollOvers() {
      // if slot header exist, we need subtract its size
      var offset = this.offset - this.param.slotHeaderSize;

      if (offset <= 0) {
        return 0;
      } // if is fixed type, that can be easily


      if (this.isFixedType()) {
        return Math.floor(offset / this.fixedSizeValue);
      }

      var low = 0;
      var middle = 0;
      var middleOffset = 0;
      var high = this.param.uniqueIds.length;

      while (low <= high) {
        // this.__bsearchCalls++
        middle = low + Math.floor((high - low) / 2);
        middleOffset = this.getIndexOffset(middle);

        if (middleOffset === offset) {
          return middle;
        } else if (middleOffset < offset) {
          low = middle + 1;
        } else if (middleOffset > offset) {
          high = middle - 1;
        }
      }

      return low > 0 ? --low : 0;
    } // return a scroll offset from given index, can efficiency be improved more here?
    // although the call frequency is very high, its only a superposition of numbers

  }, {
    key: "getIndexOffset",
    value: function getIndexOffset(givenIndex) {
      if (!givenIndex) {
        return 0;
      }

      var offset = 0;
      var indexSize = 0;

      for (var index = 0; index < givenIndex; index++) {
        // this.__getIndexOffsetCalls++
        indexSize = this.sizes.get(this.param.uniqueIds[index]);
        offset = offset + (typeof indexSize === 'number' ? indexSize : this.getEstimateSize());
      } // remember last calculate index


      this.lastCalcIndex = Math.max(this.lastCalcIndex, givenIndex - 1);
      this.lastCalcIndex = Math.min(this.lastCalcIndex, this.getLastIndex());
      return offset;
    } // is fixed size type

  }, {
    key: "isFixedType",
    value: function isFixedType() {
      return this.calcType === CALC_TYPE.FIXED;
    } // return the real last index

  }, {
    key: "getLastIndex",
    value: function getLastIndex() {
      return this.param.uniqueIds.length - 1;
    } // in some conditions range is broke, we need correct it
    // and then decide whether need update to next range

  }, {
    key: "checkRange",
    value: function checkRange(start, end) {
      var keeps = this.param.keeps;
      var total = this.param.uniqueIds.length; // datas less than keeps, render all

      if (total <= keeps) {
        start = 0;
        end = this.getLastIndex();
      } else if (end - start < keeps - 1) {
        // if range length is less than keeps, corrent it base on end
        start = end - keeps + 1;
      }

      if (this.range.start !== start) {
        this.updateRange(start, end);
      }
    } // setting to a new range and rerender

  }, {
    key: "updateRange",
    value: function updateRange(start, end) {
      this.range.start = start;
      this.range.end = end;
      this.range.padFront = this.getPadFront();
      this.range.padBehind = this.getPadBehind();
      this.callUpdate(this.getRange());
    } // return end base on start

  }, {
    key: "getEndByStart",
    value: function getEndByStart(start) {
      var theoryEnd = start + this.param.keeps - 1;
      var truelyEnd = Math.min(theoryEnd, this.getLastIndex());
      return truelyEnd;
    } // return total front offset

  }, {
    key: "getPadFront",
    value: function getPadFront() {
      if (this.isFixedType()) {
        return this.fixedSizeValue * this.range.start;
      } else {
        return this.getIndexOffset(this.range.start);
      }
    } // return total behind offset

  }, {
    key: "getPadBehind",
    value: function getPadBehind() {
      var end = this.range.end;
      var lastIndex = this.getLastIndex();

      if (this.isFixedType()) {
        return (lastIndex - end) * this.fixedSizeValue;
      } // if it's all calculated, return the exactly offset


      if (this.lastCalcIndex === lastIndex) {
        return this.getIndexOffset(lastIndex) - this.getIndexOffset(end);
      } else {
        // if not, use a estimated value
        return (lastIndex - end) * this.getEstimateSize();
      }
    } // get the item estimate size

  }, {
    key: "getEstimateSize",
    value: function getEstimateSize() {
      return this.isFixedType() ? this.fixedSizeValue : this.firstRangeAverageSize || this.param.estimateSize;
    }
  }]);

  return Virtual;
}();

/**
 * props declaration for default, item and slot component
 */
var VirtualProps = {
  dataKey: {
    type: [String, Function],
    required: true
  },
  dataSources: {
    type: Array,
    required: true,
    "default": function _default() {
      return [];
    }
  },
  dataComponent: {
    type: [Object, Function],
    required: true
  },
  keeps: {
    type: Number,
    "default": 30
  },
  extraProps: {
    type: Object
  },
  estimateSize: {
    type: Number,
    "default": 50
  },
  direction: {
    type: String,
    "default": 'vertical' // the other value is horizontal

  },
  start: {
    type: Number,
    "default": 0
  },
  offset: {
    type: Number,
    "default": 0
  },
  topThreshold: {
    type: Number,
    "default": 0
  },
  bottomThreshold: {
    type: Number,
    "default": 0
  },
  pageMode: {
    type: Boolean,
    "default": false
  },
  rootTag: {
    type: String,
    "default": 'div'
  },
  wrapTag: {
    type: String,
    "default": 'div'
  },
  wrapClass: {
    type: String,
    "default": 'wrap'
  },
  wrapStyle: {
    type: Object
  },
  itemTag: {
    type: String,
    "default": 'div'
  },
  itemClass: {
    type: String,
    "default": ''
  },
  itemClassAdd: {
    type: Function
  },
  itemStyle: {
    type: Object
  },
  headerTag: {
    type: String,
    "default": 'div'
  },
  headerClass: {
    type: String,
    "default": ''
  },
  headerStyle: {
    type: Object
  },
  footerTag: {
    type: String,
    "default": 'div'
  },
  footerClass: {
    type: String,
    "default": ''
  },
  footerStyle: {
    type: Object
  },
  itemScopedSlots: {
    type: Object
  }
};
var ItemProps = {
  index: {
    type: Number
  },
  event: {
    type: String
  },
  tag: {
    type: String
  },
  horizontal: {
    type: Boolean
  },
  source: {
    type: Object
  },
  component: {
    type: [Object, Function]
  },
  uniqueKey: {
    type: [String, Number]
  },
  extraProps: {
    type: Object
  },
  scopedSlots: {
    type: Object
  }
};
var SlotProps = {
  event: {
    type: String
  },
  uniqueKey: {
    type: String
  },
  tag: {
    type: String
  },
  horizontal: {
    type: Boolean
  }
};

var useResizeChange = function useResizeChange(props, rootRef, emit) {
  var resizeObserver = null;
  var shapeKey = computed(function () {
    return props.horizontal ? 'offsetWidth' : 'offsetHeight';
  });

  var getCurrentSize = function getCurrentSize() {
    return rootRef.value ? rootRef.value[shapeKey.value] : 0;
  }; // tell parent current size identify by unqiue key


  var dispatchSizeChange = function dispatchSizeChange() {
    var event = props.event,
        uniqueKey = props.uniqueKey,
        hasInitial = props.hasInitial;
    emit(event, uniqueKey, getCurrentSize(), hasInitial);
  };

  onMounted(function () {
    if (typeof ResizeObserver !== 'undefined') {
      resizeObserver = new ResizeObserver(function () {
        dispatchSizeChange();
      });
      rootRef.value && resizeObserver.observe(rootRef.value);
    }
  });
  onUpdated(function () {
    dispatchSizeChange();
  });
  onUnmounted(function () {
    if (resizeObserver) {
      resizeObserver.disconnect();
      resizeObserver = null;
    }
  });
};

var Item = defineComponent({
  name: 'VirtualListItem',
  props: ItemProps,
  emits: ['itemResize'],
  setup: function setup(props, _ref) {
    var emit = _ref.emit;
    var rootRef = ref(null);
    useResizeChange(props, rootRef, emit);
    return function () {
      var Tag = props.tag,
          Comp = props.component,
          _props$extraProps = props.extraProps,
          extraProps = _props$extraProps === void 0 ? {} : _props$extraProps,
          index = props.index,
          source = props.source,
          _props$scopedSlots = props.scopedSlots,
          scopedSlots = _props$scopedSlots === void 0 ? {} : _props$scopedSlots,
          uniqueKey = props.uniqueKey;

      var mergedProps = _objectSpread2(_objectSpread2({}, extraProps), {}, {
        source: source,
        index: index
      });

      return createVNode(Tag, {
        "key": uniqueKey,
        "ref": rootRef
      }, {
        "default": function _default() {
          return [createVNode(Comp, _objectSpread2(_objectSpread2({}, mergedProps), {}, {
            "scopedSlots": scopedSlots
          }), null)];
        }
      });
    };
  }
});
var Slot = defineComponent({
  name: 'VirtualListSlot',
  props: SlotProps,
  emits: ['slotResize'],
  setup: function setup(props, _ref2) {
    var slots = _ref2.slots,
        emit = _ref2.emit;
    var rootRef = ref(null);
    useResizeChange(props, rootRef, emit);
    return function () {
      var _slots$default;

      var Tag = props.tag,
          uniqueKey = props.uniqueKey;
      return createVNode(Tag, {
        "ref": rootRef,
        "key": uniqueKey
      }, {
        "default": function _default() {
          return [(_slots$default = slots["default"]) === null || _slots$default === void 0 ? void 0 : _slots$default.call(slots)];
        }
      });
    };
  }
});

var EVENT_TYPE;

(function (EVENT_TYPE) {
  EVENT_TYPE["ITEM"] = "itemResize";
  EVENT_TYPE["SLOT"] = "slotResize";
})(EVENT_TYPE || (EVENT_TYPE = {}));

var SLOT_TYPE;

(function (SLOT_TYPE) {
  SLOT_TYPE["HEADER"] = "thead";
  SLOT_TYPE["FOOTER"] = "tfoot";
})(SLOT_TYPE || (SLOT_TYPE = {}));

var VirtualList = defineComponent({
  name: 'VirtualList',
  props: VirtualProps,
  setup: function setup(props, _ref) {
    var emit = _ref.emit,
        slots = _ref.slots,
        expose = _ref.expose;
    var isHorizontal = props.direction === 'horizontal';
    var directionKey = isHorizontal ? 'scrollLeft' : 'scrollTop';
    var range = ref(null);
    var root = ref();
    var shepherd = ref(null);
    var virtual;
    /**
     * watch
     */

    watch(function () {
      return props.dataSources.length;
    }, function () {
      virtual.updateParam('uniqueIds', getUniqueIdFromDataSources());
      virtual.handleDataSourcesChange();
    });
    watch(function () {
      return props.keeps;
    }, function (newValue) {
      virtual.updateParam('keeps', newValue);
      virtual.handleSlotSizeChange();
    });
    watch(function () {
      return props.start;
    }, function (newValue) {
      scrollToIndex(newValue);
    });
    watch(function () {
      return props.offset;
    }, function (newValue) {
      return scrollToOffset(newValue);
    });
    /**
     * methods
     */
    // get item size by id

    var getSize = function getSize(id) {
      return virtual.sizes.get(id);
    };

    var getOffset = function getOffset() {
      if (props.pageMode) {
        return document.documentElement[directionKey] || document.body[directionKey];
      } else {
        return root.value ? Math.ceil(root.value[directionKey]) : 0;
      }
    }; // return client viewport size


    var getClientSize = function getClientSize() {
      var key = isHorizontal ? 'clientWidth' : 'clientHeight';

      if (props.pageMode) {
        return document.documentElement[key] || document.body[key];
      } else {
        return root.value ? Math.ceil(root.value[key]) : 0;
      }
    }; // return all scroll size


    var getScrollSize = function getScrollSize() {
      var key = isHorizontal ? 'scrollWidth' : 'scrollHeight';

      if (props.pageMode) {
        return document.documentElement[key] || document.body[key];
      } else {
        return root.value ? Math.ceil(root.value[key]) : 0;
      }
    };

    var emitEvent = function emitEvent(offset, clientSize, scrollSize, evt) {
      emit('scroll', evt, virtual.getRange());

      if (virtual.isFront() && !!props.dataSources.length && offset - props.topThreshold <= 0) {
        emit('totop');
      } else if (virtual.isBehind() && offset + clientSize + props.bottomThreshold >= scrollSize) {
        emit('tobottom');
      }
    };

    var onScroll = function onScroll(evt) {
      var offset = getOffset();
      var clientSize = getClientSize();
      var scrollSize = getScrollSize(); // iOS scroll-spring-back behavior will make direction mistake

      if (offset < 0 || offset + clientSize > scrollSize + 1 || !scrollSize) {
        return;
      }

      virtual.handleScroll(offset);
      emitEvent(offset, clientSize, scrollSize, evt);
    };

    var getUniqueIdFromDataSources = function getUniqueIdFromDataSources() {
      var dataKey = props.dataKey,
          _props$dataSources = props.dataSources,
          dataSources = _props$dataSources === void 0 ? [] : _props$dataSources;
      return dataSources.map(function (dataSource) {
        return typeof dataKey === 'function' ? dataKey(dataSource) : dataSource[dataKey];
      });
    };

    var onRangeChanged = function onRangeChanged(newRange) {
      range.value = newRange;
    };

    var installVirtual = function installVirtual() {
      virtual = new Virtual({
        slotHeaderSize: 0,
        slotFooterSize: 0,
        keeps: props.keeps,
        estimateSize: props.estimateSize,
        buffer: Math.round(props.keeps / 3),
        // recommend for a third of keeps
        uniqueIds: getUniqueIdFromDataSources()
      }, onRangeChanged); // sync initial range

      range.value = virtual.getRange();
    }; // set current scroll position to a expectant index


    var scrollToIndex = function scrollToIndex(index) {
      // scroll to bottom
      if (index >= props.dataSources.length - 1) {
        scrollToBottom();
      } else {
        var offset = virtual.getOffset(index);
        scrollToOffset(offset);
      }
    }; // set current scroll position to a expectant offset


    var scrollToOffset = function scrollToOffset(offset) {
      if (props.pageMode) {
        document.body[directionKey] = offset;
        document.documentElement[directionKey] = offset;
      } else {
        if (root.value) {
          root.value[directionKey] = offset;
        }
      }
    }; // get the real render slots based on range data
    // in-place patch strategy will try to reuse components as possible
    // so those components that are reused will not trigger lifecycle mounted


    var getRenderSlots = function getRenderSlots() {
      var slots = [];
      var _range$value = range.value,
          start = _range$value.start,
          end = _range$value.end;
      var dataSources = props.dataSources,
          dataKey = props.dataKey,
          itemClass = props.itemClass,
          itemTag = props.itemTag,
          itemStyle = props.itemStyle,
          extraProps = props.extraProps,
          dataComponent = props.dataComponent,
          itemScopedSlots = props.itemScopedSlots;

      for (var index = start; index <= end; index++) {
        var dataSource = dataSources[index];

        if (dataSource) {
          var uniqueKey = typeof dataKey === 'function' ? dataKey(dataSource) : dataSource[dataKey];

          if (typeof uniqueKey === 'string' || typeof uniqueKey === 'number') {
            slots.push(createVNode(Item, {
              "index": index,
              "tag": itemTag,
              "event": EVENT_TYPE.ITEM,
              "horizontal": isHorizontal,
              "uniqueKey": uniqueKey,
              "source": dataSource,
              "extraProps": extraProps,
              "component": dataComponent,
              "scopedSlots": itemScopedSlots,
              "style": itemStyle,
              "class": "".concat(itemClass).concat(props.itemClassAdd ? ' ' + props.itemClassAdd(index) : ''),
              "onItemResize": onItemResized
            }, null));
          } else {
            console.warn("Cannot get the data-key '".concat(dataKey, "' from data-sources."));
          }
        } else {
          console.warn("Cannot get the index '".concat(index, "' from data-sources."));
        }
      }

      return slots;
    }; // event called when each item mounted or size changed


    var onItemResized = function onItemResized(id, size) {
      virtual.saveSize(id, size);
      emit('resized', id, size);
    }; // event called when slot mounted or size changed


    var onSlotResized = function onSlotResized(type, size, hasInit) {
      if (type === SLOT_TYPE.HEADER) {
        virtual.updateParam('slotHeaderSize', size);
      } else if (type === SLOT_TYPE.FOOTER) {
        virtual.updateParam('slotFooterSize', size);
      }

      if (hasInit) {
        virtual.handleSlotSizeChange();
      }
    }; // set current scroll position to bottom


    var scrollToBottom = function scrollToBottom() {
      if (shepherd.value) {
        var offset = shepherd.value[isHorizontal ? 'offsetLeft' : 'offsetTop'];
        scrollToOffset(offset); // check if it's really scrolled to the bottom
        // maybe list doesn't render and calculate to last range
        // so we need retry in next event loop until it really at bottom

        setTimeout(function () {
          if (getOffset() + getClientSize() < getScrollSize()) {
            scrollToBottom();
          }
        }, 3);
      }
    }; // when using page mode we need update slot header size manually
    // taking root offset relative to the browser as slot header size


    var updatePageModeFront = function updatePageModeFront() {
      if (root.value) {
        var rect = root.value.getBoundingClientRect();
        var defaultView = root.value.ownerDocument.defaultView;
        var offsetFront = isHorizontal ? rect.left + defaultView.pageXOffset : rect.top + defaultView.pageYOffset;
        virtual.updateParam('slotHeaderSize', offsetFront);
      }
    }; // get the total number of stored (rendered) items


    var getSizes = function getSizes() {
      return virtual.sizes.size;
    };
    /**
     * life cycles
     */


    onBeforeMount(function () {
      installVirtual();
    }); // set back offset when awake from keep-alive

    onActivated(function () {
      scrollToOffset(virtual.offset);
    });
    onMounted(function () {
      // set position
      if (props.start) {
        scrollToIndex(props.start);
      } else if (props.offset) {
        scrollToOffset(props.offset);
      } // in page mode we bind scroll event to document


      if (props.pageMode) {
        updatePageModeFront();
        document.addEventListener('scroll', onScroll, {
          passive: false
        });
      }
    });
    onUnmounted(function () {
      virtual.destroy();

      if (props.pageMode) {
        document.removeEventListener('scroll', onScroll);
      }
    });
    /**
     * public methods
     */

    expose({
      scrollToBottom: scrollToBottom,
      getSizes: getSizes,
      getSize: getSize,
      getOffset: getOffset,
      getScrollSize: getScrollSize,
      getClientSize: getClientSize,
      scrollToOffset: scrollToOffset,
      scrollToIndex: scrollToIndex
    });
    return function () {
      var pageMode = props.pageMode,
          RootTag = props.rootTag,
          WrapTag = props.wrapTag,
          wrapClass = props.wrapClass,
          wrapStyle = props.wrapStyle,
          headerTag = props.headerTag,
          headerClass = props.headerClass,
          headerStyle = props.headerStyle,
          footerTag = props.footerTag,
          footerClass = props.footerClass,
          footerStyle = props.footerStyle;
      var _ref2 = range.value,
          padFront = _ref2.padFront,
          padBehind = _ref2.padBehind;
      var paddingStyle = {
        padding: isHorizontal ? "0px ".concat(padBehind, "px 0px ").concat(padFront, "px") : "".concat(padFront, "px 0px ").concat(padBehind, "px")
      };
      var wrapperStyle = wrapStyle ? Object.assign({}, wrapStyle, paddingStyle) : paddingStyle;
      var header = slots.header,
          footer = slots.footer;
      return createVNode(RootTag, {
        "ref": root,
        "onScroll": !pageMode && onScroll
      }, {
        "default": function _default() {
          return [header && createVNode(Slot, {
            "class": headerClass,
            "style": headerStyle,
            "tag": headerTag,
            "event": EVENT_TYPE.SLOT,
            "uniqueKey": SLOT_TYPE.HEADER,
            "onSlotResize": onSlotResized
          }, {
            "default": function _default() {
              return [header()];
            }
          }), createVNode(WrapTag, {
            "class": wrapClass,
            "style": wrapperStyle
          }, {
            "default": function _default() {
              return [getRenderSlots()];
            }
          }), footer && createVNode(Slot, {
            "class": footerClass,
            "style": footerStyle,
            "tag": footerTag,
            "event": EVENT_TYPE.SLOT,
            "uniqueKey": SLOT_TYPE.FOOTER,
            "onSlotResize": onSlotResized
          }, {
            "default": function _default() {
              return [footer()];
            }
          }), createVNode("div", {
            "ref": shepherd,
            "style": {
              width: isHorizontal ? '0px' : '100%',
              height: isHorizontal ? '100%' : '0px'
            }
          }, null)];
        }
      });
    };
  }
});

export { VirtualList as default };