318 lines
9.8 KiB
Plaintext
318 lines
9.8 KiB
Plaintext
"use client";
|
|
import {
|
|
listbox_section_default
|
|
} from "./chunk-L637PYVS.mjs";
|
|
import {
|
|
listbox_item_default
|
|
} from "./chunk-V45NB7OG.mjs";
|
|
|
|
// src/virtualized-listbox.tsx
|
|
import { useMemo as useMemo2, useRef as useRef2, useState } from "react";
|
|
import { mergeProps } from "@react-aria/utils";
|
|
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
import { isEmpty } from "@heroui/shared-utils";
|
|
|
|
// ../scroll-shadow/src/use-scroll-shadow.ts
|
|
import { mapPropsVariants } from "@heroui/system";
|
|
import { scrollShadow } from "@heroui/theme";
|
|
import { useDOMRef } from "@heroui/react-utils";
|
|
|
|
// ../../hooks/use-data-scroll-overflow/src/index.ts
|
|
import { capitalize } from "@heroui/shared-utils";
|
|
import { useEffect, useRef } from "react";
|
|
function useDataScrollOverflow(props = {}) {
|
|
const {
|
|
domRef,
|
|
isEnabled = true,
|
|
overflowCheck = "vertical",
|
|
visibility = "auto",
|
|
offset = 0,
|
|
onVisibilityChange,
|
|
updateDeps = []
|
|
} = props;
|
|
const visibleRef = useRef(visibility);
|
|
useEffect(() => {
|
|
const el = domRef == null ? void 0 : domRef.current;
|
|
if (!el || !isEnabled)
|
|
return;
|
|
const setAttributes = (direction, hasBefore, hasAfter, prefix, suffix) => {
|
|
if (visibility === "auto") {
|
|
const both = `${prefix}${capitalize(suffix)}Scroll`;
|
|
if (hasBefore && hasAfter) {
|
|
el.dataset[both] = "true";
|
|
el.removeAttribute(`data-${prefix}-scroll`);
|
|
el.removeAttribute(`data-${suffix}-scroll`);
|
|
} else {
|
|
el.dataset[`${prefix}Scroll`] = hasBefore.toString();
|
|
el.dataset[`${suffix}Scroll`] = hasAfter.toString();
|
|
el.removeAttribute(`data-${prefix}-${suffix}-scroll`);
|
|
}
|
|
} else {
|
|
const next = hasBefore && hasAfter ? "both" : hasBefore ? prefix : hasAfter ? suffix : "none";
|
|
if (next !== visibleRef.current) {
|
|
onVisibilityChange == null ? void 0 : onVisibilityChange(next);
|
|
visibleRef.current = next;
|
|
}
|
|
}
|
|
};
|
|
const checkOverflow = () => {
|
|
const directions = [
|
|
{ type: "vertical", prefix: "top", suffix: "bottom" },
|
|
{ type: "horizontal", prefix: "left", suffix: "right" }
|
|
];
|
|
for (const { type, prefix, suffix } of directions) {
|
|
if (overflowCheck === type || overflowCheck === "both") {
|
|
const hasBefore = type === "vertical" ? el.scrollTop > offset : el.scrollLeft > offset;
|
|
const hasAfter = type === "vertical" ? el.scrollTop + el.clientHeight + offset < el.scrollHeight : el.scrollLeft + el.clientWidth + offset < el.scrollWidth;
|
|
setAttributes(type, hasBefore, hasAfter, prefix, suffix);
|
|
}
|
|
}
|
|
};
|
|
const clearOverflow = () => {
|
|
["top", "bottom", "top-bottom", "left", "right", "left-right"].forEach((attr) => {
|
|
el.removeAttribute(`data-${attr}-scroll`);
|
|
});
|
|
};
|
|
checkOverflow();
|
|
el.addEventListener("scroll", checkOverflow);
|
|
if (visibility !== "auto") {
|
|
clearOverflow();
|
|
if (visibility === "both") {
|
|
el.dataset.topBottomScroll = String(overflowCheck === "vertical");
|
|
el.dataset.leftRightScroll = String(overflowCheck === "horizontal");
|
|
} else {
|
|
el.dataset.topBottomScroll = "false";
|
|
el.dataset.leftRightScroll = "false";
|
|
["top", "bottom", "left", "right"].forEach((attr) => {
|
|
el.dataset[`${attr}Scroll`] = String(visibility === attr);
|
|
});
|
|
}
|
|
}
|
|
return () => {
|
|
el.removeEventListener("scroll", checkOverflow);
|
|
clearOverflow();
|
|
};
|
|
}, [...updateDeps, isEnabled, visibility, overflowCheck, onVisibilityChange, domRef]);
|
|
}
|
|
|
|
// ../scroll-shadow/src/use-scroll-shadow.ts
|
|
import { useMemo } from "react";
|
|
import { objectToDeps } from "@heroui/shared-utils";
|
|
function useScrollShadow(originalProps) {
|
|
var _a;
|
|
const [props, variantProps] = mapPropsVariants(originalProps, scrollShadow.variantKeys);
|
|
const {
|
|
ref,
|
|
as,
|
|
children,
|
|
className,
|
|
style,
|
|
size = 40,
|
|
offset = 0,
|
|
visibility = "auto",
|
|
isEnabled = true,
|
|
onVisibilityChange,
|
|
...otherProps
|
|
} = props;
|
|
const Component = as || "div";
|
|
const domRef = useDOMRef(ref);
|
|
useDataScrollOverflow({
|
|
domRef,
|
|
offset,
|
|
visibility,
|
|
isEnabled,
|
|
onVisibilityChange,
|
|
updateDeps: [children],
|
|
overflowCheck: (_a = originalProps.orientation) != null ? _a : "vertical"
|
|
});
|
|
const styles = useMemo(
|
|
() => scrollShadow({
|
|
...variantProps,
|
|
className
|
|
}),
|
|
[objectToDeps(variantProps), className]
|
|
);
|
|
const getBaseProps = (props2 = {}) => {
|
|
var _a2;
|
|
return {
|
|
ref: domRef,
|
|
className: styles,
|
|
"data-orientation": (_a2 = originalProps.orientation) != null ? _a2 : "vertical",
|
|
style: {
|
|
"--scroll-shadow-size": `${size}px`,
|
|
...style,
|
|
...props2.style
|
|
},
|
|
...otherProps,
|
|
...props2
|
|
};
|
|
};
|
|
return { Component, styles, domRef, children, getBaseProps };
|
|
}
|
|
|
|
// src/virtualized-listbox.tsx
|
|
import { filterDOMProps } from "@heroui/react-utils";
|
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
var getItemSizesForCollection = (collection, itemHeight) => {
|
|
const sizes = [];
|
|
for (const item of collection) {
|
|
if (item.type === "section") {
|
|
sizes.push(([...item.childNodes].length + 1) * itemHeight);
|
|
} else {
|
|
sizes.push(itemHeight);
|
|
}
|
|
}
|
|
return sizes;
|
|
};
|
|
var getScrollState = (element) => {
|
|
if (!element || element.scrollTop === void 0 || element.clientHeight === void 0 || element.scrollHeight === void 0) {
|
|
return {
|
|
isTop: false,
|
|
isBottom: false,
|
|
isMiddle: false
|
|
};
|
|
}
|
|
const isAtTop = element.scrollTop === 0;
|
|
const isAtBottom = Math.ceil(element.scrollTop + element.clientHeight) >= element.scrollHeight;
|
|
const isInMiddle = !isAtTop && !isAtBottom;
|
|
return {
|
|
isTop: isAtTop,
|
|
isBottom: isAtBottom,
|
|
isMiddle: isInMiddle
|
|
};
|
|
};
|
|
var VirtualizedListbox = (props) => {
|
|
const {
|
|
Component,
|
|
state,
|
|
color,
|
|
variant,
|
|
itemClasses,
|
|
getBaseProps,
|
|
topContent,
|
|
bottomContent,
|
|
hideEmptyContent,
|
|
hideSelectedIcon,
|
|
shouldHighlightOnFocus,
|
|
disableAnimation,
|
|
getEmptyContentProps,
|
|
getListProps,
|
|
scrollShadowProps
|
|
} = props;
|
|
const { virtualization } = props;
|
|
if (!virtualization || !isEmpty(virtualization) && !virtualization.maxListboxHeight && !virtualization.itemHeight) {
|
|
throw new Error(
|
|
"You are using a virtualized listbox. VirtualizedListbox requires 'virtualization' props with 'maxListboxHeight' and 'itemHeight' properties. This error might have originated from autocomplete components that use VirtualizedListbox. Please provide these props to use the virtualized listbox."
|
|
);
|
|
}
|
|
const { maxListboxHeight, itemHeight } = virtualization;
|
|
const listHeight = Math.min(maxListboxHeight, itemHeight * state.collection.size);
|
|
const parentRef = useRef2(null);
|
|
const itemSizes = useMemo2(
|
|
() => getItemSizesForCollection([...state.collection], itemHeight),
|
|
[state.collection, itemHeight]
|
|
);
|
|
const rowVirtualizer = useVirtualizer({
|
|
count: [...state.collection].length,
|
|
getScrollElement: () => parentRef.current,
|
|
estimateSize: (i) => itemSizes[i]
|
|
});
|
|
const virtualItems = rowVirtualizer.getVirtualItems();
|
|
const { getBaseProps: getBasePropsScrollShadow } = useScrollShadow({ ...scrollShadowProps });
|
|
const renderRow = (virtualItem) => {
|
|
var _a;
|
|
const item = [...state.collection][virtualItem.index];
|
|
if (!item) {
|
|
return null;
|
|
}
|
|
const itemProps = {
|
|
color,
|
|
item,
|
|
state,
|
|
variant,
|
|
disableAnimation,
|
|
hideSelectedIcon,
|
|
...item.props
|
|
};
|
|
const virtualizerStyle = {
|
|
position: "absolute",
|
|
top: 0,
|
|
left: 0,
|
|
width: "100%",
|
|
height: `${virtualItem.size}px`,
|
|
transform: `translateY(${virtualItem.start}px)`
|
|
};
|
|
if (item.type === "section") {
|
|
return /* @__PURE__ */ jsx(
|
|
listbox_section_default,
|
|
{
|
|
...itemProps,
|
|
itemClasses,
|
|
style: { ...virtualizerStyle, ...itemProps.style }
|
|
},
|
|
item.key
|
|
);
|
|
}
|
|
let listboxItem = /* @__PURE__ */ jsx(
|
|
listbox_item_default,
|
|
{
|
|
...itemProps,
|
|
classNames: mergeProps(itemClasses, (_a = item.props) == null ? void 0 : _a.classNames),
|
|
shouldHighlightOnFocus,
|
|
style: { ...virtualizerStyle, ...itemProps.style }
|
|
},
|
|
item.key
|
|
);
|
|
if (item.wrapper) {
|
|
listboxItem = item.wrapper(listboxItem);
|
|
}
|
|
return listboxItem;
|
|
};
|
|
const [scrollState, setScrollState] = useState({
|
|
isTop: false,
|
|
isBottom: true,
|
|
isMiddle: false
|
|
});
|
|
const content = /* @__PURE__ */ jsxs(Component, { ...getListProps(), children: [
|
|
!state.collection.size && !hideEmptyContent && /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("div", { ...getEmptyContentProps() }) }),
|
|
/* @__PURE__ */ jsx(
|
|
"div",
|
|
{
|
|
...filterDOMProps(getBasePropsScrollShadow()),
|
|
ref: parentRef,
|
|
"data-bottom-scroll": scrollState.isTop,
|
|
"data-top-bottom-scroll": scrollState.isMiddle,
|
|
"data-top-scroll": scrollState.isBottom,
|
|
style: {
|
|
height: maxListboxHeight,
|
|
overflow: "auto"
|
|
},
|
|
onScroll: (e) => {
|
|
setScrollState(getScrollState(e.target));
|
|
},
|
|
children: listHeight > 0 && itemHeight > 0 && /* @__PURE__ */ jsx(
|
|
"div",
|
|
{
|
|
style: {
|
|
height: `${rowVirtualizer.getTotalSize()}px`,
|
|
width: "100%",
|
|
position: "relative"
|
|
},
|
|
children: virtualItems.map((virtualItem) => renderRow(virtualItem))
|
|
}
|
|
)
|
|
}
|
|
)
|
|
] });
|
|
return /* @__PURE__ */ jsxs("div", { ...getBaseProps(), children: [
|
|
topContent,
|
|
content,
|
|
bottomContent
|
|
] });
|
|
};
|
|
var virtualized_listbox_default = VirtualizedListbox;
|
|
|
|
export {
|
|
virtualized_listbox_default
|
|
};
|