import { jsxs, jsx } from 'react/jsx-runtime';
import { createContext, useContext, forwardRef, useRef, useId, useEffect, useMemo, useState } from 'react';
import { C as Checkbox } from './Checkbox-Ckl8EFpF.js';
import { c as cx } from './index-De1g9FRV.js';
import { l as listViewStyles } from './ListView.module-CRk9NfLG.js';
import { u as useMergeRefs } from './useMergeRefs-Dfmtq9cI.js';
import { useTrackingId } from './useTrackingId.js';
import { g as getActiveElement } from './getActiveElement-BcSyVE7S.js';
import { u as useLayoutPropsUtil } from './useLayoutPropsUtil-CB_zHDbW.js';

function mapOrder(array, order, key) {
  const sortedArray = [...array];
  sortedArray.sort(function(a, b) {
    const A = a[key], B = b[key];
    if (order.indexOf(A) > order.indexOf(B)) {
      return 1;
    } else {
      return -1;
    }
  });
  return sortedArray;
}

const ListViewContext = createContext(null);
function useListView() {
  const context = useContext(ListViewContext);
  if (context == null) {
    throw new Error("useListView must be wrapped in <ListView />");
  }
  return context;
}

const ListViewOption = forwardRef(
  (props, ref) => {
    const {
      className,
      children,
      onChange,
      style,
      disabled: disabledProp,
      ...remainingProps
    } = props;
    const optionRef = useRef(null);
    const mergedRef = useMergeRefs([ref, optionRef]);
    const cellRef = useRef(null);
    const rowId = useId();
    const {
      selectedItems,
      setSelectedItems,
      indeterminateItems,
      controlled,
      onSelectionChange
    } = useListView();
    const disabled = "item" in remainingProps ? disabledProp || remainingProps.item.disabled : disabledProp;
    useEffect(() => {
      const focusables = optionRef.current?.querySelectorAll(
        "button:not(:disabled):not([aria-disabled='true']), input:not(:disabled):not([aria-disabled='true']), [tabindex='0']:not(:disabled):not([aria-disabled='true'])"
      );
      focusables?.forEach((item, i) => {
        item.setAttribute("tabindex", "-1");
        if (item.hasAttribute("id")) return;
        item.setAttribute("id", `${rowId}-${i}`);
      });
    }, [rowId]);
    const label = "item" in remainingProps ? remainingProps.item.label : remainingProps.label;
    const isSelected = useMemo(
      () => matchInArray(label, selectedItems),
      [selectedItems, label]
    );
    const isIndeterminate = useMemo(
      () => matchInArray(label, indeterminateItems),
      [indeterminateItems, label]
    );
    const onChangeHandler = (e) => {
      onChange?.(e, { checked: isSelected });
    };
    const onClickHandler = () => {
      updateSelectedItems();
    };
    const removeItemFromSelectedItems = (itemLabelToRemove) => {
      return selectedItems.filter(
        (potentialRemove) => {
          if (typeof potentialRemove === "string") {
            return potentialRemove !== itemLabelToRemove;
          }
          return potentialRemove.label !== itemLabelToRemove;
        }
      );
    };
    const addItemFromSelectedItems = (itemToAdd) => {
      return [...selectedItems, itemToAdd];
    };
    function updateSelectedItems() {
      const isPartOfSelectedItems = matchInArray(label, selectedItems);
      const callback = controlled ? onSelectionChange : setSelectedItems;
      if (isPartOfSelectedItems) {
        callback?.(removeItemFromSelectedItems(label));
      } else {
        callback?.(
          addItemFromSelectedItems(
            "item" in remainingProps ? remainingProps.item : remainingProps.label
          )
        );
      }
    }
    const data = {
      label: "item" in props ? props.item?.label : props.label
    };
    const trackingId = useTrackingId({
      name: "ListViewOption",
      data,
      hasOverride: !!props["data-tracking-id"]
    });
    const {
      item: _item,
      label: _label,
      ...forwardedProps
    } = remainingProps;
    return /* @__PURE__ */ jsxs(
      "div",
      {
        "data-tracking-id": trackingId,
        className: cx([listViewStyles["listview-option"]], className),
        "data-anv": "listview-option",
        style,
        ref: mergedRef,
        "aria-disabled": disabled,
        "aria-selected": isSelected,
        "aria-label": label ?? "Select",
        role: "row",
        ...forwardedProps,
        children: [
          /* @__PURE__ */ jsx("div", { className: listViewStyles["cell"], ref: cellRef, role: "gridcell", children: /* @__PURE__ */ jsx(
            Checkbox,
            {
              disabled,
              checked: isIndeterminate ? void 0 : isSelected,
              indeterminate: isIndeterminate,
              "aria-label": label ?? "Select",
              onChange: onChangeHandler,
              onClick: onClickHandler
            }
          ) }),
          children
        ]
      }
    );
  }
);
ListViewOption.displayName = "ListViewOption";
function matchInArray(itemLabel, array) {
  return array.some(
    (i) => itemLabel === i || itemLabel === i.label
  );
}

