{"version":3,"file":"ReadingTime.CeK7bitN.mjs","names":["useState","useMemo"],"sources":["https:/framerusercontent.com/modules/E3Aq2uEZJbLPE31p6LSm/WjNhxS5GOYnDtxBxqWOL/ReadingTime.js"],"sourcesContent":["import{jsx as _jsx}from\"react/jsx-runtime\";import{addPropertyControls,ControlType}from\"framer\";import{fontControls}from\"https://framer.com/m/framer/default-utils.js@^0.45.0\";import{useEffect,useState,useMemo}from\"react\";/**\n * WordCounter\n * A tiny utility component that can show either the total word count\n * of a rich-text string or an estimated reading time. Now with full\n * support for variable fonts (font-variation-settings) in addition to\n * OpenType features.\n *\n * Added:\n * - displayMode \"timeLeft\" that decreases as the user scrolls through\n *   a target article container, accounting for sticky positioning.\n * - targetSelector + stickyTop props to compute accurate progress.\n */export function WordCounter(props){const{font,fontColor,title,displayMode,targetSelector,stickyTop=0,readingSpeed=200}=props;// ────────────────────────────────────────────────────────────────\n// Word-count logic\n// ────────────────────────────────────────────────────────────────\nconst[countWords,setCountWords]=useState(0);useEffect(()=>{if(typeof window===\"undefined\")return;const div=document.createElement(\"div\");div.innerHTML=typeof title===\"string\"?title:JSON.stringify(title);const text=div.textContent||div.innerText||\"\";const words=text.trim().length===0?0:text.trim().split(/\\s+/).filter(Boolean).length;setCountWords(words);},[title]);// ────────────────────────────────────────────────────────────────\n// Sticky-aware reading progress (0..1) for \"time left\"\n// ────────────────────────────────────────────────────────────────\nconst[progress,setProgress]=useState(0);useEffect(()=>{if(typeof window===\"undefined\")return;let articleEl=null;let resizeObs=null;let ticking=false;const getArticle=()=>targetSelector?document.querySelector(targetSelector):null;const computeProgress=()=>{const doc=document.documentElement;const body=document.body;const viewportH=window.innerHeight||doc.clientHeight||0;// If we have a target article container, use sticky math.\narticleEl=articleEl||getArticle();if(articleEl){const rect=articleEl.getBoundingClientRect();const articleH=Math.max(articleEl.scrollHeight,rect.height);// Total distance the document must travel while sticky:\n// (article height) - (visible viewport below the pinned sticky top)\nconst visibleWindowBelowSticky=Math.max(0,viewportH-stickyTop);const totalScrollable=Math.max(1,articleH-visibleWindowBelowSticky);// How far we've progressed once the article's top has reached the stickyTop line.\n// rect.top decreases as we scroll; when rect.top <= stickyTop we are \"in\" the sticky phase.\nconst raw=stickyTop-rect.top;// Clamp to [0, totalScrollable] to handle before/after boundaries.\nconst scrolled=Math.min(Math.max(raw,0),totalScrollable);const p=Math.min(1,Math.max(0,scrolled/totalScrollable));setProgress(p);return;}// Fallback: whole-page progress\nconst scrollTop=window.pageYOffset||doc.scrollTop||body.scrollTop||0;const docHeight=Math.max(body.scrollHeight,doc.scrollHeight,body.offsetHeight,doc.offsetHeight,body.clientHeight,doc.clientHeight);const scrollable=Math.max(1,docHeight-viewportH);const p=Math.min(1,Math.max(0,scrollTop/scrollable));setProgress(p);};const onScroll=()=>{if(!ticking){ticking=true;requestAnimationFrame(()=>{computeProgress();ticking=false;});}};const onResize=()=>{computeProgress();};// Initial compute\ncomputeProgress();// Listeners\nwindow.addEventListener(\"scroll\",onScroll,{passive:true});window.addEventListener(\"resize\",onResize);// Observe article size changes (images loading, fonts, etc.)\narticleEl=getArticle();if(articleEl&&\"ResizeObserver\"in window){resizeObs=new ResizeObserver(()=>computeProgress());resizeObs.observe(articleEl);}return()=>{window.removeEventListener(\"scroll\",onScroll);window.removeEventListener(\"resize\",onResize);if(resizeObs&&articleEl)resizeObs.unobserve(articleEl);};},[targetSelector,stickyTop]);// ────────────────────────────────────────────────────────────────\n// Font helpers\n// ────────────────────────────────────────────────────────────────\nconst fontStyle=useMemo(()=>{if(!font)return{};// Separate variable axes and OpenType features from the rest\nconst{variable,opentype,...base}=font;const style={...base};// Apply OpenType feature settings → font-feature-settings\nif(opentype&&typeof opentype===\"object\"&&Object.keys(opentype).length){style.fontFeatureSettings=Object.entries(opentype).filter(([,enabled])=>enabled).map(([tag,value])=>`\"${tag}\" ${value}`).join(\", \");}// Apply variable font axis values → font-variation-settings\nif(variable&&typeof variable===\"object\"&&Object.keys(variable).length){style.fontVariationSettings=Object.entries(variable).map(([axis,value])=>`\"${axis}\" ${value}`).join(\", \");// Provide the most common axis as fallbacks when possible\nif(variable.wght&&!style.fontWeight)style.fontWeight=variable.wght;if(variable.slnt&&!style.fontStyle)style.fontStyle=\"oblique\";}return style;},[font]);// ────────────────────────────────────────────────────────────────\n// Render\n// ────────────────────────────────────────────────────────────────\nconst totalMinutes=Math.ceil(countWords/readingSpeed);// Remaining time scales by (1 - progress) through the article container.\nconst remainingMinutes=Math.max(0,Math.ceil((1-progress)*countWords/readingSpeed));const display=(()=>{if(displayMode===\"words\")return`${countWords}`;if(displayMode===\"timeLeft\")return`${remainingMinutes} min left`;// default \"time\"\nreturn`${totalMinutes} min read`;})();return /*#__PURE__*/_jsx(\"div\",{style:{...fontStyle,color:fontColor,display:\"flex\",justifyContent:\"left\",alignItems:\"left\",padding:0},children:display});}// ────────────────────────────────────────────────────────────────────\n// Defaults & Property Controls\n// ────────────────────────────────────────────────────────────────────\nWordCounter.defaultProps={title:\"<p>Your rich text here</p>\",font:fontControls.defaultProps,fontColor:\"#000\",displayMode:\"words\",targetSelector:\"\",stickyTop:0,readingSpeed:200};addPropertyControls(WordCounter,{title:{title:\"Title\",//@ts-ignore – Framer-specific prop type\ntype:ControlType.RichText},font:{type:ControlType.Font,controls:\"extended\",title:\"Font\"},fontColor:{title:\"Font Color\",type:ControlType.Color,defaultValue:WordCounter.defaultProps.fontColor},displayMode:{title:\"Display Mode\",type:ControlType.SegmentedEnum,options:[\"words\",\"time\",\"timeLeft\"],optionTitles:[\"Words\",\"Total Time\",\"Time Left\"],defaultValue:WordCounter.defaultProps.displayMode},targetSelector:{title:\"Target Selector\",type:ControlType.String,defaultValue:WordCounter.defaultProps.targetSelector,placeholder:\"#article or .post\"},stickyTop:{title:\"Sticky Top (px)\",type:ControlType.Number,defaultValue:WordCounter.defaultProps.stickyTop,min:0,displayStepper:true,step:1,unit:\"px\"},readingSpeed:{title:\"Reading Speed (WPM)\",type:ControlType.Number,defaultValue:WordCounter.defaultProps.readingSpeed,min:60,max:600,displayStepper:true,step:10}});\nexport const __FramerMetadata__ = {\"exports\":{\"WordCounter\":{\"type\":\"reactComponent\",\"name\":\"WordCounter\",\"slots\":[],\"annotations\":{\"framerContractVersion\":\"1\"}},\"__FramerMetadata__\":{\"type\":\"variable\"}}}\n//# sourceMappingURL=./ReadingTime.map"],"mappings":"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"}