UNPKG

@gfazioli/mantine-flip

Version:

Flip component is a wrapper for any component that can be flipped. It is used to create cards, flip boxes and more.

264 lines (260 loc) 8.27 kB
'use client'; 'use strict'; var React = require('react'); var core = require('@mantine/core'); var hooks = require('@mantine/hooks'); var Flip_context = require('./Flip.context.cjs'); var FlipBack = require('./FlipBack/FlipBack.cjs'); var FlipFront = require('./FlipFront/FlipFront.cjs'); var FlipTarget = require('./FlipTarget/FlipTarget.cjs'); var Flip_module = require('./Flip.module.css.cjs'); const defaultProps = { direction: "horizontal", directionFlipIn: "negative", directionFlipOut: "positive" }; const SPRING_EASING = "linear(0, 0.009, 0.035 2.1%, 0.141 4.4%, 0.723 12.9%, 0.938 16.7%, 1.017, 1.077 21.8%, 1.121 24%, 1.149 26.3%, 1.159, 1.163 29%, 1.154 31.4%, 1.075 38.5%, 1.033 42.7%, 1.001 48%, 0.985 52.2%, 0.98 56.3%, 0.984 64.3%, 1 100%)"; const resolveEasing = (easing) => { if (easing === void 0) { return "ease-in-out"; } if (easing === "spring") { return SPRING_EASING; } return easing; }; const varsResolver = core.createVarsResolver((_, { perspective, easing, duration }) => ({ root: { "--flip-perspective": perspective === void 0 ? "1000px" : perspective, "--flip-transition-duration": duration === void 0 ? ".8s" : `${duration}s`, "--flip-transition-timing-function": resolveEasing(easing) }, "flip-container": {}, "flip-front-face": {}, "flip-back-face": {} })); const Flip = core.polymorphicFactory((_props) => { const { ref, ...restProps } = _props; const props = core.useProps("Flip", defaultProps, restProps); const { perspective, duration, easing, classNames, style, styles, unstyled, vars, children, className, flipped, defaultFlipped, direction, directionFlipIn, directionFlipOut, onChange, onBack, onFront, onTransitionEnd, disabled, lazyBack, swipeable, swipeThreshold, ...others } = props; const initialFlipped = flipped ?? defaultFlipped ?? false; const [rotateValue, setRotateValue] = React.useState(initialFlipped ? -180 : 0); const backMountedRef = React.useRef(initialFlipped || !lazyBack); const rootRef = React.useRef(null); const touchStartRef = React.useRef(null); const toggleFlipRef = React.useRef(() => { }); if (!lazyBack && !backMountedRef.current) { backMountedRef.current = true; } const [_flipped, setFlipped] = hooks.useUncontrolled({ value: flipped, defaultValue: defaultFlipped, finalValue: false, onChange }); const getStyles = core.useStyles({ name: "Flip", props, classes: Flip_module, className, style, classNames, styles, unstyled, vars, varsResolver }); const directionResetRef = React.useRef(false); hooks.useDidUpdate(() => { if (_flipped) { backMountedRef.current = true; } }, [_flipped]); hooks.useDidUpdate(() => { if (_flipped) { directionResetRef.current = true; } setRotateValue(0); if (flipped === void 0) { setFlipped(false); } }, [directionFlipIn, directionFlipOut, direction]); hooks.useDidUpdate(() => { if (directionResetRef.current) { directionResetRef.current = false; return; } if (directionFlipIn === "negative" && directionFlipOut === "positive") { setRotateValue((v) => v ? v + 180 : -180); } if (directionFlipIn === "negative" && directionFlipOut === "negative") { setRotateValue((v) => v - 180); } if (directionFlipIn === "positive" && directionFlipOut === "negative") { setRotateValue((v) => v ? v - 180 : 180); } if (directionFlipIn === "positive" && directionFlipOut === "positive") { setRotateValue((v) => v + 180); } }, [_flipped]); const childrenArray = React.Children.toArray(children); const compoundFront = childrenArray.find( (child) => React.isValidElement(child) && child.type?.displayName === "FlipFront" ); const compoundBack = childrenArray.find( (child) => React.isValidElement(child) && child.type?.displayName === "FlipBack" ); const isCompound = !!(compoundFront || compoundBack); if (isCompound) { if (!compoundFront || !compoundBack) { throw new Error( "Flip component requires both Flip.Front and Flip.Back when using compound components" ); } if (childrenArray.length !== 2) { throw new Error( "Flip component with Flip.Front and Flip.Back must have exactly two children, no extra elements allowed" ); } } else if (childrenArray.length !== 2) { throw new Error( "Flip component must have exactly two children, or use Flip.Front and Flip.Back" ); } const frontChild = isCompound ? compoundFront.props.children : childrenArray[0]; const backChild = isCompound ? compoundBack.props.children : childrenArray[1]; const getDirectionIn = React.useMemo(() => { if (direction === "horizontal") { return { transform: `rotateY(${rotateValue}deg)` }; } return { transform: `rotateX(${rotateValue}deg)` }; }, [direction, rotateValue]); const getBackRotation = React.useMemo(() => { if (direction === "horizontal") { return { transform: "rotateY(180deg)" }; } return { transform: "rotateX(180deg)" }; }, [direction]); const toggleFlip = React.useCallback(() => { if (disabled) { return; } if (_flipped) { setFlipped(false); onFront?.(); } else { backMountedRef.current = true; setFlipped(true); onBack?.(); } }, [disabled, _flipped, onFront, onBack]); toggleFlipRef.current = toggleFlip; const handleTransitionEnd = React.useCallback( (event) => { if (event.propertyName === "transform" && event.target === event.currentTarget) { onTransitionEnd?.(); } }, [onTransitionEnd] ); React.useEffect(() => { if (!swipeable || disabled) { return void 0; } const el = rootRef.current; if (!el) { return void 0; } const threshold = swipeThreshold ?? 50; const handleTouchStart = (e) => { const touch = e.touches[0]; touchStartRef.current = { x: touch.clientX, y: touch.clientY }; }; const handleTouchEnd = (e) => { if (!touchStartRef.current) { return; } const touch = e.changedTouches[0]; const dx = touch.clientX - touchStartRef.current.x; const dy = touch.clientY - touchStartRef.current.y; touchStartRef.current = null; if (direction === "horizontal" && Math.abs(dx) > threshold && Math.abs(dx) > Math.abs(dy)) { toggleFlipRef.current(); } else if (direction === "vertical" && Math.abs(dy) > threshold && Math.abs(dy) > Math.abs(dx)) { toggleFlipRef.current(); } }; el.addEventListener("touchstart", handleTouchStart, { passive: true }); el.addEventListener("touchend", handleTouchEnd, { passive: true }); return () => { el.removeEventListener("touchstart", handleTouchStart); el.removeEventListener("touchend", handleTouchEnd); }; }, [swipeable, disabled, direction, swipeThreshold]); return /* @__PURE__ */ React.createElement( Flip_context.FlipContextProvider, { value: { toggleFlip, flipped: _flipped, disabled: !!disabled } }, /* @__PURE__ */ React.createElement(core.Box, { ref: hooks.useMergedRef(ref, rootRef), ...getStyles("root"), "aria-live": "polite", ...others }, /* @__PURE__ */ React.createElement( "div", { ...getStyles("flip-container", { style: getDirectionIn }), onTransitionEnd: handleTransitionEnd }, /* @__PURE__ */ React.createElement( "div", { ...getStyles("flip-front-face", { style: { zIndex: 0 } }), inert: _flipped || void 0 }, frontChild ), /* @__PURE__ */ React.createElement( "div", { ...getStyles("flip-back-face", { style: getBackRotation }), inert: !_flipped || void 0 }, backMountedRef.current ? backChild : null ) )) ); }); Flip.classes = Flip_module; Flip.displayName = "Flip"; Flip.Target = FlipTarget.FlipTarget; Flip.Front = FlipFront.FlipFront; Flip.Back = FlipBack.FlipBack; exports.Flip = Flip; //# sourceMappingURL=Flip.cjs.map