{"version":3,"file":"CountingDisplay.Bkp8xO2j.mjs","names":["useState","useRef","useMemo"],"sources":["https:/framerusercontent.com/modules/N3u8GMwZT8oq1UEzIyM5/DS05L9FMZqT2wvD3iShC/CountingDisplay.js"],"sourcesContent":["/**\n * =============================================================================\n * ANIMATED COUNTER FRAMER COMPONENT\n * =============================================================================\n *\n * Animated numeric counter component with smooth scrolling digits effect.\n * Displays a fluid transition from a starting value to a target value,\n * with a pulsating luminous dot and customizable glow effects.\n *\n * @author Geoffrey Bacconnier - Dafolle.io\n * @version 1.1.0\n * @date 2025\n *\n * Features:\n * - Smooth digit animation with easing\n * - Automatic number formatting (thousands separators)\n * - Animated luminous dot with pulsation\n * - Customizable glow effects\n * - Auto-start or click-to-start modes\n * - Configurable prefix and suffix\n * - Fully customizable font via Framer\n *\n * =============================================================================\n */import{jsx as _jsx,jsxs as _jsxs}from\"react/jsx-runtime\";import{useState,useEffect,useMemo,useRef}from\"react\";import{motion,useMotionValue,animate,useInView}from\"framer-motion\";import{addPropertyControls,ControlType,useIsStaticRenderer}from\"framer\";/**\n * Animated counter with scrolling digits\n *\n * This component displays an animated numeric counter whose digits scroll\n * from a starting value to a target value, with auto or manual start option,\n * an animated luminous dot and a customizable glow effect on the text.\n *\n * @component\n * @param {CountingDisplayProps} props - Component properties\n * @returns {JSX.Element} Animated counter component\n *\n * @framerIntrinsicWidth 300\n * @framerIntrinsicHeight 80\n *\n * @framerSupportedLayoutWidth any-prefer-fixed\n * @framerSupportedLayoutHeight any-prefer-fixed\n *\n * @author Geoffrey Bacconnier - Dafolle.io\n */export default function CountingDisplay(props){// =============================================================================\n// PROPS DESTRUCTURING WITH DEFAULT VALUES\n// =============================================================================\nconst{startingValue=15271,targetValue=15981,prefix=\"+15 \",suffix=\"\",duration=2,backgroundColor=\"#000000\",textColor=\"#9AFF00\",glowColor=\"#9AFF00\",showGlow=true,font,dotColor=\"#9AFF00\",showDot=true,autoStart=true,triggerMode=\"auto\",padding=\"20px\",gap=\"0.3em\"}=props;// =============================================================================\n// STATES AND ANIMATION VARIABLES\n// =============================================================================\n// Currently displayed value (updated during animation)\nconst[displayValue,setDisplayValue]=useState(startingValue);// Animation in progress indicator\nconst[isAnimating,setIsAnimating]=useState(false);// Track if animation has been triggered (for viewport mode)\nconst[hasTriggered,setHasTriggered]=useState(false);// Static render mode detection (Framer preview)\nconst isStatic=useIsStaticRenderer();// Motion value for smooth Framer Motion animation\nconst motionValue=useMotionValue(startingValue);// Ref for viewport detection\nconst containerRef=useRef(null);// Detect if component is in viewport (with 10% margin)\nconst isInView=useInView(containerRef,{once:false,amount:.3});// =============================================================================\n// ANIMATION START FUNCTION\n// =============================================================================\n/**\n     * Starts the counter animation from starting value to target value\n     * Uses Framer Motion for smooth animation with easing\n     *\n     * @returns {AnimationControls | undefined} Animation controls\n     */const startAnimation=()=>{// No animation in static render mode\nif(isStatic)return;// Initialize animation state\nsetIsAnimating(true);setDisplayValue(startingValue);motionValue.set(startingValue);// Start animation with Framer Motion\nconst controls=animate(motionValue,targetValue,{duration:duration,ease:\"easeOut\",onUpdate:value=>{// Update displayed value every frame\nsetDisplayValue(Math.floor(value));},onComplete:()=>{// Animation end\nsetIsAnimating(false);setDisplayValue(targetValue);}});return controls;};// =============================================================================\n// AUTO-START EFFECT\n// =============================================================================\n/**\n     * Starts animation based on trigger mode:\n     * - \"auto\": Starts automatically after 500ms delay\n     * - \"viewport\": Starts when component enters viewport\n     * - \"click\": Starts only when user clicks (handled in onClick)\n     */useEffect(()=>{// In static mode, always display target value\nif(isStatic){setDisplayValue(targetValue);return;}// Manual click mode - no auto start\nif(triggerMode===\"click\"){setDisplayValue(startingValue);return;}// Viewport mode - trigger when in view\nif(triggerMode===\"viewport\"){if(isInView&&!hasTriggered&&!isAnimating){setHasTriggered(true);setTimeout(()=>{startAnimation();},100);}return;}// Auto mode (legacy autoStart prop)\nif(triggerMode===\"auto\"&&autoStart){if(!hasTriggered){setHasTriggered(true);setTimeout(()=>{startAnimation();},500);}}else if(triggerMode===\"auto\"&&!autoStart){setDisplayValue(targetValue);}},[isInView,triggerMode,hasTriggered,isAnimating]);// =============================================================================\n// NUMBER FORMATTING\n// =============================================================================\n/**\n     * Formats a number by adding spaces as thousands separators\n     * Example: 15981 → \"15 981\"\n     *\n     * @param {number} num - Number to format\n     * @returns {string} Formatted number with separators\n     */const formatNumber=num=>{return Math.floor(num).toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g,\" \");};/**\n     * Formatted and memoized value to avoid unnecessary recalculations\n     * Uses target value in static mode, otherwise current animation value\n     */const formattedValue=useMemo(()=>{return formatNumber(isStatic?targetValue:displayValue);},[displayValue,targetValue,isStatic]);// =============================================================================\n// FRAMER LAYOUT MODE DETECTION\n// =============================================================================\n// Check if component uses fixed layout (width/height 100%)\nconst isFixedWidth=props?.style&&props.style.width===\"100%\";const isFixedHeight=props?.style&&props.style.height===\"100%\";// =============================================================================\n// COMPONENT RENDER\n// =============================================================================\nreturn(// Main container with click handling in manual mode\n/*#__PURE__*/_jsxs(\"div\",{ref:containerRef,style:{...props.style,position:\"relative\",backgroundColor,display:\"flex\",alignItems:\"center\",justifyContent:\"center\",gap:gap,padding:padding,borderRadius:\"8px\",overflow:\"hidden\",// Pointer cursor only in manual click mode\ncursor:triggerMode===\"click\"&&!isStatic?\"pointer\":\"default\",// Layout adaptation according to Framer mode\n...isFixedWidth?{}:{minWidth:\"max-content\"},...isFixedHeight?{}:{minHeight:\"max-content\"},// Apply custom font (inherited by children)\n...font},onClick:()=>{// Start animation on click if click mode\nif(!isStatic&&triggerMode===\"click\"&&!isAnimating){startAnimation();}},children:[showDot&&/*#__PURE__*/_jsx(motion.div,{// Pulsation animation: scale and opacity\nanimate:isStatic?{}:{scale:[1,1.2,1],opacity:[.8,1,.8]},transition:{duration:2,repeat:Infinity,ease:\"easeInOut\"},style:{width:\"0.4em\",height:\"0.4em\",borderRadius:\"50%\",backgroundColor:dotColor,// Glow effect with multiple box-shadows\nboxShadow:showGlow?`0 0 10px ${glowColor}, 0 0 20px ${glowColor}, 0 0 30px ${glowColor}`:\"none\",flexShrink:0}}),/*#__PURE__*/_jsxs(\"div\",{style:{display:\"flex\",alignItems:\"center\",whiteSpace:\"nowrap\"},children:[prefix&&/*#__PURE__*/_jsx(\"span\",{style:{color:textColor},children:prefix}),/*#__PURE__*/_jsx(\"span\",{style:{color:textColor,minWidth:\"1ch\"},children:formattedValue}),suffix&&/*#__PURE__*/_jsx(\"span\",{style:{color:textColor},children:suffix})]})]}));}// =============================================================================\n// FRAMER CONTROLS CONFIGURATION\n// =============================================================================\n/**\n * Property controls definition for Framer editor\n * Allows visual component configuration without touching the code\n *\n * @author Geoffrey Bacconnier - Dafolle.io\n */addPropertyControls(CountingDisplay,{// =========================================================================\n// COUNTER VALUES\n// =========================================================================\nstartingValue:{type:ControlType.Number,title:\"Starting Value\",defaultValue:15271,min:0,max:999999,step:1},targetValue:{type:ControlType.Number,title:\"Target Value\",defaultValue:15981,min:0,max:999999,step:1},// =========================================================================\n// PREFIX AND SUFFIX\n// =========================================================================\nprefix:{type:ControlType.String,title:\"Prefix\",defaultValue:\"+15 \",placeholder:\"+15\"},suffix:{type:ControlType.String,title:\"Suffix\",defaultValue:\"\",placeholder:\"€, %, etc.\"},// =========================================================================\n// ANIMATION\n// =========================================================================\ntriggerMode:{type:ControlType.Enum,title:\"Trigger Mode\",options:[\"auto\",\"viewport\",\"click\"],optionTitles:[\"Auto\",\"On Scroll\",\"On Click\"],defaultValue:\"viewport\"},duration:{type:ControlType.Number,title:\"Duration (s)\",defaultValue:2,min:.5,max:600,step:.1,unit:\"s\"},autoStart:{type:ControlType.Boolean,title:\"Auto Start (Legacy)\",defaultValue:true,enabledTitle:\"Auto\",disabledTitle:\"Manual\",hidden:({triggerMode})=>triggerMode!==\"auto\"},// =========================================================================\n// LUMINOUS DOT\n// =========================================================================\nshowDot:{type:ControlType.Boolean,title:\"Luminous Dot\",defaultValue:true,enabledTitle:\"Shown\",disabledTitle:\"Hidden\"},dotColor:{type:ControlType.Color,title:\"Dot Color\",defaultValue:\"#9AFF00\",hidden:({showDot})=>!showDot},// =========================================================================\n// COLORS\n// =========================================================================\nbackgroundColor:{type:ControlType.Color,title:\"Background\",defaultValue:\"#000000\"},textColor:{type:ControlType.Color,title:\"Text Color\",defaultValue:\"#9AFF00\"},// =========================================================================\n// GLOW EFFECT\n// =========================================================================\nshowGlow:{type:ControlType.Boolean,title:\"Glow Effect\",defaultValue:true,enabledTitle:\"Enabled\",disabledTitle:\"Disabled\"},glowColor:{type:ControlType.Color,title:\"Glow Color\",defaultValue:\"#9AFF00\",hidden:({showGlow})=>!showGlow},// =========================================================================\n// TYPOGRAPHY\n// =========================================================================\nfont:{type:ControlType.Font,title:\"Font\",defaultValue:{fontSize:\"20px\",variant:\"Bold\",letterSpacing:\"0.05em\",lineHeight:\"1em\"},controls:\"extended\",defaultFontType:\"sans-serif\"},// =========================================================================\n// SPACING AND LAYOUT\n// =========================================================================\npadding:{type:ControlType.Padding,title:\"Padding\",defaultValue:\"20px\"},gap:{type:ControlType.String,title:\"Dot/Counter Gap\",defaultValue:\"0.3em\",placeholder:\"0.3em, 10px, etc.\"}});\nexport const __FramerMetadata__ = {\"exports\":{\"default\":{\"type\":\"reactComponent\",\"name\":\"CountingDisplay\",\"slots\":[],\"annotations\":{\"framerIntrinsicHeight\":\"80\",\"framerSupportedLayoutWidth\":\"any-prefer-fixed\",\"framerSupportedLayoutHeight\":\"any-prefer-fixed\",\"framerContractVersion\":\"1\",\"framerIntrinsicWidth\":\"300\"}},\"__FramerMetadata__\":{\"type\":\"variable\"}}}\n//# sourceMappingURL=./CountingDisplay.map"],"mappings":"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"}