import type { Ref } from "vue";
import { ref } from "vue";
import { addClass, hasClass, prefixStyle } from "../utils/dom";
import type { WaterfallProps } from "../types/waterfall";
import type { CssStyleObject, Nullable } from "../types/util";
const transform = prefixStyle("transform");
const duration = prefixStyle("animation-duration");
const delay = prefixStyle("animation-delay");
const transition = prefixStyle("transition");
const fillMode = prefixStyle("animation-fill-mode");
export function useLayout(
props: WaterfallProps,
colWidth: Ref<number>,
cols: Ref<number>,
offsetX: Ref<number>,
waterfallWrapper: Ref<Nullable<HTMLElement>>
) {
const posY = ref<number[]>([]);
const wrapperHeight = ref(0);
const getX = (index: number): number => {
const count = props.hasAroundGutter ? index + 1 : index;
return props.gutter * count + colWidth.value * index + offsetX.value;
};
const initY = (): void => {
posY.value = new Array(cols.value).fill(
props.hasAroundGutter ? props.gutter : 0
);
};
const animation = addAnimation(props);
const layoutHandle = async () => {
initY();
const items: HTMLElement[] = [];
if (waterfallWrapper && waterfallWrapper.value) {
waterfallWrapper.value.childNodes.forEach((el: any) => {
if (el!.className === "waterfall-item") items.push(el);
});
}
if (items.length === 0) return false;
for (let i = 0; i < items.length; i++) {
const curItem = items[i] as HTMLElement;
const minY = Math.min.apply(null, posY.value);
const minYIndex = posY.value.indexOf(minY);
const curX = getX(minYIndex);
const style = curItem.style as CssStyleObject;
if (transform) style[transform] = `translate3d(${curX}px,${minY}px, 0)`;
style.width = `${colWidth.value}px`;
const { height } = curItem.getBoundingClientRect();
posY.value[minYIndex] += height + props.gutter;
animation(curItem, () => {
if (transition) style[transition] = "transform .3s";
});
}
wrapperHeight.value = Math.max.apply(null, posY.value);
};
return {
wrapperHeight,
layoutHandle,
};
}
function addAnimation(props: WaterfallProps) {
return (item: HTMLElement, callback?: () => void) => {
const content = item!.firstChild as HTMLElement;
if (content && !hasClass(content, props.animationPrefix)) {
const durationSec = `${props.animationDuration / 1000}s`;
const delaySec = `${props.animationDelay / 1000}s`;
const style = content.style as CssStyleObject;
style.visibility = "visible";
if (duration) style[duration] = durationSec;
if (delay) style[delay] = delaySec;
if (fillMode) style[fillMode] = "both";
addClass(content, props.animationPrefix);
addClass(content, props.animationEffect);
setTimeout(() => {
const itemStyle = item.style as CssStyleObject;
itemStyle.visibility = "visible";
if (callback) callback();
}, props.animationDuration + props.animationDelay);
}
};
}