{"version":3,"file":"ReadTime.Da0Q0-ay.mjs","names":["useRef","useState"],"sources":["https:/framerusercontent.com/modules/4lwCgmg2AOuONtBkm1K1/dzSbp77XFLJFss5k2AYz/ReadTime.js"],"sourcesContent":["import{jsx as _jsx,jsxs as _jsxs}from\"react/jsx-runtime\";import{addPropertyControls,ControlType}from\"framer\";import{useState,useEffect,useRef}from\"react\";import{createPortal}from\"react-dom\";/**\n * ReadTime — Framer Code Component\n *\n * Calculates and displays the estimated reading time for article content.\n * Accounts for both word count and image viewing time (Medium's formula).\n *\n * Connect the \"Content\" prop to your CMS Rich Text field.\n *\n * @framerSupportedLayoutWidth any\n * @framerSupportedLayoutHeight any\n */// ── helpers ────────────────────────────────────────────────────────────\nfunction imageReadingSeconds(imageCount){let seconds=0;for(let i=0;i<imageCount;i++){seconds+=Math.max(12-i,3);}return seconds;}function calculateReadTime(wordCount,imageCount,wpm,countImages){const wordMinutes=wordCount/wpm;const imageMinutes=countImages?imageReadingSeconds(imageCount)/60:0;return Math.ceil(wordMinutes+imageMinutes);}const MIN_WORD_THRESHOLD=10;export default function ReadTime({content,emptyState=\"Connect content...\",wordsPerMinute=238,countImages=true,prefix=\"\",suffixSingular=\"min read\",suffixPlural=\"min read\",showIcon=false,gap=6,separatorBefore=\"\",separatorAfter=\"\",font,textTransform=\"none\",color=\"#0A0A0A\",style}){const hiddenRef=useRef(null);const[wordCount,setWordCount]=useState(0);const[imageCount,setImageCount]=useState(0);useEffect(()=>{const frame=requestAnimationFrame(()=>{if(hiddenRef.current){const text=hiddenRef.current.textContent||\"\";const cleaned=text.replace(/\\s+/g,\" \").trim();const words=cleaned?cleaned.split(/\\s+/).filter(Boolean).length:0;const images=hiddenRef.current.querySelectorAll(\"img\").length;setWordCount(words);setImageCount(images);}});return()=>cancelAnimationFrame(frame);},[content]);// ── Shared text styles ──\nconst textStyles={...font,textTransform:textTransform,color,whiteSpace:\"nowrap\"};// ── Hidden renderer via portal — rendered into document.body\n//    so it's never nested inside a parent <a> tag ──\nconst hiddenRenderer=typeof document!==\"undefined\"?/*#__PURE__*/createPortal(/*#__PURE__*/_jsx(\"div\",{ref:hiddenRef,\"aria-hidden\":\"true\",style:{position:\"absolute\",width:0,height:0,overflow:\"hidden\",visibility:\"hidden\",pointerEvents:\"none\"},children:content}),document.body):null;// ── Empty state ──\nconst hasContent=wordCount>=MIN_WORD_THRESHOLD||imageCount>0;if(!hasContent){if(emptyState){return /*#__PURE__*/_jsxs(\"div\",{style:{...textStyles,opacity:.5,...style},children:[hiddenRenderer,emptyState]});}return /*#__PURE__*/_jsx(\"div\",{style:{display:\"contents\",...style},children:hiddenRenderer});}// ── Calculation ──\nconst minutes=Math.max(1,calculateReadTime(wordCount,imageCount,wordsPerMinute,countImages));const suffix=minutes===1?suffixSingular:suffixPlural;const label=[prefix,`${minutes}`,suffix].filter(Boolean).join(\" \");const parts=[];if(separatorBefore)parts.push(separatorBefore);parts.push(label);if(separatorAfter)parts.push(separatorAfter);const display=parts.join(\" \");const clockIcon=/*#__PURE__*/_jsxs(\"svg\",{xmlns:\"http://www.w3.org/2000/svg\",width:\"1em\",height:\"1em\",viewBox:\"0 0 24 24\",fill:\"none\",stroke:\"currentColor\",strokeWidth:\"2\",strokeLinecap:\"round\",strokeLinejoin:\"round\",style:{flexShrink:0},children:[/*#__PURE__*/_jsx(\"circle\",{cx:\"12\",cy:\"12\",r:\"10\"}),/*#__PURE__*/_jsx(\"polyline\",{points:\"12 6 12 12 16 14\"})]});return /*#__PURE__*/_jsxs(\"div\",{style:{display:\"flex\",alignItems:\"center\",gap:`${gap}px`,...textStyles,...style},children:[hiddenRenderer,showIcon&&clockIcon,/*#__PURE__*/_jsx(\"span\",{children:display})]});}// ── property controls ──────────────────────────────────────────────────\naddPropertyControls(ReadTime,{// ─ Content ─\ncontent:{title:\"Content\",type:ControlType.RichText,defaultValue:\"\"},emptyState:{title:\"Empty Text\",type:ControlType.String,defaultValue:\"Connect content...\",placeholder:\"e.g. Connect content…\",description:\"Shown when no content is connected or the article is empty. Leave blank to hide the component.\"},// ─ Calculation ─\nwordsPerMinute:{title:\"Words / Min\",type:ControlType.Number,defaultValue:238,min:50,max:500,step:1,description:\"Average reading speed. 238 wpm is the research average.\"},countImages:{title:\"Count Images\",type:ControlType.Boolean,defaultValue:true,enabledTitle:\"Yes\",disabledTitle:\"No\",description:\"Add image viewing time using Medium's formula (12s first image, decreasing to 3s).\"},// ─ Label ─\nprefix:{title:\"Prefix\",type:ControlType.String,defaultValue:\"\",placeholder:\"e.g. Reading time:\"},suffixSingular:{title:\"Suffix (1)\",type:ControlType.String,defaultValue:\"min read\",placeholder:\"e.g. minute\",description:\"Shown when read time is exactly 1.\"},suffixPlural:{title:\"Suffix (2+)\",type:ControlType.String,defaultValue:\"min read\",placeholder:\"e.g. minutes\",description:\"Shown when read time is 2 or more.\"},// ─ Separators ─\nseparatorBefore:{title:\"Sep. Before\",type:ControlType.String,defaultValue:\"\",placeholder:\"e.g. \\xb7  —  |  /\",description:\"Character before the label, for inline use with other metadata.\"},separatorAfter:{title:\"Sep. After\",type:ControlType.String,defaultValue:\"\",placeholder:\"e.g. \\xb7  —  |  /\",description:\"Character after the label.\"},// ─ Icon ─\nshowIcon:{title:\"Show Icon\",type:ControlType.Boolean,defaultValue:false,enabledTitle:\"Yes\",disabledTitle:\"No\"},gap:{title:\"Icon Gap\",type:ControlType.Number,defaultValue:6,min:0,max:24,step:1,unit:\"px\",hidden:props=>props.showIcon===false},// ─ Typography ─\nfont:{title:\"Font\",type:ControlType.Font,controls:\"extended\",displayFontSize:true,displayTextAlignment:false,defaultFontType:\"sans-serif\",defaultValue:{fontSize:14,lineHeight:\"1.4em\"}},textTransform:{title:\"Text Transform\",type:ControlType.Enum,defaultValue:\"none\",options:[\"none\",\"uppercase\",\"lowercase\",\"capitalize\"],optionTitles:[\"None\",\"Uppercase\",\"Lowercase\",\"Capitalize\"]},// ─ Color ─\ncolor:{title:\"Color\",type:ControlType.Color,defaultValue:\"#0A0A0A\"}});\nexport const __FramerMetadata__ = {\"exports\":{\"default\":{\"type\":\"reactComponent\",\"name\":\"ReadTime\",\"slots\":[],\"annotations\":{\"framerContractVersion\":\"1\"}},\"__FramerMetadata__\":{\"type\":\"variable\"}}}\n//# sourceMappingURL=./ReadTime.map"],"mappings":"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"}