{"version":3,"file":"SplitText.evXynpND.mjs","names":["useRef"],"sources":["https:/framerusercontent.com/modules/kHXXgPW4TrTfnbDT7Edr/hsJ6b1vnXpBbgWbJz5sX/SplitText.js"],"sourcesContent":["import{jsx as _jsx}from\"react/jsx-runtime\";import{forwardRef,useRef,useLayoutEffect}from\"react\";import{useIsStaticRenderer}from\"framer\";// SplitTextLoad — apply to a TEXT layer via Code Overrides.\n// Text splits into clip masks and rises in with a stagger,\n// matching the GraphiteGlassNav menu motion. Tune CONFIG below.\n//\n// In-view triggering is PER UNIT, not per layer: every line (lines\n// mode), word (words mode), or word-of-letters (letters mode) is\n// observed at its own scroll position. Long copy therefore reveals as\n// you scroll — whatever the DOM structure (real paragraphs, Shift+Enter\n// blank lines, one giant text block; it makes no difference, because\n// the triggers are the rendered units themselves, not the paragraph\n// elements). Units that are on screen together chain into one\n// continuous cascade, so a first screenful reads exactly like the\n// original whole-layer animation.\n//\n// Layout note: the split runs AFTER the layer has mounted, so the\n// override owns the moment the text is rewritten and revealed. To keep\n// that moment from nudging anything below it, we lock the layer's box\n// (height + width) at its natural size the instant the effect takes over\n// and release the lock only after the original text is restored. The\n// clip masks are already height-neutral on their own; the lock is the\n// belt that guarantees siblings never move even if fonts settle late or\n// Framer re-measures an auto-height text layer at the reveal.\nconst CONFIG={duration:.75,stagger:.03,lineStagger:.11,delay:0,ease:\"cubic-bezier(0.25, 1, 0.5, 1)\",trigger:\"inview\",once:true,offset:130};// Splits every text-bearing leaf and returns GROUPS — { el, inners } —\n// where `el` is the in-flow element to observe and `inners` are the\n// rising spans it drives. lines → one group per rendered line;\n// words → one group per word; letters → one group per word, whose\n// inners are that word's letters (so a word's letters always play\n// together and the observer count stays sane).\nfunction splitElement(root,mode){const targets=[];const walk=el=>{const kids=Array.from(el.children);const hasText=Array.from(el.childNodes).some(n=>n.nodeType===3&&(n.textContent||\"\").trim());if(hasText||kids.length===0)targets.push(el);else kids.forEach(walk);};walk(root);const groups=[];const makeMask=(content,block)=>{const mask=document.createElement(\"span\");mask.style.cssText=block?\"display:block;overflow:hidden;\"+\"padding-bottom:0.14em;margin-bottom:-0.14em;\":\"display:inline-block;overflow:hidden;vertical-align:top;\"+\"white-space:pre;padding-bottom:0.14em;margin-bottom:-0.14em;\";const inner=document.createElement(\"span\");inner.style.cssText=(block?\"display:block;white-space:nowrap;\":\"display:inline-block;white-space:pre;\")+`transform:translateY(${CONFIG.offset}%);`;inner.textContent=content;mask.appendChild(inner);return{mask,inner};};const addGroup=(el,inners)=>{groups.push({el,inners,index:groups.length,played:false});};for(const t of targets){if(!(t.textContent||\"\").trim())continue;// capture content as a sequence of text runs and manual <br>\n// breaks BEFORE clearing — flattening to plain text would lose\n// Shift+Enter breaks and regroup the words differently than the\n// restored original (words visibly hop lines after the effect)\nconst items=[];for(const n of Array.from(t.childNodes)){if(n.nodeType===3)items.push({br:false,text:n.textContent});else if(n.nodeName===\"BR\")items.push({br:true});else items.push({br:false,text:n.textContent});}t.textContent=\"\";if(mode===\"lines\"){// lay out words (honoring manual breaks), read where the\n// browser puts each line, then mask each rendered line\nconst wordEls=[];for(const item of items){if(item.br){t.appendChild(document.createElement(\"br\"));continue;}const tokens=(item.text||\"\").split(/(\\s+)/).filter(s=>s.length);for(const token of tokens){if(/^\\s+$/.test(token)){t.appendChild(document.createTextNode(\" \"));}else{const w=document.createElement(\"span\");w.style.display=\"inline-block\";w.textContent=token;t.appendChild(w);wordEls.push(w);}}}const lines=[];let cur=null;let curTop=null;for(const w of wordEls){const top=w.offsetTop;if(curTop===null||Math.abs(top-curTop)>1){cur=[];lines.push(cur);curTop=top;}cur.push(w.textContent);}t.textContent=\"\";for(const line of lines){const{mask,inner}=makeMask(line.join(\" \"),true);t.appendChild(mask);addGroup(mask,[inner]);}continue;}// letters / words: spaces stay as plain text nodes so natural\n// wrap points survive; manual breaks stay as real <br>\nfor(const item of items){if(item.br){t.appendChild(document.createElement(\"br\"));continue;}const tokens=(item.text||\"\").split(/(\\s+)/).filter(s=>s.length);for(const token of tokens){if(/^\\s+$/.test(token)){t.appendChild(document.createTextNode(\" \"));continue;}if(mode===\"words\"){const{mask,inner}=makeMask(token,false);t.appendChild(mask);addGroup(mask,[inner]);}else{const word=document.createElement(\"span\");word.style.cssText=\"display:inline-block;white-space:nowrap;\";const letters=[];for(const ch of token.split(\"\")){const{mask,inner}=makeMask(ch,false);word.appendChild(mask);letters.push(inner);}t.appendChild(word);addGroup(word,letters);}}}}return groups;}function runSplit(el,mode){if(typeof window===\"undefined\"||!el)return()=>{};if(window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches)return()=>{};const original=el.innerHTML;let groups=[];let observer=null;let cancelled=false;const timers=[];const per=mode===\"lines\"?CONFIG.lineStagger:CONFIG.stagger;// Reserve the layer's height at its current, laid-out size before we\n// touch anything. Both min AND max are pinned, so the box is frozen\n// for the whole effect — it can neither shrink nor grow. Min alone\n// (a floor) stops the block collapsing, but the split render can come\n// out a hair TALLER than the plain text (in lines mode the per-line\n// descender padding doesn't always round back to zero), and that\n// extra height drops away when the original text is restored — a\n// small but visible settle. Freezing the box removes it: any\n// sub-pixel excess simply paints past the frozen box instead of\n// pushing the layout, and the restore lands on the same height the\n// animation ran at. The exact sub-pixel height is used so the frozen\n// box matches the natural box to the pixel; previous inline values\n// are captured so we hand the element back exactly as we found it.\nconst prevMinHeight=el.style.minHeight;const prevMaxHeight=el.style.maxHeight;let locked=false;const lockBox=()=>{const rect=el.getBoundingClientRect();if(rect.height>0){el.style.minHeight=`${rect.height}px`;el.style.maxHeight=`${rect.height}px`;locked=true;}};const unlockBox=()=>{if(!locked)return;el.style.minHeight=prevMinHeight;el.style.maxHeight=prevMaxHeight;locked=false;};lockBox();el.style.visibility=\"hidden\"// until fonts settle\n;// The original text is restored (and the box released) only after\n// EVERY unit has finished its reveal — restoring rewrites the whole\n// layer, so doing it early would blank units still waiting below\n// the fold.\nlet pendingGroups=0;const restoreAll=()=>{el.innerHTML=original;unlockBox();if(observer){observer.disconnect();observer=null;}};// A batch is a set of groups that entered the viewport together —\n// they chain into one continuous stagger (so an initial screenful\n// cascades exactly like the old whole-layer animation), while later\n// scroll positions start fresh batches with no accumulated delay.\nconst playBatch=batch=>{let extra=0;let maxEnd=0;for(const g of batch){g.inners.forEach((inner,i)=>{inner.style.transition=`transform ${CONFIG.duration}s ${CONFIG.ease}`;inner.style.transitionDelay=`${(CONFIG.delay+extra+i*per).toFixed(3)}s`;inner.style.transform=\"translateY(0)\";});const end=CONFIG.delay+extra+g.inners.length*per+CONFIG.duration;if(end>maxEnd)maxEnd=end;extra+=g.inners.length*per;}if(CONFIG.once){const timer=setTimeout(()=>{if(cancelled)return;pendingGroups-=batch.length;if(pendingGroups<=0)restoreAll();},maxEnd*1e3+150);timers.push(timer);}};const reset=group=>{group.inners.forEach(inner=>{inner.style.transition=\"none\";inner.style.transform=`translateY(${CONFIG.offset}%)`;void inner.offsetHeight;});};const ready=document.fonts?document.fonts.ready:Promise.resolve();ready.then(()=>{if(cancelled)return;// fonts may have changed the metrics while we waited — re-pin the\n// box to the settled size before splitting so the reveal can't\n// introduce a late jump\nunlockBox();lockBox();groups=splitElement(el,mode);pendingGroups=groups.length;el.style.visibility=\"\";if(groups.length===0){el.innerHTML=original;unlockBox();return;}if(CONFIG.trigger===\"load\"){requestAnimationFrame(()=>requestAnimationFrame(()=>playBatch(groups)));return;}const byEl=new Map(groups.map(g=>[g.el,g]));observer=new IntersectionObserver(entries=>{const entering=[];for(const e of entries){const g=byEl.get(e.target);if(!g)continue;if(e.isIntersecting){if(!g.played){g.played=true;entering.push(g);}if(CONFIG.once&&observer)observer.unobserve(g.el);}else if(!CONFIG.once&&g.played){g.played=false;reset(g);}}if(entering.length){entering.sort((a,b)=>a.index-b.index);playBatch(entering);}},{threshold:.2});groups.forEach(g=>observer.observe(g.el));});return()=>{cancelled=true;if(observer)observer.disconnect();timers.forEach(t=>clearTimeout(t));el.innerHTML=original;el.style.visibility=\"\";unlockBox();};}// Skip the effect on the Framer canvas / thumbnail / export static\n// render — there the text should just sit at its final position, and\n// running DOM surgery there only risks a flash in exports.\nfunction useSplit(mode){const local=useRef(null);const isStatic=useIsStaticRenderer();useLayoutEffect(()=>{if(isStatic)return;return runSplit(local.current,mode);},[isStatic]);return local;}export function withSplitLetters(Component){return /*#__PURE__*/forwardRef((props,ref)=>{const local=useSplit(\"letters\");return /*#__PURE__*/_jsx(Component,{ref:local,...props});});}export function withSplitWords(Component){return /*#__PURE__*/forwardRef((props,ref)=>{const local=useSplit(\"words\");return /*#__PURE__*/_jsx(Component,{ref:local,...props});});}export function withSplitLines(Component){return /*#__PURE__*/forwardRef((props,ref)=>{const local=useSplit(\"lines\");return /*#__PURE__*/_jsx(Component,{ref:local,...props});});}\nexport const __FramerMetadata__ = {\"exports\":{\"withSplitWords\":{\"type\":\"reactHoc\",\"name\":\"withSplitWords\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"withSplitLetters\":{\"type\":\"reactHoc\",\"name\":\"withSplitLetters\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"withSplitLines\":{\"type\":\"reactHoc\",\"name\":\"withSplitLines\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"__FramerMetadata__\":{\"type\":\"variable\"}}}\n//# sourceMappingURL=./SplitText.map"],"mappings":"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"}