{"version":3,"file":"cmsCollectionLayout.ClaErdIf.mjs","names":[],"sources":["https:/framerusercontent.com/modules/UbgdYJbCrYBlOiBwtGI6/q8E4QzoWXW9nmCeNLkKT/useInheritedSectionBreakpoint.js","https:/framerusercontent.com/modules/V5oXs13pLFW7p9OCzVV6/H4qL52EPbhHQ1nyiGero/cmsCollectionLayout.js"],"sourcesContent":["import{useLayoutEffect,useState}from\"react\";export const SECTION_BREAKPOINT_ATTR=\"data-bunq-section-breakpoint\";function parseBreakpoint(value){if(value===\"desktop\"||value===\"tablet\"||value===\"phone\"){return value;}return undefined;}/** Synchronous read for layout decisions on the same commit the ref is attached. */export function readInheritedSectionBreakpoint(element){if(!element)return undefined;const host=element.closest(`[${SECTION_BREAKPOINT_ATTR}]`);if(!(host instanceof HTMLElement))return undefined;return parseBreakpoint(host.getAttribute(SECTION_BREAKPOINT_ATTR));}/**\n * Reads the parent section breakpoint from {@link SECTION_BREAKPOINT_ATTR} on an\n * ancestor host. Used by CMS collection item cards when Framer overrides the\n * component breakpoint control with the item template value.\n */export function useInheritedSectionBreakpoint(ref){const[inheritedBreakpoint,setInheritedBreakpoint]=useState();useLayoutEffect(()=>{const element=ref.current;if(!element)return undefined;const host=element.closest(`[${SECTION_BREAKPOINT_ATTR}]`);if(!(host instanceof HTMLElement))return undefined;const syncBreakpoint=()=>{setInheritedBreakpoint(readInheritedSectionBreakpoint(element));};syncBreakpoint();const observer=new MutationObserver(syncBreakpoint);observer.observe(host,{attributes:true,attributeFilter:[SECTION_BREAKPOINT_ATTR]});return()=>observer.disconnect();},[ref]);return inheritedBreakpoint;}\nexport const __FramerMetadata__ = {\"exports\":{\"SECTION_BREAKPOINT_ATTR\":{\"type\":\"variable\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"readInheritedSectionBreakpoint\":{\"type\":\"function\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"useInheritedSectionBreakpoint\":{\"type\":\"function\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"__FramerMetadata__\":{\"type\":\"variable\"}}}\n//# sourceMappingURL=./useInheritedSectionBreakpoint.map","import{useEffect,useLayoutEffect}from\"react\";import{SECTION_BREAKPOINT_ATTR}from\"https://framerusercontent.com/modules/UbgdYJbCrYBlOiBwtGI6/q8E4QzoWXW9nmCeNLkKT/useInheritedSectionBreakpoint.js\";import{getTestimonialCarouselBackgroundVariant,TESTIMONIAL_CMS_BG_VARIANT_ATTR}from\"https://framerusercontent.com/modules/k5Y34R8raS8B6MCrlIFO/dKLEQKnGCPxXmrKuqThv/testimonialCarouselBackgrounds.js\";const TESTIMONIAL_COLUMN_ATTR=\"data-bunq-testimonial-column\";const TESTIMONIAL_CLONE_ATTR=\"data-bunq-testimonial-clone\";function isTestimonialColumnWrapper(element){return element instanceof HTMLElement&&element.hasAttribute(TESTIMONIAL_COLUMN_ATTR);}function getLooseCollectionItems(root){const direct=Array.from(root.children).filter(child=>!isTestimonialColumnWrapper(child));// Framer often wraps all CMS items in one flex-column container; unwrap so we can\n// split even/odd items into left/right columns instead of treating the wrapper as one item.\nif(direct.length===1){const wrapper=direct[0];const nested=Array.from(wrapper.children).filter(child=>!isTestimonialColumnWrapper(child));if(nested.length>1){return nested;}}return direct;}function applyInlineStyles(element,styles){Object.entries(styles).forEach(([property,value])=>{if(value==null)return;element.style[property]=String(value);});}/** Unwrap `display: contents` hosts so CMS list roots receive layout styles. */export function getCollectionRoots(host){return Array.from(host.children).flatMap(child=>{if(!(child instanceof HTMLElement))return[];if(child.style.display===\"contents\"&&child.childElementCount>0){return Array.from(child.children).filter(nested=>nested instanceof HTMLElement);}return[child];});}/**\n * Framer often nests all CMS items inside one flex-column wrapper. Grid layout must\n * target the element whose direct children are the repeated items, not an outer shell.\n */export function getCollectionGridRoot(root){const directChildren=Array.from(root.children).filter(child=>child instanceof HTMLElement&&!isTestimonialColumnWrapper(child));if(directChildren.length===1){const wrapper=directChildren[0];const nestedChildren=Array.from(wrapper.children).filter(child=>child instanceof HTMLElement&&!isTestimonialColumnWrapper(child));if(nestedChildren.length>1){return wrapper;}}return root;}/**\n * Grid roots that hold repeated CMS items (unwraps display:contents and nested shells).\n */export function getCmsCollectionLayoutRoots(host){const roots=getCollectionRoots(host);const candidates=roots.length>0?roots:[host];const layoutRoots=candidates.map(root=>getCollectionGridRoot(root));return[...new Set(layoutRoots)];}/**\n * Repeated CMS item elements under a collection list host.\n */export function getCmsCollectionItemElements(host){return getCmsCollectionLayoutRoots(host).flatMap(gridRoot=>Array.from(gridRoot.children).filter(child=>child instanceof HTMLElement&&!isTestimonialColumnWrapper(child)));}function applyTestimonialBackgroundVariant(element,visualIndex){const variant=getTestimonialCarouselBackgroundVariant(visualIndex);element.setAttribute(TESTIMONIAL_CMS_BG_VARIANT_ATTR,variant);element.querySelectorAll(\"article\").forEach(article=>{article.setAttribute(TESTIMONIAL_CMS_BG_VARIANT_ATTR,variant);});}/** CMS list templates often reuse Framer node ids (e.g. zYdSqddOv) on every repeated card. */function dedupeTestimonialArticleIds(host){const articles=[...host.querySelectorAll(\"article[id]\")];const seen=new Set;articles.forEach((article,index)=>{const current=article.id;const isFramerInternalId=/^[A-Za-z][A-Za-z0-9]{7,11}$/.test(current)&&!current.includes(\"-\");if(!current||seen.has(current)||isFramerInternalId){const nextId=`cms-testimonial-card-${index+1}`;const rating=article.querySelector(`[id=\"${CSS.escape(current)}-rating\"]`);article.id=nextId;if(rating)rating.id=`${nextId}-rating`;seen.add(nextId);return;}seen.add(current);});}function removeTestimonialClones(root){root.querySelectorAll(`[${TESTIMONIAL_CLONE_ATTR}]`).forEach(clone=>clone.remove());}function getOriginalColumnItems(column){return Array.from(column.children).filter(child=>child instanceof HTMLElement&&!child.hasAttribute(TESTIMONIAL_CLONE_ATTR));}function fillTestimonialColumn(column,minimumItems,visualIndexOffset,itemStyles){const originals=getOriginalColumnItems(column);originals.forEach((item,index)=>applyTestimonialBackgroundVariant(item,visualIndexOffset+index));if(minimumItems<=originals.length||originals.length===0)return;for(let index=originals.length;index<minimumItems;index+=1){const clone=originals[index%originals.length].cloneNode(true);clone.setAttribute(TESTIMONIAL_CLONE_ATTR,\"true\");applyInlineStyles(clone,itemStyles);applyTestimonialBackgroundVariant(clone,visualIndexOffset+index);column.appendChild(clone);}}/**\n * Styles Framer's generated Collection List wrapper so CMS items can inherit\n * the same layout rules as manually rendered items inside code components.\n */export function useCmsCollectionLayout(hostRef,enabled,collectionRootStyles,collectionItemStyles={},sectionBreakpoint,scrollRootRef){useLayoutEffect(()=>{if(!enabled)return undefined;const host=hostRef.current;if(!host)return undefined;const applyStyles=()=>{if(sectionBreakpoint&&host.getAttribute(SECTION_BREAKPOINT_ATTR)!==sectionBreakpoint){host.setAttribute(SECTION_BREAKPOINT_ATTR,sectionBreakpoint);}let firstScrollRoot=null;getCollectionRoots(host).forEach(root=>{const gridRoot=getCollectionGridRoot(root);if(gridRoot!==root){applyInlineStyles(root,{display:\"block\",width:\"100%\",minWidth:\"0\"});}applyInlineStyles(gridRoot,collectionRootStyles);Array.from(gridRoot.children).forEach(item=>{if(!(item instanceof HTMLElement)||isTestimonialColumnWrapper(item))return;applyInlineStyles(item,collectionItemStyles);});if(!firstScrollRoot){firstScrollRoot=gridRoot;}});if(scrollRootRef){scrollRootRef.current=firstScrollRoot;}};applyStyles();if(typeof MutationObserver===\"undefined\"){return()=>{if(scrollRootRef){scrollRootRef.current=null;}if(sectionBreakpoint){host.removeAttribute(SECTION_BREAKPOINT_ATTR);}};}const observer=new MutationObserver(applyStyles);observer.observe(host,{childList:true,subtree:true});return()=>{observer.disconnect();if(scrollRootRef){scrollRootRef.current=null;}if(sectionBreakpoint){host.removeAttribute(SECTION_BREAKPOINT_ATTR);}};},[hostRef,enabled,collectionRootStyles,collectionItemStyles,sectionBreakpoint,scrollRootRef]);}/**\n * Splits Framer CMS collection items into left/right testimonial columns (even/odd)\n * and optionally applies scroll parallax transforms to each column.\n */export function useCmsTestimonialCarouselColumns(hostRef,enabled,isPhone,trackStyles,columnStyles,itemStyles,phoneListStyles,minimumItemsPerColumn,applyParallax,leftParallaxY,rightParallaxY){/**\n     * Initial layout pass is gated behind two `requestAnimationFrame`s (≈2 frames) inside\n     * a `useEffect` (post-paint) instead of running in `useLayoutEffect`. Framer renders the\n     * underlying `CollectionList` inside a `lazy()` + `<Suspense>` boundary, so the testimonial\n     * cards hydrate **after** the parent has committed and after the parent's layout effects\n     * have run. Mutating the DOM before that deferred Suspense hydration finishes causes\n     * React to see a divergent DOM during the children's hydration pass, surfacing as React\n     * #418 mismatches at every card. Deferring past the next two frames lets Suspense settle\n     * before we splice in the column wrappers and bg-variant attributes; the MutationObserver\n     * below then keeps subsequent layout in sync.\n     */useEffect(()=>{if(!enabled)return undefined;const host=hostRef.current;if(!host)return undefined;let isApplyingLayout=false;const ensureColumn=(root,side)=>{const existing=root.querySelector(`[${TESTIMONIAL_COLUMN_ATTR}=\"${side}\"]`);if(existing){applyInlineStyles(existing,columnStyles);return existing;}const columnEl=document.createElement(\"div\");columnEl.setAttribute(TESTIMONIAL_COLUMN_ATTR,side);applyInlineStyles(columnEl,columnStyles);root.appendChild(columnEl);return columnEl;};const flattenColumns=root=>{root.querySelectorAll(`[${TESTIMONIAL_COLUMN_ATTR}]`).forEach(column=>{while(column.firstChild){root.appendChild(column.firstChild);}column.remove();});};const applyLayout=()=>{if(isApplyingLayout)return;isApplyingLayout=true;try{Array.from(host.children).forEach(collectionRoot=>{const root=collectionRoot;removeTestimonialClones(root);const looseItems=getLooseCollectionItems(root);if(isPhone){flattenColumns(root);applyInlineStyles(root,phoneListStyles);looseItems.forEach((item,index)=>{applyTestimonialBackgroundVariant(item,index);applyInlineStyles(item,itemStyles);});return;}applyInlineStyles(root,trackStyles);const leftColumn=ensureColumn(root,\"left\");const rightColumn=ensureColumn(root,\"right\");looseItems.forEach((item,index)=>{applyInlineStyles(item,itemStyles);const target=index%2===0?leftColumn:rightColumn;if(item.parentElement!==target){target.appendChild(item);}});const leftItems=getOriginalColumnItems(leftColumn);const filledLeftCount=Math.max(minimumItemsPerColumn,leftItems.length);fillTestimonialColumn(leftColumn,minimumItemsPerColumn,0,itemStyles);fillTestimonialColumn(rightColumn,minimumItemsPerColumn,filledLeftCount,itemStyles);Array.from(root.children).forEach(child=>{if(isTestimonialColumnWrapper(child))return;if(child instanceof HTMLElement&&child.childElementCount===0){child.remove();}});});}finally{setTimeout(()=>{isApplyingLayout=false;},0);}dedupeTestimonialArticleIds(host);};// Defer the first applyLayout() pass past two animation frames so React can\n// finish hydrating Framer's lazy/Suspense-wrapped CollectionList children before\n// we mutate the DOM out from under them.\nlet cancelled=false;let rafA=0;let rafB=0;rafA=requestAnimationFrame(()=>{rafB=requestAnimationFrame(()=>{if(!cancelled)applyLayout();});});let observer;if(typeof MutationObserver!==\"undefined\"){observer=new MutationObserver(()=>{if(!isApplyingLayout)applyLayout();});observer.observe(host,{childList:true,subtree:true});}return()=>{cancelled=true;cancelAnimationFrame(rafA);cancelAnimationFrame(rafB);observer?.disconnect();};},[hostRef,enabled,isPhone,trackStyles,columnStyles,itemStyles,phoneListStyles,minimumItemsPerColumn]);useLayoutEffect(()=>{if(!enabled||isPhone||!applyParallax||!leftParallaxY||!rightParallaxY)return undefined;const host=hostRef.current;if(!host)return undefined;const applyTransforms=()=>{host.querySelectorAll(`[${TESTIMONIAL_COLUMN_ATTR}=\"left\"]`).forEach(column=>{column.style.transform=`translateY(${leftParallaxY.get()})`;});host.querySelectorAll(`[${TESTIMONIAL_COLUMN_ATTR}=\"right\"]`).forEach(column=>{column.style.transform=`translateY(${rightParallaxY.get()})`;});};applyTransforms();const unsubscribeLeft=leftParallaxY.on(\"change\",applyTransforms);const unsubscribeRight=rightParallaxY.on(\"change\",applyTransforms);return()=>{unsubscribeLeft();unsubscribeRight();};},[hostRef,enabled,isPhone,applyParallax,leftParallaxY,rightParallaxY]);}\nexport const __FramerMetadata__ = {\"exports\":{\"useCmsCollectionLayout\":{\"type\":\"function\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"useCmsTestimonialCarouselColumns\":{\"type\":\"function\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"getCollectionRoots\":{\"type\":\"function\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"getCmsCollectionItemElements\":{\"type\":\"function\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"getCmsCollectionLayoutRoots\":{\"type\":\"function\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"getCollectionGridRoot\":{\"type\":\"function\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"__FramerMetadata__\":{\"type\":\"variable\"}}}\n//# sourceMappingURL=./cmsCollectionLayout.map"],"mappings":"sHAA2C,EAAc,EAAwB,kCCAib,SAAS,EAA2B,EAAQ,CAAC,OAAO,aAAmB,aAAa,EAAQ,aAAa,CAAuB,CAAE,CAEvc,SAAS,EAAkB,EAAQ,EAAO,CAAC,OAAO,QAAQ,CAAM,CAAC,CAAC,SAAS,CAAC,EAAS,KAAS,CAAI,GAAO,OAAY,EAAQ,MAAM,GAAU,OAAO,CAAK,EAAE,CAAC,CAAE,CAAiF,SAAgB,EAAmB,EAAK,CAAC,OAAO,MAAM,KAAK,EAAK,QAAQ,CAAC,CAAC,QAAQ,GAAa,aAAiB,YAAyB,EAAM,MAAM,UAAU,YAAY,EAAM,kBAAkB,EAAU,MAAM,KAAK,EAAM,QAAQ,CAAC,CAAC,OAAO,GAAQ,aAAkB,WAAW,EAAS,CAAC,CAAK,EAAhK,CAAC,CAAkK,CAAE,CAGjtB,SAAgB,EAAsB,EAAK,CAAC,IAAM,EAAe,MAAM,KAAK,EAAK,QAAQ,CAAC,CAAC,OAAO,GAAO,aAAiB,aAAa,CAAC,EAA2B,CAAK,CAAC,EAAE,GAAG,EAAe,SAAS,EAAE,CAAC,IAAM,EAAQ,EAAe,GAAqI,GAA7G,MAAM,KAAK,EAAQ,QAAQ,CAAC,CAAC,OAAO,GAAO,aAAiB,aAAa,CAAC,EAA2B,CAAK,CAAmB,CAAC,CAAC,OAAO,EAAG,OAAO,CAAS,CAAC,OAAO,CAAK,CAOpa,SAAgB,EAAuB,EAAQ,EAAQ,EAAqB,EAAqB,CAAC,EAAE,EAAkB,EAAc,CAAC,MAAoB,CAAC,GAAG,CAAC,EAAQ,OAAiB,IAAM,EAAK,EAAQ,QAAQ,GAAG,CAAC,EAAK,OAAiB,IAAM,MAAgB,CAAI,GAAmB,EAAK,aAAA,8BAAoC,IAAI,GAAmB,EAAK,aAAa,EAAwB,CAAiB,EAAG,IAAI,EAAgB,KAAK,EAAmB,CAAI,CAAC,CAAC,QAAQ,GAAM,CAAC,IAAM,EAAS,EAAsB,CAAI,EAAK,IAAW,GAAM,EAAkB,EAAK,CAAC,QAAQ,QAAQ,MAAM,OAAO,SAAS,GAAG,CAAC,EAAG,EAAkB,EAAS,CAAoB,EAAE,MAAM,KAAK,EAAS,QAAQ,CAAC,CAAC,QAAQ,GAAM,CAAI,EAAE,aAAgB,cAAc,EAA2B,CAAI,GAAS,EAAkB,EAAK,CAAoB,CAAE,CAAC,EAAE,AAAqB,IAAgB,CAAU,CAAC,EAAK,IAAe,EAAc,QAAQ,EAAiB,EAAgB,GAAd,EAAY,EAAK,OAAO,iBAAmB,IAAa,UAAU,CAAI,IAAe,EAAc,QAAQ,MAAS,GAAmB,EAAK,gBAAgB,CAAuB,CAAG,EAAG,IAAM,EAAS,IAAI,iBAAiB,CAAW,EAAuD,OAArD,EAAS,QAAQ,EAAK,CAAC,UAAU,GAAK,QAAQ,EAAI,CAAC,MAAY,CAAC,EAAS,WAAW,EAAK,IAAe,EAAc,QAAQ,MAAS,GAAmB,EAAK,gBAAgB,CAAuB,CAAG,CAAE,EAAE,CAAC,EAAQ,EAAQ,EAAqB,EAAqB,EAAkB,CAAa,CAAC,CAAE,mBAZ74C,IAAsJ,EAA8M,EAAwB"}