{"version":3,"file":"SmartTextTruncate.CygJjnAX.mjs","names":["useMemo","useState","useRef"],"sources":["https:/framerusercontent.com/modules/7Ywpu6cq0OKU2doOhYbm/XwWppuq1mp1C1tmbaD9E/SmartTextTruncate.js"],"sourcesContent":["// Text truncate component with configurable line and character limits\nimport{jsx as _jsx,jsxs as _jsxs}from\"react/jsx-runtime\";import{addPropertyControls,ControlType}from\"framer\";import{motion}from\"framer-motion\";import{useState,useLayoutEffect,useEffect,useRef,useMemo,startTransition}from\"react\";/**\n * Text Truncate Component\n *\n * @framerDisableUnlink\n *\n * @framerSupportedLayoutWidth any-prefer-fixed\n * @framerSupportedLayoutHeight auto\n *\n * @framerIntrinsicWidth 400\n */export default function SmartTextTruncate(props){const{text=\"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\",truncateBy=\"lines\",maxLines=3,maxCharacters=100,showReadMore=true,readMoreText=\"Read more\",readLessText=\"Read less\",textColor=\"#000000\",linkColor=\"#0099FF\",font,linkFont,transition={type:\"tween\",ease:\"easeInOut\",duration:.3},style}=props;// Extract actual lineHeight multiplier from font prop\nconst getLineHeightMultiplier=useMemo(()=>{if(!font?.lineHeight)return 1.5;const lh=String(font.lineHeight);if(lh.endsWith(\"em\"))return parseFloat(lh);if(lh.endsWith(\"px\")){const fontSize=font?.fontSize?parseFloat(String(font.fontSize)):16;return parseFloat(lh)/fontSize;}// Handle unitless values (e.g., \"1.3\")\nconst parsed=parseFloat(lh);return isNaN(parsed)?1.5:parsed;},[font?.lineHeight,font?.fontSize]);// Get truncated text for character mode\nconst truncatedText=useMemo(()=>{if(truncateBy===\"characters\"&&text.length>maxCharacters){return text.slice(0,maxCharacters)+\"...\";}return text;},[truncateBy,text,maxCharacters]);// Calculate initial estimated height to prevent flash\nconst estimatedCollapsedHeight=useMemo(()=>{const fontSize=font?.fontSize?parseFloat(String(font.fontSize)):16;const lineHeight=fontSize*getLineHeightMultiplier;if(truncateBy===\"lines\"){return Math.ceil(lineHeight*maxLines);}else{// For characters: estimate based on truncated text length\n// Average character width is roughly 0.5-0.6 of font size for most fonts\nconst avgCharWidth=fontSize*.55;const containerWidth=style?.width?parseFloat(String(style.width)):400;const charsPerLine=Math.floor(containerWidth/avgCharWidth);const estimatedLines=Math.ceil(truncatedText.length/Math.max(charsPerLine,1));// Use at least 1 line\nreturn Math.ceil(lineHeight*Math.max(estimatedLines,1));}},[truncateBy,maxLines,font?.fontSize,getLineHeightMultiplier,truncatedText.length,style?.width]);const[isExpanded,setIsExpanded]=useState(false);const[isTruncated,setIsTruncated]=useState(false);const[collapsedHeight,setCollapsedHeight]=useState(estimatedCollapsedHeight);const[fullHeight,setFullHeight]=useState(0);const[shouldRenderExpandedContent,setShouldRenderExpandedContent]=useState(false);const[isReady,setIsReady]=useState(false);const isMountedRef=useRef(false);const isInitialMountRef=useRef(true)// Track if this is the very first render\n;const hasUserInteractedRef=useRef(false)// Track if user has clicked read more/less\n;const textRef=useRef(null);const measureRef=useRef(null);const truncatedMeasureRef=useRef(null);useLayoutEffect(()=>{// On prop changes after initial mount, disable animation and reset interaction flag\nif(isMountedRef.current){setIsReady(false);// Reset interaction flag when props change so new measurements don't animate unnecessarily\nhasUserInteractedRef.current=false;}if(measureRef.current&&textRef.current){const actualFullHeight=Math.ceil(measureRef.current.scrollHeight);setFullHeight(actualFullHeight);if(truncateBy===\"lines\"){const lineHeight=parseFloat(getComputedStyle(textRef.current).lineHeight);const calculatedCollapsedHeight=Math.ceil(lineHeight*maxLines);setCollapsedHeight(calculatedCollapsedHeight);setIsTruncated(actualFullHeight>calculatedCollapsedHeight);}else if(truncateBy===\"characters\"&&truncatedMeasureRef.current){// For character truncation, measure the dedicated truncated text element\nconst truncatedHeight=Math.ceil(truncatedMeasureRef.current.scrollHeight);setCollapsedHeight(truncatedHeight);setIsTruncated(text.length>maxCharacters);}// After first measurement completes, no longer initial mount\nisInitialMountRef.current=false;}// Mark as mounted and ready\nisMountedRef.current=true;setIsReady(true);},[text,truncateBy,maxLines,maxCharacters,style?.width]);useEffect(()=>{if(isExpanded){setShouldRenderExpandedContent(true);}},[isExpanded]);const handleToggle=()=>{hasUserInteractedRef.current=true// Mark that user has interacted\n;startTransition(()=>setIsExpanded(!isExpanded));};const displayText=useMemo(()=>{if(isExpanded||shouldRenderExpandedContent)return text;if(truncateBy===\"characters\")return truncatedText;return text;},[isExpanded,shouldRenderExpandedContent,text,truncateBy,truncatedText]);const isFixedWidth=style&&style.width===\"100%\";const currentHeight=isExpanded?fullHeight:collapsedHeight;// For character mode: don't animate until user interacts (prevents sliding button on initial load)\n// For lines mode: always animate after measurement\nconst shouldAnimateHeight=isReady&&isMountedRef.current&&(truncateBy===\"lines\"||hasUserInteractedRef.current);// For character mode before user interaction: set height directly via style (no animation)\n// After user interaction or for lines mode: let motion handle it\nconst shouldSetHeightDirectly=truncateBy===\"characters\"&&isMountedRef.current&&!hasUserInteractedRef.current&&currentHeight>0;return /*#__PURE__*/_jsxs(\"div\",{style:{...style,position:\"relative\",...isFixedWidth?{}:{minWidth:\"max-content\"}},children:[/*#__PURE__*/_jsx(\"div\",{ref:measureRef,style:{position:\"absolute\",visibility:\"hidden\",pointerEvents:\"none\",color:textColor,whiteSpace:\"pre-wrap\",wordBreak:\"break-word\",...font,width:style?.width||\"100%\"},children:text}),truncateBy===\"characters\"&&/*#__PURE__*/_jsx(\"div\",{ref:truncatedMeasureRef,style:{position:\"absolute\",visibility:\"hidden\",pointerEvents:\"none\",color:textColor,whiteSpace:\"pre-wrap\",wordBreak:\"break-word\",...font,width:style?.width||\"100%\"},children:truncatedText}),/*#__PURE__*/_jsxs(\"div\",{children:[/*#__PURE__*/_jsx(motion.div,{ref:textRef,style:{color:textColor,whiteSpace:\"pre-wrap\",wordBreak:\"break-word\",...font,overflow:\"hidden\",// Set height directly for character mode before user interaction (no animation)\n// Otherwise let motion handle it\nheight:shouldSetHeightDirectly?currentHeight:!isMountedRef.current?estimatedCollapsedHeight:undefined},initial:false,animate:{// Don't animate height if we're setting it directly via style\nheight:shouldSetHeightDirectly?undefined:isMountedRef.current&&currentHeight>0?currentHeight:estimatedCollapsedHeight},transition:shouldAnimateHeight?transition:{duration:0},onAnimationComplete:()=>{if(!isExpanded){setShouldRenderExpandedContent(false);}},children:displayText}),showReadMore&&isTruncated&&isMountedRef.current&&/*#__PURE__*/_jsx(\"button\",{onClick:handleToggle,style:{background:\"none\",border:\"none\",padding:0,color:linkColor,cursor:\"pointer\",marginTop:8,display:\"block\",...linkFont},children:isExpanded?readLessText:readMoreText})]})]});}addPropertyControls(SmartTextTruncate,{text:{type:ControlType.String,title:\"Text\",defaultValue:\"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\",displayTextArea:true},truncateBy:{type:ControlType.Enum,title:\"Truncate By\",options:[\"lines\",\"characters\"],optionTitles:[\"Lines\",\"Characters\"],defaultValue:\"lines\",displaySegmentedControl:true},maxLines:{type:ControlType.Number,title:\"Max Lines\",defaultValue:3,min:1,max:20,step:1,hidden:({truncateBy})=>truncateBy!==\"lines\"},maxCharacters:{type:ControlType.Number,title:\"Max Characters\",defaultValue:100,min:10,max:1e3,step:10,hidden:({truncateBy})=>truncateBy!==\"characters\"},showReadMore:{type:ControlType.Boolean,title:\"Show Toggle\",defaultValue:true,enabledTitle:\"Show\",disabledTitle:\"Hide\"},readMoreText:{type:ControlType.String,title:\"Read More Text\",defaultValue:\"Read more\",hidden:({showReadMore})=>!showReadMore},readLessText:{type:ControlType.String,title:\"Read Less Text\",defaultValue:\"Read less\",hidden:({showReadMore})=>!showReadMore},font:{type:ControlType.Font,title:\"Text Font\",controls:\"extended\",defaultFontType:\"sans-serif\",defaultValue:{fontSize:\"15px\",variant:\"Medium\",letterSpacing:\"-0.01em\",lineHeight:\"1.3em\"}},textColor:{type:ControlType.Color,title:\"Text Color\",defaultValue:\"#000000\"},linkFont:{type:ControlType.Font,title:\"Link Font\",controls:\"extended\",defaultFontType:\"sans-serif\",defaultValue:{fontSize:\"14px\",variant:\"Semibold\",letterSpacing:\"-0.01em\",lineHeight:\"1em\"},hidden:({showReadMore})=>!showReadMore},linkColor:{type:ControlType.Color,title:\"Link Color\",defaultValue:\"#0099FF\",hidden:({showReadMore})=>!showReadMore},transition:{type:ControlType.Transition,title:\"Transition\",defaultValue:{type:\"tween\",ease:\"easeInOut\",duration:.3}}});\nexport const __FramerMetadata__ = {\"exports\":{\"default\":{\"type\":\"reactComponent\",\"name\":\"SmartTextTruncate\",\"slots\":[],\"annotations\":{\"framerSupportedLayoutWidth\":\"any-prefer-fixed\",\"framerSupportedLayoutHeight\":\"auto\",\"framerContractVersion\":\"1\",\"framerIntrinsicWidth\":\"400\",\"framerDisableUnlink\":\"*\"}},\"__FramerMetadata__\":{\"type\":\"variable\"}}}\n//# sourceMappingURL=./SmartTextTruncate.map"],"mappings":"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"}