import { isClient as isClient$1 } from "../../../../utils/browser.mjs";
import { isNumber, isString } from "../../../../utils/types.mjs";
import { hasOwn } from "../../../../utils/objects.mjs";
import { getScrollBarWidth } from "../../../../utils/dom/scroll.mjs";
import { useNamespace } from "../../../../hooks/use-namespace/index.mjs";
import { useCache } from "../hooks/use-cache.mjs";
import { AUTO_ALIGNMENT, BACKWARD, FORWARD, ITEM_RENDER_EVT, RTL, RTL_OFFSET_NAG, RTL_OFFSET_POS_ASC, RTL_OFFSET_POS_DESC, SCROLL_EVT } from "../defaults.mjs";
import { virtualizedGridProps } from "../props.mjs";
import { getRTLOffsetType, getScrollDir, isRTL } from "../utils.mjs";
import ScrollBar from "../components/scrollbar.mjs";
import { useGridWheel } from "../hooks/use-grid-wheel.mjs";
import { useGridTouch } from "../hooks/use-grid-touch.mjs";
import { useEventListener } from "@vueuse/core";
import { Fragment, computed, defineComponent, getCurrentInstance, h, mergeProps, nextTick, onMounted, ref, resolveDynamicComponent, unref } from "vue";

//#region ../../packages/components/virtual-list/src/builders/build-grid.ts
const createGrid = ({ name, clearCache, getColumnPosition, getColumnStartIndexForOffset, getColumnStopIndexForStartIndex, getEstimatedTotalHeight, getEstimatedTotalWidth, getColumnOffset, getRowOffset, getRowPosition, getRowStartIndexForOffset, getRowStopIndexForStartIndex, initCache, injectToInstance, validateProps }) => {
	return defineComponent({
		name: name ?? "ElVirtualList",
		props: virtualizedGridProps,
		emits: [ITEM_RENDER_EVT, SCROLL_EVT],
		setup(props, { emit, expose, slots }) {
			const ns = useNamespace("vl");
			validateProps(props);
			const instance = getCurrentInstance();
			const cache = ref(initCache(props, instance));
			injectToInstance?.(instance, cache);
			const windowRef = ref();
			const hScrollbar = ref();
			const vScrollbar = ref();
			const innerRef = ref();
			const states = ref({
				isScrolling: false,
				scrollLeft: isNumber(props.initScrollLeft) ? props.initScrollLeft : 0,
				scrollTop: isNumber(props.initScrollTop) ? props.initScrollTop : 0,
				updateRequested: false,
				xAxisScrollDir: FORWARD,
				yAxisScrollDir: FORWARD
			});
			const getItemStyleCache = useCache();
			const parsedHeight = computed(() => Number.parseInt(`${props.height}`, 10));
			const parsedWidth = computed(() => Number.parseInt(`${props.width}`, 10));
			const columnsToRender = computed(() => {
				const { totalColumn, totalRow, columnCache } = props;
				const { isScrolling, xAxisScrollDir, scrollLeft } = unref(states);
				if (totalColumn === 0 || totalRow === 0) return [
					0,
					0,
					0,
					0
				];
				const startIndex = getColumnStartIndexForOffset(props, scrollLeft, unref(cache));
				const stopIndex = getColumnStopIndexForStartIndex(props, startIndex, scrollLeft, unref(cache));
				const cacheBackward = !isScrolling || xAxisScrollDir === BACKWARD ? Math.max(1, columnCache) : 1;
				const cacheForward = !isScrolling || xAxisScrollDir === FORWARD ? Math.max(1, columnCache) : 1;
				return [
					Math.max(0, startIndex - cacheBackward),
					Math.max(0, Math.min(totalColumn - 1, stopIndex + cacheForward)),
					startIndex,
					stopIndex
				];
			});
			const rowsToRender = computed(() => {
				const { totalColumn, totalRow, rowCache } = props;
				const { isScrolling, yAxisScrollDir, scrollTop } = unref(states);
				if (totalColumn === 0 || totalRow === 0) return [
					0,
					0,
					0,
					0
				];
				const startIndex = getRowStartIndexForOffset(props, scrollTop, unref(cache));
				const stopIndex = getRowStopIndexForStartIndex(props, startIndex, scrollTop, unref(cache));
				const cacheBackward = !isScrolling || yAxisScrollDir === BACKWARD ? Math.max(1, rowCache) : 1;
				const cacheForward = !isScrolling || yAxisScrollDir === FORWARD ? Math.max(1, rowCache) : 1;
				return [
					Math.max(0, startIndex - cacheBackward),
					Math.max(0, Math.min(totalRow - 1, stopIndex + cacheForward)),
					startIndex,
					stopIndex
				];
			});
			const estimatedTotalHeight = computed(() => getEstimatedTotalHeight(props, unref(cache)));
			const estimatedTotalWidth = computed(() => getEstimatedTotalWidth(props, unref(cache)));
			const windowStyle = computed(() => [
				{
					position: "relative",
					overflow: "hidden",
					WebkitOverflowScrolling: "touch",
					willChange: "transform"
				},
				{
					direction: props.direction,
					height: isNumber(props.height) ? `${props.height}px` : props.height,
					width: isNumber(props.width) ? `${props.width}px` : props.width
				},
				props.style ?? {}
			]);
			const innerStyle = computed(() => {
				const width = `${unref(estimatedTotalWidth)}px`;
				return {
					height: `${unref(estimatedTotalHeight)}px`,
					pointerEvents: unref(states).isScrolling ? "none" : void 0,
					width,
					margin: 0,
					boxSizing: "border-box"
				};
			});
			const emitEvents = () => {
				const { totalColumn, totalRow } = props;
				if (totalColumn > 0 && totalRow > 0) {
					const [columnCacheStart, columnCacheEnd, columnVisibleStart, columnVisibleEnd] = unref(columnsToRender);
					const [rowCacheStart, rowCacheEnd, rowVisibleStart, rowVisibleEnd] = unref(rowsToRender);
					emit(ITEM_RENDER_EVT, {
						columnCacheStart,
						columnCacheEnd,
						rowCacheStart,
						rowCacheEnd,
						columnVisibleStart,
						columnVisibleEnd,
						rowVisibleStart,
						rowVisibleEnd
					});
				}
				const { scrollLeft, scrollTop, updateRequested, xAxisScrollDir, yAxisScrollDir } = unref(states);
				emit(SCROLL_EVT, {
					xAxisScrollDir,
					scrollLeft,
					yAxisScrollDir,
					scrollTop,
					updateRequested
				});
			};
			const onScroll = (e) => {
				const { clientHeight, clientWidth, scrollHeight, scrollLeft, scrollTop, scrollWidth } = e.currentTarget;
				const _states = unref(states);
				if (_states.scrollTop === scrollTop && _states.scrollLeft === scrollLeft) return;
				let _scrollLeft = scrollLeft;
				if (isRTL(props.direction)) switch (getRTLOffsetType()) {
					case RTL_OFFSET_NAG:
						_scrollLeft = -scrollLeft;
						break;
					case RTL_OFFSET_POS_DESC:
						_scrollLeft = scrollWidth - clientWidth - scrollLeft;
						break;
				}
				states.value = {
					..._states,
					isScrolling: true,
					scrollLeft: _scrollLeft,
					scrollTop: Math.max(0, Math.min(scrollTop, scrollHeight - clientHeight)),
					updateRequested: true,
					xAxisScrollDir: getScrollDir(_states.scrollLeft, _scrollLeft),
					yAxisScrollDir: getScrollDir(_states.scrollTop, scrollTop)
				};
				nextTick(() => resetIsScrolling());
				onUpdated();
				emitEvents();
			};
			const onVerticalScroll = (distance, totalSteps) => {
				const height = unref(parsedHeight);
				const offset = (estimatedTotalHeight.value - height) / totalSteps * distance;
				scrollTo({ scrollTop: Math.min(estimatedTotalHeight.value - height, offset) });
			};
			const onHorizontalScroll = (distance, totalSteps) => {
				const width = unref(parsedWidth);
				const offset = (estimatedTotalWidth.value - width) / totalSteps * distance;
				scrollTo({ scrollLeft: Math.min(estimatedTotalWidth.value - width, offset) });
			};
			const { onWheel } = useGridWheel({
				atXStartEdge: computed(() => states.value.scrollLeft <= 0),
				atXEndEdge: computed(() => states.value.scrollLeft >= estimatedTotalWidth.value - unref(parsedWidth)),
				atYStartEdge: computed(() => states.value.scrollTop <= 0),
				atYEndEdge: computed(() => states.value.scrollTop >= estimatedTotalHeight.value - unref(parsedHeight))
			}, (x, y) => {
				hScrollbar.value?.onMouseUp?.();
				vScrollbar.value?.onMouseUp?.();
				const width = unref(parsedWidth);
				const height = unref(parsedHeight);
				scrollTo({
					scrollLeft: Math.min(states.value.scrollLeft + x, estimatedTotalWidth.value - width),
					scrollTop: Math.min(states.value.scrollTop + y, estimatedTotalHeight.value - height)
				});
			});
			useEventListener(windowRef, "wheel", onWheel, { passive: false });
			const scrollTo = ({ scrollLeft = states.value.scrollLeft, scrollTop = states.value.scrollTop }) => {
				scrollLeft = Math.max(scrollLeft, 0);
				scrollTop = Math.max(scrollTop, 0);
				const _states = unref(states);
				if (scrollTop === _states.scrollTop && scrollLeft === _states.scrollLeft) return;
				states.value = {
					..._states,
					xAxisScrollDir: getScrollDir(_states.scrollLeft, scrollLeft),
					yAxisScrollDir: getScrollDir(_states.scrollTop, scrollTop),
					scrollLeft,
					scrollTop,
					updateRequested: true
				};
				nextTick(() => resetIsScrolling());
				onUpdated();
				emitEvents();
			};
			const { touchStartX, touchStartY, handleTouchStart, handleTouchMove } = useGridTouch(windowRef, states, scrollTo, estimatedTotalWidth, estimatedTotalHeight, parsedWidth, parsedHeight);
			const scrollToItem = (rowIndex = 0, columnIdx = 0, alignment = AUTO_ALIGNMENT) => {
				const _states = unref(states);
				columnIdx = Math.max(0, Math.min(columnIdx, props.totalColumn - 1));
				rowIndex = Math.max(0, Math.min(rowIndex, props.totalRow - 1));
				const scrollBarWidth = getScrollBarWidth(ns.namespace.value);
				const _cache = unref(cache);
				const estimatedHeight = getEstimatedTotalHeight(props, _cache);
				const estimatedWidth = getEstimatedTotalWidth(props, _cache);
				scrollTo({
					scrollLeft: getColumnOffset(props, columnIdx, alignment, _states.scrollLeft, _cache, estimatedWidth > props.width ? scrollBarWidth : 0),
					scrollTop: getRowOffset(props, rowIndex, alignment, _states.scrollTop, _cache, estimatedHeight > props.height ? scrollBarWidth : 0)
				});
			};
			const getItemStyle = (rowIndex, columnIndex) => {
				const { columnWidth, direction, rowHeight } = props;
				const itemStyleCache = getItemStyleCache.value(clearCache && columnWidth, clearCache && rowHeight, clearCache && direction);
				const key = `${rowIndex},${columnIndex}`;
				if (hasOwn(itemStyleCache, key)) return itemStyleCache[key];
				else {
					const [, left] = getColumnPosition(props, columnIndex, unref(cache));
					const _cache = unref(cache);
					const rtl = isRTL(direction);
					const [height, top] = getRowPosition(props, rowIndex, _cache);
					const [width] = getColumnPosition(props, columnIndex, _cache);
					itemStyleCache[key] = {
						position: "absolute",
						left: rtl ? void 0 : `${left}px`,
						right: rtl ? `${left}px` : void 0,
						top: `${top}px`,
						height: `${height}px`,
						width: `${width}px`
					};
					return itemStyleCache[key];
				}
			};
			const resetIsScrolling = () => {
				states.value.isScrolling = false;
				nextTick(() => {
					getItemStyleCache.value(-1, null, null);
				});
			};
			onMounted(() => {
				if (!isClient$1) return;
				const { initScrollLeft, initScrollTop } = props;
				const windowElement = unref(windowRef);
				if (windowElement) {
					if (isNumber(initScrollLeft)) windowElement.scrollLeft = initScrollLeft;
					if (isNumber(initScrollTop)) windowElement.scrollTop = initScrollTop;
				}
				emitEvents();
			});
			const onUpdated = () => {
				const { direction } = props;
				const { scrollLeft, scrollTop, updateRequested } = unref(states);
				const windowElement = unref(windowRef);
				if (updateRequested && windowElement) {
					if (direction === RTL) switch (getRTLOffsetType()) {
						case RTL_OFFSET_NAG:
							windowElement.scrollLeft = -scrollLeft;
							break;
						case RTL_OFFSET_POS_ASC:
							windowElement.scrollLeft = scrollLeft;
							break;
						default: {
							const { clientWidth, scrollWidth } = windowElement;
							windowElement.scrollLeft = scrollWidth - clientWidth - scrollLeft;
							break;
						}
					}
					else windowElement.scrollLeft = Math.max(0, scrollLeft);
					windowElement.scrollTop = Math.max(0, scrollTop);
				}
			};
			const { resetAfterColumnIndex, resetAfterRowIndex, resetAfter } = instance.proxy;
			expose({
				windowRef,
				innerRef,
				getItemStyleCache,
				touchStartX,
				touchStartY,
				handleTouchStart,
				handleTouchMove,
				scrollTo,
				scrollToItem,
				states,
				resetAfterColumnIndex,
				resetAfterRowIndex,
				resetAfter
			});
			const renderScrollbars = () => {
				const { scrollbarAlwaysOn, scrollbarStartGap, scrollbarEndGap, totalColumn, totalRow } = props;
				const width = unref(parsedWidth);
				const height = unref(parsedHeight);
				const estimatedWidth = unref(estimatedTotalWidth);
				const estimatedHeight = unref(estimatedTotalHeight);
				const { scrollLeft, scrollTop } = unref(states);
				return {
					horizontalScrollbar: h(ScrollBar, {
						ref: hScrollbar,
						alwaysOn: scrollbarAlwaysOn,
						startGap: scrollbarStartGap,
						endGap: scrollbarEndGap,
						class: ns.e("horizontal"),
						clientSize: width,
						layout: "horizontal",
						onScroll: onHorizontalScroll,
						ratio: width * 100 / estimatedWidth,
						scrollFrom: scrollLeft / (estimatedWidth - width),
						total: totalRow,
						visible: true
					}),
					verticalScrollbar: h(ScrollBar, {
						ref: vScrollbar,
						alwaysOn: scrollbarAlwaysOn,
						startGap: scrollbarStartGap,
						endGap: scrollbarEndGap,
						class: ns.e("vertical"),
						clientSize: height,
						layout: "vertical",
						onScroll: onVerticalScroll,
						ratio: height * 100 / estimatedHeight,
						scrollFrom: scrollTop / (estimatedHeight - height),
						total: totalColumn,
						visible: true
					})
				};
			};
			const renderItems = () => {
				const [columnStart, columnEnd] = unref(columnsToRender);
				const [rowStart, rowEnd] = unref(rowsToRender);
				const { data, totalColumn, totalRow, useIsScrolling, itemKey } = props;
				const children = [];
				if (totalRow > 0 && totalColumn > 0) for (let row = rowStart; row <= rowEnd; row++) for (let column = columnStart; column <= columnEnd; column++) {
					const key = itemKey({
						columnIndex: column,
						data,
						rowIndex: row
					});
					children.push(h(Fragment, { key }, slots.default?.({
						columnIndex: column,
						data,
						isScrolling: useIsScrolling ? unref(states).isScrolling : void 0,
						style: getItemStyle(row, column),
						rowIndex: row
					})));
				}
				return children;
			};
			const renderInner = () => {
				const Inner = resolveDynamicComponent(props.innerElement);
				const children = renderItems();
				return [h(Inner, mergeProps(props.innerProps, {
					style: unref(innerStyle),
					ref: innerRef
				}), !isString(Inner) ? { default: () => children } : children)];
			};
			const renderWindow = () => {
				const Container = resolveDynamicComponent(props.containerElement);
				const { horizontalScrollbar, verticalScrollbar } = renderScrollbars();
				const Inner = renderInner();
				return h("div", {
					key: 0,
					class: ns.e("wrapper"),
					role: props.role
				}, [
					h(Container, {
						class: props.className,
						style: unref(windowStyle),
						onScroll,
						ref: windowRef
					}, !isString(Container) ? { default: () => Inner } : Inner),
					horizontalScrollbar,
					verticalScrollbar
				]);
			};
			return renderWindow;
		}
	});
};

//#endregion
export { createGrid as default };
//# sourceMappingURL=build-grid.mjs.map