UNPKG

@egjs/flicking

Version:

Everyday 30 million people experience. It's reliable, flexible and extendable carousel.

7,083 lines 244 kB
(function(global, factory) {
  typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory(require("@egjs/component"), require("@egjs/axes"), require("@egjs/imready")) : typeof define === "function" && define.amd ? define(["@egjs/component", "@egjs/axes", "@egjs/imready"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, global.Flicking = factory(global.eg.Component, global.eg.Axes, global.eg.ImReady));
})(this, (function(Component, Axes, ImReady) {
  "use strict";var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __reflectGet = Reflect.get;
var __pow = Math.pow;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
  for (var prop in b || (b = {}))
    if (__hasOwnProp.call(b, prop))
      __defNormalProp(a, prop, b[prop]);
  if (__getOwnPropSymbols)
    for (var prop of __getOwnPropSymbols(b)) {
      if (__propIsEnum.call(b, prop))
        __defNormalProp(a, prop, b[prop]);
    }
  return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __superGet = (cls, obj, key) => __reflectGet(__getProtoOf(cls), key, obj);
var __async = (__this, __arguments, generator) => {
  return new Promise((resolve, reject) => {
    var fulfilled = (value) => {
      try {
        step(generator.next(value));
      } catch (e) {
        reject(e);
      }
    };
    var rejected = (value) => {
      try {
        step(generator.throw(value));
      } catch (e) {
        reject(e);
      }
    };
    var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
    step((generator = generator.apply(__this, __arguments)).next());
  });
};

  const ALIGN = {
    /** left/top align */
    PREV: "prev",
    /** center align */
    CENTER: "center",
    /** right/bottom align */
    NEXT: "next"
  };
  const DIRECTION = {
    /** "left" when {@link Flicking.horizontal | horizontal} is true, and "top" when {@link Flicking.horizontal | horizontal} is false */
    PREV: "PREV",
    /** "right" when {@link Flicking.horizontal | horizontal} is true, and "bottom" when {@link Flicking.horizontal | horizontal} is false */
    NEXT: "NEXT",
    /** This value usually means it's the same position */
    NONE: null
  };
  const MOVE_TYPE = {
    /** Flicking's {@link Flicking.moveType | moveType} that enables {@link SnapControl} as a Flicking's {@link Flicking.control | control} */
    SNAP: "snap",
    /** Flicking's {@link Flicking.moveType | moveType} that enables {@link FreeControl} as a Flicking's {@link Flicking.control | control} */
    FREE_SCROLL: "freeScroll",
    /** Flicking's {@link Flicking.moveType | moveType} that enables {@link StrictControl} as a Flicking's {@link Flicking.control | control} */
    STRICT: "strict"
  };
  const CLASS = {
    VIEWPORT: "flicking-viewport",
    CAMERA: "flicking-camera",
    VERTICAL: "vertical",
    HIDDEN: "flicking-hidden",
    DEFAULT_VIRTUAL: "flicking-panel"
  };
  const CIRCULAR_FALLBACK = {
    /** "linear" */
    LINEAR: "linear",
    /** "bound" */
    BOUND: "bound"
  };
  const ORDER = {
    /** "ltr" */
    LTR: "ltr",
    /** "rtl" */
    RTL: "rtl"
  };
  const MOVE_DIRECTION = {
    /** horizontal */
    HORIZONTAL: "horizontal",
    /** vertical */
    VERTICAL: "vertical"
  };
  const Values = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
    __proto__: null,
    ALIGN,
    CIRCULAR_FALLBACK,
    CLASS,
    DIRECTION,
    MOVE_DIRECTION,
    MOVE_TYPE,
    ORDER
  }, Symbol.toStringTag, { value: "Module" }));
  const EVENTS = {
    /** ready event */
    READY: "ready",
    /** beforeResize event */
    BEFORE_RESIZE: "beforeResize",
    /** afterResize event */
    AFTER_RESIZE: "afterResize",
    /** holdStart event */
    HOLD_START: "holdStart",
    /** holdEnd event */
    HOLD_END: "holdEnd",
    /** moveStart event */
    MOVE_START: "moveStart",
    /** move event */
    MOVE: "move",
    /** moveEnd event */
    MOVE_END: "moveEnd",
    /** willChange event */
    WILL_CHANGE: "willChange",
    /** changed event */
    CHANGED: "changed",
    /** willRestore event */
    WILL_RESTORE: "willRestore",
    /** restored event */
    RESTORED: "restored",
    /** select event */
    SELECT: "select",
    /** needPanel event */
    NEED_PANEL: "needPanel",
    /** visibleChange event */
    VISIBLE_CHANGE: "visibleChange",
    /** reachEdge event */
    REACH_EDGE: "reachEdge",
    /**
     * panelChange event
     * @since 4.1.0
     */
    PANEL_CHANGE: "panelChange"
  };
  const Events = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
    __proto__: null,
    EVENTS
  }, Symbol.toStringTag, { value: "Module" }));
  class AnchorPoint {
    /**
     * Index of AnchorPoint
     * @readonly
     */
    get index() {
      return this._index;
    }
    /**
     * Position of AnchorPoint
     * @readonly
     */
    get position() {
      return this._pos;
    }
    /**
     * A {@link Panel} instance AnchorPoint is referencing to
     * @readonly
     */
    get panel() {
      return this._panel;
    }
    /**
     * @param options - {@link AnchorPointOptions}
     */
    constructor(options) {
      const { index, position, panel } = options;
      this._index = index;
      this._pos = position;
      this._panel = panel;
    }
  }
  const errors = {
    WRONG_TYPE: {
      code: 0,
      message: (wrongVal, correctTypes) => `${wrongVal}(${typeof wrongVal}) is not a ${correctTypes.map((type) => `"${type}"`).join(" or ")}.`
    },
    ELEMENT_NOT_FOUND: {
      code: 1,
      message: (selector) => `Element with selector "${selector}" not found.`
    },
    VAL_MUST_NOT_NULL: {
      code: 2,
      message: (val, name) => `${name} should be provided. Given: ${val}`
    },
    NOT_ATTACHED_TO_FLICKING: {
      code: 3,
      message: 'This module is not attached to the Flicking instance. "init()" should be called first.'
    },
    WRONG_OPTION: {
      code: 4,
      message: (optionName, val) => `Option "${optionName}" is not in correct format, given: ${val}`
    },
    INDEX_OUT_OF_RANGE: {
      code: 5,
      message: (val, min, max) => `Index "${val}" is out of range: should be between ${min} and ${max}.`
    },
    POSITION_NOT_REACHABLE: {
      code: 6,
      message: (position) => `Position "${position}" is not reachable.`
    },
    TRANSFORM_NOT_SUPPORTED: {
      code: 7,
      message: "Browser does not support CSS transform."
    },
    STOP_CALLED_BY_USER: {
      code: 8,
      message: "Event stop() is called by user."
    },
    ANIMATION_INTERRUPTED: {
      code: 9,
      message: "Animation is interrupted by user input."
    },
    ANIMATION_ALREADY_PLAYING: {
      code: 10,
      message: "Animation is already playing."
    },
    NOT_ALLOWED_IN_FRAMEWORK: {
      code: 11,
      message: "This behavior is not allowed in the frameworks like React, Vue, or Angular."
    },
    NOT_INITIALIZED: {
      code: 12,
      message: "Flicking is not initialized yet, call init() first."
    },
    NO_ACTIVE: {
      code: 13,
      message: "There's no active panel that Flicking has selected. This may be due to the absence of any panels."
    },
    NOT_ALLOWED_IN_VIRTUAL: {
      code: 14,
      message: "This behavior is not allowed when the virtual option is enabled"
    }
  };
  const CODE = Object.keys(errors).reduce(
    (acc, key) => {
      acc[key] = errors[key].code;
      return acc;
    },
    {}
  );
  const MESSAGE = Object.keys(errors).reduce(
    (acc, key) => {
      acc[key] = errors[key].message;
      return acc;
    },
    {}
  );
  const merge = (target, ...sources) => {
    sources.forEach((source) => {
      Object.keys(source).forEach((key) => {
        target[key] = source[key];
      });
    });
    return target;
  };
  const getElement = (el, parent) => {
    let targetEl = null;
    if (isString(el)) {
      const parentEl = parent ? parent : document;
      const queryResult = parentEl.querySelector(el);
      if (!queryResult) {
        throw new FlickingError(MESSAGE.ELEMENT_NOT_FOUND(el), CODE.ELEMENT_NOT_FOUND);
      }
      targetEl = queryResult;
    } else if (el && el.nodeType === Node.ELEMENT_NODE) {
      targetEl = el;
    }
    if (!targetEl) {
      throw new FlickingError(MESSAGE.WRONG_TYPE(el, ["HTMLElement", "string"]), CODE.WRONG_TYPE);
    }
    return targetEl;
  };
  const checkExistence = (value, nameOnErrMsg) => {
    if (value == null) {
      throw new FlickingError(MESSAGE.VAL_MUST_NOT_NULL(value, nameOnErrMsg), CODE.VAL_MUST_NOT_NULL);
    }
  };
  const clamp = (x, min, max) => Math.max(Math.min(x, max), min);
  const getFlickingAttached = (val) => {
    if (!val) {
      throw new FlickingError(MESSAGE.NOT_ATTACHED_TO_FLICKING, CODE.NOT_ATTACHED_TO_FLICKING);
    }
    return val;
  };
  const toArray = (iterable) => [].slice.call(iterable);
  const parseAlign$1 = (align, size) => {
    let alignPoint;
    if (isString(align)) {
      switch (align) {
        case ALIGN.PREV:
          alignPoint = 0;
          break;
        case ALIGN.CENTER:
          alignPoint = 0.5 * size;
          break;
        case ALIGN.NEXT:
          alignPoint = size;
          break;
        default:
          alignPoint = parseArithmeticSize(align, size);
          if (alignPoint == null) {
            throw new FlickingError(MESSAGE.WRONG_OPTION("align", align), CODE.WRONG_OPTION);
          }
      }
    } else {
      alignPoint = align;
    }
    return alignPoint;
  };
  const parseBounce = (bounce, size) => {
    let parsedBounce;
    if (Array.isArray(bounce)) {
      parsedBounce = bounce.map((val) => parseArithmeticSize(val, size));
    } else {
      const parsedVal = parseArithmeticSize(bounce, size);
      parsedBounce = [parsedVal, parsedVal];
    }
    return parsedBounce.map((val) => {
      if (val == null) {
        throw new FlickingError(MESSAGE.WRONG_OPTION("bounce", bounce), CODE.WRONG_OPTION);
      }
      return val;
    });
  };
  const parseArithmeticSize = (cssValue, base) => {
    const parsed = parseArithmeticExpression(cssValue);
    if (parsed == null) return null;
    return parsed.percentage * base + parsed.absolute;
  };
  const parseArithmeticExpression = (cssValue) => {
    const cssRegex = /(?:(\+|-)\s*)?(\d+(?:\.\d+)?(%|px)?)/g;
    if (typeof cssValue === "number") {
      return { percentage: 0, absolute: cssValue };
    }
    const parsed = {
      percentage: 0,
      absolute: 0
    };
    let idx = 0;
    let matchResult = cssRegex.exec(cssValue);
    while (matchResult != null) {
      let sign = matchResult[1];
      const value = matchResult[2];
      const unit = matchResult[3];
      const parsedValue = parseFloat(value);
      if (idx <= 0) {
        sign = sign || "+";
      }
      if (!sign) {
        return null;
      }
      const signMultiplier = sign === "+" ? 1 : -1;
      if (unit === "%") {
        parsed.percentage += signMultiplier * (parsedValue / 100);
      } else {
        parsed.absolute += signMultiplier * parsedValue;
      }
      ++idx;
      matchResult = cssRegex.exec(cssValue);
    }
    if (idx === 0) {
      return null;
    }
    return parsed;
  };
  const parseCSSSizeValue = (val) => isString(val) ? val : `${val}px`;
  const parsePanelAlign = (align) => typeof align === "object" ? align.panel : align;
  const getDirection = (start, end) => {
    if (start === end) return DIRECTION.NONE;
    return start < end ? DIRECTION.NEXT : DIRECTION.PREV;
  };
  const parseElement = (element) => {
    if (!Array.isArray(element)) {
      element = [element];
    }
    const elements = [];
    element.forEach((el) => {
      if (isString(el)) {
        const tempDiv = document.createElement("div");
        tempDiv.innerHTML = el;
        elements.push(...toArray(tempDiv.children));
        while (tempDiv.firstChild) {
          tempDiv.removeChild(tempDiv.firstChild);
        }
      } else if (el && el.nodeType === Node.ELEMENT_NODE) {
        elements.push(el);
      } else {
        throw new FlickingError(MESSAGE.WRONG_TYPE(el, ["HTMLElement", "string"]), CODE.WRONG_TYPE);
      }
    });
    return elements;
  };
  const getMinusCompensatedIndex = (idx, max) => idx < 0 ? clamp(idx + max, 0, max) : clamp(idx, 0, max);
  const includes = (array, target) => {
    for (const val of array) {
      if (val === target) return true;
    }
    return false;
  };
  const isString = (val) => typeof val === "string";
  const circulatePosition = (pos, min, max) => {
    const size = max - min;
    if (pos < min) {
      const offset = (min - pos) % size;
      pos = max - offset;
    } else if (pos > max) {
      const offset = (pos - max) % size;
      pos = min + offset;
    }
    return pos;
  };
  const find = (array, checker) => {
    for (const val of array) {
      if (checker(val)) {
        return val;
      }
    }
    return null;
  };
  const findRight = (array, checker) => {
    for (let idx = array.length - 1; idx >= 0; idx--) {
      const val = array[idx];
      if (checker(val)) {
        return val;
      }
    }
    return null;
  };
  const findIndex = (array, checker) => {
    for (let idx = 0; idx < array.length; idx++) {
      if (checker(array[idx])) {
        return idx;
      }
    }
    return -1;
  };
  const getProgress$1 = (pos, prev, next) => (pos - prev) / (next - prev);
  const getStyle = (el) => {
    if (!el) {
      return {};
    }
    return window.getComputedStyle(el) || el.currentStyle;
  };
  const setSize = (el, {
    width,
    height
  }) => {
    if (!el) {
      return;
    }
    if (width != null) {
      if (isString(width)) {
        el.style.width = width;
      } else {
        el.style.width = `${width}px`;
      }
    }
    if (height != null) {
      if (isString(height)) {
        el.style.height = height;
      } else {
        el.style.height = `${height}px`;
      }
    }
  };
  const isBetween = (val, min, max) => val >= min && val <= max;
  const circulateIndex = (index, max) => {
    if (index >= max) {
      return index % max;
    } else if (index < 0) {
      return getMinusCompensatedIndex((index + 1) % max - 1, max);
    } else {
      return index;
    }
  };
  const range = (end) => {
    const arr = new Array(end);
    for (let i = 0; i < end; i++) {
      arr[i] = i;
    }
    return arr;
  };
  const getElementSize = ({
    el,
    horizontal,
    useFractionalSize,
    useOffset,
    style
  }) => {
    let size = 0;
    if (useFractionalSize) {
      const baseSize = parseFloat(horizontal ? style.width : style.height) || 0;
      const isBorderBoxSizing = style.boxSizing === "border-box";
      const border = horizontal ? parseFloat(style.borderLeftWidth || "0") + parseFloat(style.borderRightWidth || "0") : parseFloat(style.borderTopWidth || "0") + parseFloat(style.borderBottomWidth || "0");
      if (isBorderBoxSizing) {
        size = useOffset ? baseSize : baseSize - border;
      } else {
        const padding = horizontal ? parseFloat(style.paddingLeft || "0") + parseFloat(style.paddingRight || "0") : parseFloat(style.paddingTop || "0") + parseFloat(style.paddingBottom || "0");
        size = useOffset ? baseSize + padding + border : baseSize + padding;
      }
    } else {
      const sizeStr = horizontal ? "Width" : "Height";
      size = useOffset ? el[`offset${sizeStr}`] : el[`client${sizeStr}`];
    }
    return Math.max(size, 0);
  };
  const setPrototypeOf = Object.setPrototypeOf || ((obj, proto) => {
    obj.__proto__ = proto;
    return obj;
  });
  const camelize = (str) => {
    return str.replace(/[\s-_]([a-z])/g, (all, letter) => letter.toUpperCase());
  };
  const getDataAttributes = (element, attributePrefix) => {
    const dataAttributes = {};
    const attributes = element.attributes;
    const length = attributes.length;
    for (let i = 0; i < length; ++i) {
      const attribute = attributes[i];
      const { name, value } = attribute;
      if (name.indexOf(attributePrefix) === -1) {
        continue;
      }
      dataAttributes[camelize(name.replace(attributePrefix, ""))] = value;
    }
    return dataAttributes;
  };
  const Utils = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
    __proto__: null,
    camelize,
    checkExistence,
    circulateIndex,
    circulatePosition,
    clamp,
    find,
    findIndex,
    findRight,
    getDataAttributes,
    getDirection,
    getElement,
    getElementSize,
    getFlickingAttached,
    getMinusCompensatedIndex,
    getProgress: getProgress$1,
    getStyle,
    includes,
    isBetween,
    isString,
    merge,
    parseAlign: parseAlign$1,
    parseArithmeticExpression,
    parseArithmeticSize,
    parseBounce,
    parseCSSSizeValue,
    parseElement,
    parsePanelAlign,
    range,
    setPrototypeOf,
    setSize,
    toArray
  }, Symbol.toStringTag, { value: "Module" }));
  class FlickingError extends Error {
    /**
     * @param message - Error message
     * @param code - Error code
     */
    constructor(message, code) {
      super(message);
      setPrototypeOf(this, FlickingError.prototype);
      this.name = "FlickingError";
      this.code = code;
    }
  }
  class CameraMode {
    constructor(flicking) {
      this._flicking = flicking;
    }
    getAnchors() {
      const panels = this._flicking.renderer.panels;
      return panels.map(
        (panel, index) => new AnchorPoint({
          index,
          position: panel.position,
          panel
        })
      );
    }
    findAnchorIncludePosition(position) {
      const anchors = this._flicking.camera.anchorPoints;
      const anchorsIncludingPosition = anchors.filter((anchor) => anchor.panel.includePosition(position, true));
      return anchorsIncludingPosition.reduce((nearest, anchor) => {
        if (!nearest) return anchor;
        return Math.abs(nearest.position - position) < Math.abs(anchor.position - position) ? nearest : anchor;
      }, null);
    }
    findNearestAnchor(position) {
      const anchors = this._flicking.camera.anchorPoints;
      if (anchors.length <= 0) return null;
      let prevDist = Infinity;
      for (let anchorIdx = 0; anchorIdx < anchors.length; anchorIdx++) {
        const anchor = anchors[anchorIdx];
        const dist = Math.abs(anchor.position - position);
        if (dist > prevDist) {
          return anchors[anchorIdx - 1];
        }
        prevDist = dist;
      }
      return anchors[anchors.length - 1];
    }
    clampToReachablePosition(position) {
      const camera = this._flicking.camera;
      const range2 = camera.range;
      return clamp(position, range2.min, range2.max);
    }
    getCircularOffset() {
      return 0;
    }
    canReach(panel) {
      const camera = this._flicking.camera;
      const range2 = camera.range;
      if (panel.removed) return false;
      const panelPos = panel.position;
      return panelPos >= range2.min && panelPos <= range2.max;
    }
    canSee(panel) {
      const camera = this._flicking.camera;
      const visibleRange = camera.visibleRange;
      return panel.isVisibleOnRange(visibleRange.min, visibleRange.max);
    }
  }
  class BoundCameraMode extends CameraMode {
    checkAvailability() {
      const flicking = this._flicking;
      const renderer = flicking.renderer;
      const firstPanel = renderer.getPanel(0);
      const lastPanel = renderer.getPanel(renderer.panelCount - 1);
      if (!firstPanel || !lastPanel) {
        return false;
      }
      const viewportSize = flicking.camera.size;
      const firstPanelPrev = firstPanel.range.min;
      const lastPanelNext = lastPanel.range.max;
      const panelAreaSize = lastPanelNext - firstPanelPrev;
      const isBiggerThanViewport = viewportSize < panelAreaSize;
      return isBiggerThanViewport;
    }
    getRange() {
      const flicking = this._flicking;
      const renderer = flicking.renderer;
      const alignPos = flicking.camera.alignPosition;
      const firstPanel = renderer.getPanel(0);
      const lastPanel = renderer.getPanel(renderer.panelCount - 1);
      if (!firstPanel || !lastPanel) {
        return { min: 0, max: 0 };
      }
      const viewportSize = flicking.camera.size;
      const firstPanelPrev = firstPanel.range.min;
      const lastPanelNext = lastPanel.range.max;
      const panelAreaSize = lastPanelNext - firstPanelPrev;
      const isBiggerThanViewport = viewportSize < panelAreaSize;
      const firstPos = firstPanelPrev + alignPos;
      const lastPos = lastPanelNext - viewportSize + alignPos;
      if (isBiggerThanViewport) {
        return { min: firstPos, max: lastPos };
      } else {
        const align = flicking.camera.align;
        const alignVal = typeof align === "object" ? align.camera : align;
        const pos = firstPos + parseAlign$1(alignVal, lastPos - firstPos);
        return { min: pos, max: pos };
      }
    }
    getAnchors() {
      const flicking = this._flicking;
      const camera = flicking.camera;
      const panels = flicking.renderer.panels;
      if (panels.length <= 0) {
        return [];
      }
      const range2 = flicking.camera.range;
      const reachablePanels = panels.filter((panel) => camera.canReach(panel));
      if (reachablePanels.length > 0) {
        const shouldPrependBoundAnchor = reachablePanels[0].position !== range2.min;
        const shouldAppendBoundAnchor = reachablePanels[reachablePanels.length - 1].position !== range2.max;
        const indexOffset = shouldPrependBoundAnchor ? 1 : 0;
        const newAnchors = reachablePanels.map(
          (panel, idx) => new AnchorPoint({
            index: idx + indexOffset,
            position: panel.position,
            panel
          })
        );
        if (shouldPrependBoundAnchor) {
          newAnchors.splice(
            0,
            0,
            new AnchorPoint({
              index: 0,
              position: range2.min,
              panel: panels[reachablePanels[0].index - 1]
            })
          );
        }
        if (shouldAppendBoundAnchor) {
          newAnchors.push(
            new AnchorPoint({
              index: newAnchors.length,
              position: range2.max,
              panel: panels[reachablePanels[reachablePanels.length - 1].index + 1]
            })
          );
        }
        return newAnchors;
      } else if (range2.min !== range2.max) {
        const nearestPanelAtMin = this._findNearestPanel(range2.min, panels);
        const panelAtMin = nearestPanelAtMin.index === panels.length - 1 ? nearestPanelAtMin.prev() : nearestPanelAtMin;
        const panelAtMax = panelAtMin.next();
        return [
          new AnchorPoint({
            index: 0,
            position: range2.min,
            panel: panelAtMin
          }),
          new AnchorPoint({
            index: 1,
            position: range2.max,
            panel: panelAtMax
          })
        ];
      } else {
        return [
          new AnchorPoint({
            index: 0,
            position: range2.min,
            panel: this._findNearestPanel(range2.min, panels)
          })
        ];
      }
    }
    findAnchorIncludePosition(position) {
      const camera = this._flicking.camera;
      const range2 = camera.range;
      const anchors = camera.anchorPoints;
      if (anchors.length <= 0) return null;
      if (position <= range2.min) {
        return anchors[0];
      } else if (position >= range2.max) {
        return anchors[anchors.length - 1];
      } else {
        return super.findAnchorIncludePosition(position);
      }
    }
    /**
     * @internal
     */
    _findNearestPanel(pos, panels) {
      let prevDist = Infinity;
      for (let panelIdx = 0; panelIdx < panels.length; panelIdx++) {
        const panel = panels[panelIdx];
        const dist = Math.abs(panel.position - pos);
        if (dist > prevDist) {
          return panels[panelIdx - 1];
        }
        prevDist = dist;
      }
      return panels[panels.length - 1];
    }
  }
  class CircularCameraMode extends CameraMode {
    checkAvailability() {
      const flicking = this._flicking;
      const renderer = flicking.renderer;
      const panels = renderer.panels;
      if (panels.length <= 0) {
        return false;
      }
      const firstPanel = panels[0];
      const lastPanel = panels[panels.length - 1];
      const firstPanelPrev = firstPanel.range.min - firstPanel.margin.prev;
      const lastPanelNext = lastPanel.range.max + lastPanel.margin.next;
      const visibleSize = flicking.camera.size;
      const panelSizeSum = lastPanelNext - firstPanelPrev;
      const canSetCircularMode = panels.every((panel) => panelSizeSum - panel.size >= visibleSize);
      return canSetCircularMode;
    }
    getRange() {
      const flicking = this._flicking;
      const panels = flicking.renderer.panels;
      if (panels.length <= 0) {
        return { min: 0, max: 0 };
      }
      const firstPanel = panels[0];
      const lastPanel = panels[panels.length - 1];
      const firstPanelPrev = firstPanel.range.min - firstPanel.margin.prev;
      const lastPanelNext = lastPanel.range.max + lastPanel.margin.next;
      return { min: firstPanelPrev, max: lastPanelNext };
    }
    getAnchors() {
      const flicking = this._flicking;
      const panels = flicking.renderer.panels;
      return panels.map(
        (panel, index) => new AnchorPoint({
          index,
          position: panel.position,
          panel
        })
      );
    }
    findNearestAnchor(position) {
      const camera = this._flicking.camera;
      const anchors = camera.anchorPoints;
      if (anchors.length <= 0) return null;
      const camRange = camera.range;
      let minDist = Infinity;
      let minDistIndex = -1;
      for (let anchorIdx = 0; anchorIdx < anchors.length; anchorIdx++) {
        const anchor = anchors[anchorIdx];
        const dist = Math.min(
          Math.abs(anchor.position - position),
          Math.abs(anchor.position - camRange.min + camRange.max - position),
          Math.abs(position - camRange.min + camRange.max - anchor.position)
        );
        if (dist < minDist) {
          minDist = dist;
          minDistIndex = anchorIdx;
        }
      }
      return anchors[minDistIndex];
    }
    findAnchorIncludePosition(position) {
      const camera = this._flicking.camera;
      const range2 = camera.range;
      const anchors = camera.anchorPoints;
      const rangeDiff = camera.rangeDiff;
      const anchorCount = anchors.length;
      const positionInRange = circulatePosition(position, range2.min, range2.max);
      let anchorInRange = super.findAnchorIncludePosition(positionInRange);
      if (anchorCount > 0 && (position === range2.min || position === range2.max)) {
        const possibleAnchors = [
          anchorInRange,
          new AnchorPoint({
            index: 0,
            position: anchors[0].position + rangeDiff,
            panel: anchors[0].panel
          }),
          new AnchorPoint({
            index: anchorCount - 1,
            position: anchors[anchorCount - 1].position - rangeDiff,
            panel: anchors[anchorCount - 1].panel
          })
        ].filter((anchor) => !!anchor);
        anchorInRange = possibleAnchors.reduce((nearest, anchor) => {
          if (!nearest) return anchor;
          return Math.abs(nearest.position - position) < Math.abs(anchor.position - position) ? nearest : anchor;
        }, null);
      }
      if (!anchorInRange) return null;
      if (position < range2.min) {
        const loopCount = -Math.floor((range2.min - position) / rangeDiff) - 1;
        return new AnchorPoint({
          index: anchorInRange.index,
          position: anchorInRange.position + rangeDiff * loopCount,
          panel: anchorInRange.panel
        });
      } else if (position > range2.max) {
        const loopCount = Math.floor((position - range2.max) / rangeDiff) + 1;
        return new AnchorPoint({
          index: anchorInRange.index,
          position: anchorInRange.position + rangeDiff * loopCount,
          panel: anchorInRange.panel
        });
      }
      return anchorInRange;
    }
    getCircularOffset() {
      const flicking = this._flicking;
      const camera = flicking.camera;
      if (!camera.circularEnabled) return 0;
      const toggled = flicking.panels.filter((panel) => panel.toggled);
      const toggledPrev = toggled.filter((panel) => panel.toggleDirection === DIRECTION.PREV);
      const toggledNext = toggled.filter((panel) => panel.toggleDirection === DIRECTION.NEXT);
      return this._calcPanelAreaSum(toggledPrev) - this._calcPanelAreaSum(toggledNext);
    }
    clampToReachablePosition(position) {
      return position;
    }
    canReach(panel) {
      if (panel.removed) return false;
      return true;
    }
    canSee(panel) {
      const camera = this._flicking.camera;
      const range2 = camera.range;
      const rangeDiff = camera.rangeDiff;
      const visibleRange = camera.visibleRange;
      const visibleInCurrentRange = super.canSee(panel);
      if (visibleRange.min < range2.min) {
        return visibleInCurrentRange || panel.isVisibleOnRange(visibleRange.min + rangeDiff, visibleRange.max + rangeDiff);
      } else if (visibleRange.max > range2.max) {
        return visibleInCurrentRange || panel.isVisibleOnRange(visibleRange.min - rangeDiff, visibleRange.max - rangeDiff);
      }
      return visibleInCurrentRange;
    }
    /**
     * @internal
     */
    _calcPanelAreaSum(panels) {
      return panels.reduce((sum, panel) => sum + panel.sizeIncludingMargin, 0);
    }
  }
  class LinearCameraMode extends CameraMode {
    checkAvailability() {
      return true;
    }
    getRange() {
      var _a, _b;
      const renderer = this._flicking.renderer;
      const firstPanel = renderer.getPanel(0);
      const lastPanel = renderer.getPanel(renderer.panelCount - 1);
      return { min: (_a = firstPanel == null ? void 0 : firstPanel.position) != null ? _a : 0, max: (_b = lastPanel == null ? void 0 : lastPanel.position) != null ? _b : 0 };
    }
  }
  let Camera$1 = class Camera {
    /**
     * Creates a new Camera instance
     * @param flicking - An instance of {@link Flicking}
     * @param options - Options for the Camera
     */
    constructor(flicking, { align = ALIGN.CENTER } = {}) {
      this._lookedOffset = 0;
      this._checkTranslateSupport = () => {
        const transforms = ["webkitTransform", "msTransform", "MozTransform", "OTransform", "transform"];
        const supportedStyle = document.documentElement.style;
        let transformName = "";
        for (const prefixedTransform of transforms) {
          if (prefixedTransform in supportedStyle) {
            transformName = prefixedTransform;
          }
        }
        if (!transformName) {
          throw new FlickingError(MESSAGE.TRANSFORM_NOT_SUPPORTED, CODE.TRANSFORM_NOT_SUPPORTED);
        }
        this._transform = transformName;
      };
      this._flicking = flicking;
      this._resetInternalValues();
      this._align = align;
    }
    // Internal states getter
    /**
     * The camera element(`.flicking-camera`)
     * @readonly
     */
    get element() {
      return this._el;
    }
    /**
     * An array of the child elements of the camera element(`.flicking-camera`)
     * @readonly
     */
    get children() {
      return toArray(this._el.children);
    }
    /**
     * Current position of the camera
     * @readonly
     */
    get position() {
      return this._position;
    }
    /**
     * Align position inside the viewport where {@link Panel}'s {@link Panel.alignPosition | alignPosition} should be located at
     * @readonly
     */
    get alignPosition() {
      return this._alignPos;
    }
    /**
     * Position offset, used for the {@link Flicking.renderOnlyVisible | renderOnlyVisible} option
     * @defaultValue 0
     * @readonly
     */
    get offset() {
      return this._offset - this._circularOffset;
    }
    /**
     * Whether the `circular` option is enabled.
     * @remarks
     * The {@link Flicking.circular | circular} option can't be enabled when sum of the panel sizes are too small.
     * @defaultValue false
     * @readonly
     */
    get circularEnabled() {
      return this._circularEnabled;
    }
    /**
     * A current camera mode
     * @readonly
     */
    get mode() {
      return this._mode;
    }
    /**
     * A range that Camera's {@link Camera.position | position} can reach
     * @readonly
     */
    get range() {
      return this._range;
    }
    /**
     * A difference between Camera's minimum and maximum position that can reach
     * @readonly
     */
    get rangeDiff() {
      return this._range.max - this._range.min;
    }
    /**
     * An array of visible panels from the current position
     * @readonly
     */
    get visiblePanels() {
      return this._visiblePanels;
    }
    /**
     * A range of the visible area from the current position
     * @readonly
     */
    get visibleRange() {
      return { min: this._position - this._alignPos, max: this._position - this._alignPos + this.size };
    }
    /**
     * An array of {@link AnchorPoint}s that Camera can be stopped at
     * @readonly
     */
    get anchorPoints() {
      return this._anchors;
    }
    /**
     * A current parameters of the Camera for updating {@link AxesController}
     * @readonly
     */
    get controlParams() {
      return { range: this._range, position: this._position, circular: this._circularEnabled };
    }
    /**
     * A Boolean value indicating whether Camera's over the minimum or maximum position reachable
     * @readonly
     */
    get atEdge() {
      return this._position <= this._range.min || this._position >= this._range.max;
    }
    /**
     * Return the size of the viewport
     * @readonly
     */
    get size() {
      const flicking = this._flicking;
      return flicking ? flicking.horizontal ? flicking.viewport.width : flicking.viewport.height : 0;
    }
    /**
     * Return the camera's position progress from the first panel to last panel
     * @remarks
     * Range is from 0 to last panel's index
     * @readonly
     */
    get progress() {
      const flicking = this._flicking;
      const position = this._position + this._offset;
      const nearestAnchor = this.findNearestAnchor(this._position);
      if (!flicking || !nearestAnchor) {
        return NaN;
      }
      const nearestPanel = nearestAnchor.panel;
      const panelPos = nearestPanel.position + nearestPanel.offset;
      const bounceSize = flicking.control.controller.bounce;
      const { min: prevRange, max: nextRange } = this.range;
      const rangeDiff = this.rangeDiff;
      if (position === panelPos) {
        return nearestPanel.index;
      }
      if (position < panelPos) {
        const prevPanel = nearestPanel.prev();
        let prevPosition = prevPanel ? prevPanel.position + prevPanel.offset : prevRange - bounceSize[0];
        if (prevPosition > panelPos) {
          prevPosition -= rangeDiff;
        }
        return nearestPanel.index - 1 + getProgress$1(position, prevPosition, panelPos);
      } else {
        const nextPanel = nearestPanel.next();
        let nextPosition = nextPanel ? nextPanel.position + nextPanel.offset : nextRange + bounceSize[1];
        if (nextPosition < panelPos) {
          nextPosition += rangeDiff;
        }
        return nearestPanel.index + getProgress$1(position, panelPos, nextPosition);
      }
    }
    /**
     * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/direction | direction} CSS property applied to the camera element(`.flicking-camera`)
     * @readonly
     */
    get panelOrder() {
      return this._panelOrder;
    }
    // Options Getter
    /**
     * A value indicating where the {@link Camera.alignPosition | alignPosition} should be located at inside the viewport element
     */
    get align() {
      return this._align;
    }
    // Options Setter
    set align(val) {
      this._align = val;
    }
    /**
     * Initialize Camera
     * @remarks
     * This method is called automatically during {@link Flicking.init}. It finds the camera element inside the viewport.
     * @throws {@link InitializationErrors}
     * @returns The current instance for method chaining
     */
    init() {
      const viewportEl = this._flicking.viewport.element;
      checkExistence(viewportEl.firstElementChild, "First element child of the viewport element");
      this._el = viewportEl.firstElementChild;
      this._checkTranslateSupport();
      this._updateMode();
      this.updatePanelOrder();
      return this;
    }
    /**
     * Destroy Camera and return to initial state
     * @remarks
     * This method resets all internal values to their initial state.
     * @returns The current instance for method chaining
     */
    destroy() {
      this._resetInternalValues();
      return this;
    }
    /**
     * Move to the given position and apply CSS transform
     * @remarks
     * This method updates the camera position, toggles panels for circular mode, and refreshes visible panels.
     * @param pos - A new position
     * @throws {@link InitializationErrors}
     */
    lookAt(pos) {
      const prevOffset = this._offset;
      const isChangedOffset = this._lookedOffset !== prevOffset;
      const flicking = getFlickingAttached(this._flicking);
      const prevPos = this._position;
      this._position = pos;
      const toggled = this._togglePanels(prevPos, pos);
      this._refreshVisiblePanels();
      this._checkNeedPanel();
      this._checkReachEnd(prevPos, pos);
      if (toggled) {
        void flicking.renderer.render().then(() => {
          this.updateOffset();
          this._lookedOffset = this._offset;
        });
      } else if (isChangedOffset) {
        this.updateOffset();
        this._lookedOffset = this._offset;
      } else {
        this.applyTransform();
      }
    }
    /**
     * Return a previous {@link AnchorPoint} of given {@link AnchorPoint}
     * @remarks
     * If it does not exist, return `null` instead
     * @param anchor - A reference {@link AnchorPoint}
     * @returns The previous {@link AnchorPoint}
     */
    getPrevAnchor(anchor) {
      if (!this._circularEnabled || anchor.index !== 0) {
        return this._anchors[anchor.index - 1] || null;
      } else {
        const anchors = this._anchors;
        const rangeDiff = this.rangeDiff;
        const lastAnchor = anchors[anchors.length - 1];
        return new AnchorPoint({
          index: lastAnchor.index,
          position: lastAnchor.position - rangeDiff,
          panel: lastAnchor.panel
        });
      }
    }
    /**
     * Return a next {@link AnchorPoint} of given {@link AnchorPoint}
     * @remarks
     * If it does not exist, return `null` instead
     * @param anchor - A reference {@link AnchorPoint}
     * @returns The next {@link AnchorPoint}
     */
    getNextAnchor(anchor) {
      const anchors = this._anchors;
      if (!this._circularEnabled || anchor.index !== anchors.length - 1) {
        return anchors[anchor.index + 1] || null;
      } else {
        const rangeDiff = this.rangeDiff;
        const firstAnchor = anchors[0];
        return new AnchorPoint({
          index: firstAnchor.index,
          position: firstAnchor.position + rangeDiff,
          panel: firstAnchor.panel
        });
      }
    }
    /**
     * Return the camera's position progress in the panel below
     * @remarks
     * Value is from 0 to 1 when the camera's inside panel.
     * Value can be lower than 0 or bigger than 1 when it's in the margin area
     * @param panel - A panel to check
     * @returns Progress value from 0 to 1 (or outside this range when in margin area)
     */
    getProgressInPanel(panel) {
      const panelRange = panel.range;
      return (this._position - panelRange.min) / (panelRange.max - panelRange.min);
    }
    /**
     * Return {@link AnchorPoint} that includes given position
     * @remarks
     * If there's no {@link AnchorPoint} that includes the given position, return `null` instead
     * @param position - A position to check
     * @returns The {@link AnchorPoint} that includes the given position
     */
    findAnchorIncludePosition(position) {
      return this._mode.findAnchorIncludePosition(position);
    }
    /**
     * Return {@link AnchorPoint} nearest to given position
     * @remarks
     * If there're no {@link AnchorPoint}s, return `null` instead
     * @param position - A position to check
     * @returns The {@link AnchorPoint} nearest to the given position
     */
    findNearestAnchor(position) {
      return this._mode.findNearestAnchor(position);
    }
    /**
     * Return {@link AnchorPoint} that matches {@link Flicking.currentPanel}
     * @returns The {@link AnchorPoint} that matches current panel
     */
    findActiveAnchor() {
      var _a;
      const flicking = getFlickingAttached(this._flicking);
      const activePanel = flicking.control.activePanel;
      if (!activePanel) return null;
      return (_a = find(this._anchors, (anchor) => anchor.panel.index === activePanel.index)) != null ? _a : this.findNearestAnchor(activePanel.position);
    }
    /**
     * Clamp the given position between camera's range
     * @param position - A position to clamp
     * @returns A clamped position
     */
    clampToReachablePosition(position) {
      return this._mode.clampToReachablePosition(position);
    }
    /**
     * Check whether the given panel is inside of the Camera's range
     * @param panel - An instance of {@link Panel} to check
     * @returns Whether the panel's inside Camera's range
     */
    canReach(panel) {
      return this._mode.canReach(panel);
    }
    /**
     * Check whether the given panel element is visible at the current position
     * @param panel - An instance of {@link Panel} to check
     * @returns Whether the panel element is visible at the current position
     */
    canSee(panel) {
      return this._mode.canSee(panel);
    }
    /**
     * Update {@link Camera.range | range} of Camera
     * @remarks
     * This method recalculates the camera range based on the current panel positions and circular mode settings.
     * @throws {@link InitializationErrors}
     * @returns The current instance for method chaining
     */
    updateRange() {
      const flicking = getFlickingAttached(this._flicking);
      const renderer = flicking.renderer;
      const panels = renderer.panels;
      this._updateMode();
      this._range = this._mode.getRange();
      panels.forEach((panel) => panel.updateCircularToggleDirection());
      return this;
    }
    /**
     * Update Camera's {@link Camera.alignPosition | alignPosition}
     * @returns The current instance for method chaining
     */
    updateAlignPos() {
      const align = this._align;
      const alignVal = typeof align === "object" ? align.camera : align;
      this._alignPos = parseAlign$1(alignVal, this.size);
      return this;
    }
    /**
     * Update Camera's {@link Camera.anchorPoints | anchorPoints}
     * @remarks
     * Anchor points are positions where the camera can stop. This method recalculates them based on the current mode.
     * @throws {@link InitializationErrors}
     * @returns The current instance for method chaining
     */
    updateAnchors() {
      this._anchors = this._mode.getAnchors();
      return this;
    }
    /**
     * Update Viewport's height to visible panel's max height
     * @remarks
     * This method only takes effect when {@link FlickingOptions.horizontal | horizontal} is `true` and {@link FlickingOptions.adaptive | adaptive} is enabled.
     * @throws {@link InitializationErrors}
     */
    updateAdaptiveHeight() {
      const flicking = getFlickingAttached(this._flicking);
      const activePanel = flicking.control.activePanel;
      const visiblePanels = flicking.visiblePanels;
      const selectedPanels = [...visiblePanels];
      if (activePanel) {
        selectedPanels.push(activePanel);
      }
      if (!flicking.horizontal || !flicking.adaptive || !selectedPanels.length) return;
      const maxHeight = Math.max(...selectedPanels.map((panel) => panel.height));
      flicking.viewport.setSize({
        height: maxHeight
      });
    }
    /**
     * Update current offset of the camera
     * @returns The current instance for method chaining
     */
    updateOffset() {
      const flicking = getFlickingAttached(this._flicking);
      const position = this._position;
      const unRenderedPanels = flicking.panels.filter((panel) => !panel.rendered);
      this._offset = unRenderedPanels.filter((panel) => panel.position + panel.offset < position).reduce((offset, panel) => offset + panel.sizeIncludingMargin, 0);
      this._circularOffset = this._mode.getCircularOffset();
      this.applyTransform();
      return this;
    }
    /**
     * Update direction to match the {@link https://developer.mozilla.org/en-US/docs/Web/CSS/direction | direction} CSS property applied to the camera element
     * @returns The current instance for method chaining
     */
    updatePanelOrder() {
      const flicking = getFlickingAttached(this._flicking);
      if (!flicking.horizontal) return this;
      const el = this._el;
      const direction = getStyle(el).direction;
      if (direction !== this._panelOrder) {
        this._panelOrder = direction === ORDER.RTL ? ORDER.RTL : ORDER.LTR;
        if (flicking.initialized) {
          flicking.control.controller.updateDirection();
        }
      }
      return this;
    }
    /**
     * Reset the history of {@link Flicking.event:needPanel | needPanel} events so it can be triggered again
     * @returns The current instance for method chaining
     */
    resetNeedPanelHistory() {
      this._needPanelTriggered = { prev: false, next: false };
      return this;
    }
    /**
     * Apply "transform" style with the current position to camera element
     * @returns The current instance for method chaining
     */
    applyTransform() {
      const el = this._el;
      const flicking = getFlickingAttached(this._flicking);
      const renderer = flicking.renderer;
      if (renderer.rendering || !flicking.initialized) return this;
      const actualPosition = this._position - this._alignPos - this._offset + this._circularOffset;
      const sign = !flicking.horizontal || this._panelOrder !== ORDER.RTL ? -1 : 1;
      const posText = flicking.usePercentagePos ? `${sign * actualPosition / this.size * 100}%` : `${sign * actualPosition}px`;
      el.style[this._transform] = flicking.horizontal ? `translate(${posText})` : `translate(0, ${posText})`;
      return this;
    }
    /**
     * @internal
     * @privateRemarks
     * Resets all internal state values to their defaults. Called during construction and destruction.
     */
    _resetInternalValues() {
      this._position = 0;
      this._lookedOffset = 0;
      this._alignPos = 0;
      this._offset = 0;
      this._circularOffset = 0;
      this._circularEnabled = false;
      this._range = { min: 0, max: 0 };
      this._visiblePanels = [];
      this._anchors = [];
      this._needPanelTriggered = { prev: false, next: false };
    }
    /**
     * @internal
     * @privateRemarks
     * Updates the list of visible panels and triggers {@link VisibleChangeEvent} if panels were added or removed.
     */
    _refreshVisiblePanels() {
      const flicking = getFlickingAttached(this._flicking);
      const panels = flicking.renderer.panels;
      const newVisiblePanels = panels.filter((panel) => this.canSee(panel));
      const prevVisiblePanels = this._visiblePanels;
      this._visiblePanels = newVisiblePanels;
      const added = newVisiblePanels.filter((panel) => !includes(prevVisiblePanels, panel));
      const removed = prevVisiblePanels.filter((panel) => !includes(newVisiblePanels, panel));
      if (added.length > 0 || removed.length > 0) {
        void flicking.renderer.render().then(() => {
          flicking.trigger(
            new Component.ComponentEvent(EVENTS.VISIBLE_CHANGE, {
              added,
              removed,
              visiblePanels: newVisiblePanels
            })
          );
        });
      }
    }
    /**
     * @internal
     * @privateRemarks
     * Checks if the camera is near the edges and triggers {@link NeedPanelEvent} for infinite scrolling implementations.
     */
    _checkNeedPanel() {
      const needPanelTriggered = this._needPanelTriggered;
      if (needPanelTriggered.prev && needPanelTriggered.next) return;
      const flicking = getFlickingAttached(this._flicking);
      const panels = flicking.renderer.panels;
      if (panels.length <= 0) {
        if (!needPanelTriggered.prev) {
          flicking.trigger(new Component.ComponentEvent(EVENTS.NEED_PANEL, { direction: DIRECTION.PREV }));
          needPanelTriggered.prev = true;
        }
        if (!needPanelTriggered.next) {
          flicking.trigger(new Component.ComponentEvent(EVENTS.NEED_PANEL, { direction: DIRECTION.NEXT }));
          needPanelTriggered.next = true;
        }
        return;
      }
      const cameraPosition = this._position;
      const cameraSize = this.size;
      const cameraRange = this._range;
      const needPanelThreshold = flicking.needPanelThreshold;
      const cameraPrev = cameraPosition - this._alignPos;
      const cameraNext = cameraPrev + cameraSize;
      const firstPanel = panels[0];
      const lastPanel = panels[panels.length - 1];
      if (!needPanelTriggered.prev) {
        const firstPanelPrev = firstPanel.range.min;
        if (cameraPrev <= firstPanelPrev + needPanelThreshold || cameraPosition <= cameraRange.min + needPanelThreshold) {
          flicking.trigger(new Component.ComponentEvent(EVENTS.NEED_PANEL, { direction: DIRECTION.PREV }));
          needPanelTriggered.prev = true;
        }
      }
      if (!needPanelTriggered.next) {
        const lastPanelNext = lastPanel.range.max;
        if (cameraNext >= lastPanelNext - needPanelThreshold || cameraPosition >= cameraRange.max - needPanelThreshold) {
          flicking.trigger(new Component.ComponentEvent(EVENTS.NEED_PANEL, { direction: DIRECTION.NEXT }));
          needPanelTriggered.next = true;
        }
      }
    }
    /**
     * @internal
     * @privateRemarks
     * Checks if the camera has reached the edge of the range and triggers {@link ReachEdgeEvent}.
     */
    _checkReachEnd(prevPos, newPos) {
      const flicking = getFlickingAttached(this._flicking);
      const range2 = this._range;
      const wasBetweenRange = prevPos > range2.min && prevPos < range2.max;
      const isBetweenRange = newPos > range2.min && newPos < range2.max;
      if (!wasBetweenRange || isBetweenRange) return;
      const direction = newPos <= range2.min ? DIRECTION.PREV : DIRECTION.NEXT;
      flicking.trigger(
        new Component.ComponentEvent(EVENTS.REACH_EDGE, {
          direction
        })
      );
    }
    /**
     * @internal
     * @privateRemarks
     * Updates the camera mode based on {@link FlickingOptions.circular | circular} and {@link FlickingOptions.bound | bound} options.
     */
    _updateMode() {
      const flicking = getFlickingAttached(this._flicking);
      if (flicking.circular) {
        const circularMode = new CircularCameraMode(flicking);
        const canSetCircularMode = circularMode.checkAvailability();
        if (canSetCircularMode) {
          this._mode = circularMode;
        } else {
          const fallbackMode = flicking.circularFallback;
          this._mode = fallbackMode === CIRCULAR_FALLBACK.BOUND ? new BoundCameraMode(flicking) : new LinearCameraMode(flicking);
        }
        this._circularEnabled = canSetCircularMode;
      } else {
        this._mode = flicking.bound ? new BoundCameraMode(flicking) : new LinearCameraMode(flicking);
        this._circularEnabled = false;
      }
    }
    /**
     * @internal
     * @privateRemarks
     * Toggles panel positions for circular mode. Returns true if any panel was toggled.
     */
    _togglePanels(prevPos, pos) {
      if (pos === prevPos) return false;
      const flicking = getFlickingAttached(this._flicking);
      const panels = flicking.renderer.panels;
      const toggled = panels.map((panel) => panel.toggle(prevPos, pos));
      return toggled.some((isToggled) => isToggled);
    }
  };
  const Camera = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
    __proto__: null,
    BoundCameraMode,
    Camera: Camera$1,
    CircularCameraMode,
    LinearCameraMode
  }, Symbol.toStringTag, { value: "Module" }));
  const EVENT = {
    HOLD: "hold",
    CHANGE: "change",
    RELEASE: "release",
    ANIMATION_END: "animationEnd",
    FINISH: "finish"
  };
  const POSITION_KEY = "flick";
  var STATE_TYPE = /* @__PURE__ */ ((STATE_TYPE2) => {
    STATE_TYPE2[STATE_TYPE2["IDLE"] = 0] = "IDLE";
    STATE_TYPE2[STATE_TYPE2["HOLDING"] = 1] = "HOLDING";
    STATE_TYPE2[STATE_TYPE2["DRAGGING"] = 2] = "DRAGGING";
    STATE_TYPE2[STATE_TYPE2["ANIMATING"] = 3] = "ANIMATING";
    STATE_TYPE2[STATE_TYPE2["DISABLED"] = 4] = "DISABLED";
    return STATE_TYPE2;
  })(STATE_TYPE || {});
  class State {
    constructor() {
      this._delta = 0;
      this._targetPanel = null;
    }
    /**
     * A sum of delta values of change events from the last hold event of Axes
     * @readonly
     */
    get delta() {
      return this._delta;
    }
    /**
     * A panel to set as {@link Control.activePanel} after the animation is finished
     * @readonly
     */
    get targetPanel() {
      return this._targetPanel;
    }
    set targetPanel(val) {
      this._targetPanel = val;
    }
    /**
     * An callback which is called when state has changed to this state
     * @param prevState - An previous state
     */
    onEnter(prevState) {
      this._delta = prevState._delta;
      this._targetPanel = prevState._targetPanel;
    }
    /**
     * An event handler for Axes's {@link https://naver.github.io/egjs-axes/docs/api/Axes#event-hold | hold} event
     * @param ctx - {@link StateContext}
     */
    onHold(ctx) {
    }
    /**
     * An event handler for Axes's {@link https://naver.github.io/egjs-axes/docs/api/Axes#event-change | change} event
     * @param ctx - {@link StateContext}
     */
    onChange(ctx) {
    }
    /**
     * An event handler for Axes's {@link https://naver.github.io/egjs-axes/docs/api/Axes#event-release | release} event
     * @param ctx - {@link StateContext}
     */
    onRelease(ctx) {
    }
    /**
     * An event handler for Axes's {@link https://naver.github.io/egjs-axes/docs/api/Axes#event-animationEnd | animationEnd} event
     * @param ctx - {@link StateContext}
     */
    onAnimationEnd(ctx) {
    }
    /**
     * An event handler for Axes's {@link https://naver.github.io/egjs-axes/docs/api/Axes#event-finish | finish} event
     * @param ctx - {@link StateContext}
     */
    onFinish(ctx) {
    }
    /**
     * @internal
     */
    _moveToChangedPosition(ctx) {
      const { flicking, axesEvent, transitTo } = ctx;
      const delta = axesEvent.delta[POSITION_KEY];
      if (!delta) {
        return;
      }
      this._delta += delta;
      const camera = flicking.camera;
      const prevPosition = camera.position;
      const position = axesEvent.pos[POSITION_KEY];
      const newPosition = flicking.circularEnabled ? circulatePosition(position, camera.range.min, camera.range.max) : position;
      camera.lookAt(newPosition);
      const moveEvent = new Component.ComponentEvent(EVENTS.MOVE, {
        isTrusted: axesEvent.isTrusted,
        holding: this.holding,
        direction: getDirection(0, axesEvent.delta[POSITION_KEY]),
        axesEvent
      });
      flicking.trigger(moveEvent);
      if (moveEvent.isCanceled()) {
        camera.lookAt(prevPosition);
        transitTo(
          4
          /* DISABLED */
        );
      }
    }
  }
  class AnimatingState extends State {
    constructor() {
      super(...arguments);
      this.holding = false;
      this.animating = true;
    }
    onHold(ctx) {
      const { flicking, axesEvent, transitTo } = ctx;
      const targetPanel = this._targetPanel;
      const control = flicking.control;
      this._delta = 0;
      flicking.control.updateInput();
      if (flicking.changeOnHold && targetPanel) {
        control.setActive(targetPanel, control.activePanel, axesEvent.isTrusted);
      }
      const holdStartEvent = new Component.ComponentEvent(EVENTS.HOLD_START, { axesEvent });
      flicking.trigger(holdStartEvent);
      if (holdStartEvent.isCanceled()) {
        transitTo(STATE_TYPE.DISABLED);
      } else {
        transitTo(STATE_TYPE.DRAGGING);
      }
    }
    onChange(ctx) {
      this._moveToChangedPosition(ctx);
    }
    onFinish(ctx) {
      const { flicking, axesEvent, transitTo } = ctx;
      const control = flicking.control;
      const controller = control.controller;
      const animatingContext = controller.animatingContext;
      transitTo(STATE_TYPE.IDLE);
      flicking.trigger(
        new Component.ComponentEvent(EVENTS.MOVE_END, {
          isTrusted: axesEvent.isTrusted,
          direction: getDirection(animatingContext.start, animatingContext.end),
          axesEvent
        })
      );
      const targetPanel = this._targetPanel;
      if (targetPanel) {
        control.setActive(targetPanel, control.activePanel, axesEvent.isTrusted);
      }
    }
  }
  class DisabledState extends State {
    constructor() {
      super(...arguments);
      this.holding = false;
      this.animating = true;
    }
    onAnimationEnd(ctx) {
      const { transitTo } = ctx;
      transitTo(STATE_TYPE.IDLE);
    }
    onChange(ctx) {
      const { axesEvent, transitTo } = ctx;
      axesEvent.stop();
      transitTo(STATE_TYPE.IDLE);
    }
    onRelease(ctx) {
      const { axesEvent, transitTo } = ctx;
      if (axesEvent.delta.flick === 0) {
        transitTo(STATE_TYPE.IDLE);
      }
    }
  }
  class DraggingState extends State {
    constructor() {
      super(...arguments);
      this.holding = true;
      this.animating = true;
    }
    onChange(ctx) {
      this._moveToChangedPosition(ctx);
    }
    onRelease(ctx) {
      const { flicking, axesEvent, transitTo } = ctx;
      flicking.trigger(
        new Component.ComponentEvent(EVENTS.HOLD_END, {
          axesEvent
        })
      );
      if (flicking.renderer.panelCount <= 0) {
        transitTo(STATE_TYPE.IDLE);
        return;
      }
      transitTo(STATE_TYPE.ANIMATING);
      const control = flicking.control;
      const position = axesEvent.destPos[POSITION_KEY];
      const duration = Math.max(axesEvent.duration, flicking.duration);
      try {
        void control.moveToPosition(position, duration, axesEvent);
      } catch (_err) {
        transitTo(STATE_TYPE.IDLE);
        axesEvent.setTo({ [POSITION_KEY]: flicking.camera.position }, 0);
      }
    }
  }
  class HoldingState extends State {
    constructor() {
      super(...arguments);
      this.holding = true;
      this.animating = false;
      this._releaseEvent = null;
    }
    onChange(ctx) {
      const { flicking, axesEvent, transitTo } = ctx;
      const inputEvent = axesEvent.inputEvent;
      if (!inputEvent) {
        return;
      }
      const offset = flicking.horizontal ? inputEvent.offsetX : inputEvent.offsetY;
      const moveStartEvent = new Component.ComponentEvent(EVENTS.MOVE_START, {
        isTrusted: axesEvent.isTrusted,
        holding: this.holding,
        direction: getDirection(0, -offset),
        axesEvent
      });
      flicking.trigger(moveStartEvent);
      if (moveStartEvent.isCanceled()) {
        transitTo(STATE_TYPE.DISABLED);
      } else {
        transitTo(STATE_TYPE.DRAGGING).onChange(ctx);
      }
    }
    onRelease(ctx) {
      const { flicking, axesEvent, transitTo } = ctx;
      flicking.trigger(new Component.ComponentEvent(EVENTS.HOLD_END, { axesEvent }));
      if (axesEvent.delta.flick !== 0) {
        axesEvent.setTo({ flick: flicking.camera.position }, 0);
        transitTo(STATE_TYPE.IDLE);
        return;
      }
      this._releaseEvent = axesEvent;
    }
    onFinish(ctx) {
      const { flicking, transitTo } = ctx;
      transitTo(STATE_TYPE.IDLE);
      if (!this._releaseEvent) {
        return;
      }
      const releaseEvent = this._releaseEvent;
      const srcEvent = releaseEvent.inputEvent.srcEvent;
      let clickedElement;
      if (srcEvent.type === "touchend") {
        const touchEvent = srcEvent;
        const touch = touchEvent.changedTouches[0];
        clickedElement = document.elementFromPoint(touch.clientX, touch.clientY);
      } else {
        clickedElement = srcEvent.target;
      }
      const panels = flicking.renderer.panels;
      let clickedPanel = null;
      for (const panel of panels) {
        if (panel.contains(clickedElement)) {
          clickedPanel = panel;
          break;
        }
      }
      if (clickedPanel) {
        const cameraPosition = flicking.camera.position;
        const clickedPanelPosition = clickedPanel.position;
        flicking.trigger(
          new Component.ComponentEvent(EVENTS.SELECT, {
            index: clickedPanel.index,
            panel: clickedPanel,
            // Direction to the clicked panel
            direction: getDirection(cameraPosition, clickedPanelPosition)
          })
        );
      }
    }
  }
  class IdleState extends State {
    constructor() {
      super(...arguments);
      this.holding = false;
      this.animating = false;
    }
    onEnter() {
      this._delta = 0;
      this._targetPanel = null;
    }
    onHold(ctx) {
      const { flicking, axesEvent, transitTo } = ctx;
      if (flicking.renderer.panelCount <= 0) {
        transitTo(STATE_TYPE.DISABLED);
        return;
      }
      const holdStartEvent = new Component.ComponentEvent(EVENTS.HOLD_START, {
        axesEvent
      });
      flicking.trigger(holdStartEvent);
      if (holdStartEvent.isCanceled()) {
        transitTo(STATE_TYPE.DISABLED);
      } else {
        transitTo(STATE_TYPE.HOLDING);
      }
    }
    // By methods call
    onChange(ctx) {
      const { flicking, axesEvent, transitTo } = ctx;
      const controller = flicking.control.controller;
      const animatingContext = controller.animatingContext;
      const moveStartEvent = new Component.ComponentEvent(EVENTS.MOVE_START, {
        isTrusted: axesEvent.isTrusted,
        holding: this.holding,
        direction: getDirection(animatingContext.start, animatingContext.end),
        axesEvent
      });
      flicking.trigger(moveStartEvent);
      if (moveStartEvent.isCanceled()) {
        transitTo(STATE_TYPE.DISABLED);
      } else {
        transitTo(STATE_TYPE.ANIMATING).onChange(ctx);
      }
    }
  }
  class StateMachine {
    constructor() {
      this.transitTo = (nextStateType) => {
        let nextState;
        switch (nextStateType) {
          case STATE_TYPE.IDLE:
            nextState = new IdleState();
            break;
          case STATE_TYPE.HOLDING:
            nextState = new HoldingState();
            break;
          case STATE_TYPE.DRAGGING:
            nextState = new DraggingState();
            break;
          case STATE_TYPE.ANIMATING:
            nextState = new AnimatingState();
            break;
          case STATE_TYPE.DISABLED:
            nextState = new DisabledState();
            break;
        }
        nextState.onEnter(this._state);
        this._state = nextState;
        return this._state;
      };
      this._state = new IdleState();
    }
    get state() {
      return this._state;
    }
    fire(eventType, externalCtx) {
      const currentState = this._state;
      const ctx = __spreadProps(__spreadValues({}, externalCtx), { transitTo: this.transitTo });
      switch (eventType) {
        case EVENT.HOLD:
          currentState.onHold(ctx);
          break;
        case EVENT.CHANGE:
          currentState.onChange(ctx);
          break;
        case EVENT.RELEASE:
          currentState.onRelease(ctx);
          break;
        case EVENT.ANIMATION_END:
          currentState.onAnimationEnd(ctx);
          break;
        case EVENT.FINISH:
          currentState.onFinish(ctx);
          break;
      }
    }
  }
  class AxesController {
    constructor() {
      this._onAxesHold = () => {
        this._dragged = false;
      };
      this._onAxesChange = () => {
        var _a;
        this._dragged = !!((_a = this._panInput) == null ? void 0 : _a.isEnabled());
      };
      this._preventClickWhenDragged = (e) => {
        if (this._dragged) {
          e.preventDefault();
          e.stopPropagation();
        }
        this._dragged = false;
      };
      this._resetInternalValues();
      this._stateMachine = new StateMachine();
    }
    /**
     * An {@link https://naver.github.io/egjs-axes/docs/api/Axes | Axes} instance
     * @see https://naver.github.io/egjs-axes/docs/api/Axes
     * @readonly
     */
    get axes() {
      return this._axes;
    }
    /**
     * An {@link https://naver.github.io/egjs-axes/docs/api/PanInput | PanInput} instance
     * @see https://naver.github.io/egjs-axes/docs/api/PanInput
     * @readonly
     */
    get panInput() {
      return this._panInput;
    }
    /**
     * @internal
     */
    get stateMachine() {
      return this._stateMachine;
    }
    /**
     * A activated {@link State} that shows the current status of the user input or the animation
     */
    get state() {
      return this._stateMachine.state;
    }
    /**
     * A context of the current animation playing
     * @readonly
     */
    get animatingContext() {
      return this._animatingContext;
    }
    /**
     * A current control parameters of the Axes instance
     */
    get controlParams() {
      const axes = this._axes;
      if (!axes) {
        return {
          range: { min: 0, max: 0 },
          position: 0,
          circular: false
        };
      }
      const axis = axes.axis[POSITION_KEY];
      return {
        range: { min: axis.range[0], max: axis.range[1] },
        circular: axis.circular[0],
        position: this.position
      };
    }
    /**
     * A Boolean indicating whether the user input is enabled
     * @readonly
     */
    get enabled() {
      var _a, _b;
      return (_b = (_a = this._panInput) == null ? void 0 : _a.isEnabled()) != null ? _b : false;
    }
    /**
     * Current position value in {@link https://naver.github.io/egjs-axes/docs/api/Axes | Axes} instance
     * @readonly
     */
    get position() {
      var _a, _b;
      return (_b = (_a = this._axes) == null ? void 0 : _a.get([POSITION_KEY])[POSITION_KEY]) != null ? _b : 0;
    }
    /**
     * Current range value in {@link https://naver.github.io/egjs-axes/docs/api/Axes | Axes} instance
     * @readonly
     */
    get range() {
      var _a, _b;
      return (_b = (_a = this._axes) == null ? void 0 : _a.axis[POSITION_KEY].range) != null ? _b : [0, 0];
    }
    /**
     * Actual bounce size(px)
     * @readonly
     */
    get bounce() {
      var _a;
      return (_a = this._axes) == null ? void 0 : _a.axis[POSITION_KEY].bounce;
    }
    /**
     * Initialize AxesController
     * @remarks
     * This method creates and configures the Axes and PanInput instances.
     * @param flicking - An instance of {@link Flicking}
     * @returns The current instance for method chaining
     */
    init(flicking) {
      this._flicking = flicking;
      this._axes = new Axes(
        {
          [POSITION_KEY]: {
            range: [0, 0],
            circular: false,
            bounce: [0, 0]
          }
        },
        {
          deceleration: flicking.deceleration,
          interruptable: flicking.interruptable,
          nested: flicking.nested,
          easing: flicking.easing
        }
      );
      this._panInput = new Axes.PanInput(flicking.viewport.element, {
        inputType: flicking.inputType,
        threshold: flicking.dragThreshold,
        iOSEdgeSwipeThreshold: flicking.iOSEdgeSwipeThreshold,
        preventDefaultOnDrag: flicking.preventDefaultOnDrag,
        scale: flicking.horizontal ? [flicking.camera.panelOrder === ORDER.RTL ? 1 : -1, 0] : [0, -1],
        releaseOnScroll: true
      });
      const axes = this._axes;
      axes.connect(flicking.horizontal ? [POSITION_KEY, ""] : ["", POSITION_KEY], this._panInput);
      for (const key in EVENT) {
        const eventType = EVENT[key];
        axes.on(eventType, (e) => {
          this._stateMachine.fire(eventType, {
            flicking,
            axesEvent: e
          });
        });
      }
      return this;
    }
    /**
     * Destroy AxesController and return to initial state
     * @remarks
     * This method destroys the Axes and PanInput instances and removes all event handlers.
     */
    destroy() {
      var _a;
      if (this._axes) {
        this.removePreventClickHandler();
        this._axes.destroy();
      }
      (_a = this._panInput) == null ? void 0 : _a.destroy();
      this._resetInternalValues();
    }
    /**
     * Enable input from the user (mouse/touch)
     * @remarks
     * Enables the PanInput to receive user interactions.
     * @returns The current instance for method chaining
     */
    enable() {
      var _a;
      (_a = this._panInput) == null ? void 0 : _a.enable();
      return this;
    }
    /**
     * Disable input from the user (mouse/touch)
     * @remarks
     * Disables the PanInput to ignore user interactions.
     * @returns The current instance for method chaining
     */
    disable() {
      var _a;
      (_a = this._panInput) == null ? void 0 : _a.disable();
      return this;
    }
    /**
     * Releases ongoing user input (mouse/touch)
     * @remarks
     * Immediately releases the PanInput, simulating the user lifting their finger.
     * @returns The current instance for method chaining
     */
    release() {
      var _a;
      (_a = this._panInput) == null ? void 0 : _a.release();
      return this;
    }
    /**
     * Change the destination and duration of the animation currently playing
     * @remarks
     * This method updates the Axes animation target position and optionally the duration.
     * @param position - A position to move
     * @param duration - Duration of the animation (unit: ms)
     * @returns The current instance for method chaining
     */
    updateAnimation(position, duration) {
      var _a;
      this._animatingContext = __spreadProps(__spreadValues({}, this._animatingContext), {
        end: position
      });
      (_a = this._axes) == null ? void 0 : _a.updateAnimation({
        destPos: { [POSITION_KEY]: position },
        duration
      });
      return this;
    }
    /**
     * Stops the animation currently playing
     * @remarks
     * This method immediately stops the Axes animation at the current position.
     * @returns The current instance for method chaining
     */
    stopAnimation() {
      var _a;
      (_a = this._axes) == null ? void 0 : _a.stopAnimation();
      return this;
    }
    /**
     * Update {@link https://naver.github.io/egjs-axes/ | @egjs/axes}'s state
     * @remarks
     * This method synchronizes the Axes state with the given control parameters.
     * @param controlParams - Control parameters
     * @throws {@link InitializationErrors}
     * @returns The current instance for method chaining
     */
    update(controlParams) {
      const flicking = getFlickingAttached(this._flicking);
      const camera = flicking.camera;
      const axes = this._axes;
      const axis = axes.axis[POSITION_KEY];
      axis.circular = [controlParams.circular, controlParams.circular];
      axis.range = [controlParams.range.min, controlParams.range.max];
      axis.bounce = parseBounce(flicking.bounce, camera.size);
      axes.axisManager.set({ [POSITION_KEY]: controlParams.position });
      return this;
    }
    /**
     * Attach a handler to the camera element to prevent click events during animation
     * @remarks
     * This is used when {@link FlickingOptions.preventClickOnDrag | preventClickOnDrag} is enabled.
     * @returns The current instance for method chaining
     */
    addPreventClickHandler() {
      const flicking = getFlickingAttached(this._flicking);
      const axes = this._axes;
      const cameraEl = flicking.camera.element;
      axes.on(EVENT.HOLD, this._onAxesHold);
      axes.on(EVENT.CHANGE, this._onAxesChange);
      cameraEl.addEventListener("click", this._preventClickWhenDragged, true);
      return this;
    }
    /**
     * Detach a handler to the camera element to prevent click events during animation
     * @remarks
     * This is used when {@link FlickingOptions.preventClickOnDrag | preventClickOnDrag} is disabled.
     * @returns The current instance for method chaining
     */
    removePreventClickHandler() {
      const flicking = getFlickingAttached(this._flicking);
      const axes = this._axes;
      const cameraEl = flicking.camera.element;
      axes.off(EVENT.HOLD, this._onAxesHold);
      axes.off(EVENT.CHANGE, this._onAxesChange);
      cameraEl.removeEventListener("click", this._preventClickWhenDragged, true);
      return this;
    }
    /**
     * Run Axes's {@link https://naver.github.io/egjs-axes/docs/api/Axes#setTo | setTo} using the given position
     * @remarks
     * If the target position equals the current position, the promise resolves immediately without animation.
     * @param position - A position to move
     * @param duration - Duration of the animation (unit: ms)
     * @param axesEvent - If provided, it'll use its {@link https://naver.github.io/egjs-axes/docs/api/Axes#setTo | setTo} method instead
     * @throws {@link MovementErrors}
     * @returns A Promise which will be resolved after reaching the target position
     */
    animateTo(position, duration, axesEvent) {
      var _a;
      const axes = this._axes;
      const state = this._stateMachine.state;
      if (!axes) {
        return Promise.reject(
          new FlickingError(MESSAGE.NOT_ATTACHED_TO_FLICKING, CODE.NOT_ATTACHED_TO_FLICKING)
        );
      }
      const startPos = this.getCurrentPosition();
      if (startPos === position) {
        const flicking = getFlickingAttached(this._flicking);
        flicking.camera.lookAt(position);
        if (state.targetPanel) {
          flicking.control.setActive(state.targetPanel, flicking.control.activePanel, (_a = axesEvent == null ? void 0 : axesEvent.isTrusted) != null ? _a : false);
        }
        return Promise.resolve();
      }
      this._animatingContext = {
        start: startPos,
        end: position,
        offset: 0
      };
      const animate = () => {
        const resetContext = () => {
          this._animatingContext = { start: 0, end: 0, offset: 0 };
        };
        axes.once(EVENT.FINISH, resetContext);
        if (axesEvent) {
          axesEvent.setTo({ [POSITION_KEY]: position }, duration);
        } else {
          axes.setTo({ [POSITION_KEY]: position }, duration);
        }
      };
      return new Promise((resolve, reject) => {
        const animationFinishHandler = () => {
          axes.off(EVENT.HOLD, interruptionHandler);
          resolve();
        };
        const interruptionHandler = () => {
          axes.off(EVENT.FINISH, animationFinishHandler);
          reject(new FlickingError(MESSAGE.ANIMATION_INTERRUPTED, CODE.ANIMATION_INTERRUPTED));
        };
        axes.once(EVENT.FINISH, animationFinishHandler);
        axes.once(EVENT.HOLD, interruptionHandler);
        animate();
      });
    }
    /**
     * Returns the current axes position
     */
    getCurrentPosition() {
      var _a, _b;
      return (_b = (_a = this._axes) == null ? void 0 : _a.get([POSITION_KEY])[POSITION_KEY]) != null ? _b : 0;
    }
    updateDirection() {
      const flicking = getFlickingAttached(this._flicking);
      const axes = this._axes;
      const panInput = this._panInput;
      axes.disconnect(panInput);
      axes.connect(flicking.horizontal ? [POSITION_KEY, ""] : ["", POSITION_KEY], panInput);
      panInput.options.scale = flicking.horizontal ? [flicking.camera.panelOrder === ORDER.RTL ? 1 : -1, 0] : [0, -1];
    }
    /**
     * @internal
     * @privateRemarks
     * Resets all internal values to their defaults. Called during construction and destruction.
     */
    _resetInternalValues() {
      this._flicking = null;
      this._axes = null;
      this._panInput = null;
      this._animatingContext = { start: 0, end: 0, offset: 0 };
      this._dragged = false;
    }
  }
  let Control$1 = class Control {
    /**
     * A controller that handles the {@link https://naver.github.io/egjs-axes/ | @egjs/axes} events
     * @readonly
     */
    get controller() {
      return this._controller;
    }
    /**
     * Index number of the {@link Flicking.currentPanel | currentPanel}
     * @defaultValue 0
     * @readonly
     */
    get activeIndex() {
      var _a, _b;
      return (_b = (_a = this._activePanel) == null ? void 0 : _a.index) != null ? _b : -1;
    }
    /**
     * An active panel
     * @readonly
     */
    get activePanel() {
      return this._activePanel;
    }
    /**
     * Whether Flicking's animating
     * @readonly
     */
    get animating() {
      return this._controller.state.animating;
    }
    /**
     * Whether user is clicking or touching
     * @readonly
     */
    get holding() {
      return this._controller.state.holding;
    }
    constructor() {
      this._flicking = null;
      this._controller = new AxesController();
      this._activePanel = null;
    }
    /**
     * Initialize Control
     * @remarks
     * This method is called automatically during {@link Flicking.init}. It initializes the internal controller.
     * @param flicking - An instance of {@link Flicking}
     * @returns The current instance for method chaining
     */
    init(flicking) {
      this._flicking = flicking;
      this._controller.init(flicking);
      return this;
    }
    /**
     * Destroy Control and return to initial state
     * @remarks
     * This method destroys the internal controller and resets all internal values.
     */
    destroy() {
      this._controller.destroy();
      this._flicking = null;
      this._activePanel = null;
    }
    /**
     * Enable input from the user (mouse/touch)
     * @remarks
     * This is a shorthand for `Flicking.enableInput`.
     * @returns The current instance for method chaining
     */
    enable() {
      this._controller.enable();
      return this;
    }
    /**
     * Disable input from the user (mouse/touch)
     * @remarks
     * This is a shorthand for `Flicking.disableInput`.
     * @returns The current instance for method chaining
     */
    disable() {
      this._controller.disable();
      return this;
    }
    /**
     * Releases ongoing user input (mouse/touch)
     * @remarks
     * This method immediately releases the user's input, similar to the user lifting their finger.
     * @returns The current instance for method chaining
     */
    release() {
      this._controller.release();
      return this;
    }
    /**
     * Change the destination and duration of the animation currently playing
     * @remarks
     * This method does nothing if no animation is currently playing.
     * @param panel - The target panel to move
     * @param duration - Duration of the animation (unit: ms)
     * @param direction - Direction to move, only available in the {@link Flicking.circular | circular} mode
     * @throws {@link AnimationUpdateErrors}
     * @returns The current instance for method chaining
     */
    updateAnimation(panel, duration, direction) {
      const state = this._controller.state;
      const position = this._getPosition(panel, direction != null ? direction : DIRECTION.NONE);
      state.targetPanel = panel;
      this._controller.updateAnimation(position, duration);
      return this;
    }
    /**
     * Stops the animation currently playing
     * @remarks
     * This method does nothing if no animation is currently playing.
     * @returns The current instance for method chaining
     */
    stopAnimation() {
      const state = this._controller.state;
      state.targetPanel = null;
      this._controller.stopAnimation();
      return this;
    }
    /**
     * Update position after resizing
     * @remarks
     * This method moves the camera to the active panel's position after a resize operation.
     * @param progressInPanel - Previous camera's progress in active panel before resize
     * @throws {@link InitializationErrors}
     */
    updatePosition(progressInPanel) {
      const flicking = getFlickingAttached(this._flicking);
      const camera = flicking.camera;
      const activePanel = this._activePanel;
      if (activePanel) {
        camera.lookAt(camera.clampToReachablePosition(activePanel.position));
      }
    }
    /**
     * Update {@link Control.controller | controller}'s state
     * @remarks
     * This method synchronizes the controller state with the current camera parameters.
     * @returns The current instance for method chaining
     */
    updateInput() {
      const flicking = getFlickingAttached(this._flicking);
      const camera = flicking.camera;
      this._controller.update(camera.controlParams);
      return this;
    }
    /**
     * Reset {@link Control.activePanel | activePanel} to `null`
     * @remarks
     * This method is called when the active panel is removed from the renderer.
     * @returns The current instance for method chaining
     */
    resetActive() {
      this._activePanel = null;
      return this;
    }
    /**
     * Move {@link Camera} to the given panel
     * @param panel - The target panel to move
     * @param options - {@link MoveToPanelParams}
     * @fires {@link MovementEvents}
     * @throws {@link MovementErrors}
     * @returns A Promise which will be resolved after reaching the target panel
     */
    moveToPanel(_0, _1) {
      return __async(this, arguments, function* (panel, { duration, direction = DIRECTION.NONE, axesEvent }) {
        const position = this._getPosition(panel, direction);
        this._triggerIndexChangeEvent(panel, panel.position, axesEvent, direction);
        return this._animateToPosition({ position, duration, newActivePanel: panel, axesEvent });
      });
    }
    /**
     * @internal
     * @privateRemarks
     * Sets the active panel and triggers {@link ChangedEvent} or {@link RestoredEvent} based on whether the panel changed.
     */
    setActive(newActivePanel, prevActivePanel, isTrusted) {
      var _a;
      const flicking = getFlickingAttached(this._flicking);
      this._activePanel = newActivePanel;
      this._nextPanel = null;
      flicking.camera.updateAdaptiveHeight();
      if (newActivePanel !== prevActivePanel) {
        flicking.trigger(
          new Component.ComponentEvent(EVENTS.CHANGED, {
            index: newActivePanel.index,
            panel: newActivePanel,
            prevIndex: (_a = prevActivePanel == null ? void 0 : prevActivePanel.index) != null ? _a : -1,
            prevPanel: prevActivePanel,
            isTrusted,
            direction: prevActivePanel ? getDirection(prevActivePanel.position, newActivePanel.position) : DIRECTION.NONE
          })
        );
      } else {
        flicking.trigger(
          new Component.ComponentEvent(EVENTS.RESTORED, {
            isTrusted
          })
        );
      }
    }
    /**
     * @internal
     * @privateRemarks
     * Copies internal state from another Control instance. Used when changing moveType option.
     */
    copy(control) {
      this._flicking = control._flicking;
      this._activePanel = control._activePanel;
      this._controller = control._controller;
    }
    /**
     * @internal
     * @privateRemarks
     * Triggers {@link WillChangeEvent} or {@link WillRestoreEvent} based on whether the target panel differs from the active panel.
     */
    _triggerIndexChangeEvent(panel, position, axesEvent, direction) {
      var _a;
      const flicking = getFlickingAttached(this._flicking);
      const triggeringEvent = panel !== this._activePanel ? EVENTS.WILL_CHANGE : EVENTS.WILL_RESTORE;
      const camera = flicking.camera;
      const activePanel = this._activePanel;
      const event = new Component.ComponentEvent(triggeringEvent, {
        index: panel.index,
        panel,
        isTrusted: (axesEvent == null ? void 0 : axesEvent.isTrusted) || false,
        direction: direction != null ? direction : getDirection((_a = activePanel == null ? void 0 : activePanel.position) != null ? _a : camera.position, position)
      });
      this._nextPanel = panel;
      flicking.trigger(event);
      if (event.isCanceled()) {
        throw new FlickingError(MESSAGE.STOP_CALLED_BY_USER, CODE.STOP_CALLED_BY_USER);
      }
    }
    /**
     * @internal
     * @privateRemarks
     * Animates the camera to the target position and handles animation completion or interruption.
     */
    _animateToPosition(_0) {
      return __async(this, arguments, function* ({
        position,
        duration,
        newActivePanel,
        axesEvent
      }) {
        const flicking = getFlickingAttached(this._flicking);
        let nextDuration = duration;
        if (Math.abs(nextDuration - position) < flicking.animationThreshold) {
          nextDuration = 0;
        }
        const animate = () => this._controller.animateTo(position, nextDuration, axesEvent);
        const state = this._controller.state;
        state.targetPanel = newActivePanel;
        if (nextDuration <= 0) {
          return animate();
        } else {
          return animate().then(() => __async(this, null, function* () {
            if (flicking.initialized) {
              yield flicking.renderer.render();
            }
          })).catch((err) => {
            if (axesEvent && err instanceof FlickingError && err.code === CODE.ANIMATION_INTERRUPTED) return;
            throw err;
          });
        }
      });
    }
    /**
     * @internal
     * @privateRemarks
     * Calculates the target position for a panel, considering circular mode and direction constraints.
     */
    _getPosition(panel, direction = DIRECTION.NONE) {
      const flicking = getFlickingAttached(this._flicking);
      const camera = flicking.camera;
      let position = panel.position;
      const nearestAnchor = camera.findNearestAnchor(position);
      if (panel.removed || !nearestAnchor) {
        throw new FlickingError(MESSAGE.POSITION_NOT_REACHABLE(panel.position), CODE.POSITION_NOT_REACHABLE);
      }
      if (!camera.canReach(panel)) {
        position = nearestAnchor.position;
        panel = nearestAnchor.panel;
      } else if (flicking.circularEnabled) {
        const camPos = this._controller.position;
        const camRangeDiff = camera.rangeDiff;
        const possiblePositions = [position, position + camRangeDiff, position - camRangeDiff].filter((pos) => {
          if (direction === DIRECTION.NONE) return true;
          return direction === DIRECTION.PREV ? pos <= camPos : pos >= camPos;
        });
        position = possiblePositions.reduce((nearestPosition, pos) => {
          if (Math.abs(camPos - pos) < Math.abs(camPos - nearestPosition)) {
            return pos;
          } else {
            return nearestPosition;
          }
        }, Infinity);
      }
      return position;
    }
  };
  class FreeControl extends Control$1 {
    /**
     * Make scroll animation to stop at the start/end of the scroll area, not going out the bounce area
     * @defaultValue true
     */
    get stopAtEdge() {
      return this._stopAtEdge;
    }
    set stopAtEdge(val) {
      this._stopAtEdge = val;
    }
    constructor(options = {}) {
      super();
      const { stopAtEdge = true } = options;
      this._stopAtEdge = stopAtEdge;
    }
    /**
     * Update position after resizing
     * @remarks
     * Unlike the base Control, FreeControl preserves the progress within the panel instead of snapping to the panel position.
     * @param progressInPanel - Previous camera's progress in active panel before resize
     * @throws {@link InitializationErrors}
     */
    updatePosition(progressInPanel) {
      const flicking = getFlickingAttached(this._flicking);
      const camera = flicking.camera;
      const activePanel = this._activePanel;
      if (activePanel) {
        const panelRange = activePanel.range;
        const newPosition = panelRange.min + (panelRange.max - panelRange.min) * progressInPanel;
        camera.lookAt(camera.clampToReachablePosition(newPosition));
      }
    }
    /**
     * Move {@link Camera} to the given position
     * @remarks
     * Unlike SnapControl, FreeControl moves to the exact position without snapping to panel boundaries.
     * @param position - The target position to move
     * @param duration - Duration of the panel movement animation (unit: ms)
     * @param axesEvent - {@link https://naver.github.io/egjs-axes/docs/api/Axes#event-release | release} event of {@link https://naver.github.io/egjs-axes/ | Axes}
     * @fires {@link MovementEvents}
     * @throws {@link MovementErrors}
     * @returns A Promise which will be resolved after reaching the target position
     */
    moveToPosition(position, duration, axesEvent) {
      const flicking = getFlickingAttached(this._flicking);
      const camera = flicking.camera;
      const targetPos = camera.clampToReachablePosition(position);
      const anchorAtPosition = camera.findAnchorIncludePosition(targetPos);
      if (!anchorAtPosition) {
        return Promise.reject(
          new FlickingError(MESSAGE.POSITION_NOT_REACHABLE(position), CODE.POSITION_NOT_REACHABLE)
        );
      }
      const targetPanel = anchorAtPosition.panel;
      if (targetPanel !== this._activePanel) {
        this._triggerIndexChangeEvent(targetPanel, position, axesEvent);
      }
      return this._animateToPosition({
        position: this._stopAtEdge ? targetPos : position,
        duration,
        newActivePanel: targetPanel,
        axesEvent
      });
    }
  }
  class SnapControl extends Control$1 {
    /**
     * Maximum number of panels can go after release
     * @defaultValue Infinity
     */
    get count() {
      return this._count;
    }
    set count(val) {
      this._count = val;
    }
    constructor(options = {}) {
      super();
      const { count = Infinity } = options;
      this._count = count;
    }
    /**
     * Move {@link Camera} to the given position
     * @remarks
     * This method calculates the snap target based on the release momentum and threshold settings.
     * @param position - The target position to move
     * @param duration - Duration of the panel movement animation (unit: ms)
     * @param axesEvent - {@link https://naver.github.io/egjs-axes/docs/api/Axes#event-release | release} event of {@link https://naver.github.io/egjs-axes/ | Axes}
     * @fires {@link MovementEvents}
     * @throws {@link MovementErrors}
     * @returns A Promise which will be resolved after reaching the target position
     */
    moveToPosition(position, duration, axesEvent) {
      const flicking = getFlickingAttached(this._flicking);
      const camera = flicking.camera;
      const activeAnchor = camera.findActiveAnchor();
      const anchorAtCamera = camera.findNearestAnchor(camera.position);
      const state = this._controller.state;
      if (!activeAnchor || !anchorAtCamera) {
        return Promise.reject(
          new FlickingError(MESSAGE.POSITION_NOT_REACHABLE(position), CODE.POSITION_NOT_REACHABLE)
        );
      }
      const snapThreshold = this._calcSnapThreshold(flicking.threshold, position, activeAnchor);
      const posDelta = flicking.animating ? state.delta : position - camera.position;
      const absPosDelta = Math.abs(posDelta);
      const snapDelta = axesEvent && axesEvent.delta[POSITION_KEY] !== 0 ? Math.abs(axesEvent.delta[POSITION_KEY]) : absPosDelta;
      let targetAnchor;
      if (snapDelta >= snapThreshold && snapDelta > 0) {
        targetAnchor = this._findSnappedAnchor(position, anchorAtCamera);
      } else if (absPosDelta >= flicking.threshold && absPosDelta > 0) {
        targetAnchor = this._findAdjacentAnchor(position, posDelta, anchorAtCamera);
      } else {
        return this.moveToPanel(anchorAtCamera.panel, {
          duration,
          axesEvent
        });
      }
      this._triggerIndexChangeEvent(targetAnchor.panel, position, axesEvent);
      return this._animateToPosition({
        position: camera.clampToReachablePosition(targetAnchor.position),
        duration,
        newActivePanel: targetAnchor.panel,
        axesEvent
      });
    }
    /**
     * @internal
     * @privateRemarks
     * Finds the anchor point to snap to based on the target position and count option.
     */
    _findSnappedAnchor(position, anchorAtCamera) {
      const flicking = getFlickingAttached(this._flicking);
      const camera = flicking.camera;
      const count = this._count;
      const currentPos = camera.position;
      const clampedPosition = camera.clampToReachablePosition(position);
      const anchorAtPosition = camera.findAnchorIncludePosition(clampedPosition);
      if (!anchorAtCamera || !anchorAtPosition) {
        throw new FlickingError(MESSAGE.POSITION_NOT_REACHABLE(position), CODE.POSITION_NOT_REACHABLE);
      }
      if (!Number.isFinite(count)) {
        return anchorAtPosition;
      }
      const panelCount = flicking.panelCount;
      const anchors = camera.anchorPoints;
      let loopCount = Math.sign(position - currentPos) * Math.floor(Math.abs(position - currentPos) / camera.rangeDiff);
      if (position > currentPos && anchorAtPosition.index < anchorAtCamera.index || anchorAtPosition.position > anchorAtCamera.position && anchorAtPosition.index === anchorAtCamera.index) {
        loopCount += 1;
      } else if (position < currentPos && anchorAtPosition.index > anchorAtCamera.index || anchorAtPosition.position < anchorAtCamera.position && anchorAtPosition.index === anchorAtCamera.index) {
        loopCount -= 1;
      }
      const circularIndexOffset = loopCount * panelCount;
      const anchorAtPositionIndex = anchorAtPosition.index + circularIndexOffset;
      if (Math.abs(anchorAtPositionIndex - anchorAtCamera.index) <= count) {
        const anchor = anchors[anchorAtPosition.index];
        return new AnchorPoint({
          index: anchor.index,
          position: anchor.position + loopCount * camera.rangeDiff,
          panel: anchor.panel
        });
      }
      if (flicking.circularEnabled) {
        const targetAnchor = anchors[circulateIndex(anchorAtCamera.index + Math.sign(position - currentPos) * count, panelCount)];
        let loop = Math.floor(count / panelCount);
        if (position > currentPos && targetAnchor.index < anchorAtCamera.index) {
          loop += 1;
        } else if (position < currentPos && targetAnchor.index > anchorAtCamera.index) {
          loop -= 1;
        }
        return new AnchorPoint({
          index: targetAnchor.index,
          position: targetAnchor.position + loop * camera.rangeDiff,
          panel: targetAnchor.panel
        });
      } else {
        return anchors[clamp(anchorAtCamera.index + Math.sign(position - currentPos) * count, 0, anchors.length - 1)];
      }
    }
    /**
     * @internal
     * @privateRemarks
     * Finds the adjacent anchor point based on the movement direction.
     */
    _findAdjacentAnchor(position, posDelta, anchorAtCamera) {
      var _a;
      const flicking = getFlickingAttached(this._flicking);
      const camera = flicking.camera;
      if (camera.circularEnabled) {
        const anchorIncludePosition = camera.findAnchorIncludePosition(position);
        if (anchorIncludePosition && anchorIncludePosition.position !== anchorAtCamera.position) {
          return anchorIncludePosition;
        }
      }
      const adjacentAnchor = (_a = posDelta > 0 ? camera.getNextAnchor(anchorAtCamera) : camera.getPrevAnchor(anchorAtCamera)) != null ? _a : anchorAtCamera;
      return adjacentAnchor;
    }
    /**
     * @internal
     * @privateRemarks
     * Calculates the snap threshold based on the panel size and alignment.
     */
    _calcSnapThreshold(threshold, position, activeAnchor) {
      const isNextDirection = position > activeAnchor.position;
      const panel = activeAnchor.panel;
      const panelSize = panel.size;
      const alignPos = panel.alignPosition;
      return Math.max(
        threshold,
        isNextDirection ? panelSize - alignPos + panel.margin.next : alignPos + panel.margin.prev
      );
    }
  }
  class StrictControl extends Control$1 {
    constructor(options = {}) {
      super();
      this.setActive = (newActivePanel, prevActivePanel, isTrusted) => {
        super.setActive(newActivePanel, prevActivePanel, isTrusted);
        this.updateInput();
      };
      const { count = 1 } = options;
      this._count = count;
      this._resetIndexRange();
    }
    /**
     * Maximum number of panels that can be moved at a time
     * @defaultValue 1
     */
    get count() {
      return this._count;
    }
    set count(val) {
      this._count = val;
    }
    /**
     * Destroy Control and return to initial state
     * @remarks
     * This method also resets the index range used for movement constraints.
     */
    destroy() {
      super.destroy();
      this._resetIndexRange();
    }
    /**
     * Update {@link Control.controller | controller}'s state
     * @remarks
     * StrictControl limits the movement range based on the {@link StrictControlOptions.count | count} option.
     * @returns The current instance for method chaining
     */
    updateInput() {
      var _a;
      const flicking = getFlickingAttached(this._flicking);
      const camera = flicking.camera;
      const renderer = flicking.renderer;
      const controller = this._controller;
      const controlParams = camera.controlParams;
      const count = this._count;
      const activePanel = controller.state.animating ? (_a = camera.findNearestAnchor(camera.position)) == null ? void 0 : _a.panel : this._activePanel;
      if (!activePanel) {
        controller.update(controlParams);
        this._resetIndexRange();
        return this;
      }
      const cameraRange = controlParams.range;
      const currentPos = activePanel.position;
      const currentIndex = activePanel.index;
      const panelCount = renderer.panelCount;
      let prevPanelIndex = currentIndex - count;
      let nextPanelIndex = currentIndex + count;
      if (prevPanelIndex < 0) {
        prevPanelIndex = flicking.circularEnabled ? getMinusCompensatedIndex((prevPanelIndex + 1) % panelCount - 1, panelCount) : clamp(prevPanelIndex, 0, panelCount - 1);
      }
      if (nextPanelIndex >= panelCount) {
        nextPanelIndex = flicking.circularEnabled ? nextPanelIndex % panelCount : clamp(nextPanelIndex, 0, panelCount - 1);
      }
      const prevPanel = renderer.panels[prevPanelIndex];
      const nextPanel = renderer.panels[nextPanelIndex];
      let prevPos = Math.max(prevPanel.position, cameraRange.min);
      let nextPos = Math.min(nextPanel.position, cameraRange.max);
      if (prevPos > currentPos) {
        prevPos -= camera.rangeDiff;
      }
      if (nextPos < currentPos) {
        nextPos += camera.rangeDiff;
      }
      controlParams.range = {
        min: prevPos,
        max: nextPos
      };
      if (controlParams.circular) {
        if (controlParams.position < prevPos) {
          controlParams.position += camera.rangeDiff;
        }
        if (controlParams.position > nextPos) {
          controlParams.position -= camera.rangeDiff;
        }
      }
      controlParams.circular = false;
      controller.update(controlParams);
      this._indexRange = {
        min: prevPanel.index,
        max: nextPanel.index
      };
      return this;
    }
    moveToPanel(panel, options) {
      return __async(this, null, function* () {
        const flicking = getFlickingAttached(this._flicking);
        const camera = flicking.camera;
        const controller = this._controller;
        controller.update(camera.controlParams);
        return __superGet(StrictControl.prototype, this, "moveToPanel").call(this, panel, options);
      });
    }
    /**
     * Move {@link Camera} to the given position
     * @remarks
     * StrictControl restricts movement to panels within the allowed index range based on the count option.
     * @param position - The target position to move
     * @param duration - Duration of the panel movement animation (unit: ms)
     * @param axesEvent - {@link https://naver.github.io/egjs-axes/docs/api/Axes#event-release | release} event of {@link https://naver.github.io/egjs-axes/ | Axes}
     * @fires {@link MovementEvents}
     * @throws {@link MovementErrors}
     * @returns A Promise which will be resolved after reaching the target position
     */
    moveToPosition(position, duration, axesEvent) {
      var _a;
      const flicking = getFlickingAttached(this._flicking);
      const camera = flicking.camera;
      const currentPanel = (_a = this._nextPanel) != null ? _a : this._activePanel;
      const axesRange = this._controller.range;
      const indexRange = this._indexRange;
      const cameraRange = camera.range;
      const state = this._controller.state;
      const clampedPosition = clamp(camera.clampToReachablePosition(position), axesRange[0], axesRange[1]);
      const anchorAtPosition = camera.findAnchorIncludePosition(clampedPosition);
      if (!anchorAtPosition || !currentPanel) {
        return Promise.reject(
          new FlickingError(MESSAGE.POSITION_NOT_REACHABLE(position), CODE.POSITION_NOT_REACHABLE)
        );
      }
      const prevPos = currentPanel.position;
      const posDelta = flicking.animating ? state.delta : position - camera.position;
      const isOverThreshold = Math.abs(posDelta) >= flicking.threshold;
      const adjacentAnchor = position > prevPos ? camera.getNextAnchor(anchorAtPosition) : camera.getPrevAnchor(anchorAtPosition);
      let targetPos;
      let targetPanel;
      const anchors = camera.anchorPoints;
      const firstAnchor = anchors[0];
      const lastAnchor = anchors[anchors.length - 1];
      const shouldBounceToFirst = position < cameraRange.min && isBetween(firstAnchor.panel.index, indexRange.min, indexRange.max);
      const shouldBounceToLast = position > cameraRange.max && isBetween(lastAnchor.panel.index, indexRange.min, indexRange.max);
      const isAdjacent = adjacentAnchor && (indexRange.min <= indexRange.max ? isBetween(adjacentAnchor.index, indexRange.min, indexRange.max) : adjacentAnchor.index >= indexRange.min || adjacentAnchor.index <= indexRange.max);
      if (shouldBounceToFirst || shouldBounceToLast) {
        const targetAnchor = position < cameraRange.min ? firstAnchor : lastAnchor;
        targetPanel = targetAnchor.panel;
        targetPos = targetAnchor.position;
      } else if (isOverThreshold && anchorAtPosition.position !== currentPanel.position) {
        targetPanel = anchorAtPosition.panel;
        targetPos = anchorAtPosition.position;
      } else if (isOverThreshold && isAdjacent) {
        targetPanel = adjacentAnchor.panel;
        targetPos = adjacentAnchor.position;
      } else {
        const anchorAtCamera = camera.findNearestAnchor(camera.position);
        if (!anchorAtCamera) {
          return Promise.reject(
            new FlickingError(MESSAGE.POSITION_NOT_REACHABLE(position), CODE.POSITION_NOT_REACHABLE)
          );
        }
        return this.moveToPanel(anchorAtCamera.panel, {
          duration,
          axesEvent
        });
      }
      this._triggerIndexChangeEvent(targetPanel, position, axesEvent);
      return this._animateToPosition({
        position: targetPos,
        duration,
        newActivePanel: targetPanel,
        axesEvent
      });
    }
    /**
     * @internal
     * @privateRemarks
     * Resets the index range to default values.
     */
    _resetIndexRange() {
      this._indexRange = { min: 0, max: 0 };
    }
  }
  const Control = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
    __proto__: null,
    AnimatingState,
    AxesController,
    Control: Control$1,
    DisabledState,
    DraggingState,
    FreeControl,
    HoldingState,
    IdleState,
    SnapControl,
    State,
    StateMachine,
    StrictControl
  }, Symbol.toStringTag, { value: "Module" }));
  class AutoResizer {
    /**
     * @param flicking
     */
    constructor(flicking) {
      this._onResizeWrapper = () => {
        this._onResize([]);
      };
      this._onResize = (entries) => {
        const flicking2 = this._flicking;
        const resizeDebounce = flicking2.resizeDebounce;
        const maxResizeDebounce = flicking2.maxResizeDebounce;
        const resizedViewportElement = flicking2.element;
        const isResizedViewportOnly = entries.find((e) => e.target === flicking2.element) && entries.length === 1;
        if (isResizedViewportOnly) {
          const beforeSize = {
            width: flicking2.viewport.width,
            height: flicking2.viewport.height
          };
          const afterSize = {
            width: getElementSize({
              el: resizedViewportElement,
              horizontal: true,
              useFractionalSize: this._flicking.useFractionalSize,
              useOffset: false,
              style: getStyle(resizedViewportElement)
            }),
            height: getElementSize({
              el: resizedViewportElement,
              horizontal: false,
              useFractionalSize: this._flicking.useFractionalSize,
              useOffset: false,
              style: getStyle(resizedViewportElement)
            })
          };
          if (beforeSize.height === afterSize.height && beforeSize.width === afterSize.width) {
            return;
          }
        }
        if (resizeDebounce <= 0) {
          void flicking2.resize();
        } else {
          if (this._maxResizeDebounceTimer <= 0) {
            if (maxResizeDebounce > 0 && maxResizeDebounce >= resizeDebounce) {
              this._maxResizeDebounceTimer = window.setTimeout(this._doScheduledResize, maxResizeDebounce);
            }
          }
          if (this._resizeTimer > 0) {
            clearTimeout(this._resizeTimer);
            this._resizeTimer = 0;
          }
          this._resizeTimer = window.setTimeout(this._doScheduledResize, resizeDebounce);
        }
      };
      this._doScheduledResize = () => {
        clearTimeout(this._resizeTimer);
        clearTimeout(this._maxResizeDebounceTimer);
        this._maxResizeDebounceTimer = -1;
        this._resizeTimer = -1;
        void this._flicking.resize();
      };
      this._skipFirstResize = /* @__PURE__ */ (() => {
        let isFirstResize = true;
        return (entries) => {
          if (isFirstResize) {
            isFirstResize = false;
            return;
          }
          this._onResize(entries);
        };
      })();
      this._flicking = flicking;
      this._enabled = false;
      this._resizeObserver = null;
      this._resizeTimer = -1;
      this._maxResizeDebounceTimer = -1;
    }
    get enabled() {
      return this._enabled;
    }
    enable() {
      const flicking = this._flicking;
      const viewport = flicking.viewport;
      if (this._enabled) {
        this.disable();
      }
      if (flicking.useResizeObserver && !!window.ResizeObserver) {
        const viewportSizeNot0 = viewport.width !== 0 || viewport.height !== 0;
        const resizeObserver = viewportSizeNot0 ? new ResizeObserver(this._skipFirstResize) : new ResizeObserver(this._onResize);
        this._resizeObserver = resizeObserver;
        this.observe(flicking.viewport.element);
        if (flicking.observePanelResize) {
          this.observePanels();
        }
      } else {
        window.addEventListener("resize", this._onResizeWrapper);
      }
      this._enabled = true;
      return this;
    }
    observePanels() {
      this._flicking.panels.forEach((panel) => {
        this.observe(panel.element);
      });
      return this;
    }
    unobservePanels() {
      this._flicking.panels.forEach((panel) => {
        this.unobserve(panel.element);
      });
      return this;
    }
    observe(element) {
      const resizeObserver = this._resizeObserver;
      if (!resizeObserver) return this;
      resizeObserver.observe(element);
      return this;
    }
    unobserve(element) {
      const resizeObserver = this._resizeObserver;
      if (!resizeObserver) return this;
      resizeObserver.unobserve(element);
      if (this._flicking.observePanelResize) {
        this.unobservePanels();
      }
      return this;
    }
    disable() {
      if (!this._enabled) return this;
      const resizeObserver = this._resizeObserver;
      if (resizeObserver) {
        resizeObserver.disconnect();
        this._resizeObserver = null;
      } else {
        window.removeEventListener("resize", this._onResizeWrapper);
      }
      this._enabled = false;
      return this;
    }
  }
  class VanillaElementProvider {
    get element() {
      return this._element;
    }
    get rendered() {
      return this._rendered;
    }
    constructor(element) {
      this._element = element;
      this._rendered = true;
    }
    show(flicking) {
      const el = this.element;
      const cameraEl = flicking.camera.element;
      if (el.parentElement !== cameraEl) {
        cameraEl.appendChild(el);
        this._rendered = true;
      }
    }
    hide(flicking) {
      const el = this.element;
      const cameraEl = flicking.camera.element;
      if (el.parentElement === cameraEl) {
        cameraEl.removeChild(el);
        this._rendered = false;
      }
    }
  }
  class VirtualElementProvider {
    get element() {
      return this._virtualElement.nativeElement;
    }
    get rendered() {
      return this._virtualElement.visible;
    }
    get _virtualElement() {
      const flicking = this._flicking;
      const elIndex = this._panel.elementIndex;
      const virtualElements = flicking.virtual.elements;
      return virtualElements[elIndex];
    }
    constructor(flicking) {
      this._flicking = flicking;
    }
    init(panel) {
      this._panel = panel;
    }
    show() {
    }
    hide() {
    }
  }
  class Viewport {
    /**
     * A viewport(root) element
     * @readonly
     */
    get element() {
      return this._el;
    }
    /**
     * Viewport width, without paddings
     * @readonly
     */
    get width() {
      return this._width - this._padding.left - this._padding.right;
    }
    /**
     * Viewport height, without paddings
     * @readonly
     */
    get height() {
      return this._height - this._padding.top - this._padding.bottom;
    }
    /**
     * Viewport paddings
     * @readonly
     */
    get padding() {
      return this._padding;
    }
    /**
     * @param flicking - Flicking instance
     * @param el - A viewport element
     */
    constructor(flicking, el) {
      this._flicking = flicking;
      this._el = el;
      this._width = 0;
      this._height = 0;
      this._padding = {
        left: 0,
        right: 0,
        top: 0,
        bottom: 0
      };
      this._isBorderBoxSizing = false;
    }
    /**
     * Change viewport's size.
     * @remarks
     * This will change the actual size of `.flicking-viewport` element by changing its CSS width/height property
     * @param size - {@link SetSizeParams}
     */
    setSize(size) {
      const { width, height } = size;
      const el = this._el;
      const padding = this._padding;
      const isBorderBoxSizing = this._isBorderBoxSizing;
      if (width != null) {
        if (isString(width)) {
          el.style.width = width;
        } else {
          const newWidth = isBorderBoxSizing ? width + padding.left + padding.right : width;
          el.style.width = `${newWidth}px`;
        }
      }
      if (height != null) {
        if (isString(height)) {
          el.style.height = height;
        } else {
          const newHeight = isBorderBoxSizing ? height + padding.top + padding.bottom : height;
          el.style.height = `${newHeight}px`;
        }
      }
      this.resize();
    }
    /**
     * Measure the current size of the viewport element without updating {@link Viewport.width | width}/{@link Viewport.height | height}
     * @internal
     * @privateRemarks
     * Used inside {@link Flicking.resize} to determine whether the panels should be re-rendered (optimizeSizeUpdate)
     * while keeping the previous viewport size until the re-rendering finishes.
     * The new size is committed afterwards by calling {@link Viewport.resize | resize()}.
     * @returns Current width/height of the viewport element, without paddings
     */
    measureSize() {
      const { width, height, padding } = this._measure();
      return {
        width: width - padding.left - padding.right,
        height: height - padding.top - padding.bottom
      };
    }
    /**
     * Update width/height to the current viewport element's size
     */
    resize() {
      const { width, height, padding, isBorderBoxSizing } = this._measure();
      this._width = width;
      this._height = height;
      this._padding = padding;
      this._isBorderBoxSizing = isBorderBoxSizing;
    }
    _measure() {
      const el = this._el;
      const elStyle = getStyle(el);
      const { useFractionalSize } = this._flicking;
      return {
        width: getElementSize({
          el,
          horizontal: true,
          useFractionalSize,
          useOffset: false,
          style: elStyle
        }),
        height: getElementSize({
          el,
          horizontal: false,
          useFractionalSize,
          useOffset: false,
          style: elStyle
        }),
        padding: {
          left: elStyle.paddingLeft ? parseFloat(elStyle.paddingLeft) : 0,
          right: elStyle.paddingRight ? parseFloat(elStyle.paddingRight) : 0,
          top: elStyle.paddingTop ? parseFloat(elStyle.paddingTop) : 0,
          bottom: elStyle.paddingBottom ? parseFloat(elStyle.paddingBottom) : 0
        },
        isBorderBoxSizing: elStyle.boxSizing === "border-box"
      };
    }
  }
  class VirtualManager {
    get elements() {
      return this._elements;
    }
    // Options
    /**
     * A rendering function for the panel element's innerHTML
     */
    get renderPanel() {
      return this._renderPanel;
    }
    /**
     * Initial panel count to render
     * @readonly
     * @defaultValue -1
     */
    get initialPanelCount() {
      return this._initialPanelCount;
    }
    /**
     * Whether to cache rendered panel's innerHTML
     * @defaultValue false
     */
    get cache() {
      return this._cache;
    }
    /**
     * The class name that will be applied to rendered panel elements
     * @defaultValue "flicking-panel"
     */
    get panelClass() {
      return this._panelClass;
    }
    set renderPanel(val) {
      this._renderPanel = val;
      this._flicking.renderer.panels.forEach((panel) => panel.uncacheRenderResult());
    }
    set cache(val) {
      this._cache = val;
    }
    set panelClass(val) {
      this._panelClass = val;
    }
    constructor(flicking, options) {
      var _a, _b, _c, _d;
      this._flicking = flicking;
      this._renderPanel = (_a = options == null ? void 0 : options.renderPanel) != null ? _a : (() => "");
      this._initialPanelCount = (_b = options == null ? void 0 : options.initialPanelCount) != null ? _b : -1;
      this._cache = (_c = options == null ? void 0 : options.cache) != null ? _c : false;
      this._panelClass = (_d = options == null ? void 0 : options.panelClass) != null ? _d : CLASS.DEFAULT_VIRTUAL;
      this._elements = [];
    }
    init() {
      const flicking = this._flicking;
      if (!flicking.virtualEnabled) return;
      if (!flicking.externalRenderer && !flicking.renderExternal) {
        this._initVirtualElements();
      }
      const virtualElements = flicking.camera.children;
      this._elements = virtualElements.map((el) => ({ nativeElement: el, visible: true }));
    }
    show(index) {
      const el = this._elements[index];
      const nativeEl = el.nativeElement;
      el.visible = true;
      if (nativeEl.style.display) {
        nativeEl.style.display = "";
      }
    }
    hide(index) {
      const el = this._elements[index];
      const nativeEl = el.nativeElement;
      el.visible = false;
      nativeEl.style.display = "none";
    }
    /**
     * Add new virtual panels at the end of the list
     * @param count - The number of panels to add
     * @returns The new panels added
     */
    append(count = 1) {
      const flicking = this._flicking;
      return this.insert(flicking.panels.length, count);
    }
    /**
     * Add new virtual panels at the start of the list
     * @param count - The number of panels to add
     * @returns The new panels added
     */
    prepend(count = 1) {
      return this.insert(0, count);
    }
    /**
     * Add new virtual panels at the given index
     * @param count - The number of panels to add
     * @returns The new panels added
     */
    insert(index, count = 1) {
      if (count <= 0) return [];
      const flicking = this._flicking;
      return flicking.renderer.batchInsert({ index, elements: range(count), hasDOMInElements: false });
    }
    /**
     * Remove panels at the given index
     * @param count - The number of panels to remove
     * @returns The panels removed
     */
    remove(index, count) {
      if (count <= 0) return [];
      const flicking = this._flicking;
      return flicking.renderer.batchRemove({ index, deleteCount: count, hasDOMInElements: false });
    }
    /**
     * @internal
     */
    _initVirtualElements() {
      const flicking = this._flicking;
      const cameraElement = flicking.camera.element;
      const panelsPerView = flicking.panelsPerView;
      const fragment = document.createDocumentFragment();
      const newElements = range(panelsPerView + 1).map((idx) => {
        const panelEl = document.createElement("div");
        panelEl.className = this._panelClass;
        panelEl.dataset.elementIndex = idx.toString();
        return panelEl;
      });
      newElements.forEach((el) => {
        fragment.appendChild(el);
      });
      cameraElement.appendChild(fragment);
    }
  }
  let Renderer$1 = class Renderer {
    // Internal states Getter
    /**
     * Array of panels
     * @readonly
     * @see Panel
     */
    get panels() {
      return this._panels;
    }
    /**
     * A boolean value indicating whether rendering is in progress
     * @readonly
     * @internal
     */
    get rendering() {
      return this._rendering;
    }
    /**
     * Count of panels
     * @readonly
     */
    get panelCount() {
      return this._panels.length;
    }
    /**
     * @internal
     */
    get strategy() {
      return this._strategy;
    }
    // Options Getter
    /**
     * A {@link Panel}'s {@link Panel.align | align} value that applied to all panels
     */
    get align() {
      return this._align;
    }
    // Options Setter
    set align(val) {
      this._align = val;
      const panelAlign = parsePanelAlign(val);
      this._panels.forEach((panel) => {
        panel.align = panelAlign;
      });
    }
    /**
     * @param options - {@link RendererOptions}
     */
    constructor(options) {
      const { align = ALIGN.CENTER, strategy } = options;
      this._flicking = null;
      this._panels = [];
      this._rendering = false;
      this._align = align;
      this._strategy = strategy;
    }
    /**
     * Initialize Renderer
     * @remarks
     * This method is called automatically during {@link Flicking.init}. It collects existing panel elements.
     * @param flicking - An instance of {@link Flicking}
     * @returns The current instance for method chaining
     */
    init(flicking) {
      this._flicking = flicking;
      this._collectPanels();
      return this;
    }
    /**
     * Destroy Renderer and return to initial state
     * @remarks
     * This method clears all panel references and resets the internal state.
     */
    destroy() {
      this._flicking = null;
      this._panels = [];
    }
    /**
     * Return the {@link Panel} at the given index. `null` if it doesn't exist.
     * @remarks
     * This is equivalent to accessing `Flicking.panels[index]`.
     * @param index - Index of the panel to get
     * @returns Panel at the given index
     * @see Panel
     */
    getPanel(index) {
      return this._panels[index] || null;
    }
    forceRenderAllPanels() {
      this._panels.forEach((panel) => panel.markForShow());
      return Promise.resolve();
    }
    /**
     * Return Rendered Panels
     * @returns Rendered Panels
     */
    getRenderedPanels() {
      const flicking = getFlickingAttached(this._flicking);
      return flicking.renderer.panels.filter((panel) => panel.rendered);
    }
    /**
     * Update all panel sizes
     * @returns The current instance for method chaining
     */
    updatePanelSize() {
      const flicking = getFlickingAttached(this._flicking);
      const panels = this._panels;
      if (panels.length <= 0) return this;
      if (flicking.panelsPerView > 0) {
        const firstPanel = panels[0];
        firstPanel.resize();
        this._updatePanelSizeByGrid(firstPanel, panels);
      } else {
        flicking.panels.forEach((panel) => panel.resize());
      }
      return this;
    }
    /**
     * Insert new panels at given index
     * @remarks
     * This will increase index of panels after by the number of panels added.
     * @param items - An array of {@link BatchInsertParams}
     * @throws {@link DOMManipulationErrors}
     * @returns An array of inserted panels
     */
    batchInsert(...items) {
      const allPanelsInserted = this.batchInsertDefer(...items);
      if (allPanelsInserted.length <= 0) return [];
      this.updateAfterPanelChange(allPanelsInserted, []);
      return allPanelsInserted;
    }
    /**
     * @internal
     * @privateRemarks
     * Defers update. Camera position & others will be updated after calling updateAfterPanelChange.
     */
    batchInsertDefer(...items) {
      const panels = this._panels;
      const flicking = getFlickingAttached(this._flicking);
      const prevFirstPanel = panels[0];
      const align = parsePanelAlign(this._align);
      const allPanelsInserted = items.reduce((addedPanels, item) => {
        var _a;
        const insertingIdx = getMinusCompensatedIndex(item.index, panels.length);
        const panelsPushed = panels.slice(insertingIdx);
        const panelsInserted = item.elements.map(
          (el, idx) => this._createPanel(el, { index: insertingIdx + idx, align, flicking })
        );
        panels.splice(insertingIdx, 0, ...panelsInserted);
        if (item.hasDOMInElements) {
          this._insertPanelElements(panelsInserted, (_a = panelsPushed[0]) != null ? _a : null);
        }
        if (flicking.panelsPerView > 0) {
          const firstPanel = prevFirstPanel || panelsInserted[0].resize();
          this._updatePanelSizeByGrid(firstPanel, panelsInserted);
        } else {
          panelsInserted.forEach((panel) => panel.resize());
        }
        panelsPushed.forEach((panel) => {
          panel.increaseIndex(panelsInserted.length);
          panel.updatePosition();
        });
        return [...addedPanels, ...panelsInserted];
      }, []);
      return allPanelsInserted;
    }
    /**
     * Remove the panel at the given index
     * @remarks
     * This will decrease index of panels after by the number of panels removed.
     * @param items - An array of {@link BatchRemoveParams}
     * @throws {@link DOMManipulationErrors}
     * @returns An array of removed panels
     */
    batchRemove(...items) {
      const allPanelsRemoved = this.batchRemoveDefer(...items);
      if (allPanelsRemoved.length <= 0) return [];
      this.updateAfterPanelChange([], allPanelsRemoved);
      return allPanelsRemoved;
    }
    /**
     * @internal
     * @privateRemarks
     * Defers update. Camera position & others will be updated after calling updateAfterPanelChange.
     */
    batchRemoveDefer(...items) {
      const panels = this._panels;
      const flicking = getFlickingAttached(this._flicking);
      const { control } = flicking;
      const activePanel = control.activePanel;
      const allPanelsRemoved = items.reduce((removed, item) => {
        const { index, deleteCount } = item;
        const removingIdx = getMinusCompensatedIndex(index, panels.length);
        const panelsPulled = panels.slice(removingIdx + deleteCount);
        const panelsRemoved = panels.splice(removingIdx, deleteCount);
        if (panelsRemoved.length <= 0) return [];
        panelsPulled.forEach((panel) => {
          panel.decreaseIndex(panelsRemoved.length);
          panel.updatePosition();
        });
        if (item.hasDOMInElements) {
          this._removePanelElements(panelsRemoved);
        }
        panelsRemoved.forEach((panel) => panel.destroy());
        if (includes(panelsRemoved, activePanel)) {
          control.resetActive();
        }
        return [...removed, ...panelsRemoved];
      }, []);
      return allPanelsRemoved;
    }
    /**
     * @internal
     * @privateRemarks
     * Updates camera, control, and triggers {@link PanelChangeEvent} after panels are added or removed.
     */
    updateAfterPanelChange(panelsAdded, panelsRemoved) {
      var _a;
      const flicking = getFlickingAttached(this._flicking);
      const { camera, control } = flicking;
      const panels = this._panels;
      const activePanel = control.activePanel;
      this._updateCameraAndControl();
      if (flicking.autoResize && flicking.useResizeObserver) {
        panelsAdded.forEach((panel) => {
          if (panel.element) {
            flicking.autoResizer.observe(panel.element);
          }
        });
        panelsRemoved.forEach((panel) => {
          if (panel.element) {
            flicking.autoResizer.unobserve(panel.element);
          }
        });
      }
      void this.render();
      if (!flicking.animating) {
        if (!activePanel || activePanel.removed) {
          if (panels.length <= 0) {
            camera.lookAt(0);
          } else {
            let targetIndex = (_a = activePanel == null ? void 0 : activePanel.index) != null ? _a : 0;
            if (targetIndex > panels.length - 1) {
              targetIndex = panels.length - 1;
            }
            void control.moveToPanel(panels[targetIndex], {
              duration: 0
            }).catch(() => void 0);
          }
        } else {
          void control.moveToPanel(activePanel, {
            duration: 0
          }).catch(() => void 0);
        }
      }
      flicking.camera.updateOffset();
      if (panelsAdded.length > 0 || panelsRemoved.length > 0) {
        flicking.trigger(
          new Component.ComponentEvent(EVENTS.PANEL_CHANGE, {
            added: panelsAdded,
            removed: panelsRemoved
          })
        );
        this.checkPanelContentsReady([...panelsAdded, ...panelsRemoved]);
      }
    }
    /**
     * @internal
     * @privateRemarks
     * Checks if panel contents (images/videos) are ready and triggers resize when loaded.
     */
    checkPanelContentsReady(checkingPanels) {
      const flicking = getFlickingAttached(this._flicking);
      const resizeOnContentsReady = flicking.resizeOnContentsReady;
      const panels = this._panels;
      if (!resizeOnContentsReady || flicking.virtualEnabled) return;
      const hasContents = (panel) => panel.element && !!panel.element.querySelector("img, video");
      checkingPanels = checkingPanels.filter((panel) => hasContents(panel));
      if (checkingPanels.length <= 0) return;
      const contentsReadyChecker = new ImReady();
      checkingPanels.forEach((panel) => {
        panel.loading = true;
      });
      contentsReadyChecker.on("readyElement", (e) => {
        if (!this._flicking) {
          contentsReadyChecker.destroy();
          return;
        }
        const panel = checkingPanels[e.index];
        const camera = flicking.camera;
        const control = flicking.control;
        const prevProgressInPanel = control.activePanel ? camera.getProgressInPanel(control.activePanel) : 0;
        panel.loading = false;
        panel.resize();
        panels.slice(panel.index + 1).forEach((panelBehind) => panelBehind.updatePosition());
        if (!flicking.initialized) return;
        camera.updateRange();
        camera.updateOffset();
        camera.updateAnchors();
        if (control.animating) ;
        else {
          control.updatePosition(prevProgressInPanel);
          control.updateInput();
        }
      });
      contentsReadyChecker.on("preReady", (e) => {
        if (this._flicking) {
          void this.render();
        }
        if (e.readyCount === e.totalCount) {
          contentsReadyChecker.destroy();
        }
      });
      contentsReadyChecker.on("ready", () => {
        if (this._flicking) {
          void this.render();
        }
        contentsReadyChecker.destroy();
      });
      contentsReadyChecker.check(checkingPanels.map((panel) => panel.element));
    }
    /**
     * @internal
     * @privateRemarks
     * Updates camera range, anchors, and control input after panel changes.
     */
    _updateCameraAndControl() {
      const flicking = getFlickingAttached(this._flicking);
      const { camera, control } = flicking;
      camera.updateRange();
      camera.updateOffset();
      camera.updateAnchors();
      camera.resetNeedPanelHistory();
      control.updateInput();
    }
    /**
     * @internal
     * @privateRemarks
     * Marks only visible panels for rendering when {@link FlickingOptions.renderOnlyVisible | renderOnlyVisible} is enabled.
     */
    _showOnlyVisiblePanels(flicking) {
      const panels = flicking.renderer.panels;
      const camera = flicking.camera;
      const visibleIndexes = camera.visiblePanels.reduce((visibles, panel) => {
        visibles[panel.index] = true;
        return visibles;
      }, {});
      panels.forEach((panel) => {
        if (panel.index in visibleIndexes || panel.loading) {
          panel.markForShow();
        } else if (!flicking.holding) {
          panel.markForHide();
        }
      });
    }
    /**
     * @internal
     * @privateRemarks
     * Calculates and applies panel sizes when {@link FlickingOptions.panelsPerView | panelsPerView} is enabled.
     */
    _updatePanelSizeByGrid(referencePanel, panels) {
      const flicking = getFlickingAttached(this._flicking);
      const panelsPerView = flicking.panelsPerView;
      if (panelsPerView <= 0) {
        throw new FlickingError(MESSAGE.WRONG_OPTION("panelsPerView", panelsPerView), CODE.WRONG_OPTION);
      }
      if (panels.length <= 0) return;
      const viewportSize = flicking.camera.size;
      const gap = referencePanel.margin.prev + referencePanel.margin.next;
      const panelSize = (viewportSize - gap * (panelsPerView - 1)) / panelsPerView;
      const panelSizeObj = flicking.horizontal ? { width: panelSize } : { height: panelSize };
      const firstPanelSizeObj = __spreadValues({
        size: panelSize,
        margin: referencePanel.margin
      }, !flicking.horizontal && { height: referencePanel.height });
      if (!flicking.noPanelStyleOverride) {
        this._strategy.updatePanelSizes(flicking, panelSizeObj);
      }
      flicking.panels.forEach((panel) => panel.resize(firstPanelSizeObj));
    }
    /**
     * @internal
     * @privateRemarks
     * Removes all child elements from the camera element.
     */
    _removeAllChildsFromCamera() {
      const flicking = getFlickingAttached(this._flicking);
      const cameraElement = flicking.camera.element;
      while (cameraElement.firstChild) {
        cameraElement.removeChild(cameraElement.firstChild);
      }
    }
    /**
     * @internal
     * @privateRemarks
     * Inserts panel elements into the camera element at the specified position.
     */
    _insertPanelElements(panels, nextSibling = null) {
      const flicking = getFlickingAttached(this._flicking);
      const camera = flicking.camera;
      const cameraElement = camera.element;
      const nextSiblingElement = (nextSibling == null ? void 0 : nextSibling.element) || null;
      const fragment = document.createDocumentFragment();
      panels.forEach((panel) => fragment.appendChild(panel.element));
      const referenceElement = nextSiblingElement && nextSiblingElement.parentNode === cameraElement ? nextSiblingElement : null;
      cameraElement.insertBefore(fragment, referenceElement);
    }
    /**
     * @internal
     * @privateRemarks
     * Removes panel elements from the camera element.
     */
    _removePanelElements(panels) {
      const flicking = getFlickingAttached(this._flicking);
      const cameraElement = flicking.camera.element;
      panels.forEach((panel) => {
        if (panel.element.parentNode === cameraElement) {
          cameraElement.removeChild(panel.element);
        }
      });
    }
    /**
     * @internal
     * @privateRemarks
     * Called after rendering to apply the camera transform.
     */
    _afterRender() {
      const flicking = getFlickingAttached(this._flicking);
      flicking.camera.applyTransform();
      if (flicking.useCSSOrder) {
        const panels = flicking.panels;
        this._strategy.getRenderingIndexesByOrder(flicking).forEach((domIndex, index) => {
          var _a;
          if ((_a = panels[domIndex]) == null ? void 0 : _a.element) {
            panels[domIndex].element.style.order = `${index}`;
          }
        });
      }
    }
  };
  class ExternalRenderer extends Renderer$1 {
    /* eslint-disable @typescript-eslint/no-unused-vars */
    _removePanelElements(panels) {
    }
    _removeAllChildsFromCamera() {
    }
    /* eslint-enable @typescript-eslint/no-unused-vars */
  }
  class VanillaRenderer extends Renderer$1 {
    // eslint-disable-next-line @typescript-eslint/require-await
    render() {
      return __async(this, null, function* () {
        const flicking = getFlickingAttached(this._flicking);
        const strategy = this._strategy;
        strategy.updateRenderingPanels(flicking);
        strategy.renderPanels(flicking);
        this._resetPanelElementOrder();
        this._afterRender();
      });
    }
    _collectPanels() {
      const flicking = getFlickingAttached(this._flicking);
      const camera = flicking.camera;
      this._removeAllTextNodes();
      this._panels = this._strategy.collectPanels(flicking, camera.children);
    }
    _createPanel(el, options) {
      return this._strategy.createPanel(el, options);
    }
    /**
     * @internal
     */
    _resetPanelElementOrder() {
      const flicking = getFlickingAttached(this._flicking);
      const cameraEl = flicking.camera.element;
      let reversedElements = [];
      if (flicking.useCSSOrder) {
        reversedElements = this.getRenderedPanels().map((panel) => panel.element).reverse();
      } else {
        reversedElements = this._strategy.getRenderingElementsByOrder(flicking).reverse();
      }
      reversedElements.forEach((el, idx) => {
        const nextEl = reversedElements[idx - 1] ? reversedElements[idx - 1] : null;
        if (el.nextElementSibling !== nextEl) {
          cameraEl.insertBefore(el, nextEl);
        }
      });
    }
    /**
     * @internal
     */
    _removeAllTextNodes() {
      const flicking = getFlickingAttached(this._flicking);
      const cameraElement = flicking.camera.element;
      toArray(cameraElement.childNodes).forEach((node) => {
        if (node.nodeType === Node.TEXT_NODE) {
          cameraElement.removeChild(node);
        }
      });
    }
  }
  class Panel {
    // Internal States Getter
    /**
     * `HTMLElement` that panel's referencing
     * @readonly
     */
    get element() {
      return this._elProvider.element;
    }
    /**
     * @internal
     * @readonly
     */
    get elementProvider() {
      return this._elProvider;
    }
    /**
     * Index of the panel
     * @readonly
     */
    get index() {
      return this._index;
    }
    /**
     * Position of the panel, including {@link Panel.alignPosition | alignPosition}
     * @readonly
     */
    get position() {
      return this._pos + this._alignPos;
    }
    /**
     * Cached size of the panel element
     * @remarks
     * This is equal to {@link Panel.element | element}'s `offsetWidth` if {@link Flicking.horizontal | horizontal} is `true`, and `offsetHeight` else
     * @readonly
     */
    get size() {
      return this._size;
    }
    /**
     * Panel's size including CSS `margin`
     * @remarks
     * This value includes {@link Panel.element | element}'s margin left/right if {@link Flicking.horizontal | horizontal} is `true`, and margin top/bottom else
     * @readonly
     */
    get sizeIncludingMargin() {
      return this._size + this._margin.prev + this._margin.next;
    }
    /**
     * Height of the panel element
     * @readonly
     */
    get height() {
      return this._height;
    }
    /**
     * Cached CSS `margin` value of the panel element
     * @readonly
     */
    get margin() {
      return this._margin;
    }
    /**
     * Align position inside the panel where {@link Camera}'s {@link Camera.alignPosition | alignPosition} inside viewport should be located at
     * @readonly
     */
    get alignPosition() {
      return this._alignPos;
    }
    /**
     * A value indicating whether the panel's {@link Flicking.remove | remove}d
     * @readonly
     */
    get removed() {
      return this._removed;
    }
    /**
     * A value indicating whether the panel's element is being rendered on the screen
     * @readonly
     */
    get rendered() {
      return this._rendered;
    }
    /**
     * A value indicating whether the panel's image/video is not loaded and waiting for resize
     * @readonly
     */
    get loading() {
      return this._loading;
    }
    /**
     * Panel element's range of the bounding box
     * @readonly
     */
    get range() {
      return { min: this._pos, max: this._pos + this._size };
    }
    /**
     * A value indicating whether the panel's position is toggled by circular behavior
     * @readonly
     */
    get toggled() {
      return this._toggled;
    }
    /**
     * A direction where the panel's position is toggled
     * @readonly
     */
    get toggleDirection() {
      return this._toggleDirection;
    }
    /**
     * Actual position offset determined by {@link Panel.order}
     * @readonly
     */
    get offset() {
      const toggleDirection = this._toggleDirection;
      const cameraRangeDiff = this._flicking.camera.rangeDiff;
      return toggleDirection === DIRECTION.NONE || !this._toggled ? 0 : toggleDirection === DIRECTION.PREV ? -cameraRangeDiff : cameraRangeDiff;
    }
    /**
     * Progress of movement between previous or next panel relative to current panel
     * @readonly
     */
    get progress() {
      const flicking = this._flicking;
      return this.index - flicking.camera.progress;
    }
    /**
     * Progress of movement between points that panel is completely invisible outside of viewport(prev direction: -1, selected point: 0, next direction: 1)
     * @readonly
     */
    get outsetProgress() {
      const position = this.position + this.offset;
      const alignPosition = this._alignPos;
      const camera = this._flicking.camera;
      const camPos = camera.position;
      if (camPos === position) {
        return 0;
      }
      if (camPos < position) {
        const disappearPosNext = position + (camera.size - camera.alignPosition) + alignPosition;
        return -getProgress$1(camPos, position, disappearPosNext);
      } else {
        const disappearPosPrev = position - (camera.alignPosition + this._size - alignPosition);
        return 1 - getProgress$1(camPos, disappearPosPrev, position);
      }
    }
    /**
     * Percentage of area where panel is visible in the viewport
     * @readonly
     */
    get visibleRatio() {
      const range2 = this.range;
      const size = this._size;
      const offset = this.offset;
      const visibleRange = this._flicking.camera.visibleRange;
      const checkingRange = {
        min: range2.min + offset,
        max: range2.max + offset
      };
      if (checkingRange.max <= visibleRange.min || checkingRange.min >= visibleRange.max) {
        return 0;
      }
      let visibleSize = size;
      if (visibleRange.min > checkingRange.min) {
        visibleSize -= visibleRange.min - checkingRange.min;
      }
      if (visibleRange.max < checkingRange.max) {
        visibleSize -= checkingRange.max - visibleRange.max;
      }
      return visibleSize / size;
    }
    set loading(val) {
      this._loading = val;
    }
    // Options Getter
    /**
     * A value indicating where the {@link Panel.alignPosition | alignPosition} should be located at inside the panel element
     */
    get align() {
      return this._align;
    }
    // Options Setter
    set align(val) {
      this._align = val;
      this._updateAlignPos();
    }
    /**
     * Creates a new Panel instance
     * @param panelOptions - Options for creating the panel
     */
    constructor(panelOptions) {
      const { index, align, flicking, elementProvider } = panelOptions;
      this._index = index;
      this._flicking = flicking;
      this._elProvider = elementProvider;
      this._align = align;
      this._removed = false;
      this._rendered = true;
      this._loading = false;
      this._resetInternalStates();
    }
    /**
     * @internal
     * @privateRemarks
     * Marks this panel to be rendered on the camera element.
     */
    markForShow() {
      this._rendered = true;
      this._elProvider.show(this._flicking);
    }
    /**
     * @internal
     * @privateRemarks
     * Marks this panel to be hidden from the camera element.
     */
    markForHide() {
      this._rendered = false;
      this._elProvider.hide(this._flicking);
    }
    /**
     * Update size of the panel
     * @remarks
     * This method recalculates the panel's size, margin, and position based on the current DOM state.
     * @param cached - Predefined cached size of the panel
     * @returns The current instance for method chaining
     */
    resize(cached) {
      var _a;
      const el = this.element;
      const flicking = this._flicking;
      const { horizontal, useFractionalSize } = flicking;
      if (!el) {
        return this;
      }
      if (cached) {
        this._size = cached.size;
        this._margin = __spreadValues({}, cached.margin);
        this._height = (_a = cached.height) != null ? _a : getElementSize({
          el,
          horizontal: false,
          useFractionalSize,
          useOffset: true,
          style: getStyle(el)
        });
      } else {
        const elStyle = getStyle(el);
        this._size = getElementSize({
          el,
          horizontal,
          useFractionalSize,
          useOffset: true,
          style: elStyle
        });
        this._margin = horizontal ? {
          prev: parseFloat(elStyle.marginLeft || "0"),
          next: parseFloat(elStyle.marginRight || "0")
        } : {
          prev: parseFloat(elStyle.marginTop || "0"),
          next: parseFloat(elStyle.marginBottom || "0")
        };
        this._height = horizontal ? getElementSize({
          el,
          horizontal: false,
          useFractionalSize,
          useOffset: true,
          style: elStyle
        }) : this._size;
      }
      this.updatePosition();
      this._updateAlignPos();
      return this;
    }
    /**
     * Change panel's size
     * @remarks
     * This will change the actual size of the panel element by changing its CSS width/height property.
     * @param size - {@link SetSizeParams}
     * @returns The current instance for method chaining
     */
    setSize(size) {
      setSize(this.element, size);
      return this;
    }
    /**
     * Check whether the given element is inside of this panel's {@link Panel.element | element}
     * @remarks
     * This is useful for determining which panel contains a clicked element.
     * @param element - The HTMLElement to check
     * @returns A Boolean value indicating the element is inside of this panel {@link Panel.element | element}
     */
    contains(element) {
      var _a;
      return !!((_a = this.element) == null ? void 0 : _a.contains(element));
    }
    /**
     * Reset internal state and set {@link Panel.removed | removed} to `true`
     * @remarks
     * After calling this method, the panel should no longer be used.
     */
    destroy() {
      this._resetInternalStates();
      this._removed = true;
    }
    /**
     * Check whether the given position is inside of this panel's {@link Panel.range | range}
     * @param pos - A position to check
     * @param includeMargin - Include {@link Panel.margin | margin} to the range
     * @returns A Boolean value indicating whether the given position is included in the panel range
     */
    includePosition(pos, includeMargin = false) {
      return this.includeRange(pos, pos, includeMargin);
    }
    /**
     * Check whether the given range is fully included in this panel's area (inclusive)
     * @param min - Minimum value of the range to check
     * @param max - Maximum value of the range to check
     * @param includeMargin - Include {@link Panel.margin | margin} to the range
     * @returns A Boolean value indicating whether the given range is fully included in the panel range
     */
    includeRange(min, max, includeMargin = false) {
      const margin = this._margin;
      const panelRange = this.range;
      if (includeMargin) {
        panelRange.min -= margin.prev;
        panelRange.max += margin.next;
      }
      return max >= panelRange.min && min <= panelRange.max;
    }
    /**
     * Check whether the panel is visble in the given range (exclusive)
     * @param min - Minimum value of the range to check
     * @param max - Maximum value of the range to check
     * @returns A Boolean value indicating whether the panel is visible
     */
    isVisibleOnRange(min, max) {
      const panelRange = this.range;
      return max > panelRange.min && min < panelRange.max;
    }
    /**
     * Move {@link Camera} to this panel
     * @remarks
     * This is equivalent to calling `Flicking.moveTo(panel.index, duration)`.
     * @param duration - Duration of the animation (unit: ms). Defaults to {@link FlickingOptions.duration}
     * @fires {@link MovementEvents}
     * @throws {@link MovementErrors}
     * @returns A Promise which will be resolved after reaching the panel
     */
    focus(duration) {
      return this._flicking.moveTo(this._index, duration);
    }
    /**
     * Get previous(`index - 1`) panel.
     * @remarks
     * When the previous panel does not exist, this will return `null` instead
     * If the {@link Flicking.circularEnabled | circular} is enabled, this will return the last panel if called from the first panel
     * @returns The previous panel
     */
    prev() {
      const index = this._index;
      const flicking = this._flicking;
      const renderer = flicking.renderer;
      const panelCount = renderer.panelCount;
      if (panelCount === 1) return null;
      return flicking.circularEnabled ? renderer.getPanel(index === 0 ? panelCount - 1 : index - 1) : renderer.getPanel(index - 1);
    }
    /**
     * Get next(`index + 1`) panel.
     * @remarks
     * When the next panel does not exist, this will return `null` instead
     * If the {@link Flicking.circularEnabled | circular} is enabled, this will return the first panel if called from the last panel
     * @returns The next panel
     */
    next() {
      const index = this._index;
      const flicking = this._flicking;
      const renderer = flicking.renderer;
      const panelCount = renderer.panelCount;
      if (panelCount === 1) return null;
      return flicking.circularEnabled ? renderer.getPanel(index === panelCount - 1 ? 0 : index + 1) : renderer.getPanel(index + 1);
    }
    /**
     * @internal
     * @privateRemarks
     * Increases the panel's index by the given value. Called when panels are inserted before this panel.
     */
    increaseIndex(val) {
      this._index += Math.max(val, 0);
      return this;
    }
    /**
     * @internal
     * @privateRemarks
     * Decreases the panel's index by the given value. Called when panels are removed before this panel.
     */
    decreaseIndex(val) {
      this._index -= Math.max(val, 0);
      return this;
    }
    /**
     * @internal
     * @privateRemarks
     * Recalculates the panel's position based on the previous panel's position and margins.
     */
    updatePosition() {
      const prevPanel = this._flicking.renderer.panels[this._index - 1];
      this._pos = prevPanel ? prevPanel.range.max + prevPanel.margin.next + this._margin.prev : this._margin.prev;
      return this;
    }
    /**
     * @internal
     * @privateRemarks
     * Toggles the panel's position for circular mode. Returns true if the panel was toggled.
     */
    toggle(prevPos, newPos) {
      const toggleDirection = this._toggleDirection;
      const togglePosition = this._togglePosition;
      if (toggleDirection === DIRECTION.NONE || newPos === prevPos) return false;
      const prevToggled = this._toggled;
      if (newPos > prevPos) {
        if (togglePosition >= prevPos && togglePosition <= newPos) {
          this._toggled = toggleDirection === DIRECTION.NEXT;
        }
      } else {
        if (togglePosition <= prevPos && togglePosition >= newPos) {
          this._toggled = toggleDirection !== DIRECTION.NEXT;
        }
      }
      return prevToggled !== this._toggled;
    }
    /**
     * @internal
     * @privateRemarks
     * Updates the toggle direction for circular mode based on the panel's visibility at the camera range edges.
     */
    updateCircularToggleDirection() {
      const flicking = this._flicking;
      if (!flicking.circularEnabled) {
        this._toggleDirection = DIRECTION.NONE;
        this._togglePosition = 0;
        this._toggled = false;
        return this;
      }
      const camera = flicking.camera;
      const camRange = camera.range;
      const camAlignPosition = camera.alignPosition;
      const camVisibleRange = camera.visibleRange;
      const camVisibleSize = camVisibleRange.max - camVisibleRange.min;
      const minimumVisible = camRange.min - camAlignPosition;
      const maximumVisible = camRange.max - camAlignPosition + camVisibleSize;
      const shouldBeVisibleAtMin = this.includeRange(maximumVisible - camVisibleSize, maximumVisible, false);
      const shouldBeVisibleAtMax = this.includeRange(minimumVisible, minimumVisible + camVisibleSize, false);
      this._toggled = false;
      if (shouldBeVisibleAtMin) {
        this._toggleDirection = DIRECTION.PREV;
        this._togglePosition = this.range.max + camRange.min - camRange.max + camAlignPosition;
        this.toggle(Infinity, camera.position);
      } else if (shouldBeVisibleAtMax) {
        this._toggleDirection = DIRECTION.NEXT;
        this._togglePosition = this.range.min + camRange.max - camVisibleSize + camAlignPosition;
        this.toggle(-Infinity, camera.position);
      } else {
        this._toggleDirection = DIRECTION.NONE;
        this._togglePosition = 0;
      }
      return this;
    }
    /**
     * @internal
     * @privateRemarks
     * Recalculates the align position based on the align option and panel size.
     */
    _updateAlignPos() {
      this._alignPos = parseAlign$1(this._align, this._size);
    }
    /**
     * @internal
     * @privateRemarks
     * Resets all internal state values to their defaults.
     */
    _resetInternalStates() {
      this._size = 0;
      this._pos = 0;
      this._margin = { prev: 0, next: 0 };
      this._height = 0;
      this._alignPos = 0;
      this._toggled = false;
      this._togglePosition = 0;
      this._toggleDirection = DIRECTION.NONE;
    }
  }
  class NormalRenderingStrategy {
    constructor(options) {
      const { providerCtor } = options;
      this._providerCtor = providerCtor;
    }
    renderPanels() {
    }
    getRenderingIndexesByOrder(flicking) {
      const renderedPanels = flicking.renderer.panels.filter((panel) => panel.rendered);
      const toggledPrev = renderedPanels.filter((panel) => panel.toggled && panel.toggleDirection === DIRECTION.PREV);
      const toggledNext = renderedPanels.filter((panel) => panel.toggled && panel.toggleDirection === DIRECTION.NEXT);
      const notToggled = renderedPanels.filter((panel) => !panel.toggled);
      return [...toggledPrev, ...notToggled, ...toggledNext].map((panel) => panel.index);
    }
    getRenderingElementsByOrder(flicking) {
      const panels = flicking.panels;
      return this.getRenderingIndexesByOrder(flicking).map((index) => panels[index].element);
    }
    updateRenderingPanels(flicking) {
      if (flicking.renderOnlyVisible) {
        this._showOnlyVisiblePanels(flicking);
      } else {
        flicking.panels.forEach((panel) => panel.markForShow());
      }
    }
    collectPanels(flicking, elements) {
      const align = parsePanelAlign(flicking.renderer.align);
      return elements.map(
        (el, index) => new Panel({
          index,
          elementProvider: new this._providerCtor(el),
          align,
          flicking
        })
      );
    }
    createPanel(element, options) {
      return new Panel(__spreadProps(__spreadValues({}, options), {
        elementProvider: new this._providerCtor(element)
      }));
    }
    updatePanelSizes(flicking, size) {
      flicking.panels.forEach((panel) => panel.setSize(size));
    }
    /**
     * @internal
     */
    _showOnlyVisiblePanels(flicking) {
      const panels = flicking.renderer.panels;
      const camera = flicking.camera;
      const visibleIndexes = camera.visiblePanels.reduce((visibles, panel) => {
        visibles[panel.index] = true;
        return visibles;
      }, {});
      panels.forEach((panel) => {
        if (panel.index in visibleIndexes || panel.loading) {
          panel.markForShow();
        } else if (!flicking.holding) {
          panel.markForHide();
        }
      });
      camera.updateOffset();
    }
  }
  class VirtualPanel extends Panel {
    /**
     * `HTMLElement` that panel's referencing
     * @readonly
     */
    get element() {
      return this._elProvider.element;
    }
    /**
     * Cached innerHTML by the previous render function
     * @readonly
     */
    get cachedInnerHTML() {
      return this._cachedInnerHTML;
    }
    /**
     * A number for indexing which element it will be rendered on
     * @readonly
     */
    get elementIndex() {
      const flicking = this._flicking;
      const virtualElCount = flicking.panelsPerView + 1;
      const panelCount = flicking.panelCount;
      let index = this._index;
      if (this._toggled) {
        index = this._toggleDirection === DIRECTION.NEXT ? index + panelCount : index - panelCount;
      }
      return circulateIndex(index, virtualElCount);
    }
    /**
     * @param options - {@link VirtualPanelOptions}
     */
    constructor(options) {
      super(options);
      options.elementProvider.init(this);
      this._elProvider = options.elementProvider;
      this._cachedInnerHTML = null;
    }
    cacheRenderResult(result) {
      this._cachedInnerHTML = result;
    }
    uncacheRenderResult() {
      this._cachedInnerHTML = null;
    }
    render() {
      const flicking = this._flicking;
      const { renderPanel, cache } = flicking.virtual;
      const element = this._elProvider.element;
      const newInnerHTML = this._cachedInnerHTML || renderPanel(this, this._index);
      if (newInnerHTML === element.innerHTML) return;
      element.innerHTML = newInnerHTML;
      if (cache) {
        this.cacheRenderResult(newInnerHTML);
      }
    }
    increaseIndex(val) {
      this.uncacheRenderResult();
      return super.increaseIndex(val);
    }
    decreaseIndex(val) {
      this.uncacheRenderResult();
      return super.decreaseIndex(val);
    }
  }
  class VirtualRenderingStrategy {
    renderPanels(flicking) {
      const virtualManager = flicking.virtual;
      const visiblePanels = flicking.visiblePanels;
      const invisibleIndexes = range(flicking.panelsPerView + 1);
      visiblePanels.forEach((panel) => {
        const elementIndex = panel.elementIndex;
        panel.render();
        virtualManager.show(elementIndex);
        invisibleIndexes[elementIndex] = -1;
      });
      invisibleIndexes.filter((val) => val >= 0).forEach((idx) => {
        virtualManager.hide(idx);
      });
    }
    getRenderingIndexesByOrder(flicking) {
      const virtualManager = flicking.virtual;
      const visiblePanels = [...flicking.visiblePanels].filter((panel) => panel.rendered).sort((panel1, panel2) => {
        return panel1.position + panel1.offset - (panel2.position + panel2.offset);
      });
      if (visiblePanels.length <= 0) return virtualManager.elements.map((_, idx) => idx);
      const visibleIndexes = visiblePanels.map((panel) => panel.elementIndex);
      const invisibleIndexes = virtualManager.elements.map((el, idx) => __spreadProps(__spreadValues({}, el), { idx })).filter((el) => !el.visible).map((el) => el.idx);
      return [...visibleIndexes, ...invisibleIndexes];
    }
    getRenderingElementsByOrder(flicking) {
      const virtualManager = flicking.virtual;
      const elements = virtualManager.elements;
      return this.getRenderingIndexesByOrder(flicking).map((index) => elements[index].nativeElement);
    }
    updateRenderingPanels(flicking) {
      const panels = flicking.renderer.panels;
      const camera = flicking.camera;
      const visibleIndexes = camera.visiblePanels.reduce((visibles, panel) => {
        visibles[panel.index] = true;
        return visibles;
      }, {});
      panels.forEach((panel) => {
        if (panel.index in visibleIndexes || panel.loading) {
          panel.markForShow();
        } else {
          panel.markForHide();
        }
      });
      camera.updateOffset();
    }
    collectPanels(flicking) {
      const align = parsePanelAlign(flicking.renderer.align);
      return range(flicking.virtual.initialPanelCount).map(
        (index) => new VirtualPanel({
          index,
          elementProvider: new VirtualElementProvider(flicking),
          align,
          flicking
        })
      );
    }
    createPanel(_el, options) {
      return new VirtualPanel(__spreadProps(__spreadValues({}, options), {
        elementProvider: new VirtualElementProvider(options.flicking)
      }));
    }
    updatePanelSizes(flicking, size) {
      flicking.virtual.elements.forEach((el) => {
        setSize(el.nativeElement, size);
      });
      flicking.panels.forEach((panel) => panel.setSize(size));
    }
  }
  const Renderer = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
    __proto__: null,
    ExternalRenderer,
    NormalRenderingStrategy,
    Renderer: Renderer$1,
    VanillaRenderer,
    VirtualRenderingStrategy
  }, Symbol.toStringTag, { value: "Module" }));
  const _Flicking = class _Flicking extends Component {
    /** Creates a new Flicking instance
     * @param root - A root HTMLElement to initialize Flicking on it. When it's a typeof `string`, it should be a css selector string
     * @param options - A {@link FlickingOptions} object
     * @throws {@link InitializationErrors}
     * @example
     * ```ts
     * import Flicking from "@egjs/flicking";
     *
     * // Creating new instance of Flicking with HTMLElement
     * const flicking = new Flicking(document.querySelector(".flicking-viewport"), { circular: true });
     *
     * // Creating new instance of Flicking with CSS selector
     * const flicking2 = new Flicking(".flicking-viewport", { circular: true });
     * ```
     */
    constructor(root, options = {}) {
      super();
      this._scheduleResize = false;
      const {
        align = ALIGN.CENTER,
        defaultIndex = 0,
        horizontal = true,
        circular = false,
        circularFallback = CIRCULAR_FALLBACK.LINEAR,
        bound = false,
        adaptive = false,
        panelsPerView = -1,
        noPanelStyleOverride = false,
        resizeOnContentsReady = false,
        nested = false,
        needPanelThreshold = 0,
        preventEventsBeforeInit = true,
        deceleration = 75e-4,
        duration = 500,
        easing = (x) => 1 - __pow(1 - x, 3),
        inputType = ["mouse", "touch"],
        moveType = "snap",
        threshold = 40,
        dragThreshold = 1,
        interruptable = true,
        bounce = "20%",
        iOSEdgeSwipeThreshold = 30,
        preventClickOnDrag = true,
        preventDefaultOnDrag = false,
        disableOnInit = false,
        changeOnHold = false,
        renderOnlyVisible = false,
        virtual = null,
        autoInit = true,
        autoResize = true,
        useResizeObserver = true,
        resizeDebounce = 0,
        observePanelResize = false,
        maxResizeDebounce = 100,
        useFractionalSize = false,
        usePercentagePos = false,
        externalRenderer = null,
        renderExternal = null,
        optimizeSizeUpdate = false,
        animationThreshold = 0.5,
        useCSSOrder = false
      } = options;
      this._initialized = false;
      this._plugins = [];
      this._isResizing = false;
      this._align = align;
      this._defaultIndex = defaultIndex;
      this._horizontal = horizontal;
      this._circular = circular;
      this._circularFallback = circularFallback;
      this._bound = bound;
      this._adaptive = adaptive;
      this._panelsPerView = panelsPerView;
      this._noPanelStyleOverride = noPanelStyleOverride;
      this._resizeOnContentsReady = resizeOnContentsReady;
      this._nested = nested;
      this._virtual = virtual;
      this._needPanelThreshold = needPanelThreshold;
      this._preventEventsBeforeInit = preventEventsBeforeInit;
      this._deceleration = deceleration;
      this._duration = duration;
      this._easing = easing;
      this._inputType = inputType;
      this._moveType = moveType;
      this._threshold = threshold;
      this._dragThreshold = dragThreshold;
      this._interruptable = interruptable;
      this._bounce = bounce;
      this._iOSEdgeSwipeThreshold = iOSEdgeSwipeThreshold;
      this._preventClickOnDrag = preventClickOnDrag;
      this._preventDefaultOnDrag = preventDefaultOnDrag;
      this._disableOnInit = disableOnInit;
      this._changeOnHold = changeOnHold;
      this._renderOnlyVisible = renderOnlyVisible;
      this._autoInit = autoInit;
      this._autoResize = autoResize;
      this._useResizeObserver = useResizeObserver;
      this._resizeDebounce = resizeDebounce;
      this._maxResizeDebounce = maxResizeDebounce;
      this._observePanelResize = observePanelResize;
      this._useFractionalSize = useFractionalSize;
      this._usePercentagePos = usePercentagePos;
      this._externalRenderer = externalRenderer;
      this._renderExternal = renderExternal;
      this._optimizeSizeUpdate = optimizeSizeUpdate;
      this._animationThreshold = animationThreshold;
      this._useCSSOrder = useCSSOrder;
      this._viewport = new Viewport(this, getElement(root));
      this._autoResizer = new AutoResizer(this);
      this._renderer = this._createRenderer();
      this._camera = this._createCamera();
      this._control = this._createControl();
      this._virtualManager = new VirtualManager(this, virtual);
      if (this._autoInit) {
        void this.init();
      }
    }
    // Components
    /**
     * {@link Control} instance that manages user input and panel movement animations
     * @remarks
     * The concrete Control implementation is selected based on {@link FlickingOptions.moveType | moveType} option.
     * @privateRemarks
     * The control instance is created during construction by {@link Flicking._createControl}.
     * @readonly
     */
    get control() {
      return this._control;
    }
    /**
     * {@link Camera} instance that manages actual movement and positioning inside the viewport
     * @remarks
     * The concrete Camera implementation is selected based on {@link FlickingOptions.circular} and {@link FlickingOptions.bound} options.
     * @privateRemarks
     * The camera instance is created during construction by {@link Flicking._createCamera}.
     * @readonly
     */
    get camera() {
      return this._camera;
    }
    /**
     * {@link Renderer} instance that manages panels and their elements
     * @remarks
     * The concrete Renderer implementation is selected based on {@link Flicking.externalRenderer} and {@link FlickingOptions.virtual} options.
     * @privateRemarks
     * The renderer instance is created during construction by {@link Flicking._createRenderer}.
     * @readonly
     */
    get renderer() {
      return this._renderer;
    }
    /**
     * {@link Viewport} instance that manages viewport size and element
     * @privateRemarks
     * The viewport instance is created during construction by {@link Flicking} constructor.
     * @readonly
     */
    get viewport() {
      return this._viewport;
    }
    /**
     * {@link AutoResizer} instance that detects size changes and triggers resize when {@link FlickingOptions.autoResize | autoResize} option is enabled
     * @privateRemarks
     * The autoResizer instance is created during construction by {@link Flicking} constructor.
     * @readonly
     */
    get autoResizer() {
      return this._autoResizer;
    }
    // Internal States
    /**
     * Whether Flicking's {@link Flicking.init} is called.
     * @remarks
     * This is `true` when {@link Flicking.init} is called, and is `false` after calling {@link Flicking.destroy}.
     * Use this to check if Flicking is ready before calling certain methods that require initialization.
     * @defaultValue false
     * @readonly
     * @example
     * ```ts
     * if (flicking.initialized) {
     *   flicking.setStatus(status);
     * } else {
     *   await flicking.init();
     *   flicking.setStatus(status);
     * }
     * ```
     */
    get initialized() {
      return this._initialized;
    }
    /**
     * Whether the circular mode is actually enabled.
     * @remarks
     * The {@link FlickingOptions.circular} option may not be enabled when the sum of panel sizes is too small.
     * This property reflects the actual enabled state, which may differ from the {@link FlickingOptions.circular} option value.
     * @defaultValue false
     * @readonly
     */
    get circularEnabled() {
      return this._camera.circularEnabled;
    }
    /**
     * Whether the virtual mode is actually enabled.
     * @remarks
     * The {@link FlickingOptions.virtual} option may not be enabled when {@link FlickingOptions.panelsPerView} is less than or equal to zero.
     * This property reflects the actual enabled state, which may differ from the {@link FlickingOptions.virtual} option value.
     * @defaultValue false
     * @readonly
     */
    get virtualEnabled() {
      return this._panelsPerView > 0 && this._virtual != null;
    }
    /**
     * Index of the currently active panel.
     * @remarks
     * Returns -1 when there is no active panel. This is a shorthand for `Flicking.currentPanel.index`.
     * @readonly
     */
    get index() {
      return this._control.activeIndex;
    }
    /**
     * The root viewport element (`.flicking-viewport`).
     * @remarks
     * This is the element passed to the Flicking constructor. It is a shorthand for `Flicking.viewport.element`.
     * @readonly
     */
    get element() {
      return this._viewport.element;
    }
    /**
     * The currently active panel.
     * @remarks
     * Returns `null` when there is no active panel. This is a shorthand for `Flicking.control.activePanel`.
     * @readonly
     */
    get currentPanel() {
      return this._control.activePanel;
    }
    /**
     * Array of all panels.
     * @remarks
     * This is a shorthand for `Flicking.renderer.panels`.
     * @readonly
     */
    get panels() {
      return this._renderer.panels;
    }
    /**
     * Total number of panels.
     * @remarks
     * This is a shorthand for `Flicking.renderer.panelCount`.
     * @readonly
     */
    get panelCount() {
      return this._renderer.panelCount;
    }
    /**
     * Array of panels that are currently visible in the viewport.
     * @remarks
     * This is a shorthand for `Flicking.camera.visiblePanels`.
     * @readonly
     */
    get visiblePanels() {
      return this._camera.visiblePanels;
    }
    /**
     * Whether Flicking is currently animating.
     * @remarks
     * This is a shorthand for `Flicking.control.animating`.
     * @readonly
     */
    get animating() {
      return this._control.animating;
    }
    /**
     * Whether the user is currently clicking or touching the viewport.
     * @remarks
     * This is a shorthand for `Flicking.control.holding`.
     * @readonly
     */
    get holding() {
      return this._control.holding;
    }
    /**
     * Array of currently activated plugins.
     * @remarks
     * Plugins are added via {@link Flicking.addPlugins} and removed via {@link Flicking.removePlugins}.
     * @readonly
     */
    get activePlugins() {
      return this._plugins;
    }
    // Options Getter
    // UI / LAYOUT
    /** Current value of the {@link FlickingOptions.align | align} option. */
    get align() {
      return this._align;
    }
    /** Current value of the {@link FlickingOptions.defaultIndex | defaultIndex} option. */
    get defaultIndex() {
      return this._defaultIndex;
    }
    /** Current value of the {@link FlickingOptions.horizontal | horizontal} option. */
    get horizontal() {
      return this._horizontal;
    }
    /** Current value of the {@link FlickingOptions.circular | circular} option. */
    get circular() {
      return this._circular;
    }
    /**
     * Current value of the {@link FlickingOptions.circularFallback | circularFallback} option.
     * @since 4.5.0
     */
    get circularFallback() {
      return this._circularFallback;
    }
    /** Current value of the {@link FlickingOptions.bound | bound} option. */
    get bound() {
      return this._bound;
    }
    /** Current value of the {@link FlickingOptions.adaptive | adaptive} option. */
    get adaptive() {
      return this._adaptive;
    }
    /**
     * Current value of the {@link FlickingOptions.panelsPerView | panelsPerView} option.
     * @since 4.2.0
     */
    get panelsPerView() {
      return this._panelsPerView;
    }
    /** Current value of the {@link FlickingOptions.noPanelStyleOverride | noPanelStyleOverride} option. */
    get noPanelStyleOverride() {
      return this._noPanelStyleOverride;
    }
    /**
     * Current value of the {@link FlickingOptions.resizeOnContentsReady | resizeOnContentsReady} option.
     * @since 4.3.0
     */
    get resizeOnContentsReady() {
      return this._resizeOnContentsReady;
    }
    /**
     * Current value of the {@link FlickingOptions.nested | nested} option.
     * @since 4.7.0
     */
    get nested() {
      return this._nested;
    }
    // EVENTS
    /** Current value of the {@link FlickingOptions.needPanelThreshold | needPanelThreshold} option. */
    get needPanelThreshold() {
      return this._needPanelThreshold;
    }
    /**
     * Current value of the {@link FlickingOptions.preventEventsBeforeInit | preventEventsBeforeInit} option.
     * @since 4.2.0
     */
    get preventEventsBeforeInit() {
      return this._preventEventsBeforeInit;
    }
    // ANIMATION
    /** Current value of the {@link FlickingOptions.deceleration | deceleration} option. */
    get deceleration() {
      return this._deceleration;
    }
    /** Current value of the {@link FlickingOptions.easing | easing} option. */
    get easing() {
      return this._easing;
    }
    /** Current value of the {@link FlickingOptions.duration | duration} option. */
    get duration() {
      return this._duration;
    }
    // INPUT
    /** Current value of the {@link FlickingOptions.inputType | inputType} option. */
    get inputType() {
      return this._inputType;
    }
    /** Current value of the {@link FlickingOptions.moveType | moveType} option. */
    get moveType() {
      return this._moveType;
    }
    /** Current value of the {@link FlickingOptions.threshold | threshold} option. */
    get threshold() {
      return this._threshold;
    }
    /** Current value of the {@link FlickingOptions.dragThreshold | dragThreshold} option. */
    get dragThreshold() {
      return this._dragThreshold;
    }
    /**
     * Current value of the {@link FlickingOptions.animationThreshold | animationThreshold} option.
     * @since 4.15.0
     */
    get animationThreshold() {
      return this._animationThreshold;
    }
    /**
     * Current value of the {@link FlickingOptions.useCSSOrder | useCSSOrder} option.
     * @since 4.15.0
     */
    get useCSSOrder() {
      return this._useCSSOrder;
    }
    /** Current value of the {@link FlickingOptions.interruptable | interruptable} option. */
    get interruptable() {
      return this._interruptable;
    }
    /** Current value of the {@link FlickingOptions.bounce | bounce} option. */
    get bounce() {
      return this._bounce;
    }
    /** Current value of the {@link FlickingOptions.iOSEdgeSwipeThreshold | iOSEdgeSwipeThreshold} option. */
    get iOSEdgeSwipeThreshold() {
      return this._iOSEdgeSwipeThreshold;
    }
    /** Current value of the {@link FlickingOptions.preventClickOnDrag | preventClickOnDrag} option. */
    get preventClickOnDrag() {
      return this._preventClickOnDrag;
    }
    /**
     * Current value of the {@link FlickingOptions.preventDefaultOnDrag | preventDefaultOnDrag} option.
     * @since 4.11.0
     */
    get preventDefaultOnDrag() {
      return this._preventDefaultOnDrag;
    }
    /** Current value of the {@link FlickingOptions.disableOnInit | disableOnInit} option. */
    get disableOnInit() {
      return this._disableOnInit;
    }
    /**
     * Current value of the {@link FlickingOptions.changeOnHold | changeOnHold} option.
     * @since 4.8.0
     */
    get changeOnHold() {
      return this._changeOnHold;
    }
    // PERFORMANCE
    /** Current value of the {@link FlickingOptions.renderOnlyVisible | renderOnlyVisible} option. */
    get renderOnlyVisible() {
      return this._renderOnlyVisible;
    }
    /**
     * {@link VirtualManager} instance that manages virtual panels
     * @privateRemarks
     * The virtualManager instance is created during construction by {@link Flicking} constructor.
     * @readonly
     */
    get virtual() {
      return this._virtualManager;
    }
    // OTHERS
    /** Current value of the {@link FlickingOptions.autoInit | autoInit} option. */
    get autoInit() {
      return this._autoInit;
    }
    /** Current value of the {@link FlickingOptions.autoResize | autoResize} option. */
    get autoResize() {
      return this._autoResize;
    }
    /**
     * Current value of the {@link FlickingOptions.useResizeObserver | useResizeObserver} option.
     * @since 4.4.0
     */
    get useResizeObserver() {
      return this._useResizeObserver;
    }
    /**
     * Current value of the {@link FlickingOptions.observePanelResize | observePanelResize} option.
     * @since 4.13.1
     */
    get observePanelResize() {
      return this._observePanelResize;
    }
    /**
     * Current value of the {@link FlickingOptions.resizeDebounce | resizeDebounce} option.
     * @since 4.6.0
     */
    get resizeDebounce() {
      return this._resizeDebounce;
    }
    /**
     * Current value of the {@link FlickingOptions.maxResizeDebounce | maxResizeDebounce} option.
     * @since 4.6.0
     */
    get maxResizeDebounce() {
      return this._maxResizeDebounce;
    }
    /**
     * Current value of the {@link FlickingOptions.useFractionalSize | useFractionalSize} option.
     * @since 4.9.0
     */
    get useFractionalSize() {
      return this._useFractionalSize;
    }
    /**
     * Current value of the {@link FlickingOptions.usePercentagePos | usePercentagePos} option.
     * @since 4.17.0
     */
    get usePercentagePos() {
      return this._usePercentagePos;
    }
    /** Current value of the {@link FlickingOptions.externalRenderer | externalRenderer} option. */
    get externalRenderer() {
      return this._externalRenderer;
    }
    /**
     * @deprecated Use {@link Flicking.externalRenderer | externalRenderer} instead.
     * Current value of the {@link FlickingOptions.renderExternal | renderExternal} option.
     */
    get renderExternal() {
      return this._renderExternal;
    }
    /** @internal */
    get optimizeSizeUpdate() {
      return this._optimizeSizeUpdate;
    }
    // Options Setter
    // UI / LAYOUT
    /**
     * Sets {@link FlickingOptions.align}.
     * @privateRemarks
     * Setting this value updates the renderer and camera alignment, and triggers a resize operation.
     */
    set align(val) {
      this._align = val;
      this._renderer.align = val;
      this._camera.align = val;
      void this.resize();
    }
    set defaultIndex(val) {
      this._defaultIndex = val;
    }
    /**
     * Sets {@link FlickingOptions.horizontal}.
     * @privateRemarks
     * Setting this value updates the control direction and triggers a resize operation.
     */
    set horizontal(val) {
      this._horizontal = val;
      this._control.controller.updateDirection();
      void this.resize();
    }
    /**
     * Sets {@link FlickingOptions.circular}.
     * @privateRemarks
     * Setting this value triggers a resize operation to recalculate panel positions.
     */
    set circular(val) {
      this._circular = val;
      void this.resize();
    }
    /**
     * Sets {@link FlickingOptions.bound}.
     * @privateRemarks
     * Setting this value triggers a resize operation to recalculate panel positions.
     */
    set bound(val) {
      this._bound = val;
      void this.resize();
    }
    /**
     * Sets {@link FlickingOptions.adaptive}.
     * @privateRemarks
     * Setting this value triggers a resize operation to recalculate panel sizes.
     */
    set adaptive(val) {
      this._adaptive = val;
      void this.resize();
    }
    /**
     * Sets {@link FlickingOptions.panelsPerView}.
     * @privateRemarks
     * Setting this value triggers a resize operation to recalculate panel sizes.
     */
    set panelsPerView(val) {
      this._panelsPerView = val;
      void this.resize();
    }
    /**
     * Sets {@link FlickingOptions.noPanelStyleOverride}.
     * @privateRemarks
     * Setting this value triggers a resize operation to update panel styles.
     */
    set noPanelStyleOverride(val) {
      this._noPanelStyleOverride = val;
      void this.resize();
    }
    /**
     * Sets {@link FlickingOptions.resizeOnContentsReady}.
     * @privateRemarks
     * When set to `true`, immediately checks all panels for content readiness.
     */
    set resizeOnContentsReady(val) {
      this._resizeOnContentsReady = val;
      if (val) {
        this._renderer.checkPanelContentsReady(this._renderer.panels);
      }
    }
    /**
     * Sets {@link FlickingOptions.nested}.
     * @privateRemarks
     * Setting this value updates the control's axes options.
     */
    set nested(val) {
      this._nested = val;
      const axes = this._control.controller.axes;
      if (axes) {
        axes.options.nested = val;
      }
    }
    // EVENTS
    set needPanelThreshold(val) {
      this._needPanelThreshold = val;
    }
    set preventEventsBeforeInit(val) {
      this._preventEventsBeforeInit = val;
    }
    // ANIMATION
    /**
     * Sets {@link FlickingOptions.deceleration}.
     * @privateRemarks
     * Setting this value updates the control's axes deceleration option.
     */
    set deceleration(val) {
      this._deceleration = val;
      const axes = this._control.controller.axes;
      if (axes) {
        axes.options.deceleration = val;
      }
    }
    /**
     * Sets {@link FlickingOptions.easing}.
     * @privateRemarks
     * Setting this value updates the control's axes easing option.
     */
    set easing(val) {
      this._easing = val;
      const axes = this._control.controller.axes;
      if (axes) {
        axes.options.easing = val;
      }
    }
    set duration(val) {
      this._duration = val;
    }
    // INPUT
    /**
     * Sets {@link FlickingOptions.inputType}.
     * @privateRemarks
     * Setting this value updates the control's pan input options.
     */
    set inputType(val) {
      this._inputType = val;
      const panInput = this._control.controller.panInput;
      if (panInput) {
        panInput.options.inputType = val;
      }
    }
    /**
     * Sets {@link FlickingOptions.moveType}.
     * @privateRemarks
     * Setting this value creates a new Control instance based on the moveType and preserves the current panel position and progress.
     */
    set moveType(val) {
      this._moveType = val;
      const prevControl = this._control;
      const newControl = this._createControl();
      const activePanel = prevControl.activePanel;
      newControl.copy(prevControl);
      const prevProgressInPanel = activePanel ? this._camera.getProgressInPanel(activePanel) : 0;
      this._control = newControl;
      this._control.updatePosition(prevProgressInPanel);
      this._control.updateInput();
    }
    set threshold(val) {
      this._threshold = val;
    }
    /**
     * Sets {@link FlickingOptions.dragThreshold}.
     * @privateRemarks
     * Setting this value updates the control's pan input threshold option.
     */
    set dragThreshold(val) {
      this._dragThreshold = val;
      const panInput = this._control.controller.panInput;
      if (panInput) {
        panInput.options.threshold = val;
      }
    }
    /**
     * Sets {@link FlickingOptions.animationThreshold}.
     */
    set animationThreshold(val) {
      this._animationThreshold = val;
    }
    /**
     * Sets {@link FlickingOptions.useCSSOrder}.
     */
    set useCSSOrder(val) {
      this._useCSSOrder = val;
    }
    /**
     * Sets {@link FlickingOptions.interruptable}.
     * @privateRemarks
     * Setting this value updates the control's axes interruptable option.
     */
    set interruptable(val) {
      this._interruptable = val;
      const axes = this._control.controller.axes;
      if (axes) {
        axes.options.interruptable = val;
      }
    }
    /**
     * Sets {@link FlickingOptions.bounce}.
     * @privateRemarks
     * Setting this value updates the control input configuration.
     */
    set bounce(val) {
      this._bounce = val;
      this._control.updateInput();
    }
    /**
     * Sets {@link FlickingOptions.iOSEdgeSwipeThreshold}.
     * @privateRemarks
     * Setting this value updates the control's pan input iOS edge swipe threshold option.
     */
    set iOSEdgeSwipeThreshold(val) {
      this._iOSEdgeSwipeThreshold = val;
      const panInput = this._control.controller.panInput;
      if (panInput) {
        panInput.options.iOSEdgeSwipeThreshold = val;
      }
    }
    /**
     * Sets {@link FlickingOptions.preventClickOnDrag}.
     * @privateRemarks
     * Setting this value adds or removes the prevent click handler from the control.
     */
    set preventClickOnDrag(val) {
      const prevVal = this._preventClickOnDrag;
      if (val === prevVal) return;
      const controller = this._control.controller;
      if (val) {
        controller.addPreventClickHandler();
      } else {
        controller.removePreventClickHandler();
      }
      this._preventClickOnDrag = val;
    }
    /**
     * Sets {@link FlickingOptions.preventDefaultOnDrag}.
     * @privateRemarks
     * Setting this value updates the control's pan input preventDefaultOnDrag option.
     */
    set preventDefaultOnDrag(val) {
      this._preventDefaultOnDrag = val;
      const panInput = this._control.controller.panInput;
      if (panInput) {
        panInput.options.preventDefaultOnDrag = val;
      }
    }
    set disableOnInit(val) {
      this._disableOnInit = val;
    }
    set changeOnHold(val) {
      this._changeOnHold = val;
    }
    // PERFORMANCE
    /**
     * Sets {@link FlickingOptions.renderOnlyVisible}.
     * @privateRemarks
     * Setting this value triggers an immediate render operation.
     */
    set renderOnlyVisible(val) {
      this._renderOnlyVisible = val;
      void this._renderer.render();
    }
    // OTHERS
    /**
     * Sets {@link FlickingOptions.autoResize}.
     * @privateRemarks
     * Setting this value enables or disables the auto resizer if Flicking is already initialized.
     */
    set autoResize(val) {
      this._autoResize = val;
      if (!this._initialized) {
        return;
      }
      if (val) {
        this._autoResizer.enable();
      } else {
        this._autoResizer.disable();
      }
    }
    /**
     * Sets {@link FlickingOptions.useResizeObserver}.
     * @privateRemarks
     * Setting this value re-enables the auto resizer if Flicking is initialized and autoResize is enabled.
     */
    set useResizeObserver(val) {
      this._useResizeObserver = val;
      if (this._initialized && this._autoResize) {
        this._autoResizer.enable();
      }
    }
    /**
     * Sets {@link FlickingOptions.observePanelResize}.
     * @privateRemarks
     * Setting this value starts or stops observing panel sizes if Flicking is initialized and autoResize is enabled.
     */
    set observePanelResize(val) {
      this._observePanelResize = val;
      if (this._initialized && this._autoResize) {
        if (val) {
          this._autoResizer.observePanels();
        } else {
          this._autoResizer.unobservePanels();
        }
      }
    }
    set optimizeSizeUpdate(val) {
      this._optimizeSizeUpdate = val;
    }
    /**
     * Sets {@link FlickingOptions.usePercentagePos}.
     * @privateRemarks
     * Setting this value immediately re-applies the camera element's transform with the new unit.
     * The transform is not re-applied when Flicking is not initialized or the renderer is rendering.
     */
    set usePercentagePos(val) {
      this._usePercentagePos = val;
      this._camera.applyTransform();
    }
    /**
     * Initialize Flicking and move to the default index.
     * @remarks
     * This method is automatically called in the constructor when {@link FlickingOptions.autoInit | autoInit} is `true` (default).
     * If Flicking is already initialized, this method returns immediately without doing anything.
     * @fires {@link ReadyEvent}
     * @returns Promise that resolves when initialization is complete
     */
    init() {
      if (this._initialized) return Promise.resolve();
      const camera = this._camera;
      const renderer = this._renderer;
      const control = this._control;
      const virtualManager = this._virtualManager;
      const originalTrigger = this.trigger;
      const preventEventsBeforeInit = this._preventEventsBeforeInit;
      camera.init();
      virtualManager.init();
      renderer.init(this);
      control.init(this);
      if (preventEventsBeforeInit) {
        this.trigger = () => this;
      }
      this._initialResize();
      this._moveToInitialPanel();
      if (this._autoResize) {
        this._autoResizer.enable();
      }
      if (this._preventClickOnDrag) {
        control.controller.addPreventClickHandler();
      }
      if (this._disableOnInit) {
        this.disableInput();
      }
      renderer.checkPanelContentsReady(renderer.panels);
      this._initialized = true;
      return renderer.render().then(() => {
        this._plugins.forEach((plugin) => plugin.init(this));
        if (preventEventsBeforeInit) {
          this.trigger = originalTrigger;
        }
        this.trigger(new Component.ComponentEvent(EVENTS.READY));
      });
    }
    /**
     * Destroy Flicking and remove all event handlers.
     * @remarks
     * This method cleans up all resources including event handlers, components, and plugins.
     * After calling this method, {@link Flicking.initialized} will be `false` and the instance should not be used.
     */
    destroy() {
      this.off();
      this._autoResizer.disable();
      this._control.destroy();
      this._camera.destroy();
      this._renderer.destroy();
      this._plugins.forEach((plugin) => plugin.destroy());
      this._scheduleResize = false;
      this._initialized = false;
      this._isResizing = false;
    }
    /**
     * Move to the previous panel (current index - 1).
     * @param duration - Duration of the panel movement animation (unit: ms). Defaults to {@link FlickingOptions.duration}
     * @fires {@link MovementEvents}
     * @throws {@link MovementErrors}
     * @returns Promise that resolves after reaching the previous panel
     */
    prev(duration = this._duration) {
      var _a, _b, _c;
      return this.moveTo((_c = (_b = (_a = this._control.activePanel) == null ? void 0 : _a.prev()) == null ? void 0 : _b.index) != null ? _c : -1, duration, DIRECTION.PREV);
    }
    /**
     * Move to the next panel (current index + 1).
     * @param duration - Duration of the panel movement animation (unit: ms). Defaults to {@link FlickingOptions.duration}
     * @fires {@link MovementEvents}
     * @throws {@link MovementErrors}
     * @returns Promise that resolves after reaching the next panel
     */
    next(duration = this._duration) {
      var _a, _b, _c;
      return this.moveTo((_c = (_b = (_a = this._control.activePanel) == null ? void 0 : _a.next()) == null ? void 0 : _b.index) != null ? _c : this._renderer.panelCount, duration, DIRECTION.NEXT);
    }
    /**
     * Move to the panel with the given index.
     * @param index - The index of the panel to move to
     * @param duration - Duration of the animation (unit: ms). Defaults to {@link FlickingOptions.duration}
     * @param direction - Direction to move (circular mode only). Defaults to {@link DIRECTION.NONE}
     * @fires {@link MovementEvents}
     * @throws {@link MovementErrors}
     * @returns Promise that resolves after reaching the target panel
     */
    moveTo(index, duration = this._duration, direction = DIRECTION.NONE) {
      const renderer = this._renderer;
      const panelCount = renderer.panelCount;
      const panel = renderer.getPanel(index);
      if (!panel) {
        return Promise.reject(
          new FlickingError(MESSAGE.INDEX_OUT_OF_RANGE(index, 0, panelCount - 1), CODE.INDEX_OUT_OF_RANGE)
        );
      }
      if (this._control.animating) {
        return Promise.reject(
          new FlickingError(MESSAGE.ANIMATION_ALREADY_PLAYING, CODE.ANIMATION_ALREADY_PLAYING)
        );
      }
      if (this._control.holding) {
        this._control.controller.release();
      }
      return this._control.moveToPanel(panel, {
        duration,
        direction
      });
    }
    /**
     * Change the destination and duration of the animation currently playing.
     * @remarks
     * This method does nothing if no animation is currently playing.
     * @param index - The index of the panel to move to
     * @param duration - Duration of the animation (unit: ms)
     * @param direction - Direction to move. Only available when {@link FlickingOptions.circular} is enabled
     * @since 4.10.0
     * @throws {@link AnimationUpdateErrors}
     */
    updateAnimation(index, duration, direction) {
      if (!this._control.animating) {
        return;
      }
      const renderer = this._renderer;
      const panelCount = renderer.panelCount;
      const panel = renderer.getPanel(index);
      if (!panel) {
        throw new FlickingError(
          MESSAGE.INDEX_OUT_OF_RANGE(index, 0, panelCount - 1),
          CODE.INDEX_OUT_OF_RANGE
        );
      }
      this._control.updateAnimation(panel, duration, direction);
    }
    /**
     * Stop the animation currently playing.
     * @remarks
     * This method does nothing if no animation is currently playing.
     * @since 4.10.0
     * @fires {@link MoveEndEvent}
     */
    stopAnimation() {
      if (!this._control.animating) {
        return;
      }
      this._control.stopAnimation();
    }
    /**
     * Get the panel at the given index.
     * @param index - The index of the panel to get
     * @returns The panel at the given index, or `null` if it doesn't exist. This is a shorthand for `Flicking.renderer.getPanel(index)`.
     * @example
     * ```ts
     * const panel = flicking.getPanel(0);
     * // Which is a shorthand to...
     * const samePanel = flicking.panels[0];
     * ```
     */
    getPanel(index) {
      return this._renderer.getPanel(index);
    }
    /**
     * Enable user input (mouse/touch).
     * @remarks
     * This is a shorthand for `Flicking.control.enable`.
     * @returns The current instance for method chaining
     */
    enableInput() {
      this._control.enable();
      return this;
    }
    /**
     * Disable user input (mouse/touch).
     * @remarks
     * This is a shorthand for `Flicking.control.disable`.
     * @returns The current instance for method chaining
     */
    disableInput() {
      this._control.disable();
      return this;
    }
    /**
     * Get the current Flicking status.
     * @param options - {@link GetStatusParams}
     * @returns Status object that can be used with {@link Flicking.setStatus} to restore the state
     */
    getStatus(options = {}) {
      var _a, _b;
      const { index = true, position = true, includePanelHTML = false, visiblePanelsOnly = false } = options;
      const camera = this._camera;
      const panels = visiblePanelsOnly ? this.visiblePanels : this.panels;
      const status = {
        panels: panels.map((panel) => {
          const panelInfo = { index: panel.index };
          if (includePanelHTML) {
            panelInfo.html = panel.element.outerHTML;
          }
          return panelInfo;
        })
      };
      if (index) {
        status.index = this.index;
      }
      if (position) {
        const nearestAnchor = camera.findNearestAnchor(camera.position);
        if (nearestAnchor) {
          status.position = {
            panel: nearestAnchor.panel.index,
            progressInPanel: camera.getProgressInPanel(nearestAnchor.panel)
          };
        }
      }
      if (visiblePanelsOnly) {
        const visiblePanels = this.visiblePanels;
        status.visibleOffset = (_b = (_a = visiblePanels[0]) == null ? void 0 : _a.index) != null ? _b : 0;
      }
      return status;
    }
    /**
     * Restore Flicking to the state of the given {@link Status}.
     * @param status - {@link Status}
     * @throws {@link StatusRestoreErrors}
     */
    setStatus(status) {
      var _a;
      if (!this._initialized) {
        throw new FlickingError(MESSAGE.NOT_INITIALIZED, CODE.NOT_INITIALIZED);
      }
      const { index, position, visibleOffset, panels } = status;
      const renderer = this._renderer;
      const control = this._control;
      if (((_a = panels[0]) == null ? void 0 : _a.html) && !this._renderExternal) {
        renderer.batchRemove({
          index: 0,
          deleteCount: this.panels.length,
          hasDOMInElements: true
        });
        renderer.batchInsert({
          index: 0,
          elements: parseElement(panels.map((panel) => panel.html)),
          hasDOMInElements: true
        });
      }
      if (index != null) {
        const panelIndex = visibleOffset ? index - visibleOffset : index;
        void this.moveTo(panelIndex, 0).catch(() => void 0);
      }
      if (position && this._moveType === MOVE_TYPE.FREE_SCROLL) {
        const { panel, progressInPanel } = position;
        const panelIndex = visibleOffset ? panel - visibleOffset : panel;
        const panelRange = renderer.panels[panelIndex].range;
        const newCameraPos = panelRange.min + (panelRange.max - panelRange.min) * progressInPanel;
        void control.moveToPosition(newCameraPos, 0).catch(() => void 0);
      }
    }
    /**
     * Add plugins to Flicking.
     * @remarks
     * Plugins are automatically initialized if Flicking is already initialized.
     * @param plugins - {@link Plugin}
     * @returns The current instance for method chaining
     * @see https://github.com/naver/egjs-flicking-plugins
     */
    addPlugins(...plugins) {
      if (this._initialized) {
        plugins.forEach((item) => item.init(this));
      }
      this._plugins.push(...plugins);
      return this;
    }
    /**
     * Remove plugins from Flicking.
     * @param plugins - {@link Plugin}
     * @returns The current instance for method chaining
     * @see https://github.com/naver/egjs-flicking-plugins
     */
    removePlugins(...plugins) {
      plugins.forEach((item) => {
        const foundIndex = findIndex(this._plugins, (val) => val === item);
        if (foundIndex >= 0) {
          item.destroy();
          this._plugins.splice(foundIndex, 1);
        }
      });
      return this;
    }
    /**
     * Update viewport and panel sizes.
     * @remarks
     * This method does nothing if a resize is already in progress.
     * @fires {@link ResizeEvents}
     * @returns Promise that resolves when resize is complete
     */
    resize() {
      return __async(this, null, function* () {
        if (!this._initialized) {
          return;
        }
        if (this._isResizing) {
          this._scheduleResize = true;
          return;
        }
        this._scheduleResize = false;
        this._isResizing = true;
        const viewport = this._viewport;
        const renderer = this._renderer;
        const camera = this._camera;
        const control = this._control;
        const activePanel = control.activePanel;
        const prevWidth = viewport.width;
        const prevHeight = viewport.height;
        const prevProgressInPanel = activePanel ? camera.getProgressInPanel(activePanel) : 0;
        this.trigger(
          new Component.ComponentEvent(EVENTS.BEFORE_RESIZE, {
            width: prevWidth,
            height: prevHeight,
            element: viewport.element
          })
        );
        if (this._optimizeSizeUpdate) {
          const measuredSize = viewport.measureSize();
          if (this.horizontal && measuredSize.width !== prevWidth || !this.horizontal && measuredSize.height !== prevHeight) {
            yield renderer.forceRenderAllPanels();
          }
        } else {
          yield renderer.forceRenderAllPanels();
        }
        if (!this._initialized) {
          return;
        }
        viewport.resize();
        renderer.updatePanelSize();
        camera.updateAlignPos();
        camera.updateRange();
        camera.updateAnchors();
        camera.updateAdaptiveHeight();
        camera.updatePanelOrder();
        if (!control.animating) {
          control.updatePosition(prevProgressInPanel);
        }
        camera.updateOffset();
        yield renderer.render();
        if (!this._initialized) {
          return;
        }
        if (control.animating) ;
        else {
          control.updatePosition(prevProgressInPanel);
          control.updateInput();
        }
        const newWidth = viewport.width;
        const newHeight = viewport.height;
        const sizeChanged = newWidth !== prevWidth || newHeight !== prevHeight;
        this.trigger(
          new Component.ComponentEvent(EVENTS.AFTER_RESIZE, {
            width: viewport.width,
            height: viewport.height,
            prev: {
              width: prevWidth,
              height: prevHeight
            },
            sizeChanged,
            element: viewport.element
          })
        );
        this._isResizing = false;
        if (this._scheduleResize) {
          void this.resize();
        }
        return;
      });
    }
    /**
     * Add new panels after the last panel.
     * @param element - A new HTMLElement, outerHTML string, or an array of both
     * @throws {@link DOMManipulationErrors}
     * @returns Array of appended panels
     * @example
     * ```ts
     * const flicking = new Flicking("#flick");
     * flicking.append(document.createElement("div"));
     * flicking.append("<div>Panel</div>");
     * flicking.append(["<div>Panel</div>", document.createElement("div")]);
     * ```
     */
    append(element) {
      return this.insert(this._renderer.panelCount, element);
    }
    /**
     * Add new panels before the first panel.
     * @remarks
     * This will increase the index of existing panels by the number of panels added.
     * @param element - A new HTMLElement, outerHTML string, or an array of both
     * @throws {@link DOMManipulationErrors}
     * @returns Array of prepended panels
     * @example
     * ```ts
     * const flicking = new Flicking("#flick");
     * flicking.prepend(document.createElement("div"));
     * flicking.prepend("<div>Panel</div>");
     * flicking.prepend(["<div>Panel</div>", document.createElement("div")]);
     * ```
     */
    prepend(element) {
      return this.insert(0, element);
    }
    /**
     * Insert new panels at the given index.
     * @remarks
     * This will increase the index of panels at or after the given index by the number of panels added.
     * @param index - Index to insert new panels at
     * @param element - A new HTMLElement, outerHTML string, or an array of both
     * @throws {@link DOMManipulationErrors}
     * @returns Array of inserted panels
     * @example
     * ```ts
     * const flicking = new Flicking("#flick");
     * flicking.insert(0, document.createElement("div"));
     * flicking.insert(2, "<div>Panel</div>");
     * flicking.insert(1, ["<div>Panel</div>", document.createElement("div")]);
     * ```
     */
    insert(index, element) {
      if (this._renderExternal) {
        throw new FlickingError(MESSAGE.NOT_ALLOWED_IN_FRAMEWORK, CODE.NOT_ALLOWED_IN_FRAMEWORK);
      }
      return this._renderer.batchInsert({
        index,
        elements: parseElement(element),
        hasDOMInElements: true
      });
    }
    /**
     * Remove panels starting from the given index.
     * @remarks
     * This will decrease the index of panels after the removed ones by the number of panels removed.
     * @param index - Index of the first panel to remove
     * @param deleteCount - Number of panels to remove. Defaults to `1`
     * @throws {@link DOMManipulationErrors}
     * @returns Array of removed panels
     */
    remove(index, deleteCount = 1) {
      if (this._renderExternal) {
        throw new FlickingError(MESSAGE.NOT_ALLOWED_IN_FRAMEWORK, CODE.NOT_ALLOWED_IN_FRAMEWORK);
      }
      return this._renderer.batchRemove({
        index,
        deleteCount,
        hasDOMInElements: true
      });
    }
    /**
     * Factory method to create the appropriate Control implementation based on moveType option.
     * @internal
     * @privateRemarks
     * Called during constructor and when moveType option is changed. The moveType option must be set before calling this method.
     * Throws error if moveType is invalid.
     */
    _createControl() {
      var _a;
      const moveType = this._moveType;
      const moveTypes = Object.keys(MOVE_TYPE).map((key) => MOVE_TYPE[key]);
      const moveTypeStr = Array.isArray(moveType) ? moveType[0] : moveType;
      const moveTypeOptions = Array.isArray(moveType) ? (_a = moveType[1]) != null ? _a : {} : {};
      if (!includes(moveTypes, moveTypeStr)) {
        throw new FlickingError(
          MESSAGE.WRONG_OPTION("moveType", JSON.stringify(moveType)),
          CODE.WRONG_OPTION
        );
      }
      switch (moveTypeStr) {
        case MOVE_TYPE.SNAP:
          return new SnapControl(moveTypeOptions);
        case MOVE_TYPE.FREE_SCROLL:
          return new FreeControl(moveTypeOptions);
        case MOVE_TYPE.STRICT:
          return new StrictControl(moveTypeOptions);
      }
    }
    /**
     * Factory method to create Camera instance for managing viewport movement and positioning.
     * @internal
     * @privateRemarks
     * Called during constructor. The align option must be set before calling this method.
     * Warns if both circular and bound options are enabled (bound is ignored).
     */
    _createCamera() {
      if (this._circular && this._bound) {
        console.warn('"circular" and "bound" option cannot be used together, ignoring bound.');
      }
      return new Camera$1(this, {
        align: this._align
      });
    }
    /**
     * Factory method to create the appropriate Renderer implementation based on externalRenderer and virtual options.
     * @internal
     * @privateRemarks
     * Called during constructor. Selects ExternalRenderer if externalRenderer is provided, otherwise creates VanillaRenderer or ExternalRenderer based on renderExternal option.
     * Warns if virtual is enabled without panelsPerView.
     */
    _createRenderer() {
      const externalRenderer = this._externalRenderer;
      if (this._virtual && this._panelsPerView <= 0) {
        console.warn('"virtual" and "panelsPerView" option should be used together, ignoring virtual.');
      }
      return externalRenderer ? externalRenderer : this._renderExternal ? this._createExternalRenderer() : this._createVanillaRenderer();
    }
    /**
     * Factory method to create ExternalRenderer from renderExternal option (deprecated).
     * @internal
     * @privateRemarks
     * Called by _createRenderer when renderExternal option is set. The renderExternal option must not be null and must contain renderer class and options.
     */
    _createExternalRenderer() {
      const { renderer, rendererOptions } = this._renderExternal;
      return new renderer(__spreadValues({ align: this._align }, rendererOptions));
    }
    /**
     * Factory method to create VanillaRenderer for vanilla JavaScript rendering.
     * @internal
     * @privateRemarks
     * Called by _createRenderer when neither externalRenderer nor renderExternal is set.
     * Uses VirtualRenderingStrategy if virtual is enabled, otherwise NormalRenderingStrategy.
     */
    _createVanillaRenderer() {
      const virtual = this.virtualEnabled;
      return new VanillaRenderer({
        align: this._align,
        strategy: virtual ? new VirtualRenderingStrategy() : new NormalRenderingStrategy({
          providerCtor: VanillaElementProvider
        })
      });
    }
    /**
     * Move camera to the initial panel position based on defaultIndex option.
     * @internal
     * @privateRemarks
     * Called during init() method, after _initialResize(). Requires camera, renderer, and control to be already initialized.
     * Throws error if the initial panel position is not reachable.
     */
    _moveToInitialPanel() {
      const renderer = this._renderer;
      const control = this._control;
      const camera = this._camera;
      const defaultPanel = renderer.getPanel(this._defaultIndex) || renderer.getPanel(0);
      if (!defaultPanel) return;
      const nearestAnchor = camera.findNearestAnchor(defaultPanel.position);
      const initialPanel = nearestAnchor && defaultPanel.position !== nearestAnchor.panel.position && defaultPanel.index !== nearestAnchor.panel.index ? nearestAnchor.panel : defaultPanel;
      control.setActive(initialPanel, null, false);
      if (!nearestAnchor) {
        throw new FlickingError(
          MESSAGE.POSITION_NOT_REACHABLE(initialPanel.position),
          CODE.POSITION_NOT_REACHABLE
        );
      }
      let position = initialPanel.position;
      if (!camera.canReach(initialPanel)) {
        position = nearestAnchor.position;
      }
      camera.lookAt(position);
      control.updateInput();
      camera.updateOffset();
    }
    /**
     * Calculate initial viewport and panel sizes during initialization.
     * @internal
     * @privateRemarks
     * Called during init() method, before _moveToInitialPanel(). This is separate from the regular resize() to avoid triggering events before initialization is complete.
     * Requires viewport, renderer, camera, and control to be already initialized. Triggers BEFORE_RESIZE and AFTER_RESIZE events.
     */
    _initialResize() {
      const viewport = this._viewport;
      const renderer = this._renderer;
      const camera = this._camera;
      const control = this._control;
      this.trigger(
        new Component.ComponentEvent(EVENTS.BEFORE_RESIZE, {
          width: 0,
          height: 0,
          element: viewport.element
        })
      );
      viewport.resize();
      renderer.updatePanelSize();
      camera.updateAlignPos();
      camera.updateRange();
      camera.updateAnchors();
      camera.updateOffset();
      control.updateInput();
      const newWidth = viewport.width;
      const newHeight = viewport.height;
      const sizeChanged = newWidth !== 0 || newHeight !== 0;
      this.trigger(
        new Component.ComponentEvent(EVENTS.AFTER_RESIZE, {
          width: viewport.width,
          height: viewport.height,
          prev: {
            width: 0,
            height: 0
          },
          sizeChanged,
          element: viewport.element
        })
      );
    }
  };
  _Flicking.VERSION = "4.17.0";
  let Flicking = _Flicking;
  const SIDE_EVENTS = {
    HOLD_START: "sideHoldStart",
    HOLD_END: "sideHoldEnd",
    MOVE_START: "sideMoveStart",
    MOVE: "sideMove",
    MOVE_END: "sideMoveEnd",
    WILL_CHANGE: "sideWillChange",
    CHANGED: "sideChanged",
    WILL_RESTORE: "sideWillRestore",
    RESTORED: "sideRestored"
  };
  class CrossFlicking extends Flicking {
    constructor(root, options) {
      super(root, options);
      this._syncToCategory = (index, outerIndex) => {
        if (this._disableIndexSync) {
          return;
        }
        this.stopAnimation();
        this._sideFlicking.forEach((child, i) => {
          const { start, end } = this._sideState[i];
          if (start <= index && end >= index && outerIndex !== i) {
            child.stopAnimation();
            void child.moveTo(index, 0);
            void this.moveTo(i, 0);
          }
        });
      };
      this._setDraggable = (direction, draggable) => {
        if (!this._disableSlideOnHold) {
          return;
        }
        const dragThreshold = this._originalDragThreshold;
        const threshold = draggable ? dragThreshold && dragThreshold >= 10 ? dragThreshold : 10 : Infinity;
        if (direction === MOVE_DIRECTION.HORIZONTAL === this.horizontal) {
          this.dragThreshold = threshold;
        } else if (direction === MOVE_DIRECTION.VERTICAL === this.horizontal) {
          this._sideFlicking.forEach((child) => {
            child.dragThreshold = threshold;
          });
        }
      };
      this._setPreviousSideIndex = () => {
        this._sideFlicking.forEach((child, i) => {
          const { start, end } = this._sideState[i];
          if (this._preserveIndex) {
            if (this._nextIndex !== i) {
              if (child.index < start) {
                child.stopAnimation();
                void child.moveTo(start, 0);
              } else if (child.index > end) {
                child.stopAnimation();
                void child.moveTo(end, 0);
              }
            }
          } else {
            if (this._nextIndex !== i) {
              void child.moveTo(start, 0);
            }
          }
        });
      };
      this._addSideIndex = (e) => {
        e.sideIndex = this._sideFlicking[e.index].index;
      };
      this._onHorizontalHoldStart = () => {
        this._setDraggable(MOVE_DIRECTION.HORIZONTAL, true);
        this._moveDirection = null;
      };
      this._onHorizontalMove = (e) => {
        if (e.isTrusted && !this._moveDirection) {
          this._setDraggable(MOVE_DIRECTION.VERTICAL, false);
          this._moveDirection = MOVE_DIRECTION.HORIZONTAL;
        }
      };
      this._onHorizontalMoveEnd = (e) => {
        const visiblePanels = this.visiblePanels;
        if (visiblePanels.length > 1) {
          this._nextIndex = e.direction === "NEXT" ? visiblePanels[1].index : visiblePanels[0].index;
        } else {
          this._nextIndex = visiblePanels[0].index;
        }
        this._setDraggable(MOVE_DIRECTION.VERTICAL, true);
        this._moveDirection = null;
        requestAnimationFrame(() => this._setPreviousSideIndex());
        if (e.isTrusted) {
          this._syncToCategory(this._sideFlicking[this._nextIndex].index, this._nextIndex);
        }
      };
      this._onSideHoldStart = () => {
        this._setDraggable(MOVE_DIRECTION.VERTICAL, true);
        this._moveDirection = null;
      };
      this._onSideMove = (e) => {
        if (e.isTrusted && !this._moveDirection) {
          this._setDraggable(MOVE_DIRECTION.HORIZONTAL, false);
          this._moveDirection = MOVE_DIRECTION.VERTICAL;
        }
      };
      this._onSideMoveEnd = () => {
        this._setDraggable(MOVE_DIRECTION.HORIZONTAL, true);
        this._moveDirection = null;
      };
      this._onSideChanged = (e) => {
        if (this.visiblePanels.length < 2 && this._sideFlicking[this.index] === e.currentTarget) {
          this._syncToCategory(e.index, this.index);
        }
      };
      const { sideOptions = {}, preserveIndex = true, disableSlideOnHold = true, disableIndexSync = false } = options;
      this._moveDirection = null;
      this._nextIndex = 0;
      this._originalDragThreshold = this.dragThreshold;
      this._sideOptions = sideOptions;
      this._preserveIndex = preserveIndex;
      this._disableSlideOnHold = disableSlideOnHold;
      this._disableIndexSync = disableIndexSync;
    }
    // Components
    get sideFlicking() {
      return this._sideFlicking;
    }
    get sideIndex() {
      return this._sideFlicking.map((i) => i.index);
    }
    get sideState() {
      return this._sideState;
    }
    // Options Getter
    get sideOptions() {
      return this._sideOptions;
    }
    get preserveIndex() {
      return this._preserveIndex;
    }
    get disableSlideOnHold() {
      return this._disableSlideOnHold;
    }
    get disableIndexSync() {
      return this._disableIndexSync;
    }
    // Options Setter
    set sideOptions(val) {
      this._sideOptions = val;
    }
    set preserveIndex(val) {
      this._preserveIndex = val;
    }
    set disableSlideOnHold(val) {
      this._disableSlideOnHold = val;
    }
    set disableIndexSync(val) {
      this._disableIndexSync = val;
    }
    init() {
      return super.init().then(() => {
        this._sideState = this._createSideState();
        this._sideFlicking = this._createSideFlicking();
        this._addComponentEvents();
      });
    }
    destroy() {
      this._sideFlicking.forEach((flicking) => {
        flicking.destroy();
      });
      super.destroy();
    }
    _addComponentEvents() {
      this.on(EVENTS.HOLD_START, this._onHorizontalHoldStart);
      this.on(EVENTS.MOVE, this._onHorizontalMove);
      this.on(EVENTS.MOVE_END, this._onHorizontalMoveEnd);
      [EVENTS.CHANGED, EVENTS.WILL_CHANGE].forEach((event) => {
        this.on(event, this._addSideIndex);
      });
      this._sideFlicking.forEach((flicking, mainIndex) => {
        flicking.on(EVENTS.HOLD_START, this._onSideHoldStart);
        flicking.on(EVENTS.MOVE, this._onSideMove);
        flicking.on(EVENTS.MOVE_END, this._onSideMoveEnd);
        flicking.on(EVENTS.CHANGED, this._onSideChanged);
        Object.keys(SIDE_EVENTS).forEach((name) => {
          flicking.on(EVENTS[name], (event) => {
            this.trigger(
              new Component.ComponentEvent(SIDE_EVENTS[name], __spreadValues({
                mainIndex
              }, event))
            );
          });
        });
      });
    }
    _createSideState() {
      const viewportEl = this.element;
      const cameraEl = this.camera.element;
      const panels = toArray(cameraEl.children);
      const isCrossStructure = getDataAttributes(viewportEl, "data-cross-").structure;
      let sideState = [];
      if (!isCrossStructure) {
        const groupPanels = this._getGroupFromAttribute(panels);
        const groupKeys = Object.keys(groupPanels);
        if (groupKeys.length) {
          sideState = this._getSideStateFromGroup(groupPanels);
          this.remove(0, this.panelCount - groupKeys.length);
        } else {
          sideState = this._getSideStateFromPanels(panels);
        }
        this._createCrossStructure(sideState);
      } else {
        sideState = this._getSideStateFromCrossStructure(panels);
      }
      void this.resize();
      return sideState;
    }
    _createCrossStructure(sideState) {
      const sideCamera = document.createElement("div");
      let sidePanels = "";
      sideCamera.classList.add(CLASS.CAMERA);
      sideState.forEach((state, i) => {
        const panel = this.camera.children[i];
        sidePanels += state.element.innerHTML;
        Array.from(panel.attributes).forEach((attribute) => panel.removeAttribute(attribute.name));
      });
      sideCamera.innerHTML = sidePanels;
      sideState.forEach((_, i) => {
        const panel = this.camera.children[i];
        [CLASS.VIEWPORT, CLASS.VERTICAL].forEach((className) => {
          if (!panel.classList.contains(className)) {
            panel.classList.add(className);
          }
        });
        panel.innerHTML = sideCamera.outerHTML;
      });
      this.element.setAttribute("data-cross-structure", "true");
    }
    _getGroupFromAttribute(panels) {
      const groupKeys = [];
      const groupPanels = {};
      panels.forEach((panel) => {
        const groupKey = getDataAttributes(panel, "data-cross-").groupkey;
        if (groupKey && !includes(groupKeys, groupKey)) {
          groupKeys.push(groupKey);
          groupPanels[groupKey] = [panel];
        } else if (groupKey) {
          groupPanels[groupKey].push(panel);
        }
      });
      return groupPanels;
    }
    _getSideStateFromGroup(groupPanels) {
      return Object.keys(groupPanels).reduce((state, key) => {
        const start = state.length ? +state[state.length - 1].end + 1 : 0;
        const element = groupPanels[key].reduce((el, panel) => {
          el.innerHTML += panel.outerHTML;
          return el;
        }, document.createElement("div"));
        return [
          ...state,
          {
            key,
            start,
            end: start + groupPanels[key].length - 1,
            element
          }
        ];
      }, []);
    }
    _getSideStateFromPanels(panels) {
      return panels.reduce((state, panel, i) => {
        const start = state.length ? +state[state.length - 1].end + 1 : 0;
        return [
          ...state,
          {
            key: i.toString(),
            start,
            end: start + panel.children.length - 1,
            element: panel
          }
        ];
      }, []);
    }
    _getSideStateFromCrossStructure(panels) {
      const groupPanels = this._getGroupFromAttribute(panels);
      return this._getSideStateFromGroup(groupPanels);
    }
    _createSideFlicking() {
      return this.sideState.map((state, i) => {
        return new Flicking(this.camera.children[i], __spreadProps(__spreadValues({}, this.sideOptions), {
          horizontal: false,
          panelsPerView: 1,
          defaultIndex: state.start
        }));
      });
    }
  }
  const CrossFlicking$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
    __proto__: null,
    CrossFlicking,
    SIDE_EVENTS
  }, Symbol.toStringTag, { value: "Module" }));
  const Core = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
    __proto__: null,
    AnchorPoint,
    Panel,
    VanillaElementProvider,
    Viewport,
    VirtualElementProvider,
    VirtualManager,
    VirtualPanel
  }, Symbol.toStringTag, { value: "Module" }));
  const getDefaultCameraTransform = (align = ALIGN.CENTER, horizontal = true, firstPanelSize) => {
    const cameraAlign = getCameraAlign(align);
    const panelAlign = getPanelAlign(align);
    if (panelAlign == null) return "";
    const camPosition = `calc(${cameraAlign} - (${firstPanelSize || "0px"} * ${panelAlign.percentage}) - ${panelAlign.absolute}px)`;
    return horizontal ? `translate(${camPosition})` : `translate(0, ${camPosition})`;
  };
  const getCameraAlign = (align) => {
    const alignVal = typeof align === "object" ? align.camera : align;
    return parseAlign(alignVal);
  };
  const getPanelAlign = (align) => {
    const alignVal = typeof align === "object" ? align.panel : align;
    return parseArithmeticExpression(parseAlign(alignVal));
  };
  const parseAlign = (alignVal) => {
    if (typeof alignVal === "number") {
      return `${alignVal}px`;
    }
    switch (alignVal) {
      case ALIGN.CENTER:
        return "50%";
      case ALIGN.NEXT:
        return "100%";
      case ALIGN.PREV:
        return "0%";
      default:
        return alignVal;
    }
  };
  const getRenderingPanels = (flicking, diffResult) => {
    const removedPanels = diffResult.removed.reduce((map, idx) => {
      map[idx] = true;
      return map;
    }, {});
    const maintainedMap = diffResult.maintained.reduce((map, [prev, current]) => {
      map[prev] = current;
      return map;
    }, {});
    const renderingPanels = flicking.panels.filter((panel) => !removedPanels[panel.index]);
    if (!flicking.useCSSOrder) {
      renderingPanels.sort((panel1, panel2) => panel1.position + panel1.offset - (panel2.position + panel2.offset));
    }
    return [
      ...renderingPanels.map((panel) => diffResult.list[maintainedMap[panel.index]]),
      ...diffResult.added.map((idx) => diffResult.list[idx])
    ];
  };
  const sync = (flicking, diffResult, rendered) => {
    const renderer = flicking.renderer;
    const panels = renderer.panels;
    const prevList = [...diffResult.prevList];
    const added = [];
    const removed = [];
    if (diffResult.removed.length > 0) {
      let endIdx = -1;
      let prevIdx = -1;
      diffResult.removed.forEach((removedIdx) => {
        if (endIdx < 0) {
          endIdx = removedIdx;
        }
        if (prevIdx >= 0 && removedIdx !== prevIdx - 1) {
          removed.push(...batchRemove(renderer, prevIdx, endIdx + 1));
          endIdx = removedIdx;
          prevIdx = removedIdx;
        } else {
          prevIdx = removedIdx;
        }
        prevList.splice(removedIdx, 1);
      });
      removed.push(...batchRemove(renderer, prevIdx, endIdx + 1));
    }
    diffResult.ordered.forEach(([from, to]) => {
      const prevPanel = panels.splice(from, 1)[0];
      panels.splice(to, 0, prevPanel);
    });
    if (diffResult.ordered.length > 0) {
      panels.forEach((panel, idx) => {
        const indexDiff = idx - panel.index;
        if (indexDiff > 0) {
          panel.increaseIndex(indexDiff);
        } else {
          panel.decreaseIndex(-indexDiff);
        }
      });
      panels.sort((panel1, panel2) => panel1.index - panel2.index);
      panels.forEach((panel) => {
        panel.updatePosition();
      });
    }
    if (diffResult.added.length > 0) {
      let startIdx = -1;
      let prevIdx = -1;
      const addedElements = rendered.slice(prevList.length);
      diffResult.added.forEach((addedIdx, idx) => {
        if (startIdx < 0) {
          startIdx = idx;
        }
        if (prevIdx >= 0 && addedIdx !== prevIdx + 1) {
          added.push(...batchInsert(renderer, diffResult, addedElements, startIdx, idx + 1));
          startIdx = -1;
          prevIdx = -1;
        } else {
          prevIdx = addedIdx;
        }
      });
      if (startIdx >= 0) {
        added.push(...batchInsert(renderer, diffResult, addedElements, startIdx));
      }
    }
    if (diffResult.added.length > 0 || diffResult.removed.length > 0) {
      renderer.updateAfterPanelChange(added, removed);
    } else if (diffResult.ordered.length > 0) {
      const camera = flicking.camera;
      camera.updateRange();
      camera.updateOffset();
      camera.updateAnchors();
      camera.resetNeedPanelHistory();
    }
  };
  const batchInsert = (renderer, diffResult, addedElements, startIdx, endIdx) => {
    return renderer.batchInsertDefer(
      ...diffResult.added.slice(startIdx, endIdx).map((index, elIdx) => ({ index, elements: [addedElements[elIdx]], hasDOMInElements: false }))
    );
  };
  const batchRemove = (renderer, startIdx, endIdx) => {
    const removed = renderer.panels.slice(startIdx, endIdx);
    return renderer.batchRemoveDefer({ index: startIdx, deleteCount: removed.length, hasDOMInElements: false });
  };
  const withFlickingMethods = (prototype, flickingName) => {
    [Component.prototype, Flicking.prototype].forEach((proto) => {
      Object.getOwnPropertyNames(proto).filter((name) => !prototype[name] && name.indexOf("_") !== 0 && name !== "constructor").forEach((name) => {
        const descriptor = Object.getOwnPropertyDescriptor(proto, name);
        if (descriptor.value) {
          Object.defineProperty(prototype, name, {
            value: function(...args) {
              return descriptor.value.call(this[flickingName], ...args);
            }
          });
        } else {
          const getterDescriptor = {};
          if (descriptor.get) {
            getterDescriptor.get = function() {
              var _a;
              const flicking = this[flickingName];
              return flicking && ((_a = descriptor.get) == null ? void 0 : _a.call(flicking));
            };
          }
          if (descriptor.set) {
            getterDescriptor.set = function(...args) {
              var _a;
              return (_a = descriptor.set) == null ? void 0 : _a.call(this[flickingName], ...args);
            };
          }
          Object.defineProperty(prototype, name, getterDescriptor);
        }
      });
    });
  };
  const CFC = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
    __proto__: null,
    getDefaultCameraTransform,
    getRenderingPanels,
    sync,
    withFlickingMethods
  }, Symbol.toStringTag, { value: "Module" }));
  function keys(obj) {
    return Object.keys(obj);
  }
  function isObject(val) {
    return typeof val === "object";
  }
  function isFunction(val) {
    return typeof val === "function";
  }
  var OBSERVERS_PATH = "__observers__";
  var COMPUTED_PATH = "__computed__";
  var CFCS_DETECTED_DEPENDENCIES_VERSION = 1;
  var CFCS_DETECTED_DEPENDENCIES = "__CFCS_DETECTED_DEPENDENCIES__";
  function __spreadArray(to, from, pack) {
    if (arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
      if (ar || !(i in from)) {
        if (!ar) ar = Array.prototype.slice.call(from, 0, i);
        ar[i] = from[i];
      }
    }
    return to.concat(ar || Array.prototype.slice.call(from));
  }
  function getDetectedStack() {
    Object[CFCS_DETECTED_DEPENDENCIES] = Object[CFCS_DETECTED_DEPENDENCIES] || {};
    var versionList = Object[CFCS_DETECTED_DEPENDENCIES];
    versionList[CFCS_DETECTED_DEPENDENCIES_VERSION] = versionList[CFCS_DETECTED_DEPENDENCIES_VERSION] || [];
    return versionList[CFCS_DETECTED_DEPENDENCIES_VERSION];
  }
  function getCurrentDetected() {
    var stack = getDetectedStack();
    return stack[stack.length - 1];
  }
  var Observer = /* @__PURE__ */ (function() {
    function Observer2(value) {
      this._emitter = new Component();
      this._current = value;
    }
    var __proto = Observer2.prototype;
    Object.defineProperty(__proto, "current", {
      /**
       * return the current value.
       */
      get: function() {
        var currentDetected = getCurrentDetected();
        currentDetected === null || currentDetected === void 0 ? void 0 : currentDetected.push(this);
        return this._current;
      },
      set: function(value) {
        this._setCurrent(value);
      },
      enumerable: false,
      configurable: true
    });
    __proto.subscribe = function(callback) {
      this.current;
      this._emitter.on("update", callback);
      return this;
    };
    __proto.unsubscribe = function(callback) {
      this._emitter.off("update", callback);
      return this;
    };
    __proto._setCurrent = function(value) {
      var prevValue = this._current;
      var isUpdate = value !== prevValue;
      this._current = value;
      if (isUpdate) {
        this._emitter.trigger("update", value, prevValue);
      }
    };
    __proto.toString = function() {
      return "".concat(this.current);
    };
    __proto.valueOf = function() {
      return this.current;
    };
    return Observer2;
  })();
  function injectObserve(prototype, memberName, publicName) {
    if (publicName === void 0) {
      publicName = memberName;
    }
    var nextAttributes = {
      configurable: true,
      get: function() {
        return getObserver(this, publicName).current;
      },
      set: function(value) {
        getObserver(this, publicName, value).current = value;
      }
    };
    Object.defineProperty(prototype, memberName, nextAttributes);
    if (publicName !== memberName) {
      Object.defineProperty(prototype, publicName, {
        configurable: true,
        get: function() {
          return getObserver(this, publicName).current;
        }
      });
    }
  }
  function Observe() {
    var args = [];
    for (var _i = 0; _i < arguments.length; _i++) {
      args[_i] = arguments[_i];
    }
    if (args.length > 1) {
      return injectObserve(args[0], args[1]);
    }
    return function(prototype, memberName) {
      return injectObserve(prototype, memberName, args[0]);
    };
  }
  function injectReactiveSubscribe(object) {
    object["subscribe"] = function(name, callback) {
      this[name];
      getObserver(this, name).subscribe(callback);
    };
    object["unsubscribe"] = function(name, callback) {
      var _this = this;
      if (!name) {
        keys(getObservers(this)).forEach(function(observerName) {
          _this.unsubscribe(observerName);
        });
        return;
      }
      if (!(name in this)) {
        return;
      }
      getObserver(this, name).unsubscribe(callback);
    };
  }
  function makeReactiveObject(setup, all) {
    var result = isFunction(setup) ? setup() : setup;
    var reactiveObject = {};
    defineObservers(reactiveObject);
    keys(result).forEach(function(name) {
      var value = result[name];
      if (isObserver(value)) {
        setObserver(reactiveObject, name, value);
      } else {
        setObserver(reactiveObject, name, observe(value));
      }
      Observe(name)(reactiveObject, name);
    });
    injectReactiveSubscribe(reactiveObject);
    return reactiveObject;
  }
  function reactive(setup) {
    return makeReactiveObject(setup);
  }
  function observe(defaultValue) {
    return new Observer(defaultValue);
  }
  function withReactiveMethods(ref, methods) {
    var obj = {};
    if (!methods) {
      return obj;
    }
    methods.forEach(function(name) {
      obj[name] = function() {
        var args = [];
        for (var _i = 0; _i < arguments.length; _i++) {
          args[_i] = arguments[_i];
        }
        var current = ref.current || ref.value;
        return current[name].apply(current, args);
      };
    });
    return obj;
  }
  function defineObservers(instance) {
    var observers = {};
    Object.defineProperty(instance, OBSERVERS_PATH, {
      get: function() {
        return observers;
      }
    });
    return observers;
  }
  function getObservers(instance, isComputed) {
    var _a, _b;
    if (!instance[OBSERVERS_PATH]) {
      defineObservers(instance);
    }
    var observers = instance[OBSERVERS_PATH];
    {
      var computedList = (_b = (_a = instance === null || instance === void 0 ? void 0 : instance.constructor) === null || _a === void 0 ? void 0 : _a.prototype) === null || _b === void 0 ? void 0 : _b[COMPUTED_PATH];
      if (computedList) {
        computedList.forEach(function(name) {
          if (!(name in observers) && name in instance) {
            instance[name];
          }
        });
      }
    }
    return observers;
  }
  function getObserver(instance, name, defaultValue) {
    var observers = getObservers(instance);
    if (!observers[name]) {
      observers[name] = observe(defaultValue);
    }
    return observers[name];
  }
  function setObserver(instance, name, observer) {
    var observers = getObservers(instance);
    observers[name] = observer;
  }
  function isObserver(val) {
    return val && isObject(val) && "current" in val && "subscribe" in val && "unsubscribe" in val;
  }
  function adaptReactive(adapter, props) {
    var objectAdapter = isFunction(adapter) ? {
      setup: adapter
    } : adapter;
    function getProps() {
      var _a, _b, _c, _d, _e;
      return (_e = (_c = (_a = props === null || props === void 0 ? void 0 : props()) !== null && _a !== void 0 ? _a : (_b = objectAdapter.props) === null || _b === void 0 ? void 0 : _b.call(objectAdapter)) !== null && _c !== void 0 ? _c : (_d = objectAdapter.data) === null || _d === void 0 ? void 0 : _d.call(objectAdapter)) !== null && _e !== void 0 ? _e : {};
    }
    var eventEmitter = new Component();
    var mountedHooks = [];
    var initHooks = [];
    var destroyHooks = [];
    var onHooks = [];
    var instanceRef = {
      current: null
    };
    var offHooksList = [];
    var initialState = null;
    var eventNames = [];
    var methodNames = [];
    var onMounted = function(callback) {
      mountedHooks.push(callback);
    };
    var onInit = function(callback) {
      initHooks.push(callback);
    };
    var onDestroy = function(callback) {
      destroyHooks.push(callback);
    };
    var on = function(callback) {
      onHooks.push(callback);
    };
    var emit = function(eventName) {
      var params = [];
      for (var _i = 1; _i < arguments.length; _i++) {
        params[_i - 1] = arguments[_i];
      }
      eventEmitter.trigger.apply(eventEmitter, __spreadArray([eventName], params, false));
    };
    var setInitialState = function(state) {
      initialState = state;
    };
    var setEvents = function(events) {
      eventNames = events;
    };
    var setMethods = function(methods) {
      methodNames = methods;
    };
    if (objectAdapter.setup) {
      instanceRef.current = objectAdapter.setup({
        getProps,
        setInitialState,
        setEvents,
        setMethods,
        onMounted,
        onDestroy,
        onInit,
        emit,
        on
      }) || null;
    }
    if (objectAdapter.created) {
      instanceRef.current = objectAdapter.created(getProps()) || null;
    }
    if (objectAdapter.events) {
      setEvents(objectAdapter.events);
    }
    if (objectAdapter.state) {
      setInitialState(objectAdapter.state);
    }
    if (objectAdapter.methods) {
      setMethods(objectAdapter.methods);
    }
    if (objectAdapter.mounted) {
      onMounted(objectAdapter.mounted);
    }
    if (objectAdapter.destroy) {
      destroyHooks.push(objectAdapter.destroy);
    }
    if (objectAdapter.init) {
      initHooks.push(objectAdapter.init);
    }
    if (objectAdapter.on) {
      onHooks.push(function(instance, eventName, listener) {
        var off = objectAdapter.on(instance, eventName, listener);
        return function() {
          var _a;
          off && off();
          (_a = objectAdapter.off) === null || _a === void 0 ? void 0 : _a.call(objectAdapter, instance, eventName, listener);
        };
      });
    }
    return {
      events: function() {
        return eventNames;
      },
      state: function() {
        var inst = instanceRef.current;
        if (initialState) {
          return initialState;
        }
        if (inst) {
          var observers_1 = getObservers(inst);
          setInitialState(keys(observers_1).reduce(function(prev, cur) {
            prev[cur] = observers_1[cur].current;
            return prev;
          }, {}));
        }
        return initialState || {};
      },
      instance: function() {
        return instanceRef.current;
      },
      mounted: function() {
        var props2 = getProps();
        mountedHooks.forEach(function(hook) {
          instanceRef.current = hook(props2, instanceRef.current) || instanceRef.current;
        });
      },
      init: function() {
        var instance = instanceRef.current;
        var props2 = getProps();
        offHooksList = eventNames.map(function(eventName) {
          var listener = function() {
            var _a;
            var params = [];
            for (var _i = 0; _i < arguments.length; _i++) {
              params[_i] = arguments[_i];
            }
            (_a = eventEmitter).trigger.apply(_a, __spreadArray([eventName], params, false));
          };
          var instance2 = instanceRef.current;
          return onHooks.map(function(hook) {
            return hook(instance2, eventName, listener);
          }).filter(Boolean);
        });
        initHooks.forEach(function(hook) {
          hook(instance, props2);
        });
      },
      destroy: function() {
        offHooksList.forEach(function(offHooks) {
          offHooks.forEach(function(hook) {
            hook();
          });
        });
        eventEmitter.off();
        var instance = instanceRef.current;
        var props2 = getProps();
        destroyHooks.forEach(function(hook) {
          hook(instance, props2);
        });
      },
      methods: function() {
        return withReactiveMethods(instanceRef, methodNames);
      },
      on: function(eventName, listener) {
        eventEmitter.on(eventName, listener);
      },
      off: function(eventName, listener) {
        eventEmitter.off(eventName, listener);
      }
    };
  }
  const getIsReachStart = (flicking) => !flicking.circular && flicking.index === 0;
  const getIsReachEnd = (flicking) => !flicking.circular && flicking.index === flicking.panelCount - 1;
  const getTotalPanelCount = (flicking) => flicking.panelCount;
  const getCurrentPanelIndex = (flicking) => flicking.index;
  const getProgress = (flicking) => {
    const cam = flicking.camera;
    const progressRatio = (cam.position - cam.range.min) / (cam.range.max - cam.range.min);
    const percent = Math.min(Math.max(progressRatio, 0), 1) * 100;
    return percent;
  };
  const getIndexProgress = (flicking) => {
    const cam = flicking.camera;
    const anchorPoints = cam.anchorPoints;
    const length = anchorPoints.length;
    const cameraPosition = cam.position;
    const isCircular = flicking.circularEnabled;
    let indexProgress = 0;
    const { min, max } = cam.range;
    const firstAnchorPoint = anchorPoints[0];
    const lastAnchorPoint = anchorPoints[length - 1];
    const distanceLastToFirst = max - lastAnchorPoint.position + (firstAnchorPoint.position - min);
    anchorPoints.some((anchorPoint, index) => {
      const anchorPosition = anchorPoint.position;
      const nextAnchorPoint = anchorPoints[index + 1];
      if (index === 0 && cameraPosition <= anchorPosition) {
        if (isCircular) {
          indexProgress = (cameraPosition - anchorPosition) / distanceLastToFirst;
        } else {
          indexProgress = (cameraPosition - anchorPosition) / anchorPoint.panel.size;
        }
      } else if (index === length - 1 && cameraPosition >= anchorPosition) {
        if (isCircular) {
          indexProgress = index + (cameraPosition - anchorPosition) / distanceLastToFirst;
        } else {
          indexProgress = index + (cameraPosition - anchorPosition) / anchorPoint.panel.size;
        }
      } else if (nextAnchorPoint && anchorPosition <= cameraPosition && cameraPosition <= nextAnchorPoint.position) {
        indexProgress = index + (cameraPosition - anchorPosition) / (nextAnchorPoint.position - anchorPosition);
      } else {
        return false;
      }
      return true;
    });
    return indexProgress;
  };
  const flickingReactiveAPIAdapter = ({ onInit, onDestroy, setMethods, getProps }) => {
    var _a, _b, _c;
    let flicking;
    const moveTo = (i) => {
      if (flicking == null) {
        return Promise.reject(new Error("Flicking instance is not available"));
      }
      if (flicking == null ? void 0 : flicking.animating) {
        return Promise.resolve();
      }
      return flicking.moveTo(i);
    };
    setMethods(["moveTo"]);
    const options = getProps().options;
    const reactiveObj = reactive({
      isReachStart: (options == null ? void 0 : options.defaultIndex) ? (options == null ? void 0 : options.defaultIndex) === 0 : true,
      isReachEnd: (options == null ? void 0 : options.totalPanelCount) && (options == null ? void 0 : options.defaultIndex) ? options.defaultIndex === options.totalPanelCount - 1 : false,
      totalPanelCount: (_a = options == null ? void 0 : options.totalPanelCount) != null ? _a : 0,
      currentPanelIndex: (_b = options == null ? void 0 : options.defaultIndex) != null ? _b : 0,
      progress: 0,
      indexProgress: (_c = options == null ? void 0 : options.defaultIndex) != null ? _c : 0,
      moveTo
    });
    const onChanged = () => {
      if (flicking === void 0) return;
      reactiveObj.isReachStart = getIsReachStart(flicking);
      reactiveObj.isReachEnd = getIsReachEnd(flicking);
      reactiveObj.currentPanelIndex = getCurrentPanelIndex(flicking);
    };
    const onPanelChange = () => {
      if (flicking === void 0) return;
      onChanged();
      reactiveObj.totalPanelCount = getTotalPanelCount(flicking);
    };
    const onMove = () => {
      if (flicking === void 0) return;
      reactiveObj.progress = getProgress(flicking);
      reactiveObj.indexProgress = getIndexProgress(flicking);
    };
    onInit((inst, data) => {
      flicking = data.flicking;
      if (flicking === void 0) return;
      reactiveObj.isReachStart = getIsReachStart(flicking);
      reactiveObj.isReachEnd = getIsReachEnd(flicking);
      reactiveObj.currentPanelIndex = getCurrentPanelIndex(flicking);
      reactiveObj.progress = getProgress(flicking);
      reactiveObj.totalPanelCount = getTotalPanelCount(flicking);
      flicking == null ? void 0 : flicking.on("changed", onChanged);
      flicking == null ? void 0 : flicking.on("panelChange", onPanelChange);
      flicking == null ? void 0 : flicking.on("move", onMove);
    });
    onDestroy(() => {
      flicking == null ? void 0 : flicking.off("changed", onChanged);
      flicking == null ? void 0 : flicking.off("panelChange", onPanelChange);
      flicking == null ? void 0 : flicking.off("move", onMove);
    });
    return reactiveObj;
  };
  const connectFlickingReactiveAPI = (flicking, options) => {
    const obj = adaptReactive(flickingReactiveAPIAdapter, () => ({ flicking, options }));
    obj.mounted();
    const instance = obj.instance();
    obj.init();
    return instance;
  };
  const FlickingReactiveAPI = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
    __proto__: null,
    connectFlickingReactiveAPI,
    flickingReactiveAPIAdapter
  }, Symbol.toStringTag, { value: "Module" }));
  const Constants = __spreadValues(__spreadValues({}, Events), Values);
  merge(Flicking, Core);
  merge(Flicking, Camera);
  merge(Flicking, Control);
  merge(Flicking, Renderer);
  merge(Flicking, Constants);
  merge(Flicking, CFC);
  merge(Flicking, Utils);
  merge(Flicking, CrossFlicking$1);
  merge(Flicking, FlickingReactiveAPI);
  return Flicking;
}));
//# sourceMappingURL=flicking.js.map