@vielzeug/scroll 2.1.2 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"grid-virtualizer.d.ts","sourceRoot":"","sources":["../src/grid-virtualizer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"grid-virtualizer.d.ts","sourceRoot":"","sources":["../src/grid-virtualizer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAI/C,OAAO,EAAgB,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EAIL,KAAK,QAAQ,EAGb,KAAK,YAAY,EAGlB,MAAM,UAAU,CAAC;AAQlB,YAAY,EAAE,WAAW,EAAE,CAAC;AAI5B,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC;IAC7B,+BAA+B;IAC/B,QAAQ,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,OAAO,CAAC;IAC/C,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,OAAO,CAAC;CAChD;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6FAA6F;IAC7F,mBAAmB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,eAAe,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC;IACrD,eAAe,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC;IACrD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6DAA6D;IAC7D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,sFAAsF;IACtF,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACjD,yEAAyE;IACzE,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACtD,SAAS,CAAC,EAAE,QAAQ,CAAC;IACrB,SAAS,CAAC,EAAE,QAAQ,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0FAA0F;IAC1F,mBAAmB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,kDAAkD;IAClD,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,MAAM,CAAC,oBAAoB,CAAC,CAAC;CACzE;AAED;;;GAGG;AACH,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC;IACrD,eAAe,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC;IACrD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC/D,aAAa,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACpE,SAAS,CAAC,EAAE,QAAQ,CAAC;IACrB,SAAS,CAAC,EAAE,QAAQ,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,WAAW,CAAC;IACrC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,0EAA0E;IAC1E,YAAY,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,KAAK,IAAI,CAAC;IACnH,yGAAyG;IACzG,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,KAAK,MAAM,IAAI,CAAC;IAC3D,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,uGAAuG;IACvG,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,KAAK,MAAM,IAAI,CAAC;IAC3D,+FAA+F;IAC/F,WAAW,EAAE,CAAC,kBAAkB,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAChF,gFAAgF;IAChF,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,mBAAmB,EAAE,UAAU,GAAG,UAAU,CAAC,KAAK,IAAI,CAAC;IACpG,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,iFAAiF;IACjF,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,mBAAmB,EAAE,UAAU,GAAG,UAAU,CAAC,KAAK,IAAI,CAAC;IACjG,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,MAAM,EAAE,CAAC,IAAI,EAAE,4BAA4B,KAAK,IAAI,CAAC;IACrD,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CAC9B;AAID,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,sBAAsB,GAAG,eAAe,CA8oB5G"}
@@ -1,2 +1,2 @@
1
- import{normalizeOverscan as e,observeResize as t,resolveEstimateFn as n,toNonNegativeInt as r,toPositiveNumber as i}from"./_utils.js";import{requireNonNegativeInteger as a,requireNonNegativeNumber as o,requirePositiveNumber as s,validateOverscan as c}from"./_validation.js";import{createScrollAdapter as l}from"./_adapter.js";import{createAxis1D as u}from"./_axis1d.js";import{alignOffset as d}from"./_alignment.js";function f(f,p){typeof p.estimateRowSize==`number`&&s(p.estimateRowSize,`estimateRowSize`),typeof p.estimateColSize==`number`&&s(p.estimateColSize,`estimateColSize`),p.initialScrollTop!==void 0&&o(p.initialScrollTop,`initialScrollTop`),p.initialScrollLeft!==void 0&&o(p.initialScrollLeft,`initialScrollLeft`),p.rowGap!==void 0&&a(p.rowGap,`rowGap`),p.colGap!==void 0&&a(p.colGap,`colGap`),p.overscanY!==void 0&&c(p.overscanY,`overscanY`),p.overscanX!==void 0&&c(p.overscanX,`overscanX`);let m=a(p.rowCount,`rowCount`),h=a(p.colCount,`colCount`),g=n(p.estimateRowSize,36),_=n(p.estimateColSize,36),v=e(p.overscanY,3),y=e(p.overscanX,3),b=p.keyboardScroll??!1,x=p.onChange,S=p.onRangeChange,C=null;p.signal&&(C=p.signal({cols:[],rows:[],totalHeight:0,totalWidth:0}));function w(e){C&&(C.value=e),x?.(e)}let T=p.rowMeasurementCache??new Map,E=p.colMeasurementCache??new Map,D=0,O=0,k=0,A=0,j=[],M=[],N=!1,P=u(m,e=>T.get(e)??g(e),r(p.rowGap??0)),F=u(h,e=>E.get(e)??_(e),r(p.colGap??0)),I=!1;function L(e){let t=Number.isFinite(e)?e:0;return Math.min(Math.max(0,P.totalSize-k),Math.max(0,t))}function R(e){let t=Number.isFinite(e)?e:0;return Math.min(Math.max(0,F.totalSize-A),Math.max(0,t))}function z(){if(N)return;let e=P.totalSize,t=F.totalSize;if(m===0||h===0||k<=0||A<=0){(P.prevStart!==-1||P.prevTotalSize!==e||F.prevTotalSize!==t)&&(P.commitDedup(-1,-1),F.commitDedup(-1,-1),j=[],M=[],w({cols:[],rows:[],totalHeight:e,totalWidth:t}));return}let{renderEnd:n,renderStart:r}=P.computeRange(D,D+k,v.start,v.end),{renderEnd:i,renderStart:a}=F.computeRange(O,O+A,y.start,y.end),o=P.isDedupSame(r,n),s=F.isDedupSame(a,i);if(o&&s)return;P.commitDedup(r,n),F.commitDedup(a,i),(!o||!s)&&S&&S({firstCol:a,firstRow:r,lastCol:i,lastRow:n});let c=[];for(let e=r;e<=n;e++)c.push(P.itemAt(e));let l=[];for(let e=a;e<=i;e++)l.push(F.itemAt(e));j=c,M=l,w({cols:M,rows:j,totalHeight:e,totalWidth:t})}function B(e,t){if(!Number.isFinite(e))return!1;let n=Math.floor(e);if(n<0||n>=m)return!1;let r=i(t,-1);return r<=0||T.get(n)===r?!1:(T.set(n,r),P.markChanged(n),!0)}function V(e,t){if(!Number.isFinite(e))return!1;let n=Math.floor(e);if(n<0||n>=h)return!1;let r=i(t,-1);return r<=0||E.get(n)===r?!1:(E.set(n,r),F.markChanged(n),!0)}function H(){I||(I=!0,queueMicrotask(()=>{I=!1;let e=P.consumeMinChangedIndex();e!==1/0&&P.rebuildFrom(e);let t=F.consumeMinChangedIndex();t!==1/0&&F.rebuildFrom(t),z()}))}function U(e,t){N||B(e,t)&&H()}function W(e,t){N||V(e,t)&&H()}function G(e,t){if(N)return;let n=!1;for(let{index:t,size:r}of e)B(t,r)&&(n=!0);for(let{index:e,size:r}of t)V(e,r)&&(n=!0);n&&H()}function K(e,n){return N?()=>{}:t(Z.signal,n,t=>U(e,t.contentRect.height))}function q(e,n){return N?()=>{}:t(Z.signal,n,t=>W(e,t.contentRect.width))}function J(){N||(T.clear(),E.clear(),P.rebuild(!0),F.rebuild(!0),z())}function Y(){N||(P.rebuild(!0),F.rebuild(!0),z())}function X(e){if(N)return;let t=r(e);if(t===0)return;m+=t,P.setCount(m),P.rebuild(!0);let n=P.startAt(t),i=L(D+n);$.scrollTo(O,i,`auto`),D=i,z()}function ee(e,t={}){if(N||m===0)return;let n=Math.max(0,Math.min(Math.floor(Number.isFinite(e)?e:0),m-1)),r=t.behavior??`auto`,i=P.startAt(n),a=P.sizeAt(n),o=d(i,i+a,a,D,k,t.rowAlign??`auto`);o!==null&&$.scrollTo(O,L(o),r)}function te(e,t={}){if(N||h===0)return;let n=Math.max(0,Math.min(Math.floor(Number.isFinite(e)?e:0),h-1)),r=t.behavior??`auto`,i=F.startAt(n),a=F.sizeAt(n),o=d(i,i+a,a,O,A,t.colAlign??`auto`);o!==null&&$.scrollTo(R(o),D,r)}function ne(e,t,n={}){if(N||m===0||h===0)return;let r=Math.max(0,Math.min(Math.floor(Number.isFinite(e)?e:0),m-1)),i=Math.max(0,Math.min(Math.floor(Number.isFinite(t)?t:0),h-1)),a=n.behavior??`auto`,o=P.startAt(r),s=P.sizeAt(r),c=F.startAt(i),l=F.sizeAt(i),u=d(o,o+s,s,D,k,n.rowAlign??`auto`),f=d(c,c+l,l,O,A,n.colAlign??`auto`);(u!==null||f!==null)&&$.scrollTo(f===null?O:R(f),u===null?D:L(u),a)}function re(t){if(N)return;t.rowCount!==void 0&&a(t.rowCount,`rowCount`),t.colCount!==void 0&&a(t.colCount,`colCount`),typeof t.estimateRowSize==`number`&&s(t.estimateRowSize,`estimateRowSize`),typeof t.estimateColSize==`number`&&s(t.estimateColSize,`estimateColSize`),t.rowGap!==void 0&&a(t.rowGap,`rowGap`),t.colGap!==void 0&&a(t.colGap,`colGap`),t.overscanY!==void 0&&c(t.overscanY,`overscanY`),t.overscanX!==void 0&&c(t.overscanX,`overscanX`);let r=!1,i=!1,o=!1;if(Object.hasOwn(t,`onChange`)&&(x=t.onChange),Object.hasOwn(t,`onRangeChange`)&&(S=t.onRangeChange),t.rowCount!==void 0){let e=t.rowCount;e!==m&&(m=e,P.setCount(m),r=!0)}if(t.colCount!==void 0){let e=t.colCount;e!==h&&(h=e,F.setCount(h),i=!0)}if(t.rowGap!==void 0){let e=t.rowGap;e!==P.gap&&(P.setGap(e),r=!0)}if(t.colGap!==void 0){let e=t.colGap;e!==F.gap&&(F.setGap(e),i=!0)}if(`estimateRowSize`in t&&(g=n(t.estimateRowSize,36),T.clear(),r=!0),`estimateColSize`in t&&(_=n(t.estimateColSize,36),E.clear(),i=!0),t.overscanY!==void 0){let n=e(t.overscanY,3);(n.start!==v.start||n.end!==v.end)&&(v=n,o=!0)}if(t.overscanX!==void 0){let n=e(t.overscanX,3);(n.start!==y.start||n.end!==y.end)&&(y=n,o=!0)}Object.hasOwn(t,`keyboardScroll`)&&(b=t.keyboardScroll??!1),r&&P.rebuild(!0),i&&F.rebuild(!0),(r||i||o)&&z()}let Z=new AbortController;function Q(){N||(N=!0,Z.abort(),$.detach())}function ie(){D=L($.y.readOffset()),O=R($.x.readOffset()),z()}function ae(){k=$.y.readViewportSize(),A=$.x.readViewportSize(),z()}let $=l(f,ie,ae);if(P.rebuild(!0),F.rebuild(!0),k=$.y.readViewportSize(),A=$.x.readViewportSize(),p.initialScrollTop===void 0?p.initialScrollLeft!==void 0&&$.scrollTo(R(p.initialScrollLeft),0,`auto`):$.scrollTo(p.initialScrollLeft??0,L(p.initialScrollTop),`auto`),D=L($.y.readOffset()),O=R($.x.readOffset()),z(),b){let e=e=>{if(N||!(e instanceof KeyboardEvent)||m===0||h===0)return;let t=!1,n=D,i=O,a=Math.max(k*.8,k-100),o=(typeof g(0)==`number`?g(0):36)+r(p.rowGap??0),s=(typeof _(0)==`number`?_(0):36)+r(p.colGap??0);switch(e.key){case`ArrowUp`:n=Math.max(0,D-o),t=!0;break;case`ArrowDown`:n=Math.min(P.totalSize-k,D+o),t=!0;break;case`ArrowLeft`:i=Math.max(0,O-s),t=!0;break;case`ArrowRight`:i=Math.min(F.totalSize-A,O+s),t=!0;break;case`PageUp`:n=Math.max(0,D-a),t=!0;break;case`PageDown`:n=Math.min(P.totalSize-k,D+a),t=!0;break;case`Home`:n=0,t=!0;break;case`End`:n=Math.max(0,P.totalSize-k),t=!0}t&&(e.preventDefault(),$.scrollTo(i,n,`auto`),D=L(n),O=R(i),N||z())};typeof f==`object`&&f&&`addEventListener`in f&&(f.addEventListener(`keydown`,e),Z.signal.addEventListener(`abort`,()=>{f.removeEventListener(`keydown`,e)}))}return{get cols(){return M},get disposalSignal(){return Z.signal},dispose:Q,get disposed(){return N},invalidate:J,measureBatch:G,measureColEl:q,measureColumn:W,measureRow:U,measureRowEl:K,prependRows:X,refresh:Y,get rows(){return j},get scrollLeft(){return O},scrollToCell:ne,scrollToColumn:te,get scrollTop(){return D},scrollToRow:ee,[Symbol.dispose]:Q,get totalHeight(){return P.totalSize},get totalWidth(){return F.totalSize},update:re}}export{f as createGridVirtualizer};
1
+ import{normalizeOverscan as e,observeResize as t,resolveEstimateFn as n,toNonNegativeInt as r,toPositiveNumber as i}from"./_utils.js";import{requireNonNegativeInteger as a,requireNonNegativeNumber as o,requirePositiveNumber as s,validateOverscan as c}from"./_validation.js";import{createScrollAdapter as l}from"./_adapter.js";import{createAxis1D as u}from"./_axis1d.js";import{alignOffset as d}from"./_alignment.js";function f(f,p){typeof p.estimateRowSize==`number`&&s(p.estimateRowSize,`estimateRowSize`),typeof p.estimateColSize==`number`&&s(p.estimateColSize,`estimateColSize`),p.initialScrollTop!==void 0&&o(p.initialScrollTop,`initialScrollTop`),p.initialScrollLeft!==void 0&&o(p.initialScrollLeft,`initialScrollLeft`),p.rowGap!==void 0&&a(p.rowGap,`rowGap`),p.colGap!==void 0&&a(p.colGap,`colGap`),p.overscanY!==void 0&&c(p.overscanY,`overscanY`),p.overscanX!==void 0&&c(p.overscanX,`overscanX`);let m=a(p.rowCount,`rowCount`),h=a(p.colCount,`colCount`),g=n(p.estimateRowSize,36),_=n(p.estimateColSize,36),v=e(p.overscanY,3),y=e(p.overscanX,3),b=p.keyboardScroll??!1,x=p.onChange,S=p.onRangeChange,C=null;p.toSignal&&(C=p.toSignal({cols:[],rows:[],totalHeight:0,totalWidth:0}));function w(e){C&&(C.value=e),x?.(e)}let T=p.rowMeasurementCache??new Map,E=p.colMeasurementCache??new Map,D=0,O=0,k=0,A=0,j=[],M=[],N=!1,P=u(m,e=>T.get(e)??g(e),r(p.rowGap??0)),F=u(h,e=>E.get(e)??_(e),r(p.colGap??0)),I=!1;function L(e){let t=Number.isFinite(e)?e:0;return Math.min(Math.max(0,P.totalSize-k),Math.max(0,t))}function R(e){let t=Number.isFinite(e)?e:0;return Math.min(Math.max(0,F.totalSize-A),Math.max(0,t))}function z(){if(N)return;let e=P.totalSize,t=F.totalSize;if(m===0||h===0||k<=0||A<=0){(P.prevStart!==-1||P.prevTotalSize!==e||F.prevTotalSize!==t)&&(P.commitDedup(-1,-1),F.commitDedup(-1,-1),j=[],M=[],w({cols:[],rows:[],totalHeight:e,totalWidth:t}));return}let{renderEnd:n,renderStart:r}=P.computeRange(D,D+k,v.start,v.end),{renderEnd:i,renderStart:a}=F.computeRange(O,O+A,y.start,y.end),o=P.isDedupSame(r,n),s=F.isDedupSame(a,i);if(o&&s)return;P.commitDedup(r,n),F.commitDedup(a,i),(!o||!s)&&S&&S({firstCol:a,firstRow:r,lastCol:i,lastRow:n});let c=[];for(let e=r;e<=n;e++)c.push(P.itemAt(e));let l=[];for(let e=a;e<=i;e++)l.push(F.itemAt(e));j=c,M=l,w({cols:M,rows:j,totalHeight:e,totalWidth:t})}function B(e,t){if(!Number.isFinite(e))return!1;let n=Math.floor(e);if(n<0||n>=m)return!1;let r=i(t,-1);return r<=0||T.get(n)===r?!1:(T.set(n,r),P.markChanged(n),!0)}function V(e,t){if(!Number.isFinite(e))return!1;let n=Math.floor(e);if(n<0||n>=h)return!1;let r=i(t,-1);return r<=0||E.get(n)===r?!1:(E.set(n,r),F.markChanged(n),!0)}function H(){I||(I=!0,queueMicrotask(()=>{I=!1;let e=P.consumeMinChangedIndex();e!==1/0&&P.rebuildFrom(e);let t=F.consumeMinChangedIndex();t!==1/0&&F.rebuildFrom(t),z()}))}function U(e,t){N||B(e,t)&&H()}function W(e,t){N||V(e,t)&&H()}function G(e,t){if(N)return;let n=!1;for(let{index:t,size:r}of e)B(t,r)&&(n=!0);for(let{index:e,size:r}of t)V(e,r)&&(n=!0);n&&H()}function K(e,n){return N?()=>{}:t(Z.signal,n,t=>U(e,t.contentRect.height))}function q(e,n){return N?()=>{}:t(Z.signal,n,t=>W(e,t.contentRect.width))}function J(){N||(T.clear(),E.clear(),P.rebuild(!0),F.rebuild(!0),z())}function Y(){N||(P.rebuild(!0),F.rebuild(!0),z())}function X(e){if(N)return;let t=r(e);if(t===0)return;m+=t,P.setCount(m),P.rebuild(!0);let n=P.startAt(t),i=L(D+n);$.scrollTo(O,i,`auto`),D=i,z()}function ee(e,t={}){if(N||m===0)return;let n=Math.max(0,Math.min(Math.floor(Number.isFinite(e)?e:0),m-1)),r=t.behavior??`auto`,i=P.startAt(n),a=P.sizeAt(n),o=d(i,i+a,a,D,k,t.rowAlign??`auto`);o!==null&&$.scrollTo(O,L(o),r)}function te(e,t={}){if(N||h===0)return;let n=Math.max(0,Math.min(Math.floor(Number.isFinite(e)?e:0),h-1)),r=t.behavior??`auto`,i=F.startAt(n),a=F.sizeAt(n),o=d(i,i+a,a,O,A,t.colAlign??`auto`);o!==null&&$.scrollTo(R(o),D,r)}function ne(e,t,n={}){if(N||m===0||h===0)return;let r=Math.max(0,Math.min(Math.floor(Number.isFinite(e)?e:0),m-1)),i=Math.max(0,Math.min(Math.floor(Number.isFinite(t)?t:0),h-1)),a=n.behavior??`auto`,o=P.startAt(r),s=P.sizeAt(r),c=F.startAt(i),l=F.sizeAt(i),u=d(o,o+s,s,D,k,n.rowAlign??`auto`),f=d(c,c+l,l,O,A,n.colAlign??`auto`);(u!==null||f!==null)&&$.scrollTo(f===null?O:R(f),u===null?D:L(u),a)}function re(t){if(N)return;t.rowCount!==void 0&&a(t.rowCount,`rowCount`),t.colCount!==void 0&&a(t.colCount,`colCount`),typeof t.estimateRowSize==`number`&&s(t.estimateRowSize,`estimateRowSize`),typeof t.estimateColSize==`number`&&s(t.estimateColSize,`estimateColSize`),t.rowGap!==void 0&&a(t.rowGap,`rowGap`),t.colGap!==void 0&&a(t.colGap,`colGap`),t.overscanY!==void 0&&c(t.overscanY,`overscanY`),t.overscanX!==void 0&&c(t.overscanX,`overscanX`);let r=!1,i=!1,o=!1;if(Object.hasOwn(t,`onChange`)&&(x=t.onChange),Object.hasOwn(t,`onRangeChange`)&&(S=t.onRangeChange),t.rowCount!==void 0){let e=t.rowCount;e!==m&&(m=e,P.setCount(m),r=!0)}if(t.colCount!==void 0){let e=t.colCount;e!==h&&(h=e,F.setCount(h),i=!0)}if(t.rowGap!==void 0){let e=t.rowGap;e!==P.gap&&(P.setGap(e),r=!0)}if(t.colGap!==void 0){let e=t.colGap;e!==F.gap&&(F.setGap(e),i=!0)}if(`estimateRowSize`in t&&(g=n(t.estimateRowSize,36),T.clear(),r=!0),`estimateColSize`in t&&(_=n(t.estimateColSize,36),E.clear(),i=!0),t.overscanY!==void 0){let n=e(t.overscanY,3);(n.start!==v.start||n.end!==v.end)&&(v=n,o=!0)}if(t.overscanX!==void 0){let n=e(t.overscanX,3);(n.start!==y.start||n.end!==y.end)&&(y=n,o=!0)}Object.hasOwn(t,`keyboardScroll`)&&(b=t.keyboardScroll??!1),r&&P.rebuild(!0),i&&F.rebuild(!0),(r||i||o)&&z()}let Z=new AbortController;function Q(){N||(N=!0,Z.abort(),$.detach())}function ie(){D=L($.y.readOffset()),O=R($.x.readOffset()),z()}function ae(){k=$.y.readViewportSize(),A=$.x.readViewportSize(),z()}let $=l(f,ie,ae);if(P.rebuild(!0),F.rebuild(!0),k=$.y.readViewportSize(),A=$.x.readViewportSize(),p.initialScrollTop===void 0?p.initialScrollLeft!==void 0&&$.scrollTo(R(p.initialScrollLeft),0,`auto`):$.scrollTo(p.initialScrollLeft??0,L(p.initialScrollTop),`auto`),D=L($.y.readOffset()),O=R($.x.readOffset()),z(),b){let e=e=>{if(N||!(e instanceof KeyboardEvent)||m===0||h===0)return;let t=!1,n=D,i=O,a=Math.max(k*.8,k-100),o=(typeof g(0)==`number`?g(0):36)+r(p.rowGap??0),s=(typeof _(0)==`number`?_(0):36)+r(p.colGap??0);switch(e.key){case`ArrowUp`:n=Math.max(0,D-o),t=!0;break;case`ArrowDown`:n=Math.min(P.totalSize-k,D+o),t=!0;break;case`ArrowLeft`:i=Math.max(0,O-s),t=!0;break;case`ArrowRight`:i=Math.min(F.totalSize-A,O+s),t=!0;break;case`PageUp`:n=Math.max(0,D-a),t=!0;break;case`PageDown`:n=Math.min(P.totalSize-k,D+a),t=!0;break;case`Home`:n=0,t=!0;break;case`End`:n=Math.max(0,P.totalSize-k),t=!0}t&&(e.preventDefault(),$.scrollTo(i,n,`auto`),D=L(n),O=R(i),N||z())};typeof f==`object`&&f&&`addEventListener`in f&&(f.addEventListener(`keydown`,e),Z.signal.addEventListener(`abort`,()=>{f.removeEventListener(`keydown`,e)}))}return{get cols(){return M},get disposalSignal(){return Z.signal},dispose:Q,get disposed(){return N},invalidate:J,measureBatch:G,measureColEl:q,measureColumn:W,measureRow:U,measureRowEl:K,prependRows:X,refresh:Y,get rows(){return j},get scrollLeft(){return O},scrollToCell:ne,scrollToColumn:te,get scrollTop(){return D},scrollToRow:ee,[Symbol.dispose]:Q,get totalHeight(){return P.totalSize},get totalWidth(){return F.totalSize},update:re}}export{f as createGridVirtualizer};
2
2
  //# sourceMappingURL=grid-virtualizer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"grid-virtualizer.js","names":[],"sources":["../src/grid-virtualizer.ts"],"sourcesContent":["import type { Signal } from '@vielzeug/ripple';\n\nimport { createScrollAdapter } from './_adapter';\nimport { alignOffset } from './_alignment';\nimport { createAxis1D, type VirtualItem } from './_axis1d';\nimport {\n DEFAULT_ESTIMATE_SIZE,\n DEFAULT_OVERSCAN,\n normalizeOverscan,\n type Overscan,\n observeResize,\n resolveEstimateFn,\n type ScrollTarget,\n toNonNegativeInt,\n toPositiveNumber,\n} from './_utils';\nimport {\n requireNonNegativeInteger,\n requireNonNegativeNumber,\n requirePositiveNumber,\n validateOverscan,\n} from './_validation';\n\nexport type { VirtualItem };\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface GridVirtualizerState {\n /**\n * Visible column descriptors.\n * Callers form the cross-product `rows × cols` as needed — no O(r×c) Cell\n * allocation per scroll tick (R8).\n */\n readonly cols: VirtualItem[];\n /** Visible row descriptors. */\n readonly rows: VirtualItem[];\n readonly totalHeight: number;\n readonly totalWidth: number;\n}\n\nexport interface GridRangeChangeEvent {\n firstCol: number;\n firstRow: number;\n lastCol: number;\n lastRow: number;\n}\n\nexport interface ScrollToCellOptions {\n behavior?: ScrollBehavior;\n colAlign?: 'auto' | 'center' | 'end' | 'start';\n rowAlign?: 'auto' | 'center' | 'end' | 'start';\n}\n\nexport interface GridVirtualizerOptions {\n colCount: number;\n colGap?: number;\n /** External measurement cache for columns. Share across instances for scroll restoration. */\n colMeasurementCache?: Map<number, number>;\n estimateColSize?: number | ((col: number) => number);\n estimateRowSize?: number | ((row: number) => number);\n initialScrollLeft?: number;\n initialScrollTop?: number;\n /** Enable keyboard navigation (Arrow/Page/Home/End keys). */\n keyboardScroll?: boolean;\n /** Called after every render cycle with the new state. Replace through `update()`. */\n onChange?: (state: GridVirtualizerState) => void;\n /** Zero-allocation alternative to onChange for range-based consumers. */\n onRangeChange?: (range: GridRangeChangeEvent) => void;\n overscanX?: Overscan;\n overscanY?: Overscan;\n rowCount: number;\n rowGap?: number;\n /** External measurement cache for rows. Share across instances for scroll restoration. */\n rowMeasurementCache?: Map<number, number>;\n /** Optional signal factory for reactive state. */\n signal?: (init: GridVirtualizerState) => Signal<GridVirtualizerState>;\n}\n\n/**\n * Options accepted by `update()`. Explicit interface instead of\n * `Partial<Omit<...>>` for better IDE hover (R7).\n */\nexport interface GridVirtualizerUpdateOptions {\n colCount?: number;\n colGap?: number;\n estimateColSize?: number | ((col: number) => number);\n estimateRowSize?: number | ((row: number) => number);\n keyboardScroll?: boolean;\n onChange?: ((state: GridVirtualizerState) => void) | undefined;\n onRangeChange?: ((range: GridRangeChangeEvent) => void) | undefined;\n overscanX?: Overscan;\n overscanY?: Overscan;\n rowCount?: number;\n rowGap?: number;\n}\n\nexport interface GridVirtualizer {\n readonly cols: VirtualItem[];\n readonly disposalSignal: AbortSignal;\n dispose: () => void;\n readonly disposed: boolean;\n invalidate: () => void;\n /** Measure rows and columns in a single coordinated rebuild pass (R4). */\n measureBatch: (rows: Array<{ index: number; size: number }>, cols: Array<{ index: number; size: number }>) => void;\n /** Attach a ResizeObserver to `el` to auto-measure the column width on resize. Returns disconnect fn. */\n measureColEl: (col: number, el: HTMLElement) => () => void;\n measureColumn: (col: number, size: number) => void;\n measureRow: (row: number, size: number) => void;\n /** Attach a ResizeObserver to `el` to auto-measure the row height on resize. Returns disconnect fn. */\n measureRowEl: (row: number, el: HTMLElement) => () => void;\n /** Prepend `additionalRowCount` rows at the top while keeping the viewport visually stable. */\n prependRows: (additionalRowCount: number) => void;\n refresh: () => void;\n readonly rows: VirtualItem[];\n readonly scrollLeft: number;\n scrollToCell: (row: number, col: number, options?: ScrollToCellOptions) => void;\n /** Scroll to bring a row into view without changing the horizontal position. */\n scrollToColumn: (col: number, options?: Pick<ScrollToCellOptions, 'behavior' | 'colAlign'>) => void;\n readonly scrollTop: number;\n /** Scroll to bring a column into view without changing the vertical position. */\n scrollToRow: (row: number, options?: Pick<ScrollToCellOptions, 'behavior' | 'rowAlign'>) => void;\n readonly totalHeight: number;\n readonly totalWidth: number;\n update: (next: GridVirtualizerUpdateOptions) => void;\n [Symbol.dispose]: () => void;\n}\n\n// ─── Implementation ────────────────────────────────────────────────────────────\n\nexport function createGridVirtualizer(target: ScrollTarget, options: GridVirtualizerOptions): GridVirtualizer {\n if (typeof options.estimateRowSize === 'number') requirePositiveNumber(options.estimateRowSize, 'estimateRowSize');\n\n if (typeof options.estimateColSize === 'number') requirePositiveNumber(options.estimateColSize, 'estimateColSize');\n\n if (options.initialScrollTop !== undefined) requireNonNegativeNumber(options.initialScrollTop, 'initialScrollTop');\n\n if (options.initialScrollLeft !== undefined) requireNonNegativeNumber(options.initialScrollLeft, 'initialScrollLeft');\n\n if (options.rowGap !== undefined) requireNonNegativeInteger(options.rowGap, 'rowGap');\n\n if (options.colGap !== undefined) requireNonNegativeInteger(options.colGap, 'colGap');\n\n if (options.overscanY !== undefined) validateOverscan(options.overscanY, 'overscanY');\n\n if (options.overscanX !== undefined) validateOverscan(options.overscanX, 'overscanX');\n\n let rowCount = requireNonNegativeInteger(options.rowCount, 'rowCount');\n let colCount = requireNonNegativeInteger(options.colCount, 'colCount');\n let estimateRowFn = resolveEstimateFn(options.estimateRowSize, DEFAULT_ESTIMATE_SIZE);\n let estimateColFn = resolveEstimateFn(options.estimateColSize, DEFAULT_ESTIMATE_SIZE);\n let overscanY = normalizeOverscan(options.overscanY, DEFAULT_OVERSCAN);\n let overscanX = normalizeOverscan(options.overscanX, DEFAULT_OVERSCAN);\n let keyboardScrollEnabled = options.keyboardScroll ?? false;\n\n let onChange = options.onChange;\n let onRangeChange = options.onRangeChange;\n\n // Optional signal for reactive state\n let stateSignal: Signal<GridVirtualizerState> | null = null;\n if (options.signal) {\n const initialState: GridVirtualizerState = { cols: [], rows: [], totalHeight: 0, totalWidth: 0 };\n stateSignal = options.signal(initialState);\n }\n\n // Helper to emit state to both callback and signal\n function emitState(state: GridVirtualizerState): void {\n if (stateSignal) stateSignal.value = state;\n onChange?.(state);\n }\n\n // External or internal measurement caches (numeric row/col index keys).\n const measuredRows: Map<number, number> = options.rowMeasurementCache ?? new Map();\n const measuredCols: Map<number, number> = options.colMeasurementCache ?? new Map();\n\n let scrollTop = 0;\n let scrollLeft = 0;\n let viewportHeight = 0;\n let viewportWidth = 0;\n let rows: VirtualItem[] = [];\n let cols: VirtualItem[] = [];\n let disposed = false;\n\n // ─── Axis1D instances (R1) ─────────────────────────────────────────────────\n // sizeAt closures capture live `measuredRows/Cols` and `estimateFn` vars.\n\n const rowAx = createAxis1D(\n rowCount,\n (r) => measuredRows.get(r) ?? estimateRowFn(r),\n toNonNegativeInt(options.rowGap ?? 0),\n );\n\n const colAx = createAxis1D(\n colCount,\n (c) => measuredCols.get(c) ?? estimateColFn(c),\n toNonNegativeInt(options.colGap ?? 0),\n );\n\n // R4: Single coordinated flush flag — prevents two separate microtasks.\n let pendingBatchBuild = false;\n\n // ─── Clamping ──────────────────────────────────────────────────────────────\n\n function clampTop(offset: number): number {\n const safe = Number.isFinite(offset) ? offset : 0;\n\n return Math.min(Math.max(0, rowAx.totalSize - viewportHeight), Math.max(0, safe));\n }\n\n function clampLeft(offset: number): number {\n const safe = Number.isFinite(offset) ? offset : 0;\n\n return Math.min(Math.max(0, colAx.totalSize - viewportWidth), Math.max(0, safe));\n }\n\n // ─── Compute and emit ──────────────────────────────────────────────────────\n\n function computeVisible(): void {\n if (disposed) return;\n\n const totalHeight = rowAx.totalSize;\n const totalWidth = colAx.totalSize;\n\n if (rowCount === 0 || colCount === 0 || viewportHeight <= 0 || viewportWidth <= 0) {\n const wasEmpty =\n rowAx.prevStart === -1 && rowAx.prevTotalSize === totalHeight && colAx.prevTotalSize === totalWidth;\n\n if (!wasEmpty) {\n // commitDedup(-1, -1) captures the current totalSize so the guard doesn't\n // fire on every subsequent event while the empty state persists (Bug 2).\n rowAx.commitDedup(-1, -1);\n colAx.commitDedup(-1, -1);\n rows = [];\n cols = [];\n emitState({ cols: [], rows: [], totalHeight, totalWidth });\n }\n\n return;\n }\n\n const { renderEnd: renderLastRow, renderStart: renderFirstRow } = rowAx.computeRange(\n scrollTop,\n scrollTop + viewportHeight,\n overscanY.start,\n overscanY.end,\n );\n\n const { renderEnd: renderLastCol, renderStart: renderFirstCol } = colAx.computeRange(\n scrollLeft,\n scrollLeft + viewportWidth,\n overscanX.start,\n overscanX.end,\n );\n\n const rowSame = rowAx.isDedupSame(renderFirstRow, renderLastRow);\n const colSame = colAx.isDedupSame(renderFirstCol, renderLastCol);\n\n if (rowSame && colSame) return;\n\n rowAx.commitDedup(renderFirstRow, renderLastRow);\n colAx.commitDedup(renderFirstCol, renderLastCol);\n\n const rangeChanged = !rowSame || !colSame;\n\n if (rangeChanged && onRangeChange) {\n onRangeChange({\n firstCol: renderFirstCol,\n firstRow: renderFirstRow,\n lastCol: renderLastCol,\n lastRow: renderLastRow,\n });\n }\n\n // R8: Build rows[] and cols[] separately — no cross-product VirtualCell[].\n const nextRows: VirtualItem[] = [];\n\n for (let r = renderFirstRow; r <= renderLastRow; r++) {\n nextRows.push(rowAx.itemAt(r));\n }\n\n const nextCols: VirtualItem[] = [];\n\n for (let c = renderFirstCol; c <= renderLastCol; c++) {\n nextCols.push(colAx.itemAt(c));\n }\n\n rows = nextRows;\n cols = nextCols;\n\n emitState({ cols, rows, totalHeight, totalWidth });\n }\n\n // ─── Measurement helpers ───────────────────────────────────────────────────\n\n function recordRowMeasurement(row: number, size: number): boolean {\n if (!Number.isFinite(row)) return false;\n\n const r = Math.floor(row);\n\n if (r < 0 || r >= rowCount) return false;\n\n const s = toPositiveNumber(size, -1);\n\n if (s <= 0) return false;\n\n if (measuredRows.get(r) === s) return false;\n\n measuredRows.set(r, s);\n rowAx.markChanged(r);\n\n return true;\n }\n\n function recordColMeasurement(col: number, size: number): boolean {\n if (!Number.isFinite(col)) return false;\n\n const c = Math.floor(col);\n\n if (c < 0 || c >= colCount) return false;\n\n const s = toPositiveNumber(size, -1);\n\n if (s <= 0) return false;\n\n if (measuredCols.get(c) === s) return false;\n\n measuredCols.set(c, s);\n colAx.markChanged(c);\n\n return true;\n }\n\n /**\n * R4: Single coordinated flush for both axes.\n * Replaces the previous two independent microtask schedules.\n */\n function scheduleFlush(): void {\n if (pendingBatchBuild) return;\n\n pendingBatchBuild = true;\n queueMicrotask(() => {\n pendingBatchBuild = false;\n\n // consumeMinChangedIndex() atomically reads and resets minChangedIndex.\n // Without the reset, every subsequent flush would rebuild from index 0\n // instead of the actual earliest changed index (Bug 3).\n const fromRow = rowAx.consumeMinChangedIndex();\n\n if (fromRow !== Infinity) rowAx.rebuildFrom(fromRow);\n\n const fromCol = colAx.consumeMinChangedIndex();\n\n if (fromCol !== Infinity) colAx.rebuildFrom(fromCol);\n\n computeVisible();\n });\n }\n\n // ─── Public measurement API ────────────────────────────────────────────────\n\n function measureRow(row: number, size: number): void {\n if (disposed) return;\n\n if (recordRowMeasurement(row, size)) scheduleFlush();\n }\n\n function measureColumn(col: number, size: number): void {\n if (disposed) return;\n\n if (recordColMeasurement(col, size)) scheduleFlush();\n }\n\n /** Measure rows and columns in a single coordinated rebuild pass (R4). */\n function measureBatch(\n rowEntries: Array<{ index: number; size: number }>,\n colEntries: Array<{ index: number; size: number }>,\n ): void {\n if (disposed) return;\n\n let changed = false;\n\n for (const { index, size } of rowEntries) {\n if (recordRowMeasurement(index, size)) changed = true;\n }\n\n for (const { index, size } of colEntries) {\n if (recordColMeasurement(index, size)) changed = true;\n }\n\n if (changed) scheduleFlush();\n }\n\n function measureRowEl(row: number, el: HTMLElement): () => void {\n if (disposed) return () => {};\n\n return observeResize(ac.signal, el, (entry) => measureRow(row, entry.contentRect.height));\n }\n\n function measureColEl(col: number, el: HTMLElement): () => void {\n if (disposed) return () => {};\n\n return observeResize(ac.signal, el, (entry) => measureColumn(col, entry.contentRect.width));\n }\n\n function invalidate(): void {\n if (disposed) return;\n\n measuredRows.clear();\n measuredCols.clear();\n rowAx.rebuild(true);\n colAx.rebuild(true);\n computeVisible();\n }\n\n function refresh(): void {\n if (disposed) return;\n\n rowAx.rebuild(true);\n colAx.rebuild(true);\n computeVisible();\n }\n\n function prependRows(additionalRowCount: number): void {\n if (disposed) return;\n\n const n = toNonNegativeInt(additionalRowCount);\n\n if (n === 0) return;\n\n rowCount += n;\n rowAx.setCount(rowCount);\n rowAx.rebuild(true);\n\n const prependedHeight = rowAx.startAt(n);\n const newScrollTop = clampTop(scrollTop + prependedHeight);\n\n adapter.scrollTo(scrollLeft, newScrollTop, 'auto');\n scrollTop = newScrollTop;\n\n computeVisible();\n }\n\n function scrollToRow(row: number, opts: Pick<ScrollToCellOptions, 'behavior' | 'rowAlign'> = {}): void {\n if (disposed || rowCount === 0) return;\n\n const safeRow = Math.max(0, Math.min(Math.floor(Number.isFinite(row) ? row : 0), rowCount - 1));\n const behavior = opts.behavior ?? 'auto';\n const rowStart = rowAx.startAt(safeRow);\n const rowSize = rowAx.sizeAt(safeRow);\n const targetTop = alignOffset(\n rowStart,\n rowStart + rowSize,\n rowSize,\n scrollTop,\n viewportHeight,\n opts.rowAlign ?? 'auto',\n );\n\n if (targetTop === null) return;\n\n adapter.scrollTo(scrollLeft, clampTop(targetTop), behavior);\n }\n\n function scrollToColumn(col: number, opts: Pick<ScrollToCellOptions, 'behavior' | 'colAlign'> = {}): void {\n if (disposed || colCount === 0) return;\n\n const safeCol = Math.max(0, Math.min(Math.floor(Number.isFinite(col) ? col : 0), colCount - 1));\n const behavior = opts.behavior ?? 'auto';\n const colStart = colAx.startAt(safeCol);\n const colSize = colAx.sizeAt(safeCol);\n const targetLeft = alignOffset(\n colStart,\n colStart + colSize,\n colSize,\n scrollLeft,\n viewportWidth,\n opts.colAlign ?? 'auto',\n );\n\n if (targetLeft === null) return;\n\n adapter.scrollTo(clampLeft(targetLeft), scrollTop, behavior);\n }\n\n function scrollToCell(row: number, col: number, opts: ScrollToCellOptions = {}): void {\n if (disposed || rowCount === 0 || colCount === 0) return;\n\n const safeRow = Math.max(0, Math.min(Math.floor(Number.isFinite(row) ? row : 0), rowCount - 1));\n const safeCol = Math.max(0, Math.min(Math.floor(Number.isFinite(col) ? col : 0), colCount - 1));\n const behavior = opts.behavior ?? 'auto';\n\n const rowStart = rowAx.startAt(safeRow);\n const rowSize = rowAx.sizeAt(safeRow);\n const colStart = colAx.startAt(safeCol);\n const colSize = colAx.sizeAt(safeCol);\n\n const targetTop = alignOffset(\n rowStart,\n rowStart + rowSize,\n rowSize,\n scrollTop,\n viewportHeight,\n opts.rowAlign ?? 'auto',\n );\n const targetLeft = alignOffset(\n colStart,\n colStart + colSize,\n colSize,\n scrollLeft,\n viewportWidth,\n opts.colAlign ?? 'auto',\n );\n\n if (targetTop === null && targetLeft === null) return;\n\n adapter.scrollTo(\n targetLeft !== null ? clampLeft(targetLeft) : scrollLeft,\n targetTop !== null ? clampTop(targetTop) : scrollTop,\n behavior,\n );\n }\n\n function update(next: GridVirtualizerUpdateOptions): void {\n if (disposed) return;\n\n if (next.rowCount !== undefined) requireNonNegativeInteger(next.rowCount, 'rowCount');\n\n if (next.colCount !== undefined) requireNonNegativeInteger(next.colCount, 'colCount');\n\n if (typeof next.estimateRowSize === 'number') requirePositiveNumber(next.estimateRowSize, 'estimateRowSize');\n\n if (typeof next.estimateColSize === 'number') requirePositiveNumber(next.estimateColSize, 'estimateColSize');\n\n if (next.rowGap !== undefined) requireNonNegativeInteger(next.rowGap, 'rowGap');\n\n if (next.colGap !== undefined) requireNonNegativeInteger(next.colGap, 'colGap');\n\n if (next.overscanY !== undefined) validateOverscan(next.overscanY, 'overscanY');\n\n if (next.overscanX !== undefined) validateOverscan(next.overscanX, 'overscanX');\n\n let rebuildRows = false;\n let rebuildCols = false;\n let needsCompute = false;\n\n if (Object.hasOwn(next, 'onChange')) onChange = next.onChange;\n\n if (Object.hasOwn(next, 'onRangeChange')) onRangeChange = next.onRangeChange;\n\n if (next.rowCount !== undefined) {\n const n = next.rowCount;\n\n if (n !== rowCount) {\n rowCount = n;\n rowAx.setCount(rowCount);\n rebuildRows = true;\n }\n }\n\n if (next.colCount !== undefined) {\n const n = next.colCount;\n\n if (n !== colCount) {\n colCount = n;\n colAx.setCount(colCount);\n rebuildCols = true;\n }\n }\n\n if (next.rowGap !== undefined) {\n const n = next.rowGap;\n\n if (n !== rowAx.gap) {\n rowAx.setGap(n);\n rebuildRows = true;\n }\n }\n\n if (next.colGap !== undefined) {\n const n = next.colGap;\n\n if (n !== colAx.gap) {\n colAx.setGap(n);\n rebuildCols = true;\n }\n }\n\n if ('estimateRowSize' in next) {\n estimateRowFn = resolveEstimateFn(next.estimateRowSize, DEFAULT_ESTIMATE_SIZE);\n measuredRows.clear();\n rebuildRows = true;\n }\n\n if ('estimateColSize' in next) {\n estimateColFn = resolveEstimateFn(next.estimateColSize, DEFAULT_ESTIMATE_SIZE);\n measuredCols.clear();\n rebuildCols = true;\n }\n\n if (next.overscanY !== undefined) {\n const n = normalizeOverscan(next.overscanY, DEFAULT_OVERSCAN);\n\n if (n.start !== overscanY.start || n.end !== overscanY.end) {\n overscanY = n;\n needsCompute = true;\n }\n }\n\n if (next.overscanX !== undefined) {\n const n = normalizeOverscan(next.overscanX, DEFAULT_OVERSCAN);\n\n if (n.start !== overscanX.start || n.end !== overscanX.end) {\n overscanX = n;\n needsCompute = true;\n }\n }\n\n if (Object.hasOwn(next, 'keyboardScroll')) {\n keyboardScrollEnabled = next.keyboardScroll ?? false;\n }\n\n if (rebuildRows) rowAx.rebuild(true);\n\n if (rebuildCols) colAx.rebuild(true);\n\n if (rebuildRows || rebuildCols || needsCompute) computeVisible();\n }\n\n const ac = new AbortController();\n\n function disposeImpl(): void {\n if (disposed) return;\n\n disposed = true;\n ac.abort();\n adapter.detach();\n }\n\n // ─── Bootstrap (R9: read viewport BEFORE writing initial scroll) ───────────\n\n function handleScroll(): void {\n scrollTop = clampTop(adapter.y.readOffset());\n scrollLeft = clampLeft(adapter.x.readOffset());\n computeVisible();\n }\n\n function handleResize(): void {\n viewportHeight = adapter.y.readViewportSize();\n viewportWidth = adapter.x.readViewportSize();\n computeVisible();\n }\n\n const adapter = createScrollAdapter(target, handleScroll, handleResize);\n\n rowAx.rebuild(true);\n colAx.rebuild(true);\n\n // R9: Read viewport dimensions BEFORE writing initial scroll positions so that\n // computeVisible() sees a valid viewport on the very first call.\n viewportHeight = adapter.y.readViewportSize();\n viewportWidth = adapter.x.readViewportSize();\n\n if (options.initialScrollTop !== undefined) {\n adapter.scrollTo(options.initialScrollLeft ?? 0, clampTop(options.initialScrollTop), 'auto');\n } else if (options.initialScrollLeft !== undefined) {\n adapter.scrollTo(clampLeft(options.initialScrollLeft), 0, 'auto');\n }\n\n scrollTop = clampTop(adapter.y.readOffset());\n scrollLeft = clampLeft(adapter.x.readOffset());\n\n computeVisible();\n\n // ─── Keyboard scroll support ──────────────────────────────────────────────────\n\n if (keyboardScrollEnabled) {\n const handleKeyDown = (e: Event): void => {\n if (disposed) return;\n if (!(e instanceof KeyboardEvent)) return;\n if (rowCount === 0 || colCount === 0) return;\n\n let handled = false;\n let newTop = scrollTop;\n let newLeft = scrollLeft;\n const pageScrollSize = Math.max(viewportHeight * 0.8, viewportHeight - 100);\n const rowStepSize =\n (typeof estimateRowFn(0) === 'number' ? estimateRowFn(0) : DEFAULT_ESTIMATE_SIZE) +\n toNonNegativeInt(options.rowGap ?? 0);\n const colStepSize =\n (typeof estimateColFn(0) === 'number' ? estimateColFn(0) : DEFAULT_ESTIMATE_SIZE) +\n toNonNegativeInt(options.colGap ?? 0);\n\n switch (e.key) {\n case 'ArrowUp':\n newTop = Math.max(0, scrollTop - rowStepSize);\n handled = true;\n break;\n case 'ArrowDown':\n newTop = Math.min(rowAx.totalSize - viewportHeight, scrollTop + rowStepSize);\n handled = true;\n break;\n case 'ArrowLeft':\n newLeft = Math.max(0, scrollLeft - colStepSize);\n handled = true;\n break;\n case 'ArrowRight':\n newLeft = Math.min(colAx.totalSize - viewportWidth, scrollLeft + colStepSize);\n handled = true;\n break;\n case 'PageUp':\n newTop = Math.max(0, scrollTop - pageScrollSize);\n handled = true;\n break;\n case 'PageDown':\n newTop = Math.min(rowAx.totalSize - viewportHeight, scrollTop + pageScrollSize);\n handled = true;\n break;\n case 'Home':\n newTop = 0;\n handled = true;\n break;\n case 'End':\n newTop = Math.max(0, rowAx.totalSize - viewportHeight);\n handled = true;\n break;\n }\n\n if (handled) {\n e.preventDefault();\n adapter.scrollTo(newLeft, newTop, 'auto');\n scrollTop = clampTop(newTop);\n scrollLeft = clampLeft(newLeft);\n if (!disposed) computeVisible();\n }\n };\n\n if (typeof target === 'object' && target !== null && 'addEventListener' in target) {\n (target as EventTarget).addEventListener('keydown', handleKeyDown);\n ac.signal.addEventListener('abort', () => {\n (target as EventTarget).removeEventListener('keydown', handleKeyDown);\n });\n }\n }\n\n return {\n get cols() {\n return cols;\n },\n get disposalSignal() {\n return ac.signal;\n },\n dispose: disposeImpl,\n get disposed() {\n return disposed;\n },\n invalidate,\n measureBatch,\n measureColEl,\n measureColumn,\n measureRow,\n measureRowEl,\n prependRows,\n refresh,\n get rows() {\n return rows;\n },\n get scrollLeft() {\n return scrollLeft;\n },\n scrollToCell,\n scrollToColumn,\n get scrollTop() {\n return scrollTop;\n },\n scrollToRow,\n [Symbol.dispose]: disposeImpl,\n get totalHeight() {\n return rowAx.totalSize;\n },\n get totalWidth() {\n return colAx.totalSize;\n },\n update,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"grid-virtualizer.js","names":[],"sources":["../src/grid-virtualizer.ts"],"sourcesContent":["import type { Signal } from '@vielzeug/ripple';\n\nimport { createScrollAdapter } from './_adapter';\nimport { alignOffset } from './_alignment';\nimport { createAxis1D, type VirtualItem } from './_axis1d';\nimport {\n DEFAULT_ESTIMATE_SIZE,\n DEFAULT_OVERSCAN,\n normalizeOverscan,\n type Overscan,\n observeResize,\n resolveEstimateFn,\n type ScrollTarget,\n toNonNegativeInt,\n toPositiveNumber,\n} from './_utils';\nimport {\n requireNonNegativeInteger,\n requireNonNegativeNumber,\n requirePositiveNumber,\n validateOverscan,\n} from './_validation';\n\nexport type { VirtualItem };\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface GridVirtualizerState {\n /**\n * Visible column descriptors.\n * Callers form the cross-product `rows × cols` as needed — no O(r×c) Cell\n * allocation per scroll tick (R8).\n */\n readonly cols: VirtualItem[];\n /** Visible row descriptors. */\n readonly rows: VirtualItem[];\n readonly totalHeight: number;\n readonly totalWidth: number;\n}\n\nexport interface GridRangeChangeEvent {\n firstCol: number;\n firstRow: number;\n lastCol: number;\n lastRow: number;\n}\n\nexport interface ScrollToCellOptions {\n behavior?: ScrollBehavior;\n colAlign?: 'auto' | 'center' | 'end' | 'start';\n rowAlign?: 'auto' | 'center' | 'end' | 'start';\n}\n\nexport interface GridVirtualizerOptions {\n colCount: number;\n colGap?: number;\n /** External measurement cache for columns. Share across instances for scroll restoration. */\n colMeasurementCache?: Map<number, number>;\n estimateColSize?: number | ((col: number) => number);\n estimateRowSize?: number | ((row: number) => number);\n initialScrollLeft?: number;\n initialScrollTop?: number;\n /** Enable keyboard navigation (Arrow/Page/Home/End keys). */\n keyboardScroll?: boolean;\n /** Called after every render cycle with the new state. Replace through `update()`. */\n onChange?: (state: GridVirtualizerState) => void;\n /** Zero-allocation alternative to onChange for range-based consumers. */\n onRangeChange?: (range: GridRangeChangeEvent) => void;\n overscanX?: Overscan;\n overscanY?: Overscan;\n rowCount: number;\n rowGap?: number;\n /** External measurement cache for rows. Share across instances for scroll restoration. */\n rowMeasurementCache?: Map<number, number>;\n /** Optional signal factory for reactive state. */\n toSignal?: (init: GridVirtualizerState) => Signal<GridVirtualizerState>;\n}\n\n/**\n * Options accepted by `update()`. Explicit interface instead of\n * `Partial<Omit<...>>` for better IDE hover (R7).\n */\nexport interface GridVirtualizerUpdateOptions {\n colCount?: number;\n colGap?: number;\n estimateColSize?: number | ((col: number) => number);\n estimateRowSize?: number | ((row: number) => number);\n keyboardScroll?: boolean;\n onChange?: ((state: GridVirtualizerState) => void) | undefined;\n onRangeChange?: ((range: GridRangeChangeEvent) => void) | undefined;\n overscanX?: Overscan;\n overscanY?: Overscan;\n rowCount?: number;\n rowGap?: number;\n}\n\nexport interface GridVirtualizer {\n readonly cols: VirtualItem[];\n readonly disposalSignal: AbortSignal;\n dispose: () => void;\n readonly disposed: boolean;\n invalidate: () => void;\n /** Measure rows and columns in a single coordinated rebuild pass (R4). */\n measureBatch: (rows: Array<{ index: number; size: number }>, cols: Array<{ index: number; size: number }>) => void;\n /** Attach a ResizeObserver to `el` to auto-measure the column width on resize. Returns disconnect fn. */\n measureColEl: (col: number, el: HTMLElement) => () => void;\n measureColumn: (col: number, size: number) => void;\n measureRow: (row: number, size: number) => void;\n /** Attach a ResizeObserver to `el` to auto-measure the row height on resize. Returns disconnect fn. */\n measureRowEl: (row: number, el: HTMLElement) => () => void;\n /** Prepend `additionalRowCount` rows at the top while keeping the viewport visually stable. */\n prependRows: (additionalRowCount: number) => void;\n refresh: () => void;\n readonly rows: VirtualItem[];\n readonly scrollLeft: number;\n scrollToCell: (row: number, col: number, options?: ScrollToCellOptions) => void;\n /** Scroll to bring a row into view without changing the horizontal position. */\n scrollToColumn: (col: number, options?: Pick<ScrollToCellOptions, 'behavior' | 'colAlign'>) => void;\n readonly scrollTop: number;\n /** Scroll to bring a column into view without changing the vertical position. */\n scrollToRow: (row: number, options?: Pick<ScrollToCellOptions, 'behavior' | 'rowAlign'>) => void;\n readonly totalHeight: number;\n readonly totalWidth: number;\n update: (next: GridVirtualizerUpdateOptions) => void;\n [Symbol.dispose]: () => void;\n}\n\n// ─── Implementation ────────────────────────────────────────────────────────────\n\nexport function createGridVirtualizer(target: ScrollTarget, options: GridVirtualizerOptions): GridVirtualizer {\n if (typeof options.estimateRowSize === 'number') requirePositiveNumber(options.estimateRowSize, 'estimateRowSize');\n\n if (typeof options.estimateColSize === 'number') requirePositiveNumber(options.estimateColSize, 'estimateColSize');\n\n if (options.initialScrollTop !== undefined) requireNonNegativeNumber(options.initialScrollTop, 'initialScrollTop');\n\n if (options.initialScrollLeft !== undefined) requireNonNegativeNumber(options.initialScrollLeft, 'initialScrollLeft');\n\n if (options.rowGap !== undefined) requireNonNegativeInteger(options.rowGap, 'rowGap');\n\n if (options.colGap !== undefined) requireNonNegativeInteger(options.colGap, 'colGap');\n\n if (options.overscanY !== undefined) validateOverscan(options.overscanY, 'overscanY');\n\n if (options.overscanX !== undefined) validateOverscan(options.overscanX, 'overscanX');\n\n let rowCount = requireNonNegativeInteger(options.rowCount, 'rowCount');\n let colCount = requireNonNegativeInteger(options.colCount, 'colCount');\n let estimateRowFn = resolveEstimateFn(options.estimateRowSize, DEFAULT_ESTIMATE_SIZE);\n let estimateColFn = resolveEstimateFn(options.estimateColSize, DEFAULT_ESTIMATE_SIZE);\n let overscanY = normalizeOverscan(options.overscanY, DEFAULT_OVERSCAN);\n let overscanX = normalizeOverscan(options.overscanX, DEFAULT_OVERSCAN);\n let keyboardScrollEnabled = options.keyboardScroll ?? false;\n\n let onChange = options.onChange;\n let onRangeChange = options.onRangeChange;\n\n // Optional signal for reactive state\n let stateSignal: Signal<GridVirtualizerState> | null = null;\n if (options.toSignal) {\n const initialState: GridVirtualizerState = { cols: [], rows: [], totalHeight: 0, totalWidth: 0 };\n stateSignal = options.toSignal(initialState);\n }\n\n // Helper to emit state to both callback and signal\n function emitState(state: GridVirtualizerState): void {\n if (stateSignal) stateSignal.value = state;\n onChange?.(state);\n }\n\n // External or internal measurement caches (numeric row/col index keys).\n const measuredRows: Map<number, number> = options.rowMeasurementCache ?? new Map();\n const measuredCols: Map<number, number> = options.colMeasurementCache ?? new Map();\n\n let scrollTop = 0;\n let scrollLeft = 0;\n let viewportHeight = 0;\n let viewportWidth = 0;\n let rows: VirtualItem[] = [];\n let cols: VirtualItem[] = [];\n let disposed = false;\n\n // ─── Axis1D instances (R1) ─────────────────────────────────────────────────\n // sizeAt closures capture live `measuredRows/Cols` and `estimateFn` vars.\n\n const rowAx = createAxis1D(\n rowCount,\n (r) => measuredRows.get(r) ?? estimateRowFn(r),\n toNonNegativeInt(options.rowGap ?? 0),\n );\n\n const colAx = createAxis1D(\n colCount,\n (c) => measuredCols.get(c) ?? estimateColFn(c),\n toNonNegativeInt(options.colGap ?? 0),\n );\n\n // R4: Single coordinated flush flag — prevents two separate microtasks.\n let pendingBatchBuild = false;\n\n // ─── Clamping ──────────────────────────────────────────────────────────────\n\n function clampTop(offset: number): number {\n const safe = Number.isFinite(offset) ? offset : 0;\n\n return Math.min(Math.max(0, rowAx.totalSize - viewportHeight), Math.max(0, safe));\n }\n\n function clampLeft(offset: number): number {\n const safe = Number.isFinite(offset) ? offset : 0;\n\n return Math.min(Math.max(0, colAx.totalSize - viewportWidth), Math.max(0, safe));\n }\n\n // ─── Compute and emit ──────────────────────────────────────────────────────\n\n function computeVisible(): void {\n if (disposed) return;\n\n const totalHeight = rowAx.totalSize;\n const totalWidth = colAx.totalSize;\n\n if (rowCount === 0 || colCount === 0 || viewportHeight <= 0 || viewportWidth <= 0) {\n const wasEmpty =\n rowAx.prevStart === -1 && rowAx.prevTotalSize === totalHeight && colAx.prevTotalSize === totalWidth;\n\n if (!wasEmpty) {\n // commitDedup(-1, -1) captures the current totalSize so the guard doesn't\n // fire on every subsequent event while the empty state persists (Bug 2).\n rowAx.commitDedup(-1, -1);\n colAx.commitDedup(-1, -1);\n rows = [];\n cols = [];\n emitState({ cols: [], rows: [], totalHeight, totalWidth });\n }\n\n return;\n }\n\n const { renderEnd: renderLastRow, renderStart: renderFirstRow } = rowAx.computeRange(\n scrollTop,\n scrollTop + viewportHeight,\n overscanY.start,\n overscanY.end,\n );\n\n const { renderEnd: renderLastCol, renderStart: renderFirstCol } = colAx.computeRange(\n scrollLeft,\n scrollLeft + viewportWidth,\n overscanX.start,\n overscanX.end,\n );\n\n const rowSame = rowAx.isDedupSame(renderFirstRow, renderLastRow);\n const colSame = colAx.isDedupSame(renderFirstCol, renderLastCol);\n\n if (rowSame && colSame) return;\n\n rowAx.commitDedup(renderFirstRow, renderLastRow);\n colAx.commitDedup(renderFirstCol, renderLastCol);\n\n const rangeChanged = !rowSame || !colSame;\n\n if (rangeChanged && onRangeChange) {\n onRangeChange({\n firstCol: renderFirstCol,\n firstRow: renderFirstRow,\n lastCol: renderLastCol,\n lastRow: renderLastRow,\n });\n }\n\n // R8: Build rows[] and cols[] separately — no cross-product VirtualCell[].\n const nextRows: VirtualItem[] = [];\n\n for (let r = renderFirstRow; r <= renderLastRow; r++) {\n nextRows.push(rowAx.itemAt(r));\n }\n\n const nextCols: VirtualItem[] = [];\n\n for (let c = renderFirstCol; c <= renderLastCol; c++) {\n nextCols.push(colAx.itemAt(c));\n }\n\n rows = nextRows;\n cols = nextCols;\n\n emitState({ cols, rows, totalHeight, totalWidth });\n }\n\n // ─── Measurement helpers ───────────────────────────────────────────────────\n\n function recordRowMeasurement(row: number, size: number): boolean {\n if (!Number.isFinite(row)) return false;\n\n const r = Math.floor(row);\n\n if (r < 0 || r >= rowCount) return false;\n\n const s = toPositiveNumber(size, -1);\n\n if (s <= 0) return false;\n\n if (measuredRows.get(r) === s) return false;\n\n measuredRows.set(r, s);\n rowAx.markChanged(r);\n\n return true;\n }\n\n function recordColMeasurement(col: number, size: number): boolean {\n if (!Number.isFinite(col)) return false;\n\n const c = Math.floor(col);\n\n if (c < 0 || c >= colCount) return false;\n\n const s = toPositiveNumber(size, -1);\n\n if (s <= 0) return false;\n\n if (measuredCols.get(c) === s) return false;\n\n measuredCols.set(c, s);\n colAx.markChanged(c);\n\n return true;\n }\n\n /**\n * R4: Single coordinated flush for both axes.\n * Replaces the previous two independent microtask schedules.\n */\n function scheduleFlush(): void {\n if (pendingBatchBuild) return;\n\n pendingBatchBuild = true;\n queueMicrotask(() => {\n pendingBatchBuild = false;\n\n // consumeMinChangedIndex() atomically reads and resets minChangedIndex.\n // Without the reset, every subsequent flush would rebuild from index 0\n // instead of the actual earliest changed index (Bug 3).\n const fromRow = rowAx.consumeMinChangedIndex();\n\n if (fromRow !== Infinity) rowAx.rebuildFrom(fromRow);\n\n const fromCol = colAx.consumeMinChangedIndex();\n\n if (fromCol !== Infinity) colAx.rebuildFrom(fromCol);\n\n computeVisible();\n });\n }\n\n // ─── Public measurement API ────────────────────────────────────────────────\n\n function measureRow(row: number, size: number): void {\n if (disposed) return;\n\n if (recordRowMeasurement(row, size)) scheduleFlush();\n }\n\n function measureColumn(col: number, size: number): void {\n if (disposed) return;\n\n if (recordColMeasurement(col, size)) scheduleFlush();\n }\n\n /** Measure rows and columns in a single coordinated rebuild pass (R4). */\n function measureBatch(\n rowEntries: Array<{ index: number; size: number }>,\n colEntries: Array<{ index: number; size: number }>,\n ): void {\n if (disposed) return;\n\n let changed = false;\n\n for (const { index, size } of rowEntries) {\n if (recordRowMeasurement(index, size)) changed = true;\n }\n\n for (const { index, size } of colEntries) {\n if (recordColMeasurement(index, size)) changed = true;\n }\n\n if (changed) scheduleFlush();\n }\n\n function measureRowEl(row: number, el: HTMLElement): () => void {\n if (disposed) return () => {};\n\n return observeResize(ac.signal, el, (entry) => measureRow(row, entry.contentRect.height));\n }\n\n function measureColEl(col: number, el: HTMLElement): () => void {\n if (disposed) return () => {};\n\n return observeResize(ac.signal, el, (entry) => measureColumn(col, entry.contentRect.width));\n }\n\n function invalidate(): void {\n if (disposed) return;\n\n measuredRows.clear();\n measuredCols.clear();\n rowAx.rebuild(true);\n colAx.rebuild(true);\n computeVisible();\n }\n\n function refresh(): void {\n if (disposed) return;\n\n rowAx.rebuild(true);\n colAx.rebuild(true);\n computeVisible();\n }\n\n function prependRows(additionalRowCount: number): void {\n if (disposed) return;\n\n const n = toNonNegativeInt(additionalRowCount);\n\n if (n === 0) return;\n\n rowCount += n;\n rowAx.setCount(rowCount);\n rowAx.rebuild(true);\n\n const prependedHeight = rowAx.startAt(n);\n const newScrollTop = clampTop(scrollTop + prependedHeight);\n\n adapter.scrollTo(scrollLeft, newScrollTop, 'auto');\n scrollTop = newScrollTop;\n\n computeVisible();\n }\n\n function scrollToRow(row: number, opts: Pick<ScrollToCellOptions, 'behavior' | 'rowAlign'> = {}): void {\n if (disposed || rowCount === 0) return;\n\n const safeRow = Math.max(0, Math.min(Math.floor(Number.isFinite(row) ? row : 0), rowCount - 1));\n const behavior = opts.behavior ?? 'auto';\n const rowStart = rowAx.startAt(safeRow);\n const rowSize = rowAx.sizeAt(safeRow);\n const targetTop = alignOffset(\n rowStart,\n rowStart + rowSize,\n rowSize,\n scrollTop,\n viewportHeight,\n opts.rowAlign ?? 'auto',\n );\n\n if (targetTop === null) return;\n\n adapter.scrollTo(scrollLeft, clampTop(targetTop), behavior);\n }\n\n function scrollToColumn(col: number, opts: Pick<ScrollToCellOptions, 'behavior' | 'colAlign'> = {}): void {\n if (disposed || colCount === 0) return;\n\n const safeCol = Math.max(0, Math.min(Math.floor(Number.isFinite(col) ? col : 0), colCount - 1));\n const behavior = opts.behavior ?? 'auto';\n const colStart = colAx.startAt(safeCol);\n const colSize = colAx.sizeAt(safeCol);\n const targetLeft = alignOffset(\n colStart,\n colStart + colSize,\n colSize,\n scrollLeft,\n viewportWidth,\n opts.colAlign ?? 'auto',\n );\n\n if (targetLeft === null) return;\n\n adapter.scrollTo(clampLeft(targetLeft), scrollTop, behavior);\n }\n\n function scrollToCell(row: number, col: number, opts: ScrollToCellOptions = {}): void {\n if (disposed || rowCount === 0 || colCount === 0) return;\n\n const safeRow = Math.max(0, Math.min(Math.floor(Number.isFinite(row) ? row : 0), rowCount - 1));\n const safeCol = Math.max(0, Math.min(Math.floor(Number.isFinite(col) ? col : 0), colCount - 1));\n const behavior = opts.behavior ?? 'auto';\n\n const rowStart = rowAx.startAt(safeRow);\n const rowSize = rowAx.sizeAt(safeRow);\n const colStart = colAx.startAt(safeCol);\n const colSize = colAx.sizeAt(safeCol);\n\n const targetTop = alignOffset(\n rowStart,\n rowStart + rowSize,\n rowSize,\n scrollTop,\n viewportHeight,\n opts.rowAlign ?? 'auto',\n );\n const targetLeft = alignOffset(\n colStart,\n colStart + colSize,\n colSize,\n scrollLeft,\n viewportWidth,\n opts.colAlign ?? 'auto',\n );\n\n if (targetTop === null && targetLeft === null) return;\n\n adapter.scrollTo(\n targetLeft !== null ? clampLeft(targetLeft) : scrollLeft,\n targetTop !== null ? clampTop(targetTop) : scrollTop,\n behavior,\n );\n }\n\n function update(next: GridVirtualizerUpdateOptions): void {\n if (disposed) return;\n\n if (next.rowCount !== undefined) requireNonNegativeInteger(next.rowCount, 'rowCount');\n\n if (next.colCount !== undefined) requireNonNegativeInteger(next.colCount, 'colCount');\n\n if (typeof next.estimateRowSize === 'number') requirePositiveNumber(next.estimateRowSize, 'estimateRowSize');\n\n if (typeof next.estimateColSize === 'number') requirePositiveNumber(next.estimateColSize, 'estimateColSize');\n\n if (next.rowGap !== undefined) requireNonNegativeInteger(next.rowGap, 'rowGap');\n\n if (next.colGap !== undefined) requireNonNegativeInteger(next.colGap, 'colGap');\n\n if (next.overscanY !== undefined) validateOverscan(next.overscanY, 'overscanY');\n\n if (next.overscanX !== undefined) validateOverscan(next.overscanX, 'overscanX');\n\n let rebuildRows = false;\n let rebuildCols = false;\n let needsCompute = false;\n\n if (Object.hasOwn(next, 'onChange')) onChange = next.onChange;\n\n if (Object.hasOwn(next, 'onRangeChange')) onRangeChange = next.onRangeChange;\n\n if (next.rowCount !== undefined) {\n const n = next.rowCount;\n\n if (n !== rowCount) {\n rowCount = n;\n rowAx.setCount(rowCount);\n rebuildRows = true;\n }\n }\n\n if (next.colCount !== undefined) {\n const n = next.colCount;\n\n if (n !== colCount) {\n colCount = n;\n colAx.setCount(colCount);\n rebuildCols = true;\n }\n }\n\n if (next.rowGap !== undefined) {\n const n = next.rowGap;\n\n if (n !== rowAx.gap) {\n rowAx.setGap(n);\n rebuildRows = true;\n }\n }\n\n if (next.colGap !== undefined) {\n const n = next.colGap;\n\n if (n !== colAx.gap) {\n colAx.setGap(n);\n rebuildCols = true;\n }\n }\n\n if ('estimateRowSize' in next) {\n estimateRowFn = resolveEstimateFn(next.estimateRowSize, DEFAULT_ESTIMATE_SIZE);\n measuredRows.clear();\n rebuildRows = true;\n }\n\n if ('estimateColSize' in next) {\n estimateColFn = resolveEstimateFn(next.estimateColSize, DEFAULT_ESTIMATE_SIZE);\n measuredCols.clear();\n rebuildCols = true;\n }\n\n if (next.overscanY !== undefined) {\n const n = normalizeOverscan(next.overscanY, DEFAULT_OVERSCAN);\n\n if (n.start !== overscanY.start || n.end !== overscanY.end) {\n overscanY = n;\n needsCompute = true;\n }\n }\n\n if (next.overscanX !== undefined) {\n const n = normalizeOverscan(next.overscanX, DEFAULT_OVERSCAN);\n\n if (n.start !== overscanX.start || n.end !== overscanX.end) {\n overscanX = n;\n needsCompute = true;\n }\n }\n\n if (Object.hasOwn(next, 'keyboardScroll')) {\n keyboardScrollEnabled = next.keyboardScroll ?? false;\n }\n\n if (rebuildRows) rowAx.rebuild(true);\n\n if (rebuildCols) colAx.rebuild(true);\n\n if (rebuildRows || rebuildCols || needsCompute) computeVisible();\n }\n\n const ac = new AbortController();\n\n function disposeImpl(): void {\n if (disposed) return;\n\n disposed = true;\n ac.abort();\n adapter.detach();\n }\n\n // ─── Bootstrap (R9: read viewport BEFORE writing initial scroll) ───────────\n\n function handleScroll(): void {\n scrollTop = clampTop(adapter.y.readOffset());\n scrollLeft = clampLeft(adapter.x.readOffset());\n computeVisible();\n }\n\n function handleResize(): void {\n viewportHeight = adapter.y.readViewportSize();\n viewportWidth = adapter.x.readViewportSize();\n computeVisible();\n }\n\n const adapter = createScrollAdapter(target, handleScroll, handleResize);\n\n rowAx.rebuild(true);\n colAx.rebuild(true);\n\n // R9: Read viewport dimensions BEFORE writing initial scroll positions so that\n // computeVisible() sees a valid viewport on the very first call.\n viewportHeight = adapter.y.readViewportSize();\n viewportWidth = adapter.x.readViewportSize();\n\n if (options.initialScrollTop !== undefined) {\n adapter.scrollTo(options.initialScrollLeft ?? 0, clampTop(options.initialScrollTop), 'auto');\n } else if (options.initialScrollLeft !== undefined) {\n adapter.scrollTo(clampLeft(options.initialScrollLeft), 0, 'auto');\n }\n\n scrollTop = clampTop(adapter.y.readOffset());\n scrollLeft = clampLeft(adapter.x.readOffset());\n\n computeVisible();\n\n // ─── Keyboard scroll support ──────────────────────────────────────────────────\n\n if (keyboardScrollEnabled) {\n const handleKeyDown = (e: Event): void => {\n if (disposed) return;\n if (!(e instanceof KeyboardEvent)) return;\n if (rowCount === 0 || colCount === 0) return;\n\n let handled = false;\n let newTop = scrollTop;\n let newLeft = scrollLeft;\n const pageScrollSize = Math.max(viewportHeight * 0.8, viewportHeight - 100);\n const rowStepSize =\n (typeof estimateRowFn(0) === 'number' ? estimateRowFn(0) : DEFAULT_ESTIMATE_SIZE) +\n toNonNegativeInt(options.rowGap ?? 0);\n const colStepSize =\n (typeof estimateColFn(0) === 'number' ? estimateColFn(0) : DEFAULT_ESTIMATE_SIZE) +\n toNonNegativeInt(options.colGap ?? 0);\n\n switch (e.key) {\n case 'ArrowUp':\n newTop = Math.max(0, scrollTop - rowStepSize);\n handled = true;\n break;\n case 'ArrowDown':\n newTop = Math.min(rowAx.totalSize - viewportHeight, scrollTop + rowStepSize);\n handled = true;\n break;\n case 'ArrowLeft':\n newLeft = Math.max(0, scrollLeft - colStepSize);\n handled = true;\n break;\n case 'ArrowRight':\n newLeft = Math.min(colAx.totalSize - viewportWidth, scrollLeft + colStepSize);\n handled = true;\n break;\n case 'PageUp':\n newTop = Math.max(0, scrollTop - pageScrollSize);\n handled = true;\n break;\n case 'PageDown':\n newTop = Math.min(rowAx.totalSize - viewportHeight, scrollTop + pageScrollSize);\n handled = true;\n break;\n case 'Home':\n newTop = 0;\n handled = true;\n break;\n case 'End':\n newTop = Math.max(0, rowAx.totalSize - viewportHeight);\n handled = true;\n break;\n }\n\n if (handled) {\n e.preventDefault();\n adapter.scrollTo(newLeft, newTop, 'auto');\n scrollTop = clampTop(newTop);\n scrollLeft = clampLeft(newLeft);\n if (!disposed) computeVisible();\n }\n };\n\n if (typeof target === 'object' && target !== null && 'addEventListener' in target) {\n (target as EventTarget).addEventListener('keydown', handleKeyDown);\n ac.signal.addEventListener('abort', () => {\n (target as EventTarget).removeEventListener('keydown', handleKeyDown);\n });\n }\n }\n\n return {\n get cols() {\n return cols;\n },\n get disposalSignal() {\n return ac.signal;\n },\n dispose: disposeImpl,\n get disposed() {\n return disposed;\n },\n invalidate,\n measureBatch,\n measureColEl,\n measureColumn,\n measureRow,\n measureRowEl,\n prependRows,\n refresh,\n get rows() {\n return rows;\n },\n get scrollLeft() {\n return scrollLeft;\n },\n scrollToCell,\n scrollToColumn,\n get scrollTop() {\n return scrollTop;\n },\n scrollToRow,\n [Symbol.dispose]: disposeImpl,\n get totalHeight() {\n return rowAx.totalSize;\n },\n get totalWidth() {\n return colAx.totalSize;\n },\n update,\n };\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require("./_utils.cjs"),t=require("./_validation.cjs"),n=require("./virtualizer.cjs");function r(e){let t=[];for(let n=0;n<e.length;n++){let r=e[n];t.push({isHeader:!0,itemIndex:-1,label:r.label,sectionIndex:n});for(let e=0;e<r.items.length;e++)t.push({isHeader:!1,item:r.items[e],itemIndex:e,sectionIndex:n})}return t}function i(i,a){typeof a.estimateHeaderSize==`number`&&t.requirePositiveNumber(a.estimateHeaderSize,`estimateHeaderSize`),typeof a.estimateItemSize==`number`&&t.requirePositiveNumber(a.estimateItemSize,`estimateItemSize`),a.overscan!==void 0&&t.validateOverscan(a.overscan),a.scrollEndDelay!==void 0&&t.requireNonNegativeNumber(a.scrollEndDelay,`scrollEndDelay`);let o=a.sections,s=r(o);function c(t){return typeof t==`function`?e=>{let n=s[e];return t(o[n.sectionIndex],n.sectionIndex)}:e.resolveEstimateFn(typeof t==`number`?t:void 0,36)}let l=c(a.estimateHeaderSize),u=typeof a.estimateItemSize==`function`?a.estimateItemSize:null,d=u?null:e.resolveEstimateFn(typeof a.estimateItemSize==`number`?a.estimateItemSize:void 0,36),f=a.getItemKey;function p(e){let t=s[e];return t?t.isHeader?l(e):u?u(t.item,t.itemIndex,t.sectionIndex):d(e):36}function m(e){let t=s[e];return t?t.isHeader?`__header_${t.sectionIndex}`:f?f(t.item,t.itemIndex,t.sectionIndex):e:e}let h=!1,g=[],_=a.onChange,v=a.onScrollEnd,y=a.onScrollingChange,b=null;a.signal&&(b=a.signal({headers:[],items:[],stickyHeader:null,totalSize:0}));function x(e){b&&(b.value=e),_?.(e)}function S(e){let t=[],n=[],r=null;for(let r of e.items){let e=s[r.index];e&&(e.isHeader?n.push({...r,label:e.label??``,sectionIndex:e.sectionIndex}):t.push({...r,data:e.item,itemIndex:e.itemIndex,sectionIndex:e.sectionIndex}))}if(e.stickyItems.length>0){let t=e.stickyItems[0],n=s[t.index];n?.isHeader&&(r={...t,label:n.label??``,sectionIndex:n.sectionIndex})}return g=t,{headers:n,items:t,stickyHeader:r,totalSize:e.totalSize}}let C=n.createVirtualizer(i,{count:s.length,estimateSize:p,getItemKey:m,horizontal:a.horizontal,measurementCache:a.measurementCache,onChange:e=>{x(S(e))},onScrollEnd:e=>v?.(e),onScrollingChange:e=>y?.(e),overscan:a.overscan??3,scrollEndDelay:a.scrollEndDelay,sticky:e=>s[e]?.isHeader??!1}),w=new AbortController;function T(){h||(h=!0,w.abort(),C.dispose())}function E(e,t){if(e<0||e>=o.length)return-1;let n=0;for(let r=0;r<o.length;r++){if(r===e)return t===void 0?n:n+1+t;n+=1+o[r].items.length}return-1}return{get count(){return C.count},get disposalSignal(){return w.signal},dispose:T,get disposed(){return h},invalidate(){h||C.invalidate()},get isScrolling(){return C.isScrolling},get items(){return g},measure(e,t){h||C.measure(e,t)},measureBatch(e){h||C.measureBatch(e)},measureEl(e,t){return h?()=>{}:C.measureEl(e,t)},refresh(){h||C.refresh()},get scrollOffset(){return C.scrollOffset},scrollToBottom(e){h||C.scrollToBottom(e)},scrollToIndex(e,t){h||C.scrollToIndex(e,t)},scrollToItem(e,t,n={}){if(h)return;let r=E(e,t);r<0||C.scrollToIndex(r,n)},scrollToOffset(e,t){h||C.scrollToOffset(e,t)},scrollToSection(e,t={}){if(h)return;let n=E(e);n<0||C.scrollToIndex(n,t)},scrollToTop(e){h||C.scrollToTop(e)},get stickyItems(){return C.stickyItems},[Symbol.dispose]:T,get totalSize(){return C.totalSize},update(n,i){if(h)return;i&&(typeof i.estimateHeaderSize==`number`&&t.requirePositiveNumber(i.estimateHeaderSize,`estimateHeaderSize`),typeof i.estimateItemSize==`number`&&t.requirePositiveNumber(i.estimateItemSize,`estimateItemSize`),i.overscan!==void 0&&t.validateOverscan(i.overscan),i.scrollEndDelay!==void 0&&t.requireNonNegativeNumber(i.scrollEndDelay,`scrollEndDelay`),Object.hasOwn(i,`onChange`)&&(_=i.onChange),Object.hasOwn(i,`onScrollEnd`)&&(v=i.onScrollEnd),Object.hasOwn(i,`onScrollingChange`)&&(y=i.onScrollingChange),i.estimateHeaderSize!==void 0&&(l=c(i.estimateHeaderSize)),i.estimateItemSize!==void 0&&(u=typeof i.estimateItemSize==`function`?i.estimateItemSize:null,d=u?null:e.resolveEstimateFn(typeof i.estimateItemSize==`number`?i.estimateItemSize:void 0,36)),i.getItemKey!==void 0&&(f=i.getItemKey)),o=n;let a=s.length;s=r(o);let p={count:s.length};i?.measurementCache!==void 0&&(p.measurementCache=i.measurementCache),i?.overscan!==void 0&&(p.overscan=i.overscan),i?.scrollEndDelay!==void 0&&(p.scrollEndDelay=i.scrollEndDelay),s.length!==a||i?.measurementCache!==void 0||i?.overscan!==void 0||i?.scrollEndDelay!==void 0?C.update(p):C.refresh()}}}exports.createGroupedVirtualizer=i;
1
+ const e=require("./_utils.cjs"),t=require("./_validation.cjs"),n=require("./virtualizer.cjs");function r(e){let t=[];for(let n=0;n<e.length;n++){let r=e[n];t.push({isHeader:!0,itemIndex:-1,label:r.label,sectionIndex:n});for(let e=0;e<r.items.length;e++)t.push({isHeader:!1,item:r.items[e],itemIndex:e,sectionIndex:n})}return t}function i(i,a){typeof a.estimateHeaderSize==`number`&&t.requirePositiveNumber(a.estimateHeaderSize,`estimateHeaderSize`),typeof a.estimateItemSize==`number`&&t.requirePositiveNumber(a.estimateItemSize,`estimateItemSize`),a.overscan!==void 0&&t.validateOverscan(a.overscan),a.scrollEndDelay!==void 0&&t.requireNonNegativeNumber(a.scrollEndDelay,`scrollEndDelay`);let o=a.sections,s=r(o);function c(t){return typeof t==`function`?e=>{let n=s[e];return t(o[n.sectionIndex],n.sectionIndex)}:e.resolveEstimateFn(typeof t==`number`?t:void 0,36)}let l=c(a.estimateHeaderSize),u=typeof a.estimateItemSize==`function`?a.estimateItemSize:null,d=u?null:e.resolveEstimateFn(typeof a.estimateItemSize==`number`?a.estimateItemSize:void 0,36),f=a.getItemKey;function p(e){let t=s[e];return t?t.isHeader?l(e):u?u(t.item,t.itemIndex,t.sectionIndex):d(e):36}function m(e){let t=s[e];return t?t.isHeader?`__header_${t.sectionIndex}`:f?f(t.item,t.itemIndex,t.sectionIndex):e:e}let h=!1,g=[],_=a.onChange,v=a.onScrollEnd,y=a.onScrollingChange,b=null;a.toSignal&&(b=a.toSignal({headers:[],items:[],stickyHeader:null,totalSize:0}));function x(e){b&&(b.value=e),_?.(e)}function S(e){let t=[],n=[],r=null;for(let r of e.items){let e=s[r.index];e&&(e.isHeader?n.push({...r,label:e.label??``,sectionIndex:e.sectionIndex}):t.push({...r,data:e.item,itemIndex:e.itemIndex,sectionIndex:e.sectionIndex}))}if(e.stickyItems.length>0){let t=e.stickyItems[0],n=s[t.index];n?.isHeader&&(r={...t,label:n.label??``,sectionIndex:n.sectionIndex})}return g=t,{headers:n,items:t,stickyHeader:r,totalSize:e.totalSize}}let C=n.createVirtualizer(i,{count:s.length,estimateSize:p,getItemKey:m,horizontal:a.horizontal,measurementCache:a.measurementCache,onChange:e=>{x(S(e))},onScrollEnd:e=>v?.(e),onScrollingChange:e=>y?.(e),overscan:a.overscan??3,scrollEndDelay:a.scrollEndDelay,sticky:e=>s[e]?.isHeader??!1}),w=new AbortController;function T(){h||(h=!0,w.abort(),C.dispose())}function E(e,t){if(e<0||e>=o.length)return-1;let n=0;for(let r=0;r<o.length;r++){if(r===e)return t===void 0?n:n+1+t;n+=1+o[r].items.length}return-1}return{get count(){return C.count},get disposalSignal(){return w.signal},dispose:T,get disposed(){return h},invalidate(){h||C.invalidate()},get isScrolling(){return C.isScrolling},get items(){return g},measure(e,t){h||C.measure(e,t)},measureBatch(e){h||C.measureBatch(e)},measureEl(e,t){return h?()=>{}:C.measureEl(e,t)},refresh(){h||C.refresh()},get scrollOffset(){return C.scrollOffset},scrollToBottom(e){h||C.scrollToBottom(e)},scrollToIndex(e,t){h||C.scrollToIndex(e,t)},scrollToItem(e,t,n={}){if(h)return;let r=E(e,t);r<0||C.scrollToIndex(r,n)},scrollToOffset(e,t){h||C.scrollToOffset(e,t)},scrollToSection(e,t={}){if(h)return;let n=E(e);n<0||C.scrollToIndex(n,t)},scrollToTop(e){h||C.scrollToTop(e)},get stickyItems(){return C.stickyItems},[Symbol.dispose]:T,get totalSize(){return C.totalSize},update(n,i){if(h)return;i&&(typeof i.estimateHeaderSize==`number`&&t.requirePositiveNumber(i.estimateHeaderSize,`estimateHeaderSize`),typeof i.estimateItemSize==`number`&&t.requirePositiveNumber(i.estimateItemSize,`estimateItemSize`),i.overscan!==void 0&&t.validateOverscan(i.overscan),i.scrollEndDelay!==void 0&&t.requireNonNegativeNumber(i.scrollEndDelay,`scrollEndDelay`),Object.hasOwn(i,`onChange`)&&(_=i.onChange),Object.hasOwn(i,`onScrollEnd`)&&(v=i.onScrollEnd),Object.hasOwn(i,`onScrollingChange`)&&(y=i.onScrollingChange),i.estimateHeaderSize!==void 0&&(l=c(i.estimateHeaderSize)),i.estimateItemSize!==void 0&&(u=typeof i.estimateItemSize==`function`?i.estimateItemSize:null,d=u?null:e.resolveEstimateFn(typeof i.estimateItemSize==`number`?i.estimateItemSize:void 0,36)),i.getItemKey!==void 0&&(f=i.getItemKey)),o=n;let a=s.length;s=r(o);let p={count:s.length};i?.measurementCache!==void 0&&(p.measurementCache=i.measurementCache),i?.overscan!==void 0&&(p.overscan=i.overscan),i?.scrollEndDelay!==void 0&&(p.scrollEndDelay=i.scrollEndDelay),s.length!==a||i?.measurementCache!==void 0||i?.overscan!==void 0||i?.scrollEndDelay!==void 0?C.update(p):C.refresh()}}}exports.createGroupedVirtualizer=i;
2
2
  //# sourceMappingURL=grouped-virtualizer.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"grouped-virtualizer.cjs","names":[],"sources":["../src/grouped-virtualizer.ts"],"sourcesContent":["import type { Signal } from '@vielzeug/ripple';\n\nimport { resolveEstimateFn } from './_utils';\nimport { requireNonNegativeNumber, requirePositiveNumber, validateOverscan } from './_validation';\nimport {\n createVirtualizer,\n DEFAULT_ESTIMATE_SIZE,\n DEFAULT_OVERSCAN,\n type MeasurementCache,\n type Overscan,\n type ScrollTarget,\n type ScrollToIndexOptions,\n type VirtualItem,\n type VirtualizerState,\n type VirtualizerUpdateOptions,\n type VirtualKey,\n} from './virtualizer';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface GroupSection<T> {\n items: T[];\n label: string;\n}\n\nexport interface GroupVirtualItem<T> extends VirtualItem {\n /** Item data. */\n data: T;\n /** Index within the section. */\n itemIndex: number;\n sectionIndex: number;\n}\n\nexport interface GroupVirtualHeader extends VirtualItem {\n label: string;\n sectionIndex: number;\n}\n\nexport interface GroupVirtualizerState<T> {\n readonly headers: GroupVirtualHeader[];\n readonly items: Array<GroupVirtualItem<T>>;\n readonly stickyHeader: GroupVirtualHeader | null;\n readonly totalSize: number;\n}\n\n/**\n * Options accepted by `GroupVirtualizer.update()`. Overrides are applied\n * together with the new sections on the next render cycle.\n *\n * Intentionally excludes `horizontal` (axis cannot change at runtime) and\n * `initialOffset` (one-time bootstrap).\n */\nexport interface GroupVirtualizerUpdateOptions<T> {\n estimateHeaderSize?: number | ((section: GroupSection<T>, sectionIndex: number) => number);\n estimateItemSize?: number | ((item: T, itemIndex: number, sectionIndex: number) => number);\n getItemKey?: (item: T, itemIndex: number, sectionIndex: number) => VirtualKey;\n measurementCache?: MeasurementCache;\n onChange?: ((state: GroupVirtualizerState<T>) => void) | undefined;\n onScrollEnd?: ((offset: number) => void) | undefined;\n onScrollingChange?: ((isScrolling: boolean) => void) | undefined;\n overscan?: Overscan;\n scrollEndDelay?: number;\n}\n\nexport interface GroupVirtualizerOptions<T> {\n estimateHeaderSize?: number | ((section: GroupSection<T>, sectionIndex: number) => number);\n estimateItemSize?: number | ((item: T, itemIndex: number, sectionIndex: number) => number);\n getItemKey?: (item: T, itemIndex: number, sectionIndex: number) => VirtualKey;\n horizontal?: boolean;\n measurementCache?: MeasurementCache;\n /** Called after every render cycle with the new state. Replace through `update()`. */\n onChange?: (state: GroupVirtualizerState<T>) => void;\n /** Called when scrolling settles. Replace through `update()`. */\n onScrollEnd?: (offset: number) => void;\n /** Called when the scrolling state changes. Replace through `update()`. */\n onScrollingChange?: (isScrolling: boolean) => void;\n overscan?: Overscan;\n /**\n * Debounce delay (ms) for scroll-end detection. Defaults to 150.\n * See `VirtualizerOptions.scrollEndDelay` for details.\n */\n scrollEndDelay?: number;\n sections: Array<GroupSection<T>>;\n /** Optional signal factory for reactive state. */\n signal?: (init: GroupVirtualizerState<T>) => Signal<GroupVirtualizerState<T>>;\n}\n\nexport interface GroupVirtualizer<T> {\n readonly count: number;\n readonly disposalSignal: AbortSignal;\n dispose: () => void;\n readonly disposed: boolean;\n invalidate: () => void;\n /** `true` while the user is actively scrolling; `false` once scroll has settled. */\n readonly isScrolling: boolean;\n readonly items: ReadonlyArray<GroupVirtualItem<T>>;\n measure: (index: number, size: number) => void;\n measureBatch: (entries: Array<{ index: number; size: number }>) => void;\n measureEl: (index: number, el: HTMLElement) => () => void;\n refresh: () => void;\n readonly scrollOffset: number;\n scrollToBottom: (options?: { behavior?: ScrollBehavior }) => void;\n scrollToIndex: (index: number, options?: ScrollToIndexOptions) => void;\n scrollToItem: (sectionIndex: number, itemIndex: number, options?: ScrollToIndexOptions) => void;\n scrollToOffset: (offset: number, options?: { behavior?: ScrollBehavior }) => void;\n scrollToSection: (sectionIndex: number, options?: ScrollToIndexOptions) => void;\n scrollToTop: (options?: { behavior?: ScrollBehavior }) => void;\n readonly stickyItems: VirtualItem[];\n readonly totalSize: number;\n update: (sections: Array<GroupSection<T>>, opts?: GroupVirtualizerUpdateOptions<T>) => void;\n [Symbol.dispose]: () => void;\n}\n\n// ─── Flat entry map ───────────────────────────────────────────────────────────\n\ntype FlatHeader = { isHeader: true; itemIndex: -1; label: string; sectionIndex: number };\ntype FlatItem<T> = { isHeader: false; item: T; itemIndex: number; sectionIndex: number };\ntype FlatEntry<T> = FlatHeader | FlatItem<T>;\n\nfunction buildFlatEntries<T>(sections: Array<GroupSection<T>>): FlatEntry<T>[] {\n const flat: FlatEntry<T>[] = [];\n\n for (let s = 0; s < sections.length; s++) {\n const section = sections[s]!;\n\n flat.push({ isHeader: true, itemIndex: -1, label: section.label, sectionIndex: s });\n\n for (let i = 0; i < section.items.length; i++) {\n flat.push({ isHeader: false, item: section.items[i]!, itemIndex: i, sectionIndex: s });\n }\n }\n\n return flat;\n}\n\n// ─── Implementation ───────────────────────────────────────────────────────────\n\nexport function createGroupedVirtualizer<T>(\n target: ScrollTarget,\n options: GroupVirtualizerOptions<T>,\n): GroupVirtualizer<T> {\n if (typeof options.estimateHeaderSize === 'number')\n requirePositiveNumber(options.estimateHeaderSize, 'estimateHeaderSize');\n\n if (typeof options.estimateItemSize === 'number') requirePositiveNumber(options.estimateItemSize, 'estimateItemSize');\n\n if (options.overscan !== undefined) validateOverscan(options.overscan);\n\n if (options.scrollEndDelay !== undefined) requireNonNegativeNumber(options.scrollEndDelay, 'scrollEndDelay');\n\n let sections = options.sections;\n let flat = buildFlatEntries(sections);\n\n function buildEstimateHeader(\n estimateHeaderSize: GroupVirtualizerOptions<T>['estimateHeaderSize'],\n ): (index: number) => number {\n return typeof estimateHeaderSize === 'function'\n ? (index: number) => {\n const entry = flat[index]!;\n\n return (estimateHeaderSize as (section: GroupSection<T>, sectionIndex: number) => number)(\n sections[entry.sectionIndex]!,\n entry.sectionIndex,\n );\n }\n : resolveEstimateFn(\n typeof estimateHeaderSize === 'number' ? estimateHeaderSize : undefined,\n DEFAULT_ESTIMATE_SIZE,\n );\n }\n\n let estimateHeaderFn = buildEstimateHeader(options.estimateHeaderSize);\n let estimateItemUserFn: ((item: T, itemIndex: number, sectionIndex: number) => number) | null =\n typeof options.estimateItemSize === 'function' ? options.estimateItemSize : null;\n let estimateItemFixedFn: ((index: number) => number) | null = estimateItemUserFn\n ? null\n : resolveEstimateFn(\n typeof options.estimateItemSize === 'number' ? options.estimateItemSize : undefined,\n DEFAULT_ESTIMATE_SIZE,\n );\n let userGetItemKey = options.getItemKey;\n\n function estimateFn(globalIndex: number): number {\n const entry = flat[globalIndex];\n\n if (!entry) return DEFAULT_ESTIMATE_SIZE;\n\n if (entry.isHeader) return estimateHeaderFn(globalIndex);\n\n if (estimateItemUserFn) return estimateItemUserFn(entry.item, entry.itemIndex, entry.sectionIndex);\n\n return estimateItemFixedFn!(globalIndex);\n }\n\n function getItemKey(globalIndex: number): VirtualKey {\n const entry = flat[globalIndex];\n\n if (!entry) return globalIndex;\n\n if (entry.isHeader) return `__header_${entry.sectionIndex}`;\n\n if (userGetItemKey) return userGetItemKey(entry.item, entry.itemIndex, entry.sectionIndex);\n\n return globalIndex;\n }\n\n let destroyed = false;\n let lastItems: Array<GroupVirtualItem<T>> = [];\n let onChange = options.onChange;\n let onScrollEnd = options.onScrollEnd;\n let onScrollingChange = options.onScrollingChange;\n\n // Optional signal for reactive state\n let stateSignal: Signal<GroupVirtualizerState<T>> | null = null;\n if (options.signal) {\n const initialState: GroupVirtualizerState<T> = { headers: [], items: [], stickyHeader: null, totalSize: 0 };\n stateSignal = options.signal(initialState);\n }\n\n // Helper to emit state to both callback and signal\n function emitState(state: GroupVirtualizerState<T>): void {\n if (stateSignal) stateSignal.value = state;\n onChange?.(state);\n }\n\n function mapState(state: VirtualizerState): GroupVirtualizerState<T> {\n const items: Array<GroupVirtualItem<T>> = [];\n const headers: GroupVirtualHeader[] = [];\n let stickyHeader: GroupVirtualHeader | null = null;\n\n for (const vi of state.items) {\n const entry = flat[vi.index];\n\n if (!entry) continue;\n\n if (entry.isHeader) {\n headers.push({ ...vi, label: entry.label ?? '', sectionIndex: entry.sectionIndex });\n } else {\n items.push({ ...vi, data: entry.item, itemIndex: entry.itemIndex, sectionIndex: entry.sectionIndex });\n }\n }\n\n if (state.stickyItems.length > 0) {\n const svi = state.stickyItems[0]!;\n const entry = flat[svi.index];\n\n if (entry?.isHeader) {\n stickyHeader = { ...svi, label: entry.label ?? '', sectionIndex: entry.sectionIndex };\n }\n }\n\n lastItems = items;\n\n return { headers, items, stickyHeader, totalSize: state.totalSize };\n }\n\n const virtualizer = createVirtualizer(target, {\n count: flat.length,\n estimateSize: estimateFn,\n getItemKey,\n horizontal: options.horizontal,\n measurementCache: options.measurementCache,\n onChange: (state) => {\n const mapped = mapState(state);\n\n emitState(mapped);\n },\n onScrollEnd: (offset) => onScrollEnd?.(offset),\n onScrollingChange: (isScrolling) => onScrollingChange?.(isScrolling),\n overscan: options.overscan ?? DEFAULT_OVERSCAN,\n scrollEndDelay: options.scrollEndDelay,\n sticky: (i) => flat[i]?.isHeader ?? false,\n });\n\n const ac = new AbortController();\n\n function _dispose(): void {\n if (destroyed) return;\n\n destroyed = true;\n ac.abort();\n virtualizer.dispose();\n }\n\n // Build a flat-index lookup: [sectionIndex, itemIndex] → flat index.\n // Returns -1 when sectionIndex is out of range so callers can no-op safely.\n function flatIndexOf(sectionIndex: number, itemIndex?: number): number {\n if (sectionIndex < 0 || sectionIndex >= sections.length) return -1;\n\n let pos = 0;\n\n for (let s = 0; s < sections.length; s++) {\n if (s === sectionIndex) {\n if (itemIndex === undefined) return pos; // header\n\n return pos + 1 + itemIndex;\n }\n\n pos += 1 + sections[s]!.items.length;\n }\n\n return -1; // unreachable\n }\n\n return {\n get count() {\n return virtualizer.count;\n },\n get disposalSignal() {\n return ac.signal;\n },\n dispose: _dispose,\n get disposed() {\n return destroyed;\n },\n invalidate() {\n if (destroyed) return;\n\n virtualizer.invalidate();\n },\n get isScrolling() {\n return virtualizer.isScrolling;\n },\n get items() {\n return lastItems;\n },\n measure(index: number, size: number) {\n if (destroyed) return;\n\n virtualizer.measure(index, size);\n },\n measureBatch(entries: Array<{ index: number; size: number }>) {\n if (destroyed) return;\n\n virtualizer.measureBatch(entries);\n },\n measureEl(index: number, el: HTMLElement) {\n if (destroyed) return () => {};\n\n return virtualizer.measureEl(index, el);\n },\n refresh() {\n if (destroyed) return;\n\n virtualizer.refresh();\n },\n get scrollOffset() {\n return virtualizer.scrollOffset;\n },\n scrollToBottom(opts?: { behavior?: ScrollBehavior }) {\n if (destroyed) return;\n\n virtualizer.scrollToBottom(opts);\n },\n scrollToIndex(index: number, opts?: ScrollToIndexOptions) {\n if (destroyed) return;\n\n virtualizer.scrollToIndex(index, opts);\n },\n scrollToItem(sectionIndex, itemIndex, opts = {}) {\n if (destroyed) return;\n\n const globalIndex = flatIndexOf(sectionIndex, itemIndex);\n\n if (globalIndex < 0) return;\n\n virtualizer.scrollToIndex(globalIndex, opts);\n },\n scrollToOffset(offset: number, opts?: { behavior?: ScrollBehavior }) {\n if (destroyed) return;\n\n virtualizer.scrollToOffset(offset, opts);\n },\n scrollToSection(sectionIndex, opts = {}) {\n if (destroyed) return;\n\n const globalIndex = flatIndexOf(sectionIndex);\n\n if (globalIndex < 0) return;\n\n virtualizer.scrollToIndex(globalIndex, opts);\n },\n scrollToTop(opts?: { behavior?: ScrollBehavior }) {\n if (destroyed) return;\n\n virtualizer.scrollToTop(opts);\n },\n get stickyItems() {\n return virtualizer.stickyItems;\n },\n [Symbol.dispose]: _dispose,\n get totalSize() {\n return virtualizer.totalSize;\n },\n /**\n * Replace all sections. Closures for estimateFn and getItemKey already\n * capture the live `flat`/`sections` references — only count is passed to\n * the underlying virtualizer to avoid discarding measured sizes on every\n * data refresh. `refresh()` rebuilds offsets while preserving the cache.\n */\n update(nextSections, opts?: GroupVirtualizerUpdateOptions<T>) {\n if (destroyed) return;\n\n if (opts) {\n if (typeof opts.estimateHeaderSize === 'number')\n requirePositiveNumber(opts.estimateHeaderSize, 'estimateHeaderSize');\n\n if (typeof opts.estimateItemSize === 'number') requirePositiveNumber(opts.estimateItemSize, 'estimateItemSize');\n\n if (opts.overscan !== undefined) validateOverscan(opts.overscan);\n\n if (opts.scrollEndDelay !== undefined) requireNonNegativeNumber(opts.scrollEndDelay, 'scrollEndDelay');\n\n if (Object.hasOwn(opts, 'onChange')) onChange = opts.onChange;\n\n if (Object.hasOwn(opts, 'onScrollEnd')) onScrollEnd = opts.onScrollEnd;\n\n if (Object.hasOwn(opts, 'onScrollingChange')) onScrollingChange = opts.onScrollingChange;\n\n // Apply option overrides before rebuilding flat entries.\n if (opts.estimateHeaderSize !== undefined) {\n estimateHeaderFn = buildEstimateHeader(opts.estimateHeaderSize);\n }\n\n if (opts.estimateItemSize !== undefined) {\n estimateItemUserFn = typeof opts.estimateItemSize === 'function' ? opts.estimateItemSize : null;\n estimateItemFixedFn = estimateItemUserFn\n ? null\n : resolveEstimateFn(\n typeof opts.estimateItemSize === 'number' ? opts.estimateItemSize : undefined,\n DEFAULT_ESTIMATE_SIZE,\n );\n }\n\n if (opts.getItemKey !== undefined) userGetItemKey = opts.getItemKey;\n }\n\n sections = nextSections;\n\n const prevCount = flat.length;\n\n flat = buildFlatEntries(sections);\n\n const virtUpdateOpts: VirtualizerUpdateOptions = { count: flat.length };\n\n if (opts?.measurementCache !== undefined) virtUpdateOpts.measurementCache = opts.measurementCache;\n\n if (opts?.overscan !== undefined) virtUpdateOpts.overscan = opts.overscan;\n\n if (opts?.scrollEndDelay !== undefined) virtUpdateOpts.scrollEndDelay = opts.scrollEndDelay;\n\n if (\n flat.length !== prevCount ||\n opts?.measurementCache !== undefined ||\n opts?.overscan !== undefined ||\n opts?.scrollEndDelay !== undefined\n ) {\n virtualizer.update(virtUpdateOpts);\n } else {\n virtualizer.refresh();\n }\n },\n };\n}\n"],"mappings":"8FAuHA,SAAS,EAAoB,EAAkD,CAC7E,IAAM,EAAuB,CAAC,EAE9B,IAAK,IAAI,EAAI,EAAG,EAAI,EAAS,OAAQ,IAAK,CACxC,IAAM,EAAU,EAAS,GAEzB,EAAK,KAAK,CAAE,SAAU,GAAM,UAAW,GAAI,MAAO,EAAQ,MAAO,aAAc,CAAE,CAAC,EAElF,IAAK,IAAI,EAAI,EAAG,EAAI,EAAQ,MAAM,OAAQ,IACxC,EAAK,KAAK,CAAE,SAAU,GAAO,KAAM,EAAQ,MAAM,GAAK,UAAW,EAAG,aAAc,CAAE,CAAC,CAEzF,CAEA,OAAO,CACT,CAIA,SAAgB,EACd,EACA,EACqB,CACjB,OAAO,EAAQ,oBAAuB,UACxC,EAAA,sBAAsB,EAAQ,mBAAoB,oBAAoB,EAEpE,OAAO,EAAQ,kBAAqB,UAAU,EAAA,sBAAsB,EAAQ,iBAAkB,kBAAkB,EAEhH,EAAQ,WAAa,IAAA,IAAW,EAAA,iBAAiB,EAAQ,QAAQ,EAEjE,EAAQ,iBAAmB,IAAA,IAAW,EAAA,yBAAyB,EAAQ,eAAgB,gBAAgB,EAE3G,IAAI,EAAW,EAAQ,SACnB,EAAO,EAAiB,CAAQ,EAEpC,SAAS,EACP,EAC2B,CAC3B,OAAO,OAAO,GAAuB,WAChC,GAAkB,CACjB,IAAM,EAAQ,EAAK,GAEnB,OAAQ,EACN,EAAS,EAAM,cACf,EAAM,YACR,CACF,EACA,EAAA,kBACE,OAAO,GAAuB,SAAW,EAAqB,IAAA,GAAA,EAEhE,CACN,CAEA,IAAI,EAAmB,EAAoB,EAAQ,kBAAkB,EACjE,EACF,OAAO,EAAQ,kBAAqB,WAAa,EAAQ,iBAAmB,KAC1E,EAA0D,EAC1D,KACA,EAAA,kBACE,OAAO,EAAQ,kBAAqB,SAAW,EAAQ,iBAAmB,IAAA,GAAA,EAE5E,EACA,EAAiB,EAAQ,WAE7B,SAAS,EAAW,EAA6B,CAC/C,IAAM,EAAQ,EAAK,GAQnB,OANK,EAED,EAAM,SAAiB,EAAiB,CAAW,EAEnD,EAA2B,EAAmB,EAAM,KAAM,EAAM,UAAW,EAAM,YAAY,EAE1F,EAAqB,CAAW,EAN3B,EAOd,CAEA,SAAS,EAAW,EAAiC,CACnD,IAAM,EAAQ,EAAK,GAQnB,OANK,EAED,EAAM,SAAiB,YAAY,EAAM,eAEzC,EAAuB,EAAe,EAAM,KAAM,EAAM,UAAW,EAAM,YAAY,EAElF,EANY,CAOrB,CAEA,IAAI,EAAY,GACZ,EAAwC,CAAC,EACzC,EAAW,EAAQ,SACnB,EAAc,EAAQ,YACtB,EAAoB,EAAQ,kBAG5B,EAAuD,KACvD,EAAQ,SAEV,EAAc,EAAQ,OAAO,CADoB,QAAS,CAAC,EAAG,MAAO,CAAC,EAAG,aAAc,KAAM,UAAW,CAC3E,CAAY,GAI3C,SAAS,EAAU,EAAuC,CACpD,IAAa,EAAY,MAAQ,GACrC,IAAW,CAAK,CAClB,CAEA,SAAS,EAAS,EAAmD,CACnE,IAAM,EAAoC,CAAC,EACrC,EAAgC,CAAC,EACnC,EAA0C,KAE9C,IAAK,IAAM,KAAM,EAAM,MAAO,CAC5B,IAAM,EAAQ,EAAK,EAAG,OAEjB,IAED,EAAM,SACR,EAAQ,KAAK,CAAE,GAAG,EAAI,MAAO,EAAM,OAAS,GAAI,aAAc,EAAM,YAAa,CAAC,EAElF,EAAM,KAAK,CAAE,GAAG,EAAI,KAAM,EAAM,KAAM,UAAW,EAAM,UAAW,aAAc,EAAM,YAAa,CAAC,EAExG,CAEA,GAAI,EAAM,YAAY,OAAS,EAAG,CAChC,IAAM,EAAM,EAAM,YAAY,GACxB,EAAQ,EAAK,EAAI,OAEnB,GAAO,WACT,EAAe,CAAE,GAAG,EAAK,MAAO,EAAM,OAAS,GAAI,aAAc,EAAM,YAAa,EAExF,CAIA,MAFA,GAAY,EAEL,CAAE,UAAS,QAAO,eAAc,UAAW,EAAM,SAAU,CACpE,CAEA,IAAM,EAAc,EAAA,kBAAkB,EAAQ,CAC5C,MAAO,EAAK,OACZ,aAAc,EACd,aACA,WAAY,EAAQ,WACpB,iBAAkB,EAAQ,iBAC1B,SAAW,GAAU,CAGnB,EAFe,EAAS,CAEd,CAAM,CAClB,EACA,YAAc,GAAW,IAAc,CAAM,EAC7C,kBAAoB,GAAgB,IAAoB,CAAW,EACnE,SAAU,EAAQ,UAAA,EAClB,eAAgB,EAAQ,eACxB,OAAS,GAAM,EAAK,EAAE,EAAE,UAAY,EACtC,CAAC,EAEK,EAAK,IAAI,gBAEf,SAAS,GAAiB,CACpB,IAEJ,EAAY,GACZ,EAAG,MAAM,EACT,EAAY,QAAQ,EACtB,CAIA,SAAS,EAAY,EAAsB,EAA4B,CACrE,GAAI,EAAe,GAAK,GAAgB,EAAS,OAAQ,MAAO,GAEhE,IAAI,EAAM,EAEV,IAAK,IAAI,EAAI,EAAG,EAAI,EAAS,OAAQ,IAAK,CACxC,GAAI,IAAM,EAGR,OAFI,IAAc,IAAA,GAAkB,EAE7B,EAAM,EAAI,EAGnB,GAAO,EAAI,EAAS,EAAE,CAAE,MAAM,MAChC,CAEA,MAAO,EACT,CAEA,MAAO,CACL,IAAI,OAAQ,CACV,OAAO,EAAY,KACrB,EACA,IAAI,gBAAiB,CACnB,OAAO,EAAG,MACZ,EACA,QAAS,EACT,IAAI,UAAW,CACb,OAAO,CACT,EACA,YAAa,CACP,GAEJ,EAAY,WAAW,CACzB,EACA,IAAI,aAAc,CAChB,OAAO,EAAY,WACrB,EACA,IAAI,OAAQ,CACV,OAAO,CACT,EACA,QAAQ,EAAe,EAAc,CAC/B,GAEJ,EAAY,QAAQ,EAAO,CAAI,CACjC,EACA,aAAa,EAAiD,CACxD,GAEJ,EAAY,aAAa,CAAO,CAClC,EACA,UAAU,EAAe,EAAiB,CAGxC,OAFI,MAAwB,CAAC,EAEtB,EAAY,UAAU,EAAO,CAAE,CACxC,EACA,SAAU,CACJ,GAEJ,EAAY,QAAQ,CACtB,EACA,IAAI,cAAe,CACjB,OAAO,EAAY,YACrB,EACA,eAAe,EAAsC,CAC/C,GAEJ,EAAY,eAAe,CAAI,CACjC,EACA,cAAc,EAAe,EAA6B,CACpD,GAEJ,EAAY,cAAc,EAAO,CAAI,CACvC,EACA,aAAa,EAAc,EAAW,EAAO,CAAC,EAAG,CAC/C,GAAI,EAAW,OAEf,IAAM,EAAc,EAAY,EAAc,CAAS,EAEnD,EAAc,GAElB,EAAY,cAAc,EAAa,CAAI,CAC7C,EACA,eAAe,EAAgB,EAAsC,CAC/D,GAEJ,EAAY,eAAe,EAAQ,CAAI,CACzC,EACA,gBAAgB,EAAc,EAAO,CAAC,EAAG,CACvC,GAAI,EAAW,OAEf,IAAM,EAAc,EAAY,CAAY,EAExC,EAAc,GAElB,EAAY,cAAc,EAAa,CAAI,CAC7C,EACA,YAAY,EAAsC,CAC5C,GAEJ,EAAY,YAAY,CAAI,CAC9B,EACA,IAAI,aAAc,CAChB,OAAO,EAAY,WACrB,GACC,OAAO,SAAU,EAClB,IAAI,WAAY,CACd,OAAO,EAAY,SACrB,EAOA,OAAO,EAAc,EAAyC,CAC5D,GAAI,EAAW,OAEX,IACE,OAAO,EAAK,oBAAuB,UACrC,EAAA,sBAAsB,EAAK,mBAAoB,oBAAoB,EAEjE,OAAO,EAAK,kBAAqB,UAAU,EAAA,sBAAsB,EAAK,iBAAkB,kBAAkB,EAE1G,EAAK,WAAa,IAAA,IAAW,EAAA,iBAAiB,EAAK,QAAQ,EAE3D,EAAK,iBAAmB,IAAA,IAAW,EAAA,yBAAyB,EAAK,eAAgB,gBAAgB,EAEjG,OAAO,OAAO,EAAM,UAAU,IAAG,EAAW,EAAK,UAEjD,OAAO,OAAO,EAAM,aAAa,IAAG,EAAc,EAAK,aAEvD,OAAO,OAAO,EAAM,mBAAmB,IAAG,EAAoB,EAAK,mBAGnE,EAAK,qBAAuB,IAAA,KAC9B,EAAmB,EAAoB,EAAK,kBAAkB,GAG5D,EAAK,mBAAqB,IAAA,KAC5B,EAAqB,OAAO,EAAK,kBAAqB,WAAa,EAAK,iBAAmB,KAC3F,EAAsB,EAClB,KACA,EAAA,kBACE,OAAO,EAAK,kBAAqB,SAAW,EAAK,iBAAmB,IAAA,GAAA,EAEtE,GAGF,EAAK,aAAe,IAAA,KAAW,EAAiB,EAAK,aAG3D,EAAW,EAEX,IAAM,EAAY,EAAK,OAEvB,EAAO,EAAiB,CAAQ,EAEhC,IAAM,EAA2C,CAAE,MAAO,EAAK,MAAO,EAElE,GAAM,mBAAqB,IAAA,KAAW,EAAe,iBAAmB,EAAK,kBAE7E,GAAM,WAAa,IAAA,KAAW,EAAe,SAAW,EAAK,UAE7D,GAAM,iBAAmB,IAAA,KAAW,EAAe,eAAiB,EAAK,gBAG3E,EAAK,SAAW,GAChB,GAAM,mBAAqB,IAAA,IAC3B,GAAM,WAAa,IAAA,IACnB,GAAM,iBAAmB,IAAA,GAEzB,EAAY,OAAO,CAAc,EAEjC,EAAY,QAAQ,CAExB,CACF,CACF"}
1
+ {"version":3,"file":"grouped-virtualizer.cjs","names":[],"sources":["../src/grouped-virtualizer.ts"],"sourcesContent":["import type { Signal } from '@vielzeug/ripple';\n\nimport { resolveEstimateFn } from './_utils';\nimport { requireNonNegativeNumber, requirePositiveNumber, validateOverscan } from './_validation';\nimport {\n createVirtualizer,\n DEFAULT_ESTIMATE_SIZE,\n DEFAULT_OVERSCAN,\n type MeasurementCache,\n type Overscan,\n type ScrollTarget,\n type ScrollToIndexOptions,\n type VirtualItem,\n type VirtualizerState,\n type VirtualizerUpdateOptions,\n type VirtualKey,\n} from './virtualizer';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface GroupSection<T> {\n items: T[];\n label: string;\n}\n\nexport interface GroupVirtualItem<T> extends VirtualItem {\n /** Item data. */\n data: T;\n /** Index within the section. */\n itemIndex: number;\n sectionIndex: number;\n}\n\nexport interface GroupVirtualHeader extends VirtualItem {\n label: string;\n sectionIndex: number;\n}\n\nexport interface GroupVirtualizerState<T> {\n readonly headers: GroupVirtualHeader[];\n readonly items: Array<GroupVirtualItem<T>>;\n readonly stickyHeader: GroupVirtualHeader | null;\n readonly totalSize: number;\n}\n\n/**\n * Options accepted by `GroupVirtualizer.update()`. Overrides are applied\n * together with the new sections on the next render cycle.\n *\n * Intentionally excludes `horizontal` (axis cannot change at runtime) and\n * `initialOffset` (one-time bootstrap).\n */\nexport interface GroupVirtualizerUpdateOptions<T> {\n estimateHeaderSize?: number | ((section: GroupSection<T>, sectionIndex: number) => number);\n estimateItemSize?: number | ((item: T, itemIndex: number, sectionIndex: number) => number);\n getItemKey?: (item: T, itemIndex: number, sectionIndex: number) => VirtualKey;\n measurementCache?: MeasurementCache;\n onChange?: ((state: GroupVirtualizerState<T>) => void) | undefined;\n onScrollEnd?: ((offset: number) => void) | undefined;\n onScrollingChange?: ((isScrolling: boolean) => void) | undefined;\n overscan?: Overscan;\n scrollEndDelay?: number;\n}\n\nexport interface GroupVirtualizerOptions<T> {\n estimateHeaderSize?: number | ((section: GroupSection<T>, sectionIndex: number) => number);\n estimateItemSize?: number | ((item: T, itemIndex: number, sectionIndex: number) => number);\n getItemKey?: (item: T, itemIndex: number, sectionIndex: number) => VirtualKey;\n horizontal?: boolean;\n measurementCache?: MeasurementCache;\n /** Called after every render cycle with the new state. Replace through `update()`. */\n onChange?: (state: GroupVirtualizerState<T>) => void;\n /** Called when scrolling settles. Replace through `update()`. */\n onScrollEnd?: (offset: number) => void;\n /** Called when the scrolling state changes. Replace through `update()`. */\n onScrollingChange?: (isScrolling: boolean) => void;\n overscan?: Overscan;\n /**\n * Debounce delay (ms) for scroll-end detection. Defaults to 150.\n * See `VirtualizerOptions.scrollEndDelay` for details.\n */\n scrollEndDelay?: number;\n sections: Array<GroupSection<T>>;\n /** Optional signal factory for reactive state. */\n toSignal?: (init: GroupVirtualizerState<T>) => Signal<GroupVirtualizerState<T>>;\n}\n\nexport interface GroupVirtualizer<T> {\n readonly count: number;\n readonly disposalSignal: AbortSignal;\n dispose: () => void;\n readonly disposed: boolean;\n invalidate: () => void;\n /** `true` while the user is actively scrolling; `false` once scroll has settled. */\n readonly isScrolling: boolean;\n readonly items: ReadonlyArray<GroupVirtualItem<T>>;\n measure: (index: number, size: number) => void;\n measureBatch: (entries: Array<{ index: number; size: number }>) => void;\n measureEl: (index: number, el: HTMLElement) => () => void;\n refresh: () => void;\n readonly scrollOffset: number;\n scrollToBottom: (options?: { behavior?: ScrollBehavior }) => void;\n scrollToIndex: (index: number, options?: ScrollToIndexOptions) => void;\n scrollToItem: (sectionIndex: number, itemIndex: number, options?: ScrollToIndexOptions) => void;\n scrollToOffset: (offset: number, options?: { behavior?: ScrollBehavior }) => void;\n scrollToSection: (sectionIndex: number, options?: ScrollToIndexOptions) => void;\n scrollToTop: (options?: { behavior?: ScrollBehavior }) => void;\n readonly stickyItems: VirtualItem[];\n readonly totalSize: number;\n update: (sections: Array<GroupSection<T>>, opts?: GroupVirtualizerUpdateOptions<T>) => void;\n [Symbol.dispose]: () => void;\n}\n\n// ─── Flat entry map ───────────────────────────────────────────────────────────\n\ntype FlatHeader = { isHeader: true; itemIndex: -1; label: string; sectionIndex: number };\ntype FlatItem<T> = { isHeader: false; item: T; itemIndex: number; sectionIndex: number };\ntype FlatEntry<T> = FlatHeader | FlatItem<T>;\n\nfunction buildFlatEntries<T>(sections: Array<GroupSection<T>>): FlatEntry<T>[] {\n const flat: FlatEntry<T>[] = [];\n\n for (let s = 0; s < sections.length; s++) {\n const section = sections[s]!;\n\n flat.push({ isHeader: true, itemIndex: -1, label: section.label, sectionIndex: s });\n\n for (let i = 0; i < section.items.length; i++) {\n flat.push({ isHeader: false, item: section.items[i]!, itemIndex: i, sectionIndex: s });\n }\n }\n\n return flat;\n}\n\n// ─── Implementation ───────────────────────────────────────────────────────────\n\nexport function createGroupedVirtualizer<T>(\n target: ScrollTarget,\n options: GroupVirtualizerOptions<T>,\n): GroupVirtualizer<T> {\n if (typeof options.estimateHeaderSize === 'number')\n requirePositiveNumber(options.estimateHeaderSize, 'estimateHeaderSize');\n\n if (typeof options.estimateItemSize === 'number') requirePositiveNumber(options.estimateItemSize, 'estimateItemSize');\n\n if (options.overscan !== undefined) validateOverscan(options.overscan);\n\n if (options.scrollEndDelay !== undefined) requireNonNegativeNumber(options.scrollEndDelay, 'scrollEndDelay');\n\n let sections = options.sections;\n let flat = buildFlatEntries(sections);\n\n function buildEstimateHeader(\n estimateHeaderSize: GroupVirtualizerOptions<T>['estimateHeaderSize'],\n ): (index: number) => number {\n return typeof estimateHeaderSize === 'function'\n ? (index: number) => {\n const entry = flat[index]!;\n\n return (estimateHeaderSize as (section: GroupSection<T>, sectionIndex: number) => number)(\n sections[entry.sectionIndex]!,\n entry.sectionIndex,\n );\n }\n : resolveEstimateFn(\n typeof estimateHeaderSize === 'number' ? estimateHeaderSize : undefined,\n DEFAULT_ESTIMATE_SIZE,\n );\n }\n\n let estimateHeaderFn = buildEstimateHeader(options.estimateHeaderSize);\n let estimateItemUserFn: ((item: T, itemIndex: number, sectionIndex: number) => number) | null =\n typeof options.estimateItemSize === 'function' ? options.estimateItemSize : null;\n let estimateItemFixedFn: ((index: number) => number) | null = estimateItemUserFn\n ? null\n : resolveEstimateFn(\n typeof options.estimateItemSize === 'number' ? options.estimateItemSize : undefined,\n DEFAULT_ESTIMATE_SIZE,\n );\n let userGetItemKey = options.getItemKey;\n\n function estimateFn(globalIndex: number): number {\n const entry = flat[globalIndex];\n\n if (!entry) return DEFAULT_ESTIMATE_SIZE;\n\n if (entry.isHeader) return estimateHeaderFn(globalIndex);\n\n if (estimateItemUserFn) return estimateItemUserFn(entry.item, entry.itemIndex, entry.sectionIndex);\n\n return estimateItemFixedFn!(globalIndex);\n }\n\n function getItemKey(globalIndex: number): VirtualKey {\n const entry = flat[globalIndex];\n\n if (!entry) return globalIndex;\n\n if (entry.isHeader) return `__header_${entry.sectionIndex}`;\n\n if (userGetItemKey) return userGetItemKey(entry.item, entry.itemIndex, entry.sectionIndex);\n\n return globalIndex;\n }\n\n let disposed = false;\n let lastItems: Array<GroupVirtualItem<T>> = [];\n let onChange = options.onChange;\n let onScrollEnd = options.onScrollEnd;\n let onScrollingChange = options.onScrollingChange;\n\n // Optional signal for reactive state\n let stateSignal: Signal<GroupVirtualizerState<T>> | null = null;\n if (options.toSignal) {\n const initialState: GroupVirtualizerState<T> = { headers: [], items: [], stickyHeader: null, totalSize: 0 };\n stateSignal = options.toSignal(initialState);\n }\n\n // Helper to emit state to both callback and signal\n function emitState(state: GroupVirtualizerState<T>): void {\n if (stateSignal) stateSignal.value = state;\n onChange?.(state);\n }\n\n function mapState(state: VirtualizerState): GroupVirtualizerState<T> {\n const items: Array<GroupVirtualItem<T>> = [];\n const headers: GroupVirtualHeader[] = [];\n let stickyHeader: GroupVirtualHeader | null = null;\n\n for (const vi of state.items) {\n const entry = flat[vi.index];\n\n if (!entry) continue;\n\n if (entry.isHeader) {\n headers.push({ ...vi, label: entry.label ?? '', sectionIndex: entry.sectionIndex });\n } else {\n items.push({ ...vi, data: entry.item, itemIndex: entry.itemIndex, sectionIndex: entry.sectionIndex });\n }\n }\n\n if (state.stickyItems.length > 0) {\n const svi = state.stickyItems[0]!;\n const entry = flat[svi.index];\n\n if (entry?.isHeader) {\n stickyHeader = { ...svi, label: entry.label ?? '', sectionIndex: entry.sectionIndex };\n }\n }\n\n lastItems = items;\n\n return { headers, items, stickyHeader, totalSize: state.totalSize };\n }\n\n const virtualizer = createVirtualizer(target, {\n count: flat.length,\n estimateSize: estimateFn,\n getItemKey,\n horizontal: options.horizontal,\n measurementCache: options.measurementCache,\n onChange: (state) => {\n const mapped = mapState(state);\n\n emitState(mapped);\n },\n onScrollEnd: (offset) => onScrollEnd?.(offset),\n onScrollingChange: (isScrolling) => onScrollingChange?.(isScrolling),\n overscan: options.overscan ?? DEFAULT_OVERSCAN,\n scrollEndDelay: options.scrollEndDelay,\n sticky: (i) => flat[i]?.isHeader ?? false,\n });\n\n const ac = new AbortController();\n\n function _dispose(): void {\n if (disposed) return;\n\n disposed = true;\n ac.abort();\n virtualizer.dispose();\n }\n\n // Build a flat-index lookup: [sectionIndex, itemIndex] → flat index.\n // Returns -1 when sectionIndex is out of range so callers can no-op safely.\n function flatIndexOf(sectionIndex: number, itemIndex?: number): number {\n if (sectionIndex < 0 || sectionIndex >= sections.length) return -1;\n\n let pos = 0;\n\n for (let s = 0; s < sections.length; s++) {\n if (s === sectionIndex) {\n if (itemIndex === undefined) return pos; // header\n\n return pos + 1 + itemIndex;\n }\n\n pos += 1 + sections[s]!.items.length;\n }\n\n return -1; // unreachable\n }\n\n return {\n get count() {\n return virtualizer.count;\n },\n get disposalSignal() {\n return ac.signal;\n },\n dispose: _dispose,\n get disposed() {\n return disposed;\n },\n invalidate() {\n if (disposed) return;\n\n virtualizer.invalidate();\n },\n get isScrolling() {\n return virtualizer.isScrolling;\n },\n get items() {\n return lastItems;\n },\n measure(index: number, size: number) {\n if (disposed) return;\n\n virtualizer.measure(index, size);\n },\n measureBatch(entries: Array<{ index: number; size: number }>) {\n if (disposed) return;\n\n virtualizer.measureBatch(entries);\n },\n measureEl(index: number, el: HTMLElement) {\n if (disposed) return () => {};\n\n return virtualizer.measureEl(index, el);\n },\n refresh() {\n if (disposed) return;\n\n virtualizer.refresh();\n },\n get scrollOffset() {\n return virtualizer.scrollOffset;\n },\n scrollToBottom(opts?: { behavior?: ScrollBehavior }) {\n if (disposed) return;\n\n virtualizer.scrollToBottom(opts);\n },\n scrollToIndex(index: number, opts?: ScrollToIndexOptions) {\n if (disposed) return;\n\n virtualizer.scrollToIndex(index, opts);\n },\n scrollToItem(sectionIndex, itemIndex, opts = {}) {\n if (disposed) return;\n\n const globalIndex = flatIndexOf(sectionIndex, itemIndex);\n\n if (globalIndex < 0) return;\n\n virtualizer.scrollToIndex(globalIndex, opts);\n },\n scrollToOffset(offset: number, opts?: { behavior?: ScrollBehavior }) {\n if (disposed) return;\n\n virtualizer.scrollToOffset(offset, opts);\n },\n scrollToSection(sectionIndex, opts = {}) {\n if (disposed) return;\n\n const globalIndex = flatIndexOf(sectionIndex);\n\n if (globalIndex < 0) return;\n\n virtualizer.scrollToIndex(globalIndex, opts);\n },\n scrollToTop(opts?: { behavior?: ScrollBehavior }) {\n if (disposed) return;\n\n virtualizer.scrollToTop(opts);\n },\n get stickyItems() {\n return virtualizer.stickyItems;\n },\n [Symbol.dispose]: _dispose,\n get totalSize() {\n return virtualizer.totalSize;\n },\n /**\n * Replace all sections. Closures for estimateFn and getItemKey already\n * capture the live `flat`/`sections` references — only count is passed to\n * the underlying virtualizer to avoid discarding measured sizes on every\n * data refresh. `refresh()` rebuilds offsets while preserving the cache.\n */\n update(nextSections, opts?: GroupVirtualizerUpdateOptions<T>) {\n if (disposed) return;\n\n if (opts) {\n if (typeof opts.estimateHeaderSize === 'number')\n requirePositiveNumber(opts.estimateHeaderSize, 'estimateHeaderSize');\n\n if (typeof opts.estimateItemSize === 'number') requirePositiveNumber(opts.estimateItemSize, 'estimateItemSize');\n\n if (opts.overscan !== undefined) validateOverscan(opts.overscan);\n\n if (opts.scrollEndDelay !== undefined) requireNonNegativeNumber(opts.scrollEndDelay, 'scrollEndDelay');\n\n if (Object.hasOwn(opts, 'onChange')) onChange = opts.onChange;\n\n if (Object.hasOwn(opts, 'onScrollEnd')) onScrollEnd = opts.onScrollEnd;\n\n if (Object.hasOwn(opts, 'onScrollingChange')) onScrollingChange = opts.onScrollingChange;\n\n // Apply option overrides before rebuilding flat entries.\n if (opts.estimateHeaderSize !== undefined) {\n estimateHeaderFn = buildEstimateHeader(opts.estimateHeaderSize);\n }\n\n if (opts.estimateItemSize !== undefined) {\n estimateItemUserFn = typeof opts.estimateItemSize === 'function' ? opts.estimateItemSize : null;\n estimateItemFixedFn = estimateItemUserFn\n ? null\n : resolveEstimateFn(\n typeof opts.estimateItemSize === 'number' ? opts.estimateItemSize : undefined,\n DEFAULT_ESTIMATE_SIZE,\n );\n }\n\n if (opts.getItemKey !== undefined) userGetItemKey = opts.getItemKey;\n }\n\n sections = nextSections;\n\n const prevCount = flat.length;\n\n flat = buildFlatEntries(sections);\n\n const virtUpdateOpts: VirtualizerUpdateOptions = { count: flat.length };\n\n if (opts?.measurementCache !== undefined) virtUpdateOpts.measurementCache = opts.measurementCache;\n\n if (opts?.overscan !== undefined) virtUpdateOpts.overscan = opts.overscan;\n\n if (opts?.scrollEndDelay !== undefined) virtUpdateOpts.scrollEndDelay = opts.scrollEndDelay;\n\n if (\n flat.length !== prevCount ||\n opts?.measurementCache !== undefined ||\n opts?.overscan !== undefined ||\n opts?.scrollEndDelay !== undefined\n ) {\n virtualizer.update(virtUpdateOpts);\n } else {\n virtualizer.refresh();\n }\n },\n };\n}\n"],"mappings":"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"}
@@ -59,7 +59,7 @@ export interface GroupVirtualizerOptions<T> {
59
59
  scrollEndDelay?: number;
60
60
  sections: Array<GroupSection<T>>;
61
61
  /** Optional signal factory for reactive state. */
62
- signal?: (init: GroupVirtualizerState<T>) => Signal<GroupVirtualizerState<T>>;
62
+ toSignal?: (init: GroupVirtualizerState<T>) => Signal<GroupVirtualizerState<T>>;
63
63
  }
64
64
  export interface GroupVirtualizer<T> {
65
65
  readonly count: number;
@@ -1 +1 @@
1
- {"version":3,"file":"grouped-virtualizer.d.ts","sourceRoot":"","sources":["../src/grouped-virtualizer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"grouped-virtualizer.d.ts","sourceRoot":"","sources":["../src/grouped-virtualizer.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{resolveEstimateFn as e}from"./_utils.js";import{requireNonNegativeNumber as t,requirePositiveNumber as n,validateOverscan as r}from"./_validation.js";import{createVirtualizer as i}from"./virtualizer.js";function a(e){let t=[];for(let n=0;n<e.length;n++){let r=e[n];t.push({isHeader:!0,itemIndex:-1,label:r.label,sectionIndex:n});for(let e=0;e<r.items.length;e++)t.push({isHeader:!1,item:r.items[e],itemIndex:e,sectionIndex:n})}return t}function o(o,s){typeof s.estimateHeaderSize==`number`&&n(s.estimateHeaderSize,`estimateHeaderSize`),typeof s.estimateItemSize==`number`&&n(s.estimateItemSize,`estimateItemSize`),s.overscan!==void 0&&r(s.overscan),s.scrollEndDelay!==void 0&&t(s.scrollEndDelay,`scrollEndDelay`);let c=s.sections,l=a(c);function u(t){return typeof t==`function`?e=>{let n=l[e];return t(c[n.sectionIndex],n.sectionIndex)}:e(typeof t==`number`?t:void 0,36)}let d=u(s.estimateHeaderSize),f=typeof s.estimateItemSize==`function`?s.estimateItemSize:null,p=f?null:e(typeof s.estimateItemSize==`number`?s.estimateItemSize:void 0,36),m=s.getItemKey;function h(e){let t=l[e];return t?t.isHeader?d(e):f?f(t.item,t.itemIndex,t.sectionIndex):p(e):36}function g(e){let t=l[e];return t?t.isHeader?`__header_${t.sectionIndex}`:m?m(t.item,t.itemIndex,t.sectionIndex):e:e}let _=!1,v=[],y=s.onChange,b=s.onScrollEnd,x=s.onScrollingChange,S=null;s.signal&&(S=s.signal({headers:[],items:[],stickyHeader:null,totalSize:0}));function C(e){S&&(S.value=e),y?.(e)}function w(e){let t=[],n=[],r=null;for(let r of e.items){let e=l[r.index];e&&(e.isHeader?n.push({...r,label:e.label??``,sectionIndex:e.sectionIndex}):t.push({...r,data:e.item,itemIndex:e.itemIndex,sectionIndex:e.sectionIndex}))}if(e.stickyItems.length>0){let t=e.stickyItems[0],n=l[t.index];n?.isHeader&&(r={...t,label:n.label??``,sectionIndex:n.sectionIndex})}return v=t,{headers:n,items:t,stickyHeader:r,totalSize:e.totalSize}}let T=i(o,{count:l.length,estimateSize:h,getItemKey:g,horizontal:s.horizontal,measurementCache:s.measurementCache,onChange:e=>{C(w(e))},onScrollEnd:e=>b?.(e),onScrollingChange:e=>x?.(e),overscan:s.overscan??3,scrollEndDelay:s.scrollEndDelay,sticky:e=>l[e]?.isHeader??!1}),E=new AbortController;function D(){_||(_=!0,E.abort(),T.dispose())}function O(e,t){if(e<0||e>=c.length)return-1;let n=0;for(let r=0;r<c.length;r++){if(r===e)return t===void 0?n:n+1+t;n+=1+c[r].items.length}return-1}return{get count(){return T.count},get disposalSignal(){return E.signal},dispose:D,get disposed(){return _},invalidate(){_||T.invalidate()},get isScrolling(){return T.isScrolling},get items(){return v},measure(e,t){_||T.measure(e,t)},measureBatch(e){_||T.measureBatch(e)},measureEl(e,t){return _?()=>{}:T.measureEl(e,t)},refresh(){_||T.refresh()},get scrollOffset(){return T.scrollOffset},scrollToBottom(e){_||T.scrollToBottom(e)},scrollToIndex(e,t){_||T.scrollToIndex(e,t)},scrollToItem(e,t,n={}){if(_)return;let r=O(e,t);r<0||T.scrollToIndex(r,n)},scrollToOffset(e,t){_||T.scrollToOffset(e,t)},scrollToSection(e,t={}){if(_)return;let n=O(e);n<0||T.scrollToIndex(n,t)},scrollToTop(e){_||T.scrollToTop(e)},get stickyItems(){return T.stickyItems},[Symbol.dispose]:D,get totalSize(){return T.totalSize},update(i,o){if(_)return;o&&(typeof o.estimateHeaderSize==`number`&&n(o.estimateHeaderSize,`estimateHeaderSize`),typeof o.estimateItemSize==`number`&&n(o.estimateItemSize,`estimateItemSize`),o.overscan!==void 0&&r(o.overscan),o.scrollEndDelay!==void 0&&t(o.scrollEndDelay,`scrollEndDelay`),Object.hasOwn(o,`onChange`)&&(y=o.onChange),Object.hasOwn(o,`onScrollEnd`)&&(b=o.onScrollEnd),Object.hasOwn(o,`onScrollingChange`)&&(x=o.onScrollingChange),o.estimateHeaderSize!==void 0&&(d=u(o.estimateHeaderSize)),o.estimateItemSize!==void 0&&(f=typeof o.estimateItemSize==`function`?o.estimateItemSize:null,p=f?null:e(typeof o.estimateItemSize==`number`?o.estimateItemSize:void 0,36)),o.getItemKey!==void 0&&(m=o.getItemKey)),c=i;let s=l.length;l=a(c);let h={count:l.length};o?.measurementCache!==void 0&&(h.measurementCache=o.measurementCache),o?.overscan!==void 0&&(h.overscan=o.overscan),o?.scrollEndDelay!==void 0&&(h.scrollEndDelay=o.scrollEndDelay),l.length!==s||o?.measurementCache!==void 0||o?.overscan!==void 0||o?.scrollEndDelay!==void 0?T.update(h):T.refresh()}}}export{o as createGroupedVirtualizer};
1
+ import{resolveEstimateFn as e}from"./_utils.js";import{requireNonNegativeNumber as t,requirePositiveNumber as n,validateOverscan as r}from"./_validation.js";import{createVirtualizer as i}from"./virtualizer.js";function a(e){let t=[];for(let n=0;n<e.length;n++){let r=e[n];t.push({isHeader:!0,itemIndex:-1,label:r.label,sectionIndex:n});for(let e=0;e<r.items.length;e++)t.push({isHeader:!1,item:r.items[e],itemIndex:e,sectionIndex:n})}return t}function o(o,s){typeof s.estimateHeaderSize==`number`&&n(s.estimateHeaderSize,`estimateHeaderSize`),typeof s.estimateItemSize==`number`&&n(s.estimateItemSize,`estimateItemSize`),s.overscan!==void 0&&r(s.overscan),s.scrollEndDelay!==void 0&&t(s.scrollEndDelay,`scrollEndDelay`);let c=s.sections,l=a(c);function u(t){return typeof t==`function`?e=>{let n=l[e];return t(c[n.sectionIndex],n.sectionIndex)}:e(typeof t==`number`?t:void 0,36)}let d=u(s.estimateHeaderSize),f=typeof s.estimateItemSize==`function`?s.estimateItemSize:null,p=f?null:e(typeof s.estimateItemSize==`number`?s.estimateItemSize:void 0,36),m=s.getItemKey;function h(e){let t=l[e];return t?t.isHeader?d(e):f?f(t.item,t.itemIndex,t.sectionIndex):p(e):36}function g(e){let t=l[e];return t?t.isHeader?`__header_${t.sectionIndex}`:m?m(t.item,t.itemIndex,t.sectionIndex):e:e}let _=!1,v=[],y=s.onChange,b=s.onScrollEnd,x=s.onScrollingChange,S=null;s.toSignal&&(S=s.toSignal({headers:[],items:[],stickyHeader:null,totalSize:0}));function C(e){S&&(S.value=e),y?.(e)}function w(e){let t=[],n=[],r=null;for(let r of e.items){let e=l[r.index];e&&(e.isHeader?n.push({...r,label:e.label??``,sectionIndex:e.sectionIndex}):t.push({...r,data:e.item,itemIndex:e.itemIndex,sectionIndex:e.sectionIndex}))}if(e.stickyItems.length>0){let t=e.stickyItems[0],n=l[t.index];n?.isHeader&&(r={...t,label:n.label??``,sectionIndex:n.sectionIndex})}return v=t,{headers:n,items:t,stickyHeader:r,totalSize:e.totalSize}}let T=i(o,{count:l.length,estimateSize:h,getItemKey:g,horizontal:s.horizontal,measurementCache:s.measurementCache,onChange:e=>{C(w(e))},onScrollEnd:e=>b?.(e),onScrollingChange:e=>x?.(e),overscan:s.overscan??3,scrollEndDelay:s.scrollEndDelay,sticky:e=>l[e]?.isHeader??!1}),E=new AbortController;function D(){_||(_=!0,E.abort(),T.dispose())}function O(e,t){if(e<0||e>=c.length)return-1;let n=0;for(let r=0;r<c.length;r++){if(r===e)return t===void 0?n:n+1+t;n+=1+c[r].items.length}return-1}return{get count(){return T.count},get disposalSignal(){return E.signal},dispose:D,get disposed(){return _},invalidate(){_||T.invalidate()},get isScrolling(){return T.isScrolling},get items(){return v},measure(e,t){_||T.measure(e,t)},measureBatch(e){_||T.measureBatch(e)},measureEl(e,t){return _?()=>{}:T.measureEl(e,t)},refresh(){_||T.refresh()},get scrollOffset(){return T.scrollOffset},scrollToBottom(e){_||T.scrollToBottom(e)},scrollToIndex(e,t){_||T.scrollToIndex(e,t)},scrollToItem(e,t,n={}){if(_)return;let r=O(e,t);r<0||T.scrollToIndex(r,n)},scrollToOffset(e,t){_||T.scrollToOffset(e,t)},scrollToSection(e,t={}){if(_)return;let n=O(e);n<0||T.scrollToIndex(n,t)},scrollToTop(e){_||T.scrollToTop(e)},get stickyItems(){return T.stickyItems},[Symbol.dispose]:D,get totalSize(){return T.totalSize},update(i,o){if(_)return;o&&(typeof o.estimateHeaderSize==`number`&&n(o.estimateHeaderSize,`estimateHeaderSize`),typeof o.estimateItemSize==`number`&&n(o.estimateItemSize,`estimateItemSize`),o.overscan!==void 0&&r(o.overscan),o.scrollEndDelay!==void 0&&t(o.scrollEndDelay,`scrollEndDelay`),Object.hasOwn(o,`onChange`)&&(y=o.onChange),Object.hasOwn(o,`onScrollEnd`)&&(b=o.onScrollEnd),Object.hasOwn(o,`onScrollingChange`)&&(x=o.onScrollingChange),o.estimateHeaderSize!==void 0&&(d=u(o.estimateHeaderSize)),o.estimateItemSize!==void 0&&(f=typeof o.estimateItemSize==`function`?o.estimateItemSize:null,p=f?null:e(typeof o.estimateItemSize==`number`?o.estimateItemSize:void 0,36)),o.getItemKey!==void 0&&(m=o.getItemKey)),c=i;let s=l.length;l=a(c);let h={count:l.length};o?.measurementCache!==void 0&&(h.measurementCache=o.measurementCache),o?.overscan!==void 0&&(h.overscan=o.overscan),o?.scrollEndDelay!==void 0&&(h.scrollEndDelay=o.scrollEndDelay),l.length!==s||o?.measurementCache!==void 0||o?.overscan!==void 0||o?.scrollEndDelay!==void 0?T.update(h):T.refresh()}}}export{o as createGroupedVirtualizer};
2
2
  //# sourceMappingURL=grouped-virtualizer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"grouped-virtualizer.js","names":[],"sources":["../src/grouped-virtualizer.ts"],"sourcesContent":["import type { Signal } from '@vielzeug/ripple';\n\nimport { resolveEstimateFn } from './_utils';\nimport { requireNonNegativeNumber, requirePositiveNumber, validateOverscan } from './_validation';\nimport {\n createVirtualizer,\n DEFAULT_ESTIMATE_SIZE,\n DEFAULT_OVERSCAN,\n type MeasurementCache,\n type Overscan,\n type ScrollTarget,\n type ScrollToIndexOptions,\n type VirtualItem,\n type VirtualizerState,\n type VirtualizerUpdateOptions,\n type VirtualKey,\n} from './virtualizer';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface GroupSection<T> {\n items: T[];\n label: string;\n}\n\nexport interface GroupVirtualItem<T> extends VirtualItem {\n /** Item data. */\n data: T;\n /** Index within the section. */\n itemIndex: number;\n sectionIndex: number;\n}\n\nexport interface GroupVirtualHeader extends VirtualItem {\n label: string;\n sectionIndex: number;\n}\n\nexport interface GroupVirtualizerState<T> {\n readonly headers: GroupVirtualHeader[];\n readonly items: Array<GroupVirtualItem<T>>;\n readonly stickyHeader: GroupVirtualHeader | null;\n readonly totalSize: number;\n}\n\n/**\n * Options accepted by `GroupVirtualizer.update()`. Overrides are applied\n * together with the new sections on the next render cycle.\n *\n * Intentionally excludes `horizontal` (axis cannot change at runtime) and\n * `initialOffset` (one-time bootstrap).\n */\nexport interface GroupVirtualizerUpdateOptions<T> {\n estimateHeaderSize?: number | ((section: GroupSection<T>, sectionIndex: number) => number);\n estimateItemSize?: number | ((item: T, itemIndex: number, sectionIndex: number) => number);\n getItemKey?: (item: T, itemIndex: number, sectionIndex: number) => VirtualKey;\n measurementCache?: MeasurementCache;\n onChange?: ((state: GroupVirtualizerState<T>) => void) | undefined;\n onScrollEnd?: ((offset: number) => void) | undefined;\n onScrollingChange?: ((isScrolling: boolean) => void) | undefined;\n overscan?: Overscan;\n scrollEndDelay?: number;\n}\n\nexport interface GroupVirtualizerOptions<T> {\n estimateHeaderSize?: number | ((section: GroupSection<T>, sectionIndex: number) => number);\n estimateItemSize?: number | ((item: T, itemIndex: number, sectionIndex: number) => number);\n getItemKey?: (item: T, itemIndex: number, sectionIndex: number) => VirtualKey;\n horizontal?: boolean;\n measurementCache?: MeasurementCache;\n /** Called after every render cycle with the new state. Replace through `update()`. */\n onChange?: (state: GroupVirtualizerState<T>) => void;\n /** Called when scrolling settles. Replace through `update()`. */\n onScrollEnd?: (offset: number) => void;\n /** Called when the scrolling state changes. Replace through `update()`. */\n onScrollingChange?: (isScrolling: boolean) => void;\n overscan?: Overscan;\n /**\n * Debounce delay (ms) for scroll-end detection. Defaults to 150.\n * See `VirtualizerOptions.scrollEndDelay` for details.\n */\n scrollEndDelay?: number;\n sections: Array<GroupSection<T>>;\n /** Optional signal factory for reactive state. */\n signal?: (init: GroupVirtualizerState<T>) => Signal<GroupVirtualizerState<T>>;\n}\n\nexport interface GroupVirtualizer<T> {\n readonly count: number;\n readonly disposalSignal: AbortSignal;\n dispose: () => void;\n readonly disposed: boolean;\n invalidate: () => void;\n /** `true` while the user is actively scrolling; `false` once scroll has settled. */\n readonly isScrolling: boolean;\n readonly items: ReadonlyArray<GroupVirtualItem<T>>;\n measure: (index: number, size: number) => void;\n measureBatch: (entries: Array<{ index: number; size: number }>) => void;\n measureEl: (index: number, el: HTMLElement) => () => void;\n refresh: () => void;\n readonly scrollOffset: number;\n scrollToBottom: (options?: { behavior?: ScrollBehavior }) => void;\n scrollToIndex: (index: number, options?: ScrollToIndexOptions) => void;\n scrollToItem: (sectionIndex: number, itemIndex: number, options?: ScrollToIndexOptions) => void;\n scrollToOffset: (offset: number, options?: { behavior?: ScrollBehavior }) => void;\n scrollToSection: (sectionIndex: number, options?: ScrollToIndexOptions) => void;\n scrollToTop: (options?: { behavior?: ScrollBehavior }) => void;\n readonly stickyItems: VirtualItem[];\n readonly totalSize: number;\n update: (sections: Array<GroupSection<T>>, opts?: GroupVirtualizerUpdateOptions<T>) => void;\n [Symbol.dispose]: () => void;\n}\n\n// ─── Flat entry map ───────────────────────────────────────────────────────────\n\ntype FlatHeader = { isHeader: true; itemIndex: -1; label: string; sectionIndex: number };\ntype FlatItem<T> = { isHeader: false; item: T; itemIndex: number; sectionIndex: number };\ntype FlatEntry<T> = FlatHeader | FlatItem<T>;\n\nfunction buildFlatEntries<T>(sections: Array<GroupSection<T>>): FlatEntry<T>[] {\n const flat: FlatEntry<T>[] = [];\n\n for (let s = 0; s < sections.length; s++) {\n const section = sections[s]!;\n\n flat.push({ isHeader: true, itemIndex: -1, label: section.label, sectionIndex: s });\n\n for (let i = 0; i < section.items.length; i++) {\n flat.push({ isHeader: false, item: section.items[i]!, itemIndex: i, sectionIndex: s });\n }\n }\n\n return flat;\n}\n\n// ─── Implementation ───────────────────────────────────────────────────────────\n\nexport function createGroupedVirtualizer<T>(\n target: ScrollTarget,\n options: GroupVirtualizerOptions<T>,\n): GroupVirtualizer<T> {\n if (typeof options.estimateHeaderSize === 'number')\n requirePositiveNumber(options.estimateHeaderSize, 'estimateHeaderSize');\n\n if (typeof options.estimateItemSize === 'number') requirePositiveNumber(options.estimateItemSize, 'estimateItemSize');\n\n if (options.overscan !== undefined) validateOverscan(options.overscan);\n\n if (options.scrollEndDelay !== undefined) requireNonNegativeNumber(options.scrollEndDelay, 'scrollEndDelay');\n\n let sections = options.sections;\n let flat = buildFlatEntries(sections);\n\n function buildEstimateHeader(\n estimateHeaderSize: GroupVirtualizerOptions<T>['estimateHeaderSize'],\n ): (index: number) => number {\n return typeof estimateHeaderSize === 'function'\n ? (index: number) => {\n const entry = flat[index]!;\n\n return (estimateHeaderSize as (section: GroupSection<T>, sectionIndex: number) => number)(\n sections[entry.sectionIndex]!,\n entry.sectionIndex,\n );\n }\n : resolveEstimateFn(\n typeof estimateHeaderSize === 'number' ? estimateHeaderSize : undefined,\n DEFAULT_ESTIMATE_SIZE,\n );\n }\n\n let estimateHeaderFn = buildEstimateHeader(options.estimateHeaderSize);\n let estimateItemUserFn: ((item: T, itemIndex: number, sectionIndex: number) => number) | null =\n typeof options.estimateItemSize === 'function' ? options.estimateItemSize : null;\n let estimateItemFixedFn: ((index: number) => number) | null = estimateItemUserFn\n ? null\n : resolveEstimateFn(\n typeof options.estimateItemSize === 'number' ? options.estimateItemSize : undefined,\n DEFAULT_ESTIMATE_SIZE,\n );\n let userGetItemKey = options.getItemKey;\n\n function estimateFn(globalIndex: number): number {\n const entry = flat[globalIndex];\n\n if (!entry) return DEFAULT_ESTIMATE_SIZE;\n\n if (entry.isHeader) return estimateHeaderFn(globalIndex);\n\n if (estimateItemUserFn) return estimateItemUserFn(entry.item, entry.itemIndex, entry.sectionIndex);\n\n return estimateItemFixedFn!(globalIndex);\n }\n\n function getItemKey(globalIndex: number): VirtualKey {\n const entry = flat[globalIndex];\n\n if (!entry) return globalIndex;\n\n if (entry.isHeader) return `__header_${entry.sectionIndex}`;\n\n if (userGetItemKey) return userGetItemKey(entry.item, entry.itemIndex, entry.sectionIndex);\n\n return globalIndex;\n }\n\n let destroyed = false;\n let lastItems: Array<GroupVirtualItem<T>> = [];\n let onChange = options.onChange;\n let onScrollEnd = options.onScrollEnd;\n let onScrollingChange = options.onScrollingChange;\n\n // Optional signal for reactive state\n let stateSignal: Signal<GroupVirtualizerState<T>> | null = null;\n if (options.signal) {\n const initialState: GroupVirtualizerState<T> = { headers: [], items: [], stickyHeader: null, totalSize: 0 };\n stateSignal = options.signal(initialState);\n }\n\n // Helper to emit state to both callback and signal\n function emitState(state: GroupVirtualizerState<T>): void {\n if (stateSignal) stateSignal.value = state;\n onChange?.(state);\n }\n\n function mapState(state: VirtualizerState): GroupVirtualizerState<T> {\n const items: Array<GroupVirtualItem<T>> = [];\n const headers: GroupVirtualHeader[] = [];\n let stickyHeader: GroupVirtualHeader | null = null;\n\n for (const vi of state.items) {\n const entry = flat[vi.index];\n\n if (!entry) continue;\n\n if (entry.isHeader) {\n headers.push({ ...vi, label: entry.label ?? '', sectionIndex: entry.sectionIndex });\n } else {\n items.push({ ...vi, data: entry.item, itemIndex: entry.itemIndex, sectionIndex: entry.sectionIndex });\n }\n }\n\n if (state.stickyItems.length > 0) {\n const svi = state.stickyItems[0]!;\n const entry = flat[svi.index];\n\n if (entry?.isHeader) {\n stickyHeader = { ...svi, label: entry.label ?? '', sectionIndex: entry.sectionIndex };\n }\n }\n\n lastItems = items;\n\n return { headers, items, stickyHeader, totalSize: state.totalSize };\n }\n\n const virtualizer = createVirtualizer(target, {\n count: flat.length,\n estimateSize: estimateFn,\n getItemKey,\n horizontal: options.horizontal,\n measurementCache: options.measurementCache,\n onChange: (state) => {\n const mapped = mapState(state);\n\n emitState(mapped);\n },\n onScrollEnd: (offset) => onScrollEnd?.(offset),\n onScrollingChange: (isScrolling) => onScrollingChange?.(isScrolling),\n overscan: options.overscan ?? DEFAULT_OVERSCAN,\n scrollEndDelay: options.scrollEndDelay,\n sticky: (i) => flat[i]?.isHeader ?? false,\n });\n\n const ac = new AbortController();\n\n function _dispose(): void {\n if (destroyed) return;\n\n destroyed = true;\n ac.abort();\n virtualizer.dispose();\n }\n\n // Build a flat-index lookup: [sectionIndex, itemIndex] → flat index.\n // Returns -1 when sectionIndex is out of range so callers can no-op safely.\n function flatIndexOf(sectionIndex: number, itemIndex?: number): number {\n if (sectionIndex < 0 || sectionIndex >= sections.length) return -1;\n\n let pos = 0;\n\n for (let s = 0; s < sections.length; s++) {\n if (s === sectionIndex) {\n if (itemIndex === undefined) return pos; // header\n\n return pos + 1 + itemIndex;\n }\n\n pos += 1 + sections[s]!.items.length;\n }\n\n return -1; // unreachable\n }\n\n return {\n get count() {\n return virtualizer.count;\n },\n get disposalSignal() {\n return ac.signal;\n },\n dispose: _dispose,\n get disposed() {\n return destroyed;\n },\n invalidate() {\n if (destroyed) return;\n\n virtualizer.invalidate();\n },\n get isScrolling() {\n return virtualizer.isScrolling;\n },\n get items() {\n return lastItems;\n },\n measure(index: number, size: number) {\n if (destroyed) return;\n\n virtualizer.measure(index, size);\n },\n measureBatch(entries: Array<{ index: number; size: number }>) {\n if (destroyed) return;\n\n virtualizer.measureBatch(entries);\n },\n measureEl(index: number, el: HTMLElement) {\n if (destroyed) return () => {};\n\n return virtualizer.measureEl(index, el);\n },\n refresh() {\n if (destroyed) return;\n\n virtualizer.refresh();\n },\n get scrollOffset() {\n return virtualizer.scrollOffset;\n },\n scrollToBottom(opts?: { behavior?: ScrollBehavior }) {\n if (destroyed) return;\n\n virtualizer.scrollToBottom(opts);\n },\n scrollToIndex(index: number, opts?: ScrollToIndexOptions) {\n if (destroyed) return;\n\n virtualizer.scrollToIndex(index, opts);\n },\n scrollToItem(sectionIndex, itemIndex, opts = {}) {\n if (destroyed) return;\n\n const globalIndex = flatIndexOf(sectionIndex, itemIndex);\n\n if (globalIndex < 0) return;\n\n virtualizer.scrollToIndex(globalIndex, opts);\n },\n scrollToOffset(offset: number, opts?: { behavior?: ScrollBehavior }) {\n if (destroyed) return;\n\n virtualizer.scrollToOffset(offset, opts);\n },\n scrollToSection(sectionIndex, opts = {}) {\n if (destroyed) return;\n\n const globalIndex = flatIndexOf(sectionIndex);\n\n if (globalIndex < 0) return;\n\n virtualizer.scrollToIndex(globalIndex, opts);\n },\n scrollToTop(opts?: { behavior?: ScrollBehavior }) {\n if (destroyed) return;\n\n virtualizer.scrollToTop(opts);\n },\n get stickyItems() {\n return virtualizer.stickyItems;\n },\n [Symbol.dispose]: _dispose,\n get totalSize() {\n return virtualizer.totalSize;\n },\n /**\n * Replace all sections. Closures for estimateFn and getItemKey already\n * capture the live `flat`/`sections` references — only count is passed to\n * the underlying virtualizer to avoid discarding measured sizes on every\n * data refresh. `refresh()` rebuilds offsets while preserving the cache.\n */\n update(nextSections, opts?: GroupVirtualizerUpdateOptions<T>) {\n if (destroyed) return;\n\n if (opts) {\n if (typeof opts.estimateHeaderSize === 'number')\n requirePositiveNumber(opts.estimateHeaderSize, 'estimateHeaderSize');\n\n if (typeof opts.estimateItemSize === 'number') requirePositiveNumber(opts.estimateItemSize, 'estimateItemSize');\n\n if (opts.overscan !== undefined) validateOverscan(opts.overscan);\n\n if (opts.scrollEndDelay !== undefined) requireNonNegativeNumber(opts.scrollEndDelay, 'scrollEndDelay');\n\n if (Object.hasOwn(opts, 'onChange')) onChange = opts.onChange;\n\n if (Object.hasOwn(opts, 'onScrollEnd')) onScrollEnd = opts.onScrollEnd;\n\n if (Object.hasOwn(opts, 'onScrollingChange')) onScrollingChange = opts.onScrollingChange;\n\n // Apply option overrides before rebuilding flat entries.\n if (opts.estimateHeaderSize !== undefined) {\n estimateHeaderFn = buildEstimateHeader(opts.estimateHeaderSize);\n }\n\n if (opts.estimateItemSize !== undefined) {\n estimateItemUserFn = typeof opts.estimateItemSize === 'function' ? opts.estimateItemSize : null;\n estimateItemFixedFn = estimateItemUserFn\n ? null\n : resolveEstimateFn(\n typeof opts.estimateItemSize === 'number' ? opts.estimateItemSize : undefined,\n DEFAULT_ESTIMATE_SIZE,\n );\n }\n\n if (opts.getItemKey !== undefined) userGetItemKey = opts.getItemKey;\n }\n\n sections = nextSections;\n\n const prevCount = flat.length;\n\n flat = buildFlatEntries(sections);\n\n const virtUpdateOpts: VirtualizerUpdateOptions = { count: flat.length };\n\n if (opts?.measurementCache !== undefined) virtUpdateOpts.measurementCache = opts.measurementCache;\n\n if (opts?.overscan !== undefined) virtUpdateOpts.overscan = opts.overscan;\n\n if (opts?.scrollEndDelay !== undefined) virtUpdateOpts.scrollEndDelay = opts.scrollEndDelay;\n\n if (\n flat.length !== prevCount ||\n opts?.measurementCache !== undefined ||\n opts?.overscan !== undefined ||\n opts?.scrollEndDelay !== undefined\n ) {\n virtualizer.update(virtUpdateOpts);\n } else {\n virtualizer.refresh();\n }\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"grouped-virtualizer.js","names":[],"sources":["../src/grouped-virtualizer.ts"],"sourcesContent":["import type { Signal } from '@vielzeug/ripple';\n\nimport { resolveEstimateFn } from './_utils';\nimport { requireNonNegativeNumber, requirePositiveNumber, validateOverscan } from './_validation';\nimport {\n createVirtualizer,\n DEFAULT_ESTIMATE_SIZE,\n DEFAULT_OVERSCAN,\n type MeasurementCache,\n type Overscan,\n type ScrollTarget,\n type ScrollToIndexOptions,\n type VirtualItem,\n type VirtualizerState,\n type VirtualizerUpdateOptions,\n type VirtualKey,\n} from './virtualizer';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface GroupSection<T> {\n items: T[];\n label: string;\n}\n\nexport interface GroupVirtualItem<T> extends VirtualItem {\n /** Item data. */\n data: T;\n /** Index within the section. */\n itemIndex: number;\n sectionIndex: number;\n}\n\nexport interface GroupVirtualHeader extends VirtualItem {\n label: string;\n sectionIndex: number;\n}\n\nexport interface GroupVirtualizerState<T> {\n readonly headers: GroupVirtualHeader[];\n readonly items: Array<GroupVirtualItem<T>>;\n readonly stickyHeader: GroupVirtualHeader | null;\n readonly totalSize: number;\n}\n\n/**\n * Options accepted by `GroupVirtualizer.update()`. Overrides are applied\n * together with the new sections on the next render cycle.\n *\n * Intentionally excludes `horizontal` (axis cannot change at runtime) and\n * `initialOffset` (one-time bootstrap).\n */\nexport interface GroupVirtualizerUpdateOptions<T> {\n estimateHeaderSize?: number | ((section: GroupSection<T>, sectionIndex: number) => number);\n estimateItemSize?: number | ((item: T, itemIndex: number, sectionIndex: number) => number);\n getItemKey?: (item: T, itemIndex: number, sectionIndex: number) => VirtualKey;\n measurementCache?: MeasurementCache;\n onChange?: ((state: GroupVirtualizerState<T>) => void) | undefined;\n onScrollEnd?: ((offset: number) => void) | undefined;\n onScrollingChange?: ((isScrolling: boolean) => void) | undefined;\n overscan?: Overscan;\n scrollEndDelay?: number;\n}\n\nexport interface GroupVirtualizerOptions<T> {\n estimateHeaderSize?: number | ((section: GroupSection<T>, sectionIndex: number) => number);\n estimateItemSize?: number | ((item: T, itemIndex: number, sectionIndex: number) => number);\n getItemKey?: (item: T, itemIndex: number, sectionIndex: number) => VirtualKey;\n horizontal?: boolean;\n measurementCache?: MeasurementCache;\n /** Called after every render cycle with the new state. Replace through `update()`. */\n onChange?: (state: GroupVirtualizerState<T>) => void;\n /** Called when scrolling settles. Replace through `update()`. */\n onScrollEnd?: (offset: number) => void;\n /** Called when the scrolling state changes. Replace through `update()`. */\n onScrollingChange?: (isScrolling: boolean) => void;\n overscan?: Overscan;\n /**\n * Debounce delay (ms) for scroll-end detection. Defaults to 150.\n * See `VirtualizerOptions.scrollEndDelay` for details.\n */\n scrollEndDelay?: number;\n sections: Array<GroupSection<T>>;\n /** Optional signal factory for reactive state. */\n toSignal?: (init: GroupVirtualizerState<T>) => Signal<GroupVirtualizerState<T>>;\n}\n\nexport interface GroupVirtualizer<T> {\n readonly count: number;\n readonly disposalSignal: AbortSignal;\n dispose: () => void;\n readonly disposed: boolean;\n invalidate: () => void;\n /** `true` while the user is actively scrolling; `false` once scroll has settled. */\n readonly isScrolling: boolean;\n readonly items: ReadonlyArray<GroupVirtualItem<T>>;\n measure: (index: number, size: number) => void;\n measureBatch: (entries: Array<{ index: number; size: number }>) => void;\n measureEl: (index: number, el: HTMLElement) => () => void;\n refresh: () => void;\n readonly scrollOffset: number;\n scrollToBottom: (options?: { behavior?: ScrollBehavior }) => void;\n scrollToIndex: (index: number, options?: ScrollToIndexOptions) => void;\n scrollToItem: (sectionIndex: number, itemIndex: number, options?: ScrollToIndexOptions) => void;\n scrollToOffset: (offset: number, options?: { behavior?: ScrollBehavior }) => void;\n scrollToSection: (sectionIndex: number, options?: ScrollToIndexOptions) => void;\n scrollToTop: (options?: { behavior?: ScrollBehavior }) => void;\n readonly stickyItems: VirtualItem[];\n readonly totalSize: number;\n update: (sections: Array<GroupSection<T>>, opts?: GroupVirtualizerUpdateOptions<T>) => void;\n [Symbol.dispose]: () => void;\n}\n\n// ─── Flat entry map ───────────────────────────────────────────────────────────\n\ntype FlatHeader = { isHeader: true; itemIndex: -1; label: string; sectionIndex: number };\ntype FlatItem<T> = { isHeader: false; item: T; itemIndex: number; sectionIndex: number };\ntype FlatEntry<T> = FlatHeader | FlatItem<T>;\n\nfunction buildFlatEntries<T>(sections: Array<GroupSection<T>>): FlatEntry<T>[] {\n const flat: FlatEntry<T>[] = [];\n\n for (let s = 0; s < sections.length; s++) {\n const section = sections[s]!;\n\n flat.push({ isHeader: true, itemIndex: -1, label: section.label, sectionIndex: s });\n\n for (let i = 0; i < section.items.length; i++) {\n flat.push({ isHeader: false, item: section.items[i]!, itemIndex: i, sectionIndex: s });\n }\n }\n\n return flat;\n}\n\n// ─── Implementation ───────────────────────────────────────────────────────────\n\nexport function createGroupedVirtualizer<T>(\n target: ScrollTarget,\n options: GroupVirtualizerOptions<T>,\n): GroupVirtualizer<T> {\n if (typeof options.estimateHeaderSize === 'number')\n requirePositiveNumber(options.estimateHeaderSize, 'estimateHeaderSize');\n\n if (typeof options.estimateItemSize === 'number') requirePositiveNumber(options.estimateItemSize, 'estimateItemSize');\n\n if (options.overscan !== undefined) validateOverscan(options.overscan);\n\n if (options.scrollEndDelay !== undefined) requireNonNegativeNumber(options.scrollEndDelay, 'scrollEndDelay');\n\n let sections = options.sections;\n let flat = buildFlatEntries(sections);\n\n function buildEstimateHeader(\n estimateHeaderSize: GroupVirtualizerOptions<T>['estimateHeaderSize'],\n ): (index: number) => number {\n return typeof estimateHeaderSize === 'function'\n ? (index: number) => {\n const entry = flat[index]!;\n\n return (estimateHeaderSize as (section: GroupSection<T>, sectionIndex: number) => number)(\n sections[entry.sectionIndex]!,\n entry.sectionIndex,\n );\n }\n : resolveEstimateFn(\n typeof estimateHeaderSize === 'number' ? estimateHeaderSize : undefined,\n DEFAULT_ESTIMATE_SIZE,\n );\n }\n\n let estimateHeaderFn = buildEstimateHeader(options.estimateHeaderSize);\n let estimateItemUserFn: ((item: T, itemIndex: number, sectionIndex: number) => number) | null =\n typeof options.estimateItemSize === 'function' ? options.estimateItemSize : null;\n let estimateItemFixedFn: ((index: number) => number) | null = estimateItemUserFn\n ? null\n : resolveEstimateFn(\n typeof options.estimateItemSize === 'number' ? options.estimateItemSize : undefined,\n DEFAULT_ESTIMATE_SIZE,\n );\n let userGetItemKey = options.getItemKey;\n\n function estimateFn(globalIndex: number): number {\n const entry = flat[globalIndex];\n\n if (!entry) return DEFAULT_ESTIMATE_SIZE;\n\n if (entry.isHeader) return estimateHeaderFn(globalIndex);\n\n if (estimateItemUserFn) return estimateItemUserFn(entry.item, entry.itemIndex, entry.sectionIndex);\n\n return estimateItemFixedFn!(globalIndex);\n }\n\n function getItemKey(globalIndex: number): VirtualKey {\n const entry = flat[globalIndex];\n\n if (!entry) return globalIndex;\n\n if (entry.isHeader) return `__header_${entry.sectionIndex}`;\n\n if (userGetItemKey) return userGetItemKey(entry.item, entry.itemIndex, entry.sectionIndex);\n\n return globalIndex;\n }\n\n let disposed = false;\n let lastItems: Array<GroupVirtualItem<T>> = [];\n let onChange = options.onChange;\n let onScrollEnd = options.onScrollEnd;\n let onScrollingChange = options.onScrollingChange;\n\n // Optional signal for reactive state\n let stateSignal: Signal<GroupVirtualizerState<T>> | null = null;\n if (options.toSignal) {\n const initialState: GroupVirtualizerState<T> = { headers: [], items: [], stickyHeader: null, totalSize: 0 };\n stateSignal = options.toSignal(initialState);\n }\n\n // Helper to emit state to both callback and signal\n function emitState(state: GroupVirtualizerState<T>): void {\n if (stateSignal) stateSignal.value = state;\n onChange?.(state);\n }\n\n function mapState(state: VirtualizerState): GroupVirtualizerState<T> {\n const items: Array<GroupVirtualItem<T>> = [];\n const headers: GroupVirtualHeader[] = [];\n let stickyHeader: GroupVirtualHeader | null = null;\n\n for (const vi of state.items) {\n const entry = flat[vi.index];\n\n if (!entry) continue;\n\n if (entry.isHeader) {\n headers.push({ ...vi, label: entry.label ?? '', sectionIndex: entry.sectionIndex });\n } else {\n items.push({ ...vi, data: entry.item, itemIndex: entry.itemIndex, sectionIndex: entry.sectionIndex });\n }\n }\n\n if (state.stickyItems.length > 0) {\n const svi = state.stickyItems[0]!;\n const entry = flat[svi.index];\n\n if (entry?.isHeader) {\n stickyHeader = { ...svi, label: entry.label ?? '', sectionIndex: entry.sectionIndex };\n }\n }\n\n lastItems = items;\n\n return { headers, items, stickyHeader, totalSize: state.totalSize };\n }\n\n const virtualizer = createVirtualizer(target, {\n count: flat.length,\n estimateSize: estimateFn,\n getItemKey,\n horizontal: options.horizontal,\n measurementCache: options.measurementCache,\n onChange: (state) => {\n const mapped = mapState(state);\n\n emitState(mapped);\n },\n onScrollEnd: (offset) => onScrollEnd?.(offset),\n onScrollingChange: (isScrolling) => onScrollingChange?.(isScrolling),\n overscan: options.overscan ?? DEFAULT_OVERSCAN,\n scrollEndDelay: options.scrollEndDelay,\n sticky: (i) => flat[i]?.isHeader ?? false,\n });\n\n const ac = new AbortController();\n\n function _dispose(): void {\n if (disposed) return;\n\n disposed = true;\n ac.abort();\n virtualizer.dispose();\n }\n\n // Build a flat-index lookup: [sectionIndex, itemIndex] → flat index.\n // Returns -1 when sectionIndex is out of range so callers can no-op safely.\n function flatIndexOf(sectionIndex: number, itemIndex?: number): number {\n if (sectionIndex < 0 || sectionIndex >= sections.length) return -1;\n\n let pos = 0;\n\n for (let s = 0; s < sections.length; s++) {\n if (s === sectionIndex) {\n if (itemIndex === undefined) return pos; // header\n\n return pos + 1 + itemIndex;\n }\n\n pos += 1 + sections[s]!.items.length;\n }\n\n return -1; // unreachable\n }\n\n return {\n get count() {\n return virtualizer.count;\n },\n get disposalSignal() {\n return ac.signal;\n },\n dispose: _dispose,\n get disposed() {\n return disposed;\n },\n invalidate() {\n if (disposed) return;\n\n virtualizer.invalidate();\n },\n get isScrolling() {\n return virtualizer.isScrolling;\n },\n get items() {\n return lastItems;\n },\n measure(index: number, size: number) {\n if (disposed) return;\n\n virtualizer.measure(index, size);\n },\n measureBatch(entries: Array<{ index: number; size: number }>) {\n if (disposed) return;\n\n virtualizer.measureBatch(entries);\n },\n measureEl(index: number, el: HTMLElement) {\n if (disposed) return () => {};\n\n return virtualizer.measureEl(index, el);\n },\n refresh() {\n if (disposed) return;\n\n virtualizer.refresh();\n },\n get scrollOffset() {\n return virtualizer.scrollOffset;\n },\n scrollToBottom(opts?: { behavior?: ScrollBehavior }) {\n if (disposed) return;\n\n virtualizer.scrollToBottom(opts);\n },\n scrollToIndex(index: number, opts?: ScrollToIndexOptions) {\n if (disposed) return;\n\n virtualizer.scrollToIndex(index, opts);\n },\n scrollToItem(sectionIndex, itemIndex, opts = {}) {\n if (disposed) return;\n\n const globalIndex = flatIndexOf(sectionIndex, itemIndex);\n\n if (globalIndex < 0) return;\n\n virtualizer.scrollToIndex(globalIndex, opts);\n },\n scrollToOffset(offset: number, opts?: { behavior?: ScrollBehavior }) {\n if (disposed) return;\n\n virtualizer.scrollToOffset(offset, opts);\n },\n scrollToSection(sectionIndex, opts = {}) {\n if (disposed) return;\n\n const globalIndex = flatIndexOf(sectionIndex);\n\n if (globalIndex < 0) return;\n\n virtualizer.scrollToIndex(globalIndex, opts);\n },\n scrollToTop(opts?: { behavior?: ScrollBehavior }) {\n if (disposed) return;\n\n virtualizer.scrollToTop(opts);\n },\n get stickyItems() {\n return virtualizer.stickyItems;\n },\n [Symbol.dispose]: _dispose,\n get totalSize() {\n return virtualizer.totalSize;\n },\n /**\n * Replace all sections. Closures for estimateFn and getItemKey already\n * capture the live `flat`/`sections` references — only count is passed to\n * the underlying virtualizer to avoid discarding measured sizes on every\n * data refresh. `refresh()` rebuilds offsets while preserving the cache.\n */\n update(nextSections, opts?: GroupVirtualizerUpdateOptions<T>) {\n if (disposed) return;\n\n if (opts) {\n if (typeof opts.estimateHeaderSize === 'number')\n requirePositiveNumber(opts.estimateHeaderSize, 'estimateHeaderSize');\n\n if (typeof opts.estimateItemSize === 'number') requirePositiveNumber(opts.estimateItemSize, 'estimateItemSize');\n\n if (opts.overscan !== undefined) validateOverscan(opts.overscan);\n\n if (opts.scrollEndDelay !== undefined) requireNonNegativeNumber(opts.scrollEndDelay, 'scrollEndDelay');\n\n if (Object.hasOwn(opts, 'onChange')) onChange = opts.onChange;\n\n if (Object.hasOwn(opts, 'onScrollEnd')) onScrollEnd = opts.onScrollEnd;\n\n if (Object.hasOwn(opts, 'onScrollingChange')) onScrollingChange = opts.onScrollingChange;\n\n // Apply option overrides before rebuilding flat entries.\n if (opts.estimateHeaderSize !== undefined) {\n estimateHeaderFn = buildEstimateHeader(opts.estimateHeaderSize);\n }\n\n if (opts.estimateItemSize !== undefined) {\n estimateItemUserFn = typeof opts.estimateItemSize === 'function' ? opts.estimateItemSize : null;\n estimateItemFixedFn = estimateItemUserFn\n ? null\n : resolveEstimateFn(\n typeof opts.estimateItemSize === 'number' ? opts.estimateItemSize : undefined,\n DEFAULT_ESTIMATE_SIZE,\n );\n }\n\n if (opts.getItemKey !== undefined) userGetItemKey = opts.getItemKey;\n }\n\n sections = nextSections;\n\n const prevCount = flat.length;\n\n flat = buildFlatEntries(sections);\n\n const virtUpdateOpts: VirtualizerUpdateOptions = { count: flat.length };\n\n if (opts?.measurementCache !== undefined) virtUpdateOpts.measurementCache = opts.measurementCache;\n\n if (opts?.overscan !== undefined) virtUpdateOpts.overscan = opts.overscan;\n\n if (opts?.scrollEndDelay !== undefined) virtUpdateOpts.scrollEndDelay = opts.scrollEndDelay;\n\n if (\n flat.length !== prevCount ||\n opts?.measurementCache !== undefined ||\n opts?.overscan !== undefined ||\n opts?.scrollEndDelay !== undefined\n ) {\n virtualizer.update(virtUpdateOpts);\n } else {\n virtualizer.refresh();\n }\n },\n };\n}\n"],"mappings":"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"}