UNPKG

react-slip-and-slide

Version:
1,855 lines (1,819 loc) 50.4 kB
var __defProp = Object.defineProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; // packages/utils/src/components/AnimatedBox/web/AnimatedBox.tsx import React2 from "react"; // packages/utils/src/spring/index.ts import { animated, SpringValue, to, useSpringValue } from "@react-spring/web"; // packages/utils/src/components/Box/web/Box.tsx import React from "react"; var BoxBase = ({ children, styles, willMeasure, onPress, onPressStart, web, native: _, style }, ref) => { const divRef = React.useRef(null); React.useImperativeHandle(ref, () => ({ measure: () => { return new Promise((res) => { if (willMeasure && divRef.current) { const { width, height } = divRef.current.getBoundingClientRect(); res({ width: divRef.current.offsetWidth || width, height: divRef.current.offsetHeight || height }); } else { res({ width: 0, height: 0 }); } }); }, addEventListener: (type, listener, options) => { divRef.current?.addEventListener(type, listener, options); }, removeEventListener: (type, listener, options) => { divRef.current?.removeEventListener(type, listener, options); }, dispatchEvent: (e) => !!divRef.current?.dispatchEvent(e) })); return /* @__PURE__ */ React.createElement( "div", { ref: divRef, onClick: onPress, ...web, style: { ...styles, ...web?.style, ...style }, onMouseDown: onPressStart, onTouchStart: onPressStart }, children ); }; var Box = React.forwardRef(BoxBase); // packages/utils/src/components/AnimatedBox/helpers.ts var parseTranslate = (x, y) => { if (x !== void 0 && y !== void 0) { return `translate(${x}px, ${y}px)`; } else if (x !== void 0) { return `translateX(${x}px)`; } else if (y !== void 0) { return `translateY(${y}px)`; } return ""; }; var parseTransforms = (transform2) => { return transform2?.reduce((acc, transform3) => { const [trs] = Object.entries(transform3); const [key, val] = trs; acc[key] = val; return acc; }, {}); }; var selectTransforms = (transforms) => { return { rotateX: transforms?.rotateX, rotateY: transforms?.rotateY, rotateZ: transforms?.rotateZ, rotate: transforms?.rotate, scale: transforms?.scale, scaleX: transforms?.scaleX, scaleY: transforms?.scaleY, transform: to( [transforms?.translateX, transforms?.translateY], parseTranslate ) }; }; var springTransformsStyles = (transform2) => { const transformsObject = parseTransforms(transform2); return selectTransforms(transformsObject); }; // packages/utils/src/components/AnimatedBox/web/AnimatedBox.tsx var AnimatedProxy = animated(Box); var AnimatedBox = React2.forwardRef( ({ children, style, ...rest }, ref) => { const transforms = React2.useMemo( () => springTransformsStyles(style?.transform), [style?.transform] ); return /* @__PURE__ */ React2.createElement( AnimatedProxy, { ref, style: { ...style, ...transforms }, ...rest }, children ); } ); // packages/utils/src/components/GestureContainer/web/GestureContainer.tsx import { useGesture } from "@use-gesture/react"; import { Lethargy } from "lethargy-ts"; import { throttle as throttle2 } from "lodash"; import React11 from "react"; // packages/utils/src/context/utils.ts var utils_exports = {}; __export(utils_exports, { initializeContextData: () => initializeContextData, processContextData: () => processContextData }); import { clamp as clamp2, sumBy, uniqueId } from "lodash"; // packages/utils/src/utilities/helpers.ts import { clamp } from "lodash"; import React3 from "react"; var typedMemo = React3.memo; function getCurrentDynamicIndex(_input, ranges, validDirection, direction, clampOffset, rangeOffsetPosition) { let finalIndex = 0; const input = Math.abs(clamp(_input, clampOffset.MAX, clampOffset.MIN)); const index = ranges.findIndex((item2) => { return item2.range.start <= input && input <= item2.range.end; }); if (index === -1) { return finalIndex; } const item = ranges[index]; const itemOffset = item.range[rangeOffsetPosition]; const offset = input; if (offset > itemOffset && validDirection === "left") { finalIndex = item.index + 1; } else if (offset < itemOffset && validDirection === "right") { finalIndex = item.index - 1; } else { finalIndex = item.index; } if (!direction) { finalIndex = item.index; } return clampIndex(finalIndex, ranges); } var getNextDynamicOffset = ({ offsetX, ranges, lastValidDirection, direction, clampOffset, rangeOffsetPosition }) => { const currIndex = getCurrentDynamicIndex( offsetX, ranges, lastValidDirection, direction, clampOffset, rangeOffsetPosition ); const offset = ranges[currIndex]?.range[rangeOffsetPosition] || 0; return -offset; }; var clampIndex = (index, data) => clamp(index, 0, data.length - 1); var getDynamicRangeSum = (itemDimensionMap) => { let previousSum = 0; const range = []; itemDimensionMap.forEach(({ width = 0 }, index) => { range.push({ index, width, range: { start: previousSum, center: previousSum + width / 2, end: previousSum + width } }); previousSum += width; }); return range; }; var isInRange = (index, { dataLength, viewSize, offsetX, visibleItems }) => { const upperAmount = Math.round(visibleItems / 2); const lowerAmount = visibleItems - upperAmount; const fixedIndex = Math.round(-offsetX / viewSize); const currentIndex = fixedIndex < 0 ? fixedIndex % dataLength + dataLength : fixedIndex; const lowerRange = [ (currentIndex + dataLength - lowerAmount) % dataLength, (currentIndex + dataLength - 1) % dataLength ]; const upperRange = [ currentIndex % dataLength, (currentIndex + upperAmount) % dataLength ]; if (lowerRange[0] < dataLength && lowerRange[0] > lowerRange[1] || upperRange[0] > upperRange[1]) { lowerRange[1] = dataLength - 1; upperRange[0] = 0; } const isInLowerRange = index >= lowerRange[0] && index <= lowerRange[1]; const isInUpperRange = index >= upperRange[0] && index <= upperRange[1]; if (isInLowerRange || isInUpperRange) { return true; } return false; }; var useIsFirstRender = () => { const isFirst = React3.useRef(true); if (isFirst.current) { isFirst.current = false; return true; } return isFirst.current; }; var processClampOffsets = ({ wrapperWidth, sideMargins, centered, containerWidth, itemDimensionMode, ranges }) => { let MIN = 0; let MAX = 0; if (itemDimensionMode === "static") { MAX = -wrapperWidth + containerWidth; if (centered) { const _MAX_CENTERED = MAX - sideMargins * 2; MAX = _MAX_CENTERED; } } else { const position = centered ? "center" : "start"; const firstDynamicOffset = -(ranges[0]?.range[position] || 0); const sideMargins2 = !centered ? (containerWidth - (ranges[ranges.length - 1]?.width || 0)) / 2 : 0; const initialCorrection = itemDimensionMode === "dynamic" && centered ? firstDynamicOffset : 0; MIN = initialCorrection; MAX = -ranges[ranges.length - 1]?.range[position] + sideMargins2 * 2 || 0; } if (wrapperWidth < containerWidth && !centered) { MAX = MIN; } return { MIN, MAX }; }; var elementDimensionStyles = ({ width, height }) => { return { width: width === 0 ? void 0 : width, minWidth: width === 0 ? void 0 : width, height: height === 0 ? void 0 : height, minHeight: height === 0 ? void 0 : height }; }; // packages/utils/src/context/utils.ts var getLargestDynamicItem = (itemDimensionMap) => { const widest = Math.max(...itemDimensionMap.map((d) => d.width || 0)); const highest = Math.max(...itemDimensionMap.map((d) => d.height || 0)); return { width: isFinite(widest) ? widest : 0, height: isFinite(highest) ? highest : 0 }; }; var getDynamicWrapperWidth = (itemDimensionMap) => { return sumBy(itemDimensionMap, ({ width }) => width); }; function processContextData(data) { const { dataLength, itemDimensions: { width: itemWidth = 0, height: _itemHeight = 0 }, wrapperWidth: _wrapperWidth, container, centered, fullWidthItem, itemDimensionMode, itemDimensionMap, needsMeasurements } = data; const largestItem = getLargestDynamicItem(itemDimensionMap); const dynamicWrapperWidth = getDynamicWrapperWidth(itemDimensionMap); const itemDimensions = { width: fullWidthItem ? container.width : itemWidth || largestItem.width, height: needsMeasurements ? largestItem.height : _itemHeight }; const containerDimensions = { width: container.width, height: container.height || itemDimensions.height }; const wrapperWidth = itemDimensionMode === "static" ? dataLength * itemDimensions.width : dynamicWrapperWidth; const sideMargins = (containerDimensions.width - itemDimensions.width) / 2; const { MIN, MAX } = processClampOffsets({ wrapperWidth, sideMargins, containerWidth: containerDimensions.width, centered, itemDimensionMode, ranges: data.ranges }); return { ...data, wrapperWidth, clampOffset: { MIN, MAX }, itemDimensionMode, itemDimensions, container: containerDimensions }; } function initializeContextData(props) { const { _testId = "", data, itemHeight = 0, itemWidth = 0, fullWidthItem, infinite: _infinite, visibleItems = 0, containerWidth, containerHeight, interpolators, centered, momentumMultiplier = 2, animateStartup = true, initialIndex, loadingTime } = props; const itemDimensionMode = itemWidth || fullWidthItem ? "static" : "dynamic"; const infinite = itemDimensionMode === "static" && !!_infinite; const loadingType = visibleItems === 0 ? "eager" : "lazy"; const engineMode = infinite || loadingType === "lazy" ? "multi" : "single"; const shouldAnimatedStartup = !!animateStartup || !!initialIndex || !!loadingTime || itemDimensionMode === "dynamic"; const needsMeasurements = itemDimensionMode === "dynamic" || itemDimensionMode === "static" && engineMode === "multi" && !itemHeight && !containerHeight; const initialContextData = { _testId, initId: uniqueId("init-"), needsMeasurements, infinite, itemDimensionMode, loadingType, isReady: false, shouldAnimatedStartup, engineMode, data: props.data, itemDimensions: { width: props.itemWidth || (fullWidthItem ? containerWidth : 0) || 0, height: props.itemHeight || 0 }, container: { width: containerWidth || 0, height: containerHeight || itemHeight || 0 }, centered: !!centered, visibleItems: props.visibleItems || 0, dataLength: data.length, wrapperWidth: 0, clampOffset: { MIN: 0, MAX: 0 }, fullWidthItem: !!fullWidthItem, itemDimensionMap: [], ranges: [], interpolators, rangeOffsetPosition: centered ? "center" : "start", momentumMultiplier: clamp2(momentumMultiplier, 0, 10), currentIndex: typeof initialIndex === "number" ? initialIndex : initialIndex?.index || 0, OffsetX: null }; return initialContextData; } // packages/utils/src/context/hooks.ts var hooks_exports = {}; __export(hooks_exports, { useDataContext: () => useDataContext }); import React10 from "react"; // packages/utils/src/context/provider.tsx var provider_exports = {}; __export(provider_exports, { DataProvider: () => DataProvider, dataContext: () => dataContext }); import { nothing, produce } from "immer"; import { uniqueId as uniqueId2 } from "lodash"; import React9 from "react"; // packages/utils/src/utilities/displacement.tsx var displacement = ({ OffsetX, index = 0, itemWidth = 200, infinite, dataLength }) => { if (infinite) { const halfData = Math.round((dataLength - 1) / 2) + 1; const halfItem = itemWidth / 2; const wrapperWidth = dataLength * itemWidth; const startPosition = index > halfData ? (index - dataLength) * itemWidth : index * itemWidth; const max = halfData * itemWidth; const min = -((dataLength - halfData - 1) * itemWidth); const input = [ -wrapperWidth, min - halfItem - startPosition, min - halfItem - startPosition, 0, max + halfItem - startPosition, max + halfItem - startPosition, wrapperWidth ]; const output = [ startPosition, max + halfItem, min - halfItem, startPosition, max + halfItem, min - halfItem, startPosition ]; return to(OffsetX, input, output, "clamp"); } return to(OffsetX, (x) => x + itemWidth * index); }; // packages/utils/src/utilities/mergeRefs.ts function mergeRefs(refs) { return (value) => { refs.forEach((ref) => { if (typeof ref === "function") { ref(value); } else if (ref !== null) { ref.current = value; } }); }; } // packages/utils/src/utilities/rubberband.ts import { clamp as clamp3 } from "lodash"; var scale = ({ size, multiplier }) => { return (offset) => { if (size === 0 || Math.abs(size) === Infinity) { return Math.pow(offset, multiplier * 5); } return offset * size * multiplier / (size + multiplier * offset); }; }; var createRubberband = ([min, max], multiplier = 0.15) => { const size = max - min; const rub = scale({ size, multiplier }); return (offset) => { if (multiplier === 0) { return clamp3(offset, min, max); } switch (true) { case offset < min: return -rub(min - offset) + min; case offset > max: return +rub(offset - max) + max; default: return offset; } }; }; // packages/utils/src/utilities/useDynamicDimension.tsx import { defer, times } from "lodash"; import React4 from "react"; var useDynamicDimension = () => { const { state: { dataLength, initId, needsMeasurements }, actions: { setItemDimensionMap, setRanges } } = Context.useDataContext(); const itemRefs = React4.useMemo(() => { if (needsMeasurements) { return times(dataLength, () => React4.createRef()); } return []; }, [dataLength]); const measure = React4.useCallback(() => { return new Promise((res) => { defer(() => { const promises = itemRefs.map((ref) => ref.current?.measure()); Promise.all(promises).then((measurements) => { res( measurements?.map((itemMeasurements) => ({ width: itemMeasurements?.width || 0, height: itemMeasurements?.height || 0 })) ); }); }); }); }, [itemRefs]); React4.useEffect(() => { if (needsMeasurements) { measure().then((itemDimensionMap) => { setItemDimensionMap(itemDimensionMap); setRanges(getDynamicRangeSum(itemDimensionMap)); }); } }, [ dataLength, initId, measure, needsMeasurements, setItemDimensionMap, setRanges ]); return { itemRefs }; }; // packages/utils/src/utilities/useInterpolation.tsx import React5 from "react"; var useInterpolation = ({ index }) => { const { state: { dataLength, itemDimensions: { width: itemWidth }, infinite, itemDimensionMode, ranges, interpolators, rangeOffsetPosition, engineMode, wrapperWidth, OffsetX: ContextOffsetX } } = Context.useDataContext(); const dynamicOffset = ranges[index]?.range[rangeOffsetPosition] || 0; const interpolatorsKeys = Object.entries(interpolators || {}); const getTranslateX = React5.useCallback(() => { const OffsetX = engineMode === "single" ? ContextOffsetX : ContextOffsetX.to((offsetX) => offsetX % wrapperWidth); const x = displacement({ OffsetX, dataLength, index, itemWidth, infinite }); if (itemDimensionMode === "static") { return x.to((val) => val / itemWidth).to([-1, 0, 1], [-itemWidth, 0, itemWidth]); } return to(OffsetX, (x2) => x2 + dynamicOffset); }, [ ContextOffsetX, dataLength, dynamicOffset, engineMode, index, infinite, itemDimensionMode, itemWidth, wrapperWidth ]); const { scale: scale2, opacity, translateX } = React5.useMemo(() => { if (itemWidth && (interpolatorsKeys.length || engineMode === "multi")) { const translateX2 = getTranslateX(); const { scale: scale3 = 1, opacity: opacity2 = 1 } = interpolatorsKeys.reduce( (acc, [key, val]) => { acc[key] = translateX2.to((val2) => val2 / itemWidth).to([-1, 0, 1], [val, 1, val], "clamp"); return acc; }, {} ); return { translateX: translateX2, scale: scale3, opacity: opacity2 }; } return { translateX: 0, scale: 1, opacity: 1 }; }, [engineMode, getTranslateX, itemWidth, interpolatorsKeys]); return { translateX, scale: scale2, opacity }; }; // packages/utils/src/utilities/useItemsRange.tsx import React6 from "react"; // packages/utils/src/utilities/useScreenDimensions.tsx import React7 from "react"; import { asyncScheduler, distinctUntilChanged, fromEvent, throttleTime } from "rxjs"; // packages/utils/src/platform/index.ts var Platform = { is: (platform) => { if (!platform) { return "web"; } const platforms = { web: true, native: false, ios: false, android: false }; return platforms[platform]; } }; // packages/utils/src/ScreenDimensions/index.ts var ScreenDimensions = () => { return { width: window.innerWidth, height: window.innerHeight }; }; // packages/utils/src/utilities/useScreenDimensions.tsx var useScreenDimensions = () => { const throttle3 = 100; const [screenDimensions, setScreenDimensions] = React7.useState(ScreenDimensions()); const set = () => { setScreenDimensions(ScreenDimensions()); }; React7.useEffect(() => { if (Platform.is("web")) { const sub$ = fromEvent(window, "resize").pipe( throttleTime(throttle3, asyncScheduler, { leading: true, trailing: true }), distinctUntilChanged() ).subscribe(set); return () => sub$.unsubscribe(); } }, []); return screenDimensions; }; // packages/utils/src/utilities/useEngine.tsx import { clamp as clamp4, defer as defer2, throttle } from "lodash"; import React8 from "react"; // packages/utils/src/utilities/config.ts var baseSpringConfig = { tension: 220, friction: 32, mass: 1 }; var snappySpringConfig = { ...baseSpringConfig, tension: 320 }; var springConfigByActionType = { drag: baseSpringConfig, wheel: baseSpringConfig, release: baseSpringConfig, correction: baseSpringConfig, wheelSnap: snappySpringConfig, navigate: snappySpringConfig, ref: snappySpringConfig }; // packages/utils/src/utilities/useEngine.tsx var useEngine = ({ snap, containerWidth: containerWidthProp, pressToSlide, rubberbandElasticity, instanceRef, initialIndex, loadingTime, animateStartup, onChange, onEdges, onReady, onItemPress }) => { const { state: { data, dataLength, itemDimensions: { width: itemWidth }, loadingType, centered, infinite, itemDimensionMode, container, wrapperWidth, clampOffset, ranges, rangeOffsetPosition, momentumMultiplier, OffsetX, isReady, shouldAnimatedStartup, initId, currentIndex }, actions: { setContainerDimensions, reInit, setCurrentIndex } } = Context.useDataContext(); const isFirstRender = useIsFirstRender(); const index = React8.useRef(currentIndex); const [_, reRender] = React8.useState(0); const lastOffset = React8.useRef(0); const containerRef = React8.useRef(null); const isDragging = React8.useRef(false); const direction = React8.useRef(false); const lastValidDirection = React8.useRef(null); const isIntentionalDrag = React8.useRef(false); const rubberband = React8.useMemo( () => createRubberband( [clampOffset.MAX, clampOffset.MIN], rubberbandElasticity ), [clampOffset.MAX, clampOffset.MIN, rubberbandElasticity] ); const actionType = React8.useRef("release"); const Opacity = useSpringValue(shouldAnimatedStartup ? 0 : 1, { config: { tension: 220, friction: 32, mass: 1 } }); const { width: screenWidth } = useScreenDimensions(); React8.useLayoutEffect(() => { defer2(() => { containerRef.current?.measure().then(({ width, height }) => { setContainerDimensions({ height: container.height || height, width: containerWidthProp || width }); }); }); }, [container, screenWidth, initId]); const setActionType = (type) => { actionType.current = type; }; const checkActionType = React8.useCallback((actionTypes) => { return actionTypes.includes(actionType.current); }, []); const clampReleaseOffset = React8.useCallback( (offset) => { if (infinite && itemDimensionMode === "static") { return offset; } return clamp4(offset, clampOffset.MAX, clampOffset.MIN); }, [clampOffset.MAX, clampOffset.MIN, infinite, itemDimensionMode] ); const processIndex = React8.useCallback( ({ offset }) => { if (itemWidth) { const modIndex = offset / itemWidth % dataLength; return offset <= 0 ? Math.abs(modIndex) : Math.abs(modIndex > 0 ? dataLength - modIndex : 0); } return currentIndex || 0; }, [currentIndex, dataLength, itemWidth] ); const getCurrentIndex = React8.useCallback( ({ offset }) => { if (infinite) { return -Math.round(offset / itemWidth); } return Math.round(processIndex({ offset })); }, [infinite, itemWidth, processIndex] ); const getRelativeIndex = React8.useCallback( ({ offset }) => { return Math.floor(processIndex({ offset })); }, [processIndex] ); const getCurrentOffset = React8.useCallback( ({ index: index2 }) => { const finalOffset = -index2 * itemWidth; return finalOffset; }, [itemWidth] ); const checkEdges = React8.useCallback( ({ offset }) => { let start = false; let end = false; if (offset >= clampOffset.MIN) { start = true; } else if (offset <= clampOffset.MAX) { end = true; } else { start = false; end = false; } if (clampOffset.MIN === clampOffset.MAX) { start = true; end = true; } return { start, end }; }, [clampOffset] ); const onCallbacks = React8.useCallback(() => { if (currentIndex !== void 0) { onChange?.(currentIndex); } if (!infinite && !isFirstRender) { onEdges?.(checkEdges({ offset: lastOffset.current })); } }, [checkEdges, currentIndex, infinite, isFirstRender, onChange, onEdges]); const onEdge = React8.useMemo( () => throttle((edges) => { onEdges?.(edges); }, 120), [onEdges] ); const clampIdx = React8.useCallback( (idx) => { return clampIndex(idx, data); }, [data] ); const handleOnSpringStart = React8.useCallback(() => { if (checkActionType(["navigate"])) { onEdge?.(checkEdges({ offset: lastOffset.current })); } }, [checkActionType, checkEdges, onEdge]); React8.useEffect(() => { if (checkActionType(["release", "navigate", "correction", "wheelSnap"])) { onCallbacks(); } }, [currentIndex]); const handleOnSpringRelease = React8.useCallback( (clampedReleaseOffset) => { if (checkActionType(["release", "navigate", "ref", "wheelSnap"])) { lastOffset.current = clampedReleaseOffset; if (itemDimensionMode === "static") { index.current = clampIdx( getRelativeIndex({ offset: lastOffset.current }) ); } else { index.current = getCurrentDynamicIndex( lastOffset.current, ranges, lastValidDirection.current, direction.current, clampOffset, rangeOffsetPosition ); } setCurrentIndex(index.current); if (loadingType === "lazy") { reRender(index.current); } } }, [ checkActionType, clampIdx, clampOffset, getRelativeIndex, itemDimensionMode, loadingType, rangeOffsetPosition, ranges, setCurrentIndex ] ); const spring = React8.useCallback( ({ offset, immediate, onRest }) => { const clampedReleaseOffset = clampReleaseOffset(offset); OffsetX.start({ to: checkActionType(["drag", "correction"]) ? offset : clampedReleaseOffset, immediate: immediate || checkActionType(["drag", "wheel"]), onStart: () => { handleOnSpringStart(); }, onRest: (x) => { onRest?.(x); }, config: springConfigByActionType[actionType.current] }); handleOnSpringRelease(clampedReleaseOffset); }, [ clampReleaseOffset, OffsetX, checkActionType, handleOnSpringRelease, handleOnSpringStart ] ); const springIt = React8.useCallback( ({ offset, immediate, actionType: type, onRest }) => { setActionType(type); spring({ offset, immediate, onRest }); }, [spring] ); const getCurrentIndexByOffset = React8.useCallback( (offset) => { let finalIndex = 0; const neutralIndex = offset / wrapperWidth * dataLength; const left = Math.ceil(neutralIndex); const right = Math.floor(neutralIndex); if (!snap) { return right; } switch (direction.current) { case "left": finalIndex = left; break; case "right": finalIndex = right; break; default: if (lastValidDirection.current === "left") { finalIndex = left; } else if (lastValidDirection.current === "right") { finalIndex = right; } break; } return finalIndex; }, [dataLength, snap, wrapperWidth] ); const nextIndexByDirection = (index2, direction2) => { if (direction2 === "next") { return index2 + 1; } else if (direction2 === "prev") { return index2 - 1; } return index2; }; const navigateByDirection = React8.useCallback( (direction2, immediate, actionType2 = "navigate") => { let targetOffset = lastOffset.current; if (itemDimensionMode === "static") { const currentIndex2 = getCurrentIndex({ offset: OffsetX.get() }); const nextIndex = nextIndexByDirection(currentIndex2, direction2); targetOffset = -nextIndex * itemWidth; } else { if (currentIndex !== void 0) { const nextIndex = clampIdx( nextIndexByDirection(currentIndex, direction2) ); targetOffset = -ranges[nextIndex].range[rangeOffsetPosition]; } } springIt({ offset: targetOffset, actionType: actionType2, immediate }); }, [ OffsetX, clampIdx, currentIndex, getCurrentIndex, itemDimensionMode, itemWidth, rangeOffsetPosition, ranges, springIt ] ); const drag = React8.useCallback( (x, actionType2) => { const offset = infinite ? x : rubberband(x); if (Platform.is("web")) { onEdge?.(checkEdges({ offset })); } springIt({ offset, actionType: actionType2 }); }, [checkEdges, infinite, onEdge, rubberband, springIt] ); const withSnap = React8.useCallback( ({ offset }) => { if (itemDimensionMode === "static") { const page = getCurrentIndexByOffset(-offset); const finalOffset = -page * itemWidth; return finalOffset; } else { const nextDynamicOffset = getNextDynamicOffset({ offsetX: offset, ranges, lastValidDirection: lastValidDirection.current, direction: direction.current, clampOffset, rangeOffsetPosition }); return nextDynamicOffset; } }, [ clampOffset, getCurrentIndexByOffset, itemDimensionMode, itemWidth, rangeOffsetPosition, ranges ] ); const withMomentum = React8.useCallback( ({ offset, v }) => { const multiplier = -(1 + momentumMultiplier); const baseVelocity = -Math.abs(v); const velocity = direction.current === "left" ? -(baseVelocity * multiplier) : direction.current === "right" ? baseVelocity * multiplier : 0; const momentumOffset = offset + velocity; return momentumOffset; }, [momentumMultiplier] ); const release = React8.useCallback( ({ offset, velocity: v }) => { let offsetX = 0; isDragging.current = false; if (snap) { if (isIntentionalDrag.current) { offsetX = withSnap({ offset }); } else { springIt({ offset: lastOffset.current, actionType: "correction" }); return; } } else { offsetX = withMomentum({ offset, v }); } springIt({ offset: offsetX, actionType: "release" }); }, [snap, springIt, withMomentum, withSnap] ); const navigateByIndex = React8.useCallback( (idx, immediate, actionType2 = "navigate", alignCentered = false) => { const nextIndex = clampIdx(idx); let targetOffset = lastOffset.current; let currentItemWidth = itemWidth; if (itemDimensionMode === "static") { targetOffset = getCurrentOffset({ index: nextIndex }); } else { if (ranges.length) { currentItemWidth = ranges[nextIndex].width; targetOffset = -ranges[nextIndex].range[rangeOffsetPosition]; } } if (alignCentered && !centered) { targetOffset = targetOffset + container.width / 2 - currentItemWidth / 2; } springIt({ offset: targetOffset, actionType: actionType2, immediate }); }, [ centered, clampIdx, container.width, getCurrentOffset, itemDimensionMode, itemWidth, rangeOffsetPosition, ranges, springIt ] ); const navigate = React8.useCallback( ({ index: index2, direction: direction2, immediate, actionType: actionType2 = "navigate", alignCentered }) => { if (!isReady) { return; } if (index2 !== void 0) { navigateByIndex(index2, immediate, actionType2, alignCentered); } else if (direction2) { navigateByDirection(direction2, immediate, actionType2); } }, [isReady, navigateByIndex, navigateByDirection] ); const initialNavigation = React8.useCallback(() => { if (initialIndex !== 0) { const alignCentered = typeof initialIndex === "object" ? initialIndex.centered : void 0; navigate({ index: currentIndex, immediate: true, alignCentered }); } }, [currentIndex, initialIndex, navigate]); const move = React8.useCallback( (offset) => { if (!isReady) { return; } springIt({ offset: OffsetX.get() + offset, actionType: "navigate" }); }, [OffsetX, isReady, springIt] ); const goTo = React8.useCallback( ({ index: index2, animated: animated2 = true, centered: centered2 }) => { if (!isReady) { return; } navigateByIndex(index2, !animated2, "ref", centered2); }, [isReady, navigateByIndex] ); const getOffset = React8.useCallback( ({ current, target }) => { if (Math.abs(target - current) >= dataLength / 2) { const off = current > dataLength / 2 ? dataLength : -dataLength; return off + (target - current); } return target - current; }, [dataLength] ); const handlePressToSlide = React8.useCallback( (_index) => { if (!pressToSlide || isDragging.current) { return; } if (!OffsetX.isAnimating) { const res = getOffset({ target: _index, current: currentIndex }); move(-(itemWidth * res)); } }, [ OffsetX.isAnimating, currentIndex, getOffset, itemWidth, move, pressToSlide ] ); const handleOnItemPress = React8.useCallback( (idx) => { if (currentIndex !== void 0) { handlePressToSlide(idx); onItemPress?.({ currentIndex, pressedItemIndex: idx }); } }, [currentIndex, handlePressToSlide, onItemPress] ); React8.useEffect(() => { if (isReady) { initialNavigation(); defer2(() => { onReady?.(true); }); if (shouldAnimatedStartup) { Opacity.start({ to: 1, delay: loadingTime, immediate: !animateStartup }); } } }, [isReady]); React8.useEffect(() => { const { end } = checkEdges({ offset: OffsetX.get() }); if (end && isReady) { springIt({ offset: clampOffset.MAX, actionType: "release" }); } }, [clampOffset.MAX]); React8.useEffect(() => { if (!isFirstRender) { onEdges?.(checkEdges({ offset: OffsetX.get() })); } }, [screenWidth, clampOffset.MAX]); React8.useEffect(() => { onEdges?.(checkEdges({ offset: OffsetX.get() })); }, []); React8.useImperativeHandle( instanceRef, () => ({ reinitialize: reInit, next: () => navigate({ direction: "next" }), previous: () => navigate({ direction: "prev" }), goTo, move }) ); return { handlers: { onDrag: drag, onRelease: release, onItemPress: handleOnItemPress, navigate }, state: { container, centered }, signals: { direction, isDragging, isIntentionalDrag, lastOffset, lastValidDirection, actionType }, containerRef, Opacity }; }; // packages/utils/src/utilities/cssToNativeStyle.ts import transform from "css-to-react-native"; import { toString } from "lodash"; // packages/utils/src/context/provider.tsx var useProducer = (initialData) => { return React9.useReducer( produce((draft, action) => { switch (action.type) { case "INIT" /* INIT */: { return action.payload; } case "RE_INIT" /* RE_INIT */: { if (draft.itemDimensionMode === "dynamic") { draft.isReady = false; } draft.initId = action.payload.initId; break; } case "SET_CONTAINER_DIMENSIONS" /* SET_CONTAINER_DIMENSIONS */: { const { width, height } = action.payload; if (height) { draft.container.height = height; } if (width) { draft.container.width = width; if (draft.itemDimensionMode === "static") { draft.isReady = true; } } break; } case "SET_WRAPPER_WIDTH" /* SET_WRAPPER_WIDTH */: { const { dataLength, itemDimensions: { width: itemWidth = 0 }, itemDimensionMode } = draft; const payloadWrapperWidth = action.payload; const nextWrapperWidth = itemDimensionMode === "static" ? dataLength * itemWidth : payloadWrapperWidth; draft.wrapperWidth = nextWrapperWidth; break; } case "SET_ITEM_DIMENSION_MAP" /* SET_ITEM_DIMENSION_MAP */: { draft.itemDimensionMap = action.payload; if (action.payload.length && draft.ranges.length) { draft.isReady = true; } break; } case "SET_RANGES" /* SET_RANGES */: { draft.ranges = action.payload; if (action.payload.length && draft.itemDimensionMap.length) { draft.isReady = true; } break; } case "SET_IS_READY" /* SET_IS_READY */: { if (draft.isReady !== action.payload) { draft.isReady = action.payload; } break; } case "SET_CURRENT_INDEX" /* SET_CURRENT_INDEX */: { if (draft.currentIndex !== action.payload) { draft.currentIndex = action.payload; } break; } default: { return nothing; } } }), initialData ); }; var dataContext = React9.createContext( {} ); function DataProvider({ props, children }) { const isFirstRender = useIsFirstRender(); const [state, dispatch] = useProducer(initializeContextData(props)); const OffsetX = useSpringValue(0, { config: baseSpringConfig }); const actions = React9.useMemo( () => ({ init: (payload) => dispatch({ type: "INIT" /* INIT */, payload }), reInit: () => dispatch({ type: "RE_INIT" /* RE_INIT */, payload: { initId: uniqueId2("init-") } }), setContainerDimensions: (payload) => dispatch({ type: "SET_CONTAINER_DIMENSIONS" /* SET_CONTAINER_DIMENSIONS */, payload }), setWrapperWidth: (payload) => dispatch({ type: "SET_WRAPPER_WIDTH" /* SET_WRAPPER_WIDTH */, payload }), setItemDimensionMap: (payload) => dispatch({ type: "SET_ITEM_DIMENSION_MAP" /* SET_ITEM_DIMENSION_MAP */, payload }), setRanges: (payload) => dispatch({ type: "SET_RANGES" /* SET_RANGES */, payload }), setIsReady: (payload) => dispatch({ type: "SET_IS_READY" /* SET_IS_READY */, payload }), setCurrentIndex: (payload) => dispatch({ type: "SET_CURRENT_INDEX" /* SET_CURRENT_INDEX */, payload }) }), [dispatch] ); React9.useEffect(() => { if (!isFirstRender) { actions.init({ ...initializeContextData(props), currentIndex: state.currentIndex }); } }, [ props.data, props.itemHeight, props.itemWidth, props.fullWidthItem, props.infinite, props.visibleItems, props.containerWidth, props.containerHeight, props.interpolators?.opacity, props.interpolators?.scale, props.centered, props.momentumMultiplier, props.intentionalDragThreshold, // eslint-disable-next-line react-hooks/exhaustive-deps ...props.listener || [] ]); const contextHandlers = React9.useMemo( () => ({ state: { ...processContextData(state), OffsetX }, actions, dispatch }), [OffsetX, actions, dispatch, state] ); return /* @__PURE__ */ React9.createElement(dataContext.Provider, { value: contextHandlers }, children); } // packages/utils/src/context/hooks.ts function useDataContext() { const { state, actions, dispatch } = React10.useContext( dataContext ); return { state, actions, dispatch }; } // packages/utils/src/context/index.ts var Context = { ...utils_exports, ...hooks_exports, ...provider_exports }; // packages/utils/src/components/GestureContainer/web/GestureContainer.tsx var GestureContainerComponent = ({ style, styles, direction, lastValidDirection, lastOffset, isIntentionalDrag, isDragging, useWheel, snap, intentionalDragThreshold, onDrag, onRelease, navigate, children }, ref) => { const { state: { OffsetX } } = Context.useDataContext(); const internalRef = React11.useRef(null); const refs = mergeRefs([ref, internalRef]); const lethargy = React11.useMemo( () => new Lethargy({ sensitivity: 2, inertiaDecay: 20, delay: 100, highVelocity: 100 }), [] ); const handleGesture = React11.useCallback( ({ active, mx, dirX, vx, actionType }) => { if (mx === 0) { onRelease({ offset: lastOffset.current, velocity: vx * 100 }); return; } const dir = dirX < 0 ? "left" : dirX > 0 ? "right" : false; direction.current = dir; if (dir) { lastValidDirection.current = dir; } const offset = lastOffset.current + mx; isIntentionalDrag.current = Math.abs(mx) >= intentionalDragThreshold; isDragging.current = Math.abs(mx) !== 0; if (active) { onDrag(offset, actionType); } else { onRelease({ offset, velocity: vx * 100 }); } }, [ direction, intentionalDragThreshold, isDragging, isIntentionalDrag, lastOffset, lastValidDirection, onDrag, onRelease ] ); const handleOnPressStart = () => { if (!snap && OffsetX.isAnimating && OffsetX.get() !== lastOffset.current) { lastOffset.current = OffsetX.get(); OffsetX.stop(); } }; const handleOnWheel = React11.useCallback( throttle2( (dir) => { const direction2 = dir < 0 ? "prev" : dir > 0 ? "next" : false; if (direction2) { navigate({ direction: direction2, actionType: "wheelSnap" }); } }, 200, { leading: true, trailing: false } ), [navigate] ); useGesture( { onTouchStart: handleOnPressStart, onMouseDown: handleOnPressStart, onDrag: ({ active, movement: [mx], direction: [dirX], velocity: [vx] }) => { handleGesture({ active, mx, dirX, vx, actionType: "drag" }); }, onWheel: ({ event, active, movement: [mx, my], direction: [dirX, dirY] }) => { const move = -mx || -my; const dir = dirX || dirY; if (snap) { if (lethargy.check(event)) { handleOnWheel(dir); } } else { handleGesture({ active, mx: move, dirX: dir, vx: 0, actionType: "wheel" }); } } }, { target: internalRef, wheel: { enabled: !!useWheel, preventDefault: true, eventOptions: { passive: false } }, drag: { filterTaps: true, axis: "x" } } ); return /* @__PURE__ */ React11.createElement( AnimatedBox, { ref: refs, willMeasure: true, style, styles: { display: "flex", position: "relative", flexDirection: "row", ...styles, touchAction: "pan-y" } }, children ); }; var GestureContainer = React11.forwardRef(GestureContainerComponent); // packages/utils/src/components/LazyLoad/LazyLoad.tsx import React12 from "react"; var LazyLoad = ({ render, children }) => { if (render) { return /* @__PURE__ */ React12.createElement(React12.Fragment, null, children); } return /* @__PURE__ */ React12.createElement(React12.Fragment, null); }; // packages/react-slip-and-slide/src/ReactSlipAndSlide.tsx import React16 from "react"; // packages/react-slip-and-slide/src/Engine.tsx import React15 from "react"; // packages/react-slip-and-slide/src/Item.tsx import React13 from "react"; function ItemBaseComponent({ index, item, renderItem, onPress }, ref) { const { state: { itemDimensions, engineMode, loadingType, itemDimensionMode, isReady } } = Context.useDataContext(); const isLazy = loadingType === "lazy"; const Opacity = useSpringValue(isLazy ? 0 : 1, { config: { tension: 220, friction: 32, mass: 1 } }); React13.useEffect(() => { if (isLazy) { Opacity.start({ to: 1 }); } }, [isLazy]); const itemStyles = React13.useRef({ display: "flex", justifyContent: "center", alignItems: "center" }); if (engineMode === "multi") { itemStyles.current = { ...itemStyles.current, position: isReady ? "absolute" : "static", flexShrink: 0, ...elementDimensionStyles(itemDimensions) }; } if (itemDimensionMode === "static") { itemStyles.current = { ...itemStyles.current, ...elementDimensionStyles(itemDimensions) }; } const { translateX, scale: scale2, opacity } = useInterpolation({ index }); const memoRenderItem = React13.useMemo(() => { return renderItem({ item, index }); }, [index, item, renderItem]); return /* @__PURE__ */ React13.createElement( AnimatedBox, { willMeasure: true, ref, onPress, web: { onDragStart: (e) => e.preventDefault() }, style: { transform: [ { translateX: engineMode === "multi" ? translateX : 0 }, { scale: scale2 } ], opacity }, styles: itemStyles.current }, /* @__PURE__ */ React13.createElement(LazyLayout, { isLazy, itemDimensions }, memoRenderItem) ); } var LazyLayout = ({ isLazy, itemDimensions, children }) => { const Opacity = useSpringValue(isLazy ? 0 : 1, { config: { tension: 220, friction: 32, mass: 1 } }); React13.useEffect(() => { if (isLazy) { Opacity.start({ to: 1 }); } }, [isLazy]); if (isLazy) { return /* @__PURE__ */ React13.createElement( AnimatedBox, { styles: { display: "flex", alignItems: "center", ...elementDimensionStyles(itemDimensions), width: "100%" }, style: { opacity: Opacity } }, children ); } return /* @__PURE__ */ React13.createElement(React13.Fragment, null, children); }; var ItemBase = React13.forwardRef(ItemBaseComponent); // packages/react-slip-and-slide/src/LayoutManager.tsx import React14 from "react"; var LayoutManager = ({ children }) => { const { state: { itemDimensions, itemDimensionMode, engineMode, centered, OffsetX } } = Context.useDataContext(); if (engineMode === "single") { const dynamicCenteredCorrectionStyles = { transform: itemDimensionMode === "dynamic" && centered ? `translateX(${itemDimensions.width / 2}px)` : void 0 }; return /* @__PURE__ */ React14.createElement(Box, { styles: dynamicCenteredCorrectionStyles }, /* @__PURE__ */ React14.createElement( AnimatedBox, { style: { transform: [{ translateX: OffsetX }] }, styles: { display: "flex", flexDirection: "row", alignItems: "center", ...elementDimensionStyles(itemDimensions) } }, children )); } return /* @__PURE__ */ React14.createElement(React14.Fragment, null, children); }; // packages/react-slip-and-slide/src/Engine.tsx var Engine = ({ onItemPress, renderItem }) => { const { state: { data, dataLength, itemDimensions, loadingType, visibleItems, OffsetX } } = Context.useDataContext(); const { itemRefs } = useDynamicDimension(); const shouldRender = React15.useCallback( (i) => { if (loadingType === "eager") { return true; } return isInRange(i, { dataLength, viewSize: itemDimensions.width, visibleItems: visibleItems || Math.round(dataLength / 2), offsetX: OffsetX.get() }); }, [OffsetX, dataLength, itemDimensions.width, loadingType, visibleItems] ); return /* @__PURE__ */ React15.createElement(LayoutManager, null, data.map((item, index) => /* @__PURE__ */ React15.createElement(LazyLoad, { key: index, render: shouldRender(index) }, /* @__PURE__ */ React15.createElement( ItemBase, { ref: itemRefs[index], index, item, renderItem, onPress: () => onItemPress(index) } )))); }; // packages/react-slip-and-slide/src/ReactSlipAndSlide.tsx function ReactSlipAndSlideComponent({ snap, containerWidth, pressToSlide, animateStartup = true, rubberbandElasticity = 0.1, overflowHidden = true, intentionalDragThreshold = 20, useWheel, initialIndex, loadingTime, onChange, onEdges, onReady, onItemPress, renderItem }, ref) { const { handlers, state, signals, containerRef, Opacity } = useEngine({ snap, containerWidth, pressToSlide, animateStartup, rubberbandElasticity, instanceRef: ref, initialIndex, loadingTime, onChange, onEdges, onReady, onItemPress }); const gestureContainerStyles = { justifyContent: state.centered ? "center" : "flex-start", ...elementDimensionStyles(state.container), width: containerWidth || "100%", minWidth: containerWidth || "100%", overflow: overflowHidden ? "hidden" : void 0 }; return /* @__PURE__ */ React16.createElement( GestureContainer, { ref: containerRef, direction: signals.direction, isDragging: signals.isDragging, isIntentionalDrag: signals.isIntentionalDrag, lastOffset: signals.lastOffset, lastValidDirection: signals.lastValidDirection, useWheel, style: { opacity: Opacity }, styles: gestureContainerStyles, snap, intentionalDragThreshold, onDrag: handlers.onDrag, onRelease: handlers.onRelease, navigate: handlers.navigate }, /* @__PURE__ */ React16.createElement(Engine, { onItemPress: handlers.onItemPress, renderItem }) ); } var ForwardReactSlipAndSlideRef = React16.forwardRef( ReactSlipAndSlideComponent ); function ReactSlipAndSlideWithContext(props, ref) { return /* @__PURE__ */ React16.createElement( Context.DataProvider, { props }, props.childrenPosition === "above" && props.child