import * as React from "react";
import { r as reactDomExports } from "./index-D8-Zjpbd.js";
function arrayMove(array, from, to) {
  array = array.slice();
  array.splice(to < 0 ? array.length + to : to, 0, array.splice(from, 1)[0]);
  return array;
}
function arrayRemove(array, index) {
  array = array.slice();
  array.splice(index, 1);
  return array;
}
function getTranslateOffset(element) {
  const style = window.getComputedStyle(element);
  return Math.max(parseInt(style["margin-top"], 10), parseInt(style["margin-bottom"], 10)) + element.getBoundingClientRect().height;
}
function isTouchEvent(event) {
  return event.touches && event.touches.length || event.changedTouches && event.changedTouches.length;
}
function transformItem(element, offsetY = 0, offsetX = 0) {
  if (!element)
    return;
  if (offsetY === null || offsetX === null) {
    element.style.removeProperty("transform");
    return;
  }
  element.style.transform = `translate(${offsetX}px, ${offsetY}px)`;
}
function setItemTransition(element, duration, timing) {
  if (element) {
    element.style["transition"] = `transform ${duration}ms${timing ? ` ${timing}` : ""}`;
  }
}
function binarySearch(array, targetValue) {
  let min = 0;
  let max = array.length - 1;
  let guess;
  while (min <= max) {
    guess = Math.floor((max + min) / 2);
    if (!array[guess + 1] || array[guess] <= targetValue && array[guess + 1] >= targetValue) {
      return guess;
    } else if (array[guess] < targetValue && array[guess + 1] < targetValue) {
      min = guess + 1;
    } else {
      max = guess - 1;
    }
  }
  return -1;
}
const schd = (fn) => {
  let lastArgs = [];
  let frameId = null;
  const wrapperFn = (...args) => {
    lastArgs = args;
    if (frameId) {
      return;
    }
    frameId = requestAnimationFrame(() => {
      frameId = null;
      fn(...lastArgs);
    });
  };
  wrapperFn.cancel = () => {
    if (frameId) {
      cancelAnimationFrame(frameId);
    }
  };
  return wrapperFn;
};
function checkIfInteractive(target, rootElement) {
  const DISABLED_ELEMENTS = [
    "input",
    "textarea",
    "select",
    "option",
    "optgroup",
    "video",
    "audio",
    "button",
    "a"
  ];
  const DISABLED_ROLES = [
    "button",
    "link",
    "checkbox",
    "radio",
    "switch",
    "tab"
  ];
  if (!target || !rootElement)
    return false;
  while (target !== rootElement) {
    if (target.getAttribute("data-movable-handle")) {
      return false;
    }
    if (DISABLED_ELEMENTS.includes(target.tagName.toLowerCase())) {
      return true;
    }
    const role = target.getAttribute("role");
    if (role && DISABLED_ROLES.includes(role.toLowerCase())) {
      return true;
    }
    if (target.tagName.toLowerCase() === "label" && target.hasAttribute("for")) {
      return true;
    }
    if (target.tagName)
      target = target.parentElement;
  }
  return false;
}
const AUTOSCROLL_ACTIVE_OFFSET = 200;
const AUTOSCROLL_SPEED_RATIO = 10;
const AUTOSCROLL_DELTA_THRESHOLD = 10;
class List extends React.Component {
  listRef = React.createRef();
  ghostRef = React.createRef();
  topOffsets = [];
  itemTranslateOffsets = [];
  initialYOffset = 0;
  lastScroll = 0;
  lastYOffset = 0;
  lastListYOffset = 0;
  dropTimeout;
  needle = -1;
  afterIndex = -2;
  state = {
    itemDragged: -1,
    itemDraggedOutOfBounds: -1,
    selectedItem: -1,
    initialX: 0,
    initialY: 0,
    targetX: 0,
    targetY: 0,
    targetHeight: 0,
    targetWidth: 0,
    liveText: "",
    scrollingSpeed: 0,
    scrollWindow: false
  };
  schdOnMouseMove;
  schdOnTouchMove;
  schdOnEnd;
  constructor(props) {
    super(props);
    this.schdOnMouseMove = schd(this.onMouseMove);
    this.schdOnTouchMove = schd(this.onTouchMove);
    this.schdOnEnd = schd(this.onEnd);
  }
  componentDidMount() {
    this.calculateOffsets();
    document.addEventListener("touchstart", this.onMouseOrTouchStart, {
      passive: false,
      capture: false
    });
    document.addEventListener("mousedown", this.onMouseOrTouchStart);
  }
  componentDidUpdate(_prevProps, prevState) {
    if (prevState.scrollingSpeed !== this.state.scrollingSpeed && prevState.scrollingSpeed === 0) {
      this.doScrolling();
    }
  }
  componentWillUnmount() {
    document.removeEventListener("touchstart", this.onMouseOrTouchStart);
    document.removeEventListener("mousedown", this.onMouseOrTouchStart);
    if (this.dropTimeout) {
      window.clearTimeout(this.dropTimeout);
    }
    this.schdOnMouseMove.cancel();
    this.schdOnTouchMove.cancel();
    this.schdOnEnd.cancel();
  }
  doScrolling = () => {
    const { scrollingSpeed, scrollWindow } = this.state;
    const listEl = this.listRef.current;
    window.requestAnimationFrame(() => {
      if (scrollWindow) {
        window.scrollTo(window.pageXOffset, window.pageYOffset + scrollingSpeed * 1.5);
      } else {
        listEl.scrollTop += scrollingSpeed;
      }
      if (scrollingSpeed !== 0) {
        this.doScrolling();
      }
    });
  };
  getChildren = () => {
    if (this.listRef && this.listRef.current) {
      return Array.from(this.listRef.current.children);
    }
    console.warn("No items found in the List container. Did you forget to pass & spread the `props` param in renderList?");
    return [];
  };
  static defaultProps = {
    transitionDuration: 300,
    lockVertically: false,
    removableByMove: false,
    voiceover: {
      item: (position) => `You are currently at a draggable item at position ${position}. Press space bar to lift.`,
      lifted: (position) => `You have lifted item at position ${position}. Press j to move down, k to move up, space bar to drop and escape to cancel.`,
      moved: (position, up) => `You have moved the lifted item ${up ? "up" : "down"} to position ${position}. Press j to move down, k to move up, space bar to drop and escape to cancel.`,
      dropped: (from, to) => `You have dropped the item. It has moved from position ${from} to ${to}.`,
      canceled: (position) => `You have cancelled the movement. The item has returned to its starting position of ${position}.`
    }
  };
  calculateOffsets = () => {
    this.topOffsets = this.getChildren().map((item) => item.getBoundingClientRect().top);
    this.itemTranslateOffsets = this.getChildren().map((item) => getTranslateOffset(item));
  };
  getTargetIndex = (e) => {
    return this.getChildren().findIndex((child) => child === e.target || child.contains(e.target));
  };
  onMouseOrTouchStart = (e) => {
    if (this.dropTimeout && this.state.itemDragged > -1) {
      window.clearTimeout(this.dropTimeout);
      this.finishDrop();
    }
    const isTouch = isTouchEvent(e);
    if (!isTouch && e.button !== 0)
      return;
    const index = this.getTargetIndex(e);
    if (index === -1 || this.props.disabled || // @ts-ignore
    this.props.values[index] && this.props.values[index].disabled) {
      if (this.state.selectedItem !== -1) {
        this.setState({ selectedItem: -1 });
        this.finishDrop();
      }
      return;
    }
    const listItemTouched = this.getChildren()[index];
    const handle = listItemTouched.querySelector("[data-movable-handle]");
    if (handle && !handle.contains(e.target)) {
      return;
    }
    if (checkIfInteractive(e.target, listItemTouched)) {
      return;
    }
    e.preventDefault();
    this.props.beforeDrag && this.props.beforeDrag({
      elements: this.getChildren(),
      index
    });
    if (isTouch) {
      const opts = { passive: false };
      listItemTouched.style.touchAction = "none";
      document.addEventListener("touchend", this.schdOnEnd, opts);
      document.addEventListener("touchmove", this.schdOnTouchMove, opts);
      document.addEventListener("touchcancel", this.schdOnEnd, opts);
    } else {
      document.addEventListener("mousemove", this.schdOnMouseMove);
      document.addEventListener("mouseup", this.schdOnEnd);
      const listItemDragged = this.getChildren()[this.state.itemDragged];
      if (listItemDragged && listItemDragged.style) {
        listItemDragged.style.touchAction = "";
      }
    }
    this.onStart(listItemTouched, isTouch ? e.touches[0].clientX : e.clientX, isTouch ? e.touches[0].clientY : e.clientY, index);
  };
  getYOffset = () => {
    const listScroll = this.listRef.current ? this.listRef.current.scrollTop : 0;
    return window.pageYOffset + listScroll;
  };
  onStart = (target, clientX, clientY, index) => {
    if (this.state.selectedItem > -1) {
      this.setState({ selectedItem: -1 });
      this.needle = -1;
    }
    const targetRect = target.getBoundingClientRect();
    const targetStyles = window.getComputedStyle(target);
    this.calculateOffsets();
    this.initialYOffset = this.getYOffset();
    this.lastYOffset = window.pageYOffset;
    this.lastListYOffset = this.listRef.current.scrollTop;
    this.setState({
      itemDragged: index,
      targetX: targetRect.left - parseInt(targetStyles["margin-left"], 10),
      targetY: targetRect.top - parseInt(targetStyles["margin-top"], 10),
      targetHeight: targetRect.height,
      targetWidth: targetRect.width,
      initialX: clientX,
      initialY: clientY
    });
  };
  onMouseMove = (e) => {
    e.cancelable && e.preventDefault();
    this.onMove(e.clientX, e.clientY);
  };
  onTouchMove = (e) => {
    e.cancelable && e.preventDefault();
    this.onMove(e.touches[0].clientX, e.touches[0].clientY);
  };
  onWheel = (e) => {
    if (this.state.itemDragged < 0)
      return;
    this.lastScroll = this.listRef.current.scrollTop += e.deltaY;
    this.moveOtherItems();
  };
  onMove = (clientX, clientY) => {
    if (this.state.itemDragged === -1)
      return null;
    transformItem(this.ghostRef.current, clientY - this.state.initialY, this.props.lockVertically ? 0 : clientX - this.state.initialX);
    this.autoScrolling(clientY, clientY - this.state.initialY);
    this.moveOtherItems();
  };
  moveOtherItems = () => {
    const targetRect = this.ghostRef.current.getBoundingClientRect();
    const itemVerticalCenter = targetRect.top + targetRect.height / 2;
    const offset = getTranslateOffset(this.getChildren()[this.state.itemDragged]);
    const currentYOffset = this.getYOffset();
    if (this.initialYOffset !== currentYOffset) {
      this.topOffsets = this.topOffsets.map((offset2) => offset2 - (currentYOffset - this.initialYOffset));
      this.initialYOffset = currentYOffset;
    }
    if (this.isDraggedItemOutOfBounds() && this.props.removableByMove) {
      this.afterIndex = this.topOffsets.length + 1;
    } else {
      this.afterIndex = binarySearch(this.topOffsets, itemVerticalCenter);
    }
    this.animateItems(this.afterIndex === -1 ? 0 : this.afterIndex, this.state.itemDragged, offset);
  };
  autoScrolling = (clientY, delta) => {
    const { top, bottom, height } = this.listRef.current.getBoundingClientRect();
    const viewportHeight = window.innerHeight || document.documentElement.clientHeight;
    if (bottom > viewportHeight && viewportHeight - clientY < AUTOSCROLL_ACTIVE_OFFSET && delta > AUTOSCROLL_DELTA_THRESHOLD) {
      this.setState({
        scrollingSpeed: Math.min(Math.round((AUTOSCROLL_ACTIVE_OFFSET - (viewportHeight - clientY)) / AUTOSCROLL_SPEED_RATIO), Math.round((delta - AUTOSCROLL_DELTA_THRESHOLD) / AUTOSCROLL_SPEED_RATIO)),
        scrollWindow: true
      });
    } else if (top < 0 && clientY < AUTOSCROLL_ACTIVE_OFFSET && delta < -AUTOSCROLL_DELTA_THRESHOLD) {
      this.setState({
        scrollingSpeed: Math.max(Math.round((AUTOSCROLL_ACTIVE_OFFSET - clientY) / -AUTOSCROLL_SPEED_RATIO), Math.round((delta + AUTOSCROLL_DELTA_THRESHOLD) / AUTOSCROLL_SPEED_RATIO)),
        scrollWindow: true
      });
    } else {
      if (this.state.scrollWindow && this.state.scrollingSpeed !== 0) {
        this.setState({ scrollingSpeed: 0, scrollWindow: false });
      }
      if (height + 20 < this.listRef.current.scrollHeight) {
        let scrollingSpeed = 0;
        if (clientY - top < AUTOSCROLL_ACTIVE_OFFSET && delta < -AUTOSCROLL_DELTA_THRESHOLD) {
          scrollingSpeed = Math.max(Math.round((AUTOSCROLL_ACTIVE_OFFSET - (clientY - top)) / -AUTOSCROLL_SPEED_RATIO), Math.round((delta + AUTOSCROLL_DELTA_THRESHOLD) / AUTOSCROLL_SPEED_RATIO));
        } else if (bottom - clientY < AUTOSCROLL_ACTIVE_OFFSET && delta > AUTOSCROLL_DELTA_THRESHOLD) {
          scrollingSpeed = Math.min(Math.round((AUTOSCROLL_ACTIVE_OFFSET - (bottom - clientY)) / AUTOSCROLL_SPEED_RATIO), Math.round((delta - AUTOSCROLL_DELTA_THRESHOLD) / AUTOSCROLL_SPEED_RATIO));
        }
        if (this.state.scrollingSpeed !== scrollingSpeed) {
          this.setState({ scrollingSpeed });
        }
      }
    }
  };
  animateItems = (needle, movedItem, offset, animateMovedItem = false) => {
    this.getChildren().forEach((item, i) => {
      setItemTransition(item, this.props.transitionDuration);
      if (movedItem === i && animateMovedItem) {
        if (movedItem === needle) {
          return transformItem(item, null);
        }
        transformItem(item, movedItem < needle ? this.itemTranslateOffsets.slice(movedItem + 1, needle + 1).reduce((a, b) => a + b, 0) : this.itemTranslateOffsets.slice(needle, movedItem).reduce((a, b) => a + b, 0) * -1);
      } else if (movedItem < needle && i > movedItem && i <= needle) {
        transformItem(item, -offset);
      } else if (i < movedItem && movedItem > needle && i >= needle) {
        transformItem(item, offset);
      } else {
        transformItem(item, null);
      }
    });
  };
  isDraggedItemOutOfBounds = () => {
    const initialRect = this.getChildren()[this.state.itemDragged].getBoundingClientRect();
    const targetRect = this.ghostRef.current.getBoundingClientRect();
    if (Math.abs(initialRect.left - targetRect.left) > targetRect.width) {
      if (this.state.itemDraggedOutOfBounds === -1) {
        this.setState({ itemDraggedOutOfBounds: this.state.itemDragged });
      }
      return true;
    }
    if (this.state.itemDraggedOutOfBounds > -1) {
      this.setState({ itemDraggedOutOfBounds: -1 });
    }
    return false;
  };
  onEnd = (e) => {
    e.cancelable && e.preventDefault();
    document.removeEventListener("mousemove", this.schdOnMouseMove);
    document.removeEventListener("touchmove", this.schdOnTouchMove);
    document.removeEventListener("mouseup", this.schdOnEnd);
    document.removeEventListener("touchup", this.schdOnEnd);
    document.removeEventListener("touchcancel", this.schdOnEnd);
    const removeItem = this.props.removableByMove && this.isDraggedItemOutOfBounds();
    if (!removeItem && this.props.transitionDuration > 0 && this.afterIndex !== -2) {
      schd(() => {
        setItemTransition(this.ghostRef.current, this.props.transitionDuration, "cubic-bezier(.2,1,.1,1)");
        if (this.afterIndex < 1 && this.state.itemDragged === 0) {
          transformItem(this.ghostRef.current, 0, 0);
        } else {
          transformItem(
            this.ghostRef.current,
            // compensate window scroll
            -(window.pageYOffset - this.lastYOffset) + // compensate container scroll
            -(this.listRef.current.scrollTop - this.lastListYOffset) + (this.state.itemDragged < this.afterIndex ? this.itemTranslateOffsets.slice(this.state.itemDragged + 1, this.afterIndex + 1).reduce((a, b) => a + b, 0) : this.itemTranslateOffsets.slice(this.afterIndex < 0 ? 0 : this.afterIndex, this.state.itemDragged).reduce((a, b) => a + b, 0) * -1),
            0
          );
        }
      })();
    }
    this.dropTimeout = window.setTimeout(this.finishDrop, removeItem || this.afterIndex === -2 ? 0 : this.props.transitionDuration);
  };
  finishDrop = () => {
    const removeItem = this.props.removableByMove && this.isDraggedItemOutOfBounds();
    const oldIndex = this.state.itemDragged;
    const hasChanged = this.afterIndex > -2 && oldIndex !== this.afterIndex;
    const newIndex = hasChanged ? removeItem ? -1 : Math.max(this.afterIndex, 0) : oldIndex;
    if (removeItem || hasChanged) {
      this.props.onChange({
        oldIndex,
        newIndex,
        targetRect: this.ghostRef.current.getBoundingClientRect()
      });
    }
    this.props.afterDrag && this.props.afterDrag({
      elements: this.getChildren(),
      oldIndex,
      newIndex
    });
    this.getChildren().forEach((item) => {
      setItemTransition(item, 0);
      transformItem(item, null);
      item.style.touchAction = "";
    });
    this.setState({ itemDragged: -1, scrollingSpeed: 0 });
    this.afterIndex = -2;
    if (this.lastScroll > 0) {
      this.listRef.current.scrollTop = this.lastScroll;
      this.lastScroll = 0;
    }
  };
  onKeyDown = (e) => {
    const selectedItem = this.state.selectedItem;
    const index = this.getTargetIndex(e);
    if (checkIfInteractive(e.target, e.currentTarget)) {
      return;
    }
    if (index === -1)
      return;
    if (e.key === " ") {
      e.preventDefault();
      if (selectedItem === index) {
        if (selectedItem !== this.needle) {
          this.getChildren().forEach((item) => {
            setItemTransition(item, 0);
            transformItem(item, null);
          });
          this.props.onChange({
            oldIndex: selectedItem,
            newIndex: this.needle,
            targetRect: this.getChildren()[this.needle].getBoundingClientRect()
          });
          this.getChildren()[this.needle].focus();
        }
        this.setState({
          selectedItem: -1,
          liveText: this.props.voiceover.dropped(selectedItem + 1, this.needle + 1)
        });
        this.needle = -1;
      } else {
        this.setState({
          selectedItem: index,
          liveText: this.props.voiceover.lifted(index + 1)
        });
        this.needle = index;
        this.calculateOffsets();
      }
    }
    if ((e.key === "ArrowDown" || e.key === "j") && selectedItem > -1 && this.needle < this.props.values.length - 1) {
      e.preventDefault();
      const offset = getTranslateOffset(this.getChildren()[selectedItem]);
      this.needle++;
      this.animateItems(this.needle, selectedItem, offset, true);
      this.setState({
        liveText: this.props.voiceover.moved(this.needle + 1, false)
      });
    }
    if ((e.key === "ArrowUp" || e.key === "k") && selectedItem > -1 && this.needle > 0) {
      e.preventDefault();
      const offset = getTranslateOffset(this.getChildren()[selectedItem]);
      this.needle--;
      this.animateItems(this.needle, selectedItem, offset, true);
      this.setState({
        liveText: this.props.voiceover.moved(this.needle + 1, true)
      });
    }
    if (e.key === "Escape" && selectedItem > -1) {
      this.getChildren().forEach((item) => {
        setItemTransition(item, 0);
        transformItem(item, null);
      });
      this.setState({
        selectedItem: -1,
        liveText: this.props.voiceover.canceled(selectedItem + 1)
      });
      this.needle = -1;
    }
    if ((e.key === "Tab" || e.key === "Enter") && selectedItem > -1) {
      e.preventDefault();
    }
  };
  render() {
    const baseStyle = {
      userSelect: "none",
      WebkitUserSelect: "none",
      MozUserSelect: "none",
      msUserSelect: "none",
      boxSizing: "border-box",
      position: "relative"
    };
    const ghostStyle = {
      ...baseStyle,
      top: this.state.targetY,
      left: this.state.targetX,
      width: this.state.targetWidth,
      height: this.state.targetHeight,
      position: "fixed",
      marginTop: 0
    };
    return React.createElement(
      React.Fragment,
      null,
      this.props.renderList({
        children: this.props.values.map((value, index) => {
          const isHidden = index === this.state.itemDragged;
          const isSelected = index === this.state.selectedItem;
          const isDisabled = Boolean(this.props.disabled || this.props.values[index] && typeof this.props.values[index] === "object" && // @ts-expect-error value doesn't necessarily have a `disabled` property
          this.props.values[index].disabled);
          const props = {
            key: index,
            tabIndex: isDisabled ? -1 : 0,
            "aria-roledescription": this.props.voiceover.item(index + 1),
            onKeyDown: this.onKeyDown,
            style: {
              ...baseStyle,
              visibility: isHidden ? "hidden" : void 0,
              zIndex: isSelected ? 5e3 : 0
            }
          };
          return this.props.renderItem({
            value,
            props,
            index,
            isDragged: false,
            isSelected,
            isOutOfBounds: false,
            isDisabled
          });
        }),
        isDragged: this.state.itemDragged > -1,
        props: {
          ref: this.listRef
        }
      }),
      this.state.itemDragged > -1 && reactDomExports.createPortal(this.props.renderItem({
        value: this.props.values[this.state.itemDragged],
        props: {
          ref: this.ghostRef,
          style: ghostStyle,
          onWheel: this.onWheel
        },
        index: this.state.itemDragged,
        isDragged: true,
        isSelected: false,
        isDisabled: false,
        isOutOfBounds: this.state.itemDraggedOutOfBounds > -1
      }), this.props.container || document.body),
      React.createElement("div", { "aria-live": "assertive", role: "log", "aria-atomic": "true", style: {
        position: "absolute",
        width: "1px",
        height: "1px",
        margin: "-1px",
        border: "0px",
        padding: "0px",
        overflow: "hidden",
        clip: "rect(0px, 0px, 0px, 0px)",
        clipPath: "inset(100%)"
      } }, this.state.liveText)
    );
  }
}
export {
  List as L,
  arrayRemove as a,
  arrayMove as b
};