const ListViewOptionCell = forwardRef((props, ref) => {
  const { ...rest } = props;
  return /* @__PURE__ */ jsx("div", { role: "gridcell", "data-anv": "listview-option-cell", ...rest, ref });
});
ListViewOptionCell.displayName = "ListViewOptionCell";

const listViewKeyboardNavigation = (e, options, focusedItem, setFocusedItem, parent, currentFocusId) => {
  if (!options) return;
  let activeElement;
  switch (e.code) {
    case "Enter":
    case "Space":
      options[focusedItem.row].focusables[focusedItem.col].click();
      activeElement = getActiveElement(document);
      if (!activeElement) break;
      activeElement.click();
      e.preventDefault();
      break;
    case "ArrowDown":
      parent?.focus();
      options[focusedItem.row].focusables[focusedItem.col].removeAttribute(
        "data-interactive"
      );
      if (focusedItem.row === options.length - 1) {
        options[0].focusables[focusedItem.col].setAttribute(
          "data-interactive",
          "focus-visible"
        );
        currentFocusId(options[0].focusables[focusedItem.col].id);
        setFocusedItem((prev) => {
          return { ...prev, row: 0 };
        });
        break;
      }
      options[focusedItem.row + 1].focusables[focusedItem.col].setAttribute(
        "data-interactive",
        "focus-visible"
      );
      currentFocusId(
        options[focusedItem.row + 1].focusables[focusedItem.col].id
      );
      setFocusedItem((prev) => {
        return { ...prev, row: focusedItem.row + 1 };
      });
      break;
    case "ArrowUp":
      parent?.focus();
      options[focusedItem.row].focusables[focusedItem.col].removeAttribute(
        "data-interactive"
      );
      if (focusedItem.row === 0) {
        options[options.length - 1].focusables[focusedItem.col].setAttribute(
          "data-interactive",
          "focus-visible"
        );
        currentFocusId(
          options[options.length - 1].focusables[focusedItem.col].id
        );
        setFocusedItem((prev) => {
          return { ...prev, row: options.length - 1 };
        });
        break;
      }
      options[focusedItem.row - 1].focusables[focusedItem.col].setAttribute(
        "data-interactive",
        "focus-visible"
      );
      currentFocusId(
        options[focusedItem.row - 1].focusables[focusedItem.col].id
      );
      setFocusedItem((prev) => {
        return { ...prev, row: focusedItem.row - 1 };
      });
      break;
    case "ArrowRight":
      parent?.focus();
      options[focusedItem.row].focusables[focusedItem.col].removeAttribute(
        "data-interactive"
      );
      if (focusedItem.col === options[focusedItem.row].focusables.length - 1) {
        options[focusedItem.row].focusables[0].setAttribute(
          "data-interactive",
          "focus-visible"
        );
        currentFocusId(options[focusedItem.row].focusables[0].id);
        setFocusedItem((prev) => {
          return {
            ...prev,
            col: 0
          };
        });
        break;
      }
      options[focusedItem.row].focusables[focusedItem.col + 1].setAttribute(
        "data-interactive",
        "focus-visible"
      );
      currentFocusId(
        options[focusedItem.row].focusables[focusedItem.col + 1].id
      );
      setFocusedItem((prev) => {
        return { ...prev, col: focusedItem.col + 1 };
      });
      break;
    case "ArrowLeft":
      parent?.focus();
      options[focusedItem.row].focusables[focusedItem.col].removeAttribute(
        "data-interactive"
      );
      if (focusedItem.col === 0) {
        options[focusedItem.row].focusables[options[focusedItem.row].focusables.length - 1].setAttribute("data-interactive", "focus-visible");
        currentFocusId(
          options[focusedItem.row].focusables[options[focusedItem.row].focusables.length - 1].id
        );
        setFocusedItem((prev) => {
          return {
            ...prev,
            col: options[focusedItem.row].focusables.length - 1
          };
        });
        break;
      }
      options[focusedItem.row].focusables[focusedItem.col - 1].setAttribute(
        "data-interactive",
        "focus-visible"
      );
      currentFocusId(
        options[focusedItem.row].focusables[focusedItem.col - 1].id
      );
      setFocusedItem((prev) => {
        return { ...prev, col: focusedItem.col - 1 };
      });
      break;
  }
};
const getFocusables = (el) => {
  return el.querySelectorAll(
    "button:not(:disabled):not([aria-disabled='true']), input:not(:disabled):not([aria-disabled='true']), [tabindex='0']:not(:disabled):not([aria-disabled='true'])"
  );
};

function ListViewInner(props, ref) {
  const { layoutStyles, componentProps } = useLayoutPropsUtil(props);
  const {
    indeterminate,
    onSelectionChange,
    className,
    selected,
    defaultSelected,
    style,
    onKeyDown,
    ...remainingProps
  } = componentProps;
  const ListViewRef = useRef(null);
  const isFocusVisible = useRef(true);
  const combinedRef = useMergeRefs([ref, ListViewRef]);
  const [options, setOptions] = useState();
  const [currentActive, setCurrentActive] = useState();
  const [selectedItems, setSelectedItems] = useState(
    selected ?? defaultSelected ?? []
  );
  const [focusedItem, setFocusedItem] = useState({
    row: 0,
    col: 0
  });
  const styleCombined = {
    ...style,
    ...layoutStyles
  };
  const onFocusHandler = () => {
    if (!isFocusVisible.current) return;
    if (!options) return;
    options[focusedItem.row].focusables[focusedItem.col].setAttribute(
      "data-interactive",
      "focus-visible"
    );
    return;
  };
  const onBlurHandler = () => {
    if (!options) return;
    options[focusedItem.row].focusables[focusedItem.col]?.removeAttribute(
      "data-interactive"
    );
    return;
  };
  const onKeyDownHandler = (e) => {
    onKeyDown?.(e);
    if (!options) return;
    isFocusVisible.current = true;
    listViewKeyboardNavigation(
      e,
      options,
      focusedItem,
      setFocusedItem,
      ListViewRef.current,
      (id) => setCurrentActive(id)
    );
  };
  const onMouseDownHandler = (e) => {
    isFocusVisible.current = false;
    if (options && ListViewRef.current) {
      options[focusedItem.row].focusables[focusedItem.col]?.removeAttribute(
        "data-interactive"
      );
      const closestFocusable = e.target.closest(
        "button:not(:disabled):not([aria-disabled='true']), input:not(:disabled):not([aria-disabled='true']), [data-anv='checkbox']"
      );
      const optionRow = e.target.closest(
        '[data-anv="listview-option"]'
      );
      if (!optionRow || optionRow.ariaDisabled) return;
      const row = options.findIndex((option) => option.node === optionRow);
      if (!closestFocusable) {
        options[row].focusables[0].click();
        return;
      }
      const col = Array.prototype.indexOf.call(
        options[row].focusables,
        closestFocusable?.getAttribute("data-anv") === "checkbox" ? closestFocusable.children[0] : closestFocusable
      );
      setFocusedItem({ row, col });
    }
  };
  useEffect(() => {
    if (!ListViewRef.current) return;
    const optionArr = ListViewRef.current.querySelectorAll(
      "div[data-anv='listview-option']:not([aria-disabled='true'])"
    );
    const makeTree = Array.from(optionArr).map((option) => {
      const childFocusables = getFocusables(option);
      return {
        node: option,
        focusables: childFocusables
      };
    });
    setOptions(makeTree);
  }, [props.children]);
  const prevCallbackItems = useRef([]);
  const prevControlledItems = useRef(
    selected ?? defaultSelected ?? []
  );
  useEffect(() => {
    if (!options) return;
    const compareArr = options.map((option) => option.node.ariaLabel);
    const orderedSelectedItems = mapOrder(selectedItems, compareArr, "label");
    if (JSON.stringify(prevCallbackItems.current) === JSON.stringify(orderedSelectedItems))
      return;
    onSelectionChange?.(orderedSelectedItems);
    prevCallbackItems.current = orderedSelectedItems;
  }, [onSelectionChange, options, selectedItems]);
  useEffect(() => {
    if (!selected) return;
    const getLabel = (item) => typeof item === "string" ? item : item.label;
    const filterDuplicates = selected.reduce(
      (acc, current) => {
        const currentLabel = getLabel(current);
        const isDup = acc.find((item) => getLabel(item) === currentLabel);
        if (!isDup) {
          return [...acc, current];
        } else {
          return acc;
        }
      },
      []
    );
    if (!options) return;
    const compareArr = options.map((option) => option.node.ariaLabel);
    const orderedSelected = mapOrder(filterDuplicates, compareArr, "label");
    if (JSON.stringify(prevControlledItems.current) === JSON.stringify(orderedSelected))
      return;
    setSelectedItems(orderedSelected);
    prevControlledItems.current = orderedSelected;
  }, [options, selected]);
  const ListViewClassNames = cx([listViewStyles["listview"]], className);
  if (remainingProps.items !== void 0) {
    const { children: children2, items, ...rest2 } = remainingProps;
    const renderChildren = children2;
    return /* @__PURE__ */ jsx(
      ListViewContext.Provider,
      {
        value: {
          options,
          onSelectionChange,
          indeterminateItems: indeterminate ?? [],
          selectedItems,
          setSelectedItems,
          items,
          controlled: !!selected
        },
        children: /* @__PURE__ */ jsx(
          "div",
          {
            ref: combinedRef,
            className: ListViewClassNames,
            style: styleCombined,
            "data-anv": "listview",
            role: "grid",
            "aria-multiselectable": "true",
            onFocus: onFocusHandler,
            onBlur: onBlurHandler,
            onKeyDown: onKeyDownHandler,
            onMouseDown: onMouseDownHandler,
            "aria-activedescendant": currentActive,
            tabIndex: 0,
            ...rest2,
            children: renderChildren?.({
              items: items.map((item) => {
                return item;
              })
            })
          }
        )
      }
    );
  }
  const { children, ...rest } = remainingProps;
  return /* @__PURE__ */ jsx(
    ListViewContext.Provider,
    {
      value: {
        options,
        onSelectionChange,
        selectedItems,
        indeterminateItems: indeterminate ?? [],
        setSelectedItems,
        controlled: !!selected || !!indeterminate
      },
      children: /* @__PURE__ */ jsx(
        "div",
        {
          ref: combinedRef,
          className: ListViewClassNames,
          style: styleCombined,
          "data-anv": "listview",
          role: "grid",
          onFocus: onFocusHandler,
          onBlur: onBlurHandler,
          onKeyDown: onKeyDownHandler,
          onMouseDown: onMouseDownHandler,
          "aria-activedescendant": currentActive,
          tabIndex: 0,
          ...rest,
          children
        }
      )
    }
  );
}
ListViewInner.displayName = "ListView";
const ListView = Object.assign(
  forwardRef(ListViewInner),
  {
    /**
     * ListViewOption component for individual selectable items within a listview.
     *
     * Features:
     * - Displays selectable options with proper ARIA attributes
     * - Supports disabled state for non-selectable options
     * - Keyboard navigation support
     * - Accessible with screen reader support
     * - Automatic tracking ID generation for analytics
     *
     * @example
     * <ListView.Option item={{ label: "Option 1" }} />
     */
    Option: ListViewOption,
    /**
     * ListViewOptionCell component for rendering a cell within a listview option.
     *
     * Features:
     * - Renders a grid cell for option content
     * - Supports custom content and layout
     * - Accessible with proper ARIA attributes
     *
     * @example
     * <ListView.OptionCell>Custom cell content</ListView.OptionCell>
     */
    OptionCell: ListViewOptionCell
  }
);

export { ListView as L, ListViewOption as a, ListViewOptionCell as b };
//# sourceMappingURL=ListView-DBO9Bl1H.js.map
