Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@af-utils/virtual-core

Package Overview
Dependencies
Maintainers
1
Versions
26
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@af-utils/virtual-core - npm Package Compare versions

Comparing version 0.0.22 to 0.0.23

lib/bundlesizes.d.ts

818

lib/index.js

@@ -1,223 +0,653 @@

const t = {
RANGE: 0,
SCROLL_SIZE: 1,
SIZES: 2
}, s = t => t(), i = (t, s) => {
if (!t) throw Error(s);
}, e = (t, s, i) => {
const e = new Uint32Array(s);
return e.set(t), e.fill(i, t.length), e;
}, h = t => t instanceof HTMLElement, r = (t, s) => t.getBoundingClientRect()[s], o = (t, s, i, e) => s && t && t !== s ? t[i] + Math.round(r(s, e) - (h(t) ? r(t, e) : 0)) : 0, n = (t, s) => {
t.set(s, 1);
for (let s, i = 1, e = t.length; e > i; i++) s = i + (i & -i), e > s && (t[s] += t[i]);
}, l = (t, s, i, e) => {
for (;e > s; s += s & -s) t[s] += i;
}, c = (t, s, i) => {
for (;i > s; s += s & -s) ;
return Math.min(s, t.length);
}, a = new Set;
// src/constants/index.ts
var VirtualScrollerEvent = {
RANGE: 0,
SCROLL_SIZE: 1,
SIZES: 2
};
let f = 0;
// src/utils/misc/index.ts
var call = (fn) => fn();
var growTypedArray = (sourceArray, newLength, fillValue) => {
const resultArray = new Uint32Array(newLength);
resultArray.set(sourceArray);
resultArray.fill(fillValue, sourceArray.length);
return resultArray;
};
var isElement = (el) => el instanceof HTMLElement;
var getElementOffset = (element, scrollToKey) => element.getBoundingClientRect()[scrollToKey];
var getDistanceBetween = (scrollElement, containerElement, scrollKey, scrollToKey) => {
if (!containerElement || !scrollElement || scrollElement === containerElement) {
return 0;
}
return scrollElement[scrollKey] + Math.round(
getElementOffset(containerElement, scrollToKey) - (isElement(scrollElement) ? getElementOffset(scrollElement, scrollToKey) : 0)
);
};
const _ = () => {
--f || (a.forEach(s), a.clear());
}, u = t => a.add(t), m = {
box: "border-box"
}, d = new Uint32Array(0), S = 2147483647, v = [ "offsetHeight", "offsetWidth", "innerHeight", "innerWidth" ], E = [ "scrollTop", "scrollLeft", "scrollY", "scrollX" ], z = [ "blockSize", "inlineSize" ], b = [ "top", "left" ], y = (t, s) => Math.round(t.borderBoxSize[0][s]);
// src/utils/fTree/index.ts
var syncWithArray = (fTree, sourceArray) => {
fTree.set(sourceArray, 1);
for (let i = 1, fTreeLength = fTree.length, j; i < fTreeLength; i++) {
j = i + (i & -i);
if (j < fTreeLength) {
fTree[j] += fTree[i];
}
}
};
var update = (fTree, i, delta, limitTreeLiftingIndex) => {
for (; i < limitTreeLiftingIndex; i += i & -i) {
fTree[i] += delta;
}
};
var getLiftingLimit = (fTree, from, to) => {
for (; from < to; from += from & -from)
;
return Math.min(from, fTree.length);
};
class p {
t=v[0];
i=E[0];
h=z[0];
o=b[0];
l=0;
_=0;
u=0;
m=0;
S=0;
v=0;
p=0;
T=0;
M=6;
O=40;
I=null;
R=null;
k=d;
C=d;
L=0;
horizontal=!1;
scrollSize=0;
from=0;
to=0;
sizesHash=0;
F=new Map;
H=new Map;
K=[ null, null ];
A=[ 0, 0 ];
P=new ResizeObserver((t => {
let s = 0;
for (const i of t) {
const t = this.K.indexOf(i.target);
if (-1 !== t) {
const e = y(i, this.h) - this.A[t];
this.A[t] += e, s += e;
}
// src/models/VirtualScroller.ts
var BatchQueue = /* @__PURE__ */ new Set();
var batchLevel = 0;
var batchEnd = () => {
if (!--batchLevel) {
BatchQueue.forEach(call);
BatchQueue.clear();
}
};
var addToBatchQueue = (fn) => BatchQueue.add(fn);
var OBSERVE_OPTIONS = {
box: "border-box"
};
var ITEMS_ROOM = 32;
var DEFAULT_OVERSCAN_COUNT = 6;
var DEFAULT_ESTIMATED_WIDGET_SIZE = 200;
var DEFAULT_ESTIMATED_ITEM_SIZE = 40;
var EMPTY_TYPED_ARRAY = new Uint32Array(0);
var MAX_INT_32 = 2147483647;
var STICKY_HEADER_INDEX = 0;
var STICKY_FOOTER_INDEX = 1;
var ScrollElementSizeKeysOrdered = [
"offsetHeight" /* ELEMENT_VERTICAL */,
"offsetWidth" /* ELEMENT_HORIZONTAL */,
"innerHeight" /* WINDOW_VERTICAL */,
"innerWidth" /* WINDOW_HORIZONTAL */
];
var ScrollKeysOrdered = [
"scrollTop" /* ELEMENT_VERTICAL */,
"scrollLeft" /* ELEMENT_HORIZONTAL */,
"scrollY" /* WINDOW_VERTICAL */,
"scrollX" /* WINDOW_HORIZONTAL */
];
var ResizeObserverSizeKeysOrdered = [
"blockSize" /* VERTICAL */,
"inlineSize" /* HORIZONTAL */
];
var ScrollToKeysOrdered = [
"top" /* VERTICAL */,
"left" /* HORIZONTAL */
];
var getEntrySize = (resizeObserverEntry, sizeKey) => Math.round(resizeObserverEntry.borderBoxSize[0][sizeKey]);
var VirtualScroller = class {
z = ScrollElementSizeKeysOrdered[0];
o = ScrollKeysOrdered[0];
p = ResizeObserverSizeKeysOrdered[0];
q = ScrollToKeysOrdered[0];
A = 0;
j = 0;
r = 0;
/* It is more useful to store scrollPos - scrollElementOffset in one variable for future calculations */
d = 0;
g = 0;
B = 0;
/** {@inheritDoc VirtualScrollerRuntimeParams.itemCount} */
e = 0;
f = 0;
/** {@inheritDoc VirtualScrollerRuntimeParams.overscanCount} */
s = DEFAULT_OVERSCAN_COUNT;
/** {@inheritDoc VirtualScrollerRuntimeParams.estimatedItemSize} */
C = DEFAULT_ESTIMATED_ITEM_SIZE;
b = null;
t = null;
a = EMPTY_TYPED_ARRAY;
c = EMPTY_TYPED_ARRAY;
/**
* Most significant bit of this._itemCount;
* caching it to avoid Math.clz32 calculations on every getIndex call
*/
D = 0;
/** @readonly {@inheritDoc VirtualScrollerInitialParams.horizontal} */
horizontal = false;
/**
* @readonly
* Sum of all item sizes */
scrollSize = 0;
/**
* @readonly
* Items range start with {@link VirtualScrollerRuntimeParams.overscanCount | overscanCount} included */
from = 0;
/**
* @readonly
* Items range end with {@link VirtualScrollerRuntimeParams.overscanCount | overscanCount} included */
to = 0;
/**
* @readonly
* Hash of item sizes. Changed when at least one visible item is resized */
sizesHash = 0;
u = /* @__PURE__ */ new Map();
v = /* @__PURE__ */ new Map();
/* header and footer; lengths are hardcoded */
k = [null, null];
l = [0, 0];
E = new ResizeObserver((entries) => {
let buff = 0;
for (const entry of entries) {
const index = this.k.indexOf(entry.target);
if (index !== -1) {
const diff = getEntrySize(entry, this.p) - this.l[index];
this.l[index] += diff;
buff += diff;
}
}
this.F(buff);
});
G = new ResizeObserver((entries) => {
let buff = 0, wasAtLeastOneSizeChanged = false;
const lim = (
/*#__NOINLINE__*/
getLiftingLimit(
this.c,
this.from + 1,
this.to
)
);
for (const entry of entries) {
const index = this.u.get(entry.target);
if (index < lim) {
const diff = getEntrySize(entry, this.p) - this.a[index];
if (diff) {
wasAtLeastOneSizeChanged = true;
this.a[index] += diff;
buff += diff;
update(this.c, index + 1, diff, lim);
}
this.U(s);
}));
W=new ResizeObserver((t => {
let s = 0, i = !1;
const e = /*#__NOINLINE__*/ c(this.C, this.from + 1, this.to);
for (const h of t) {
const t = this.F.get(h.target);
if (e > t) {
const r = y(h, this.h) - this.k[t];
r && (i = !0, this.k[t] += r, s += r, l(this.C, t + 1, r, e));
}
}
}
if (wasAtLeastOneSizeChanged) {
++batchLevel;
if (buff !== 0) {
update(this.c, lim, buff, this.c.length);
this.scrollSize += buff;
this.h(1 /* SCROLL_SIZE */);
if (buff < 0) {
this.i();
}
i && (++f, 0 !== s && (l(this.C, e, s, this.C.length), this.scrollSize += s, this.Z(1),
0 > s && this.G()), this.sizesHash = this.sizesHash + 1 & S, this.Z(2), _());
}));
N=[ [], [], [] ];
X() {
const t = this.horizontal ? 1 : 0, s = t + 2 * (h(this.I) ? 0 : 1);
this.t = v[s], this.i = E[s], this.h = z[t], this.o = b[t];
}
this.sizesHash = this.sizesHash + 1 & MAX_INT_32;
this.h(2 /* SIZES */);
batchEnd();
}
Y=() => {
const t = this.I[this.t] - this.v;
t !== this.T && (this.T = t, this.updateScrollerOffset(), this.G());
};
U(t) {
t && (this.v += t, this.T -= t, this.G());
});
/**
* Providing exact type here with 2 purposes:
* - forbid using it as an array (.map, .filter, etc.)
* - if events quantity does not match - type error would be shown
*/
m = [
[],
[],
[]
];
/**
* Update property names for resize events, dimensions and scroll position extraction
*
* @remarks
* `window.resize` event must be used for window scroller, `ResizeObserver` must be used in other cases.
* `offsetWidth` is used as item size in horizontal mode, `offsetHeight` - in vertical.
*/
K() {
const h = this.horizontal ? 1 : 0;
const w = isElement(this.b) ? 0 : 1;
const i = h + 2 * w;
this.z = ScrollElementSizeKeysOrdered[i];
this.o = ScrollKeysOrdered[i];
this.p = ResizeObserverSizeKeysOrdered[h];
this.q = ScrollToKeysOrdered[h];
}
n = () => {
const availableWidgetSize = this.b[this.z] - this.B;
if (availableWidgetSize !== this.f) {
this.f = availableWidgetSize;
this.updateScrollerOffset();
this.i();
}
j=() => {};
constructor(t) {
t && (this.horizontal = !!t.horizontal, this.S = t.estimatedScrollElementOffset || 0,
this.T = t.estimatedWidgetSize ?? 200, this.set(t));
};
F(relativeOffset) {
if (relativeOffset) {
this.B += relativeOffset;
this.f -= relativeOffset;
this.i();
}
on(t, s) {
return s.forEach((s => this.N[s].push(t))), () => s.forEach((s => this.N[s].splice(this.N[s].indexOf(t) >>> 0, 1)));
}
w = () => {
};
constructor(params) {
if (params) {
this.horizontal = !!params.horizontal;
this.g = params.estimatedScrollElementOffset || 0;
this.f = params.estimatedWidgetSize ?? DEFAULT_ESTIMATED_WIDGET_SIZE;
this.set(params);
}
Z(t) {
this.N[t].forEach(f ? u : s);
}
/**
* Subscribe to model events
* @returns unsubscribe function
* @param callBack - event to be triggered
* @param events - events to subscribe
*/
on(callBack, events) {
events.forEach((evt) => this.m[evt].push(callBack));
return () => events.forEach(
(evt) => this.m[evt].splice(
// >>> 0 - protection against -1
this.m[evt].indexOf(callBack) >>> 0,
1
)
);
}
/**
* Call all `event` subscribers
* @param event - event to emit
*/
h(event) {
this.m[event].forEach(batchLevel ? addToBatchQueue : call);
}
/**
* Get item index by pixel offset;
* @param offset - pixel offset
* @returns item index;
*
* @remarks
* Time complexity: `O(log2(itemCount))`
*/
getIndex(offset) {
if (offset <= 0) {
return 0;
}
getIndex(t) {
if (0 >= t) return 0;
if (t >= this.scrollSize) return this.p - 1;
let s = 0;
for (let i = this.L, e = 0; i > 0; i >>= 1) e = s + i, e <= this.p && t > this.C[e] && (s = e,
t -= this.C[e]);
return s;
if (offset >= this.scrollSize) {
return this.e - 1;
}
getOffset(t) {
"production" !== process.env.NODE_ENV && i(t <= this.p, "index must not be > itemCount");
let s = 0;
for (;t > 0; t -= t & -t) s += this.C[t];
return s;
let index = 0;
for (let bitMask = this.D, tempIndex = 0; bitMask > 0; bitMask >>= 1) {
tempIndex = index + bitMask;
if (tempIndex <= this.e && offset > this.c[tempIndex]) {
index = tempIndex;
offset -= this.c[tempIndex];
}
}
getSize(t) {
return "production" !== process.env.NODE_ENV && i(t < this.k.length, "itemIndex must be < itemCount in getSize"),
this.k[t];
return index;
}
/**
* Get pixel offset by item index;
* @param index - item index
* @returns pixel offset
*
* @remarks
* Time complexity: `O(log2(itemCount))`
*/
getOffset(index) {
if (process.env.NODE_ENV !== "production") {
if (index >= this.e) {
throw Error("index must not be > itemCount");
}
}
get visibleFrom() {
const t = this.q();
return t + (this.m - this.getOffset(t)) / this.k[t];
let result = 0;
for (; index > 0; index -= index & -index) {
result += this.c[index];
}
B() {
const t = Math.round(this.I[this.i]) - this.S;
t > this.m ? (this.m = t, this.G()) : t < this.m && (this.m = t, this.D());
return result;
}
/**
* Get last cached item size by item index
* @param itemIndex - item index;
* @returns last cached item size
*
* @remarks
* Time complexity: `O(1)`
*/
getSize(itemIndex) {
if (process.env.NODE_ENV !== "production") {
if (itemIndex >= this.a.length) {
throw Error("itemIndex must be < itemCount in getSize");
}
}
J=t => {
this.l = t.timeStamp, this.B();
};
setScroller(t) {
if (this.I && (clearInterval(this._), clearTimeout(this.u), this.j(), this.I.removeEventListener("scroll", this.J)),
this.I = t, t) {
if (this.X(), h(t)) {
const s = new ResizeObserver(this.Y);
s.observe(t), this.j = () => s.disconnect();
} else this.Y(), addEventListener("resize", this.Y), this.j = () => removeEventListener("resize", this.Y);
t.addEventListener("scroll", this.J, {
passive: !0
}), this.updateScrollerOffset(), this.B();
}
return this.a[itemIndex];
}
/**
* Returns snapshot of current scroll position.
*
* @remarks
* {@link VirtualScrollerExactPosition}
*
* @privateRemarks
* "returns" tag is missed by api-extractor for getters (for now).
* So using Regular description + type link.
*/
get visibleFrom() {
const firstVisibleIndex = this.x();
return firstVisibleIndex + (this.d - this.getOffset(firstVisibleIndex)) / this.a[firstVisibleIndex];
}
/**
* Synchronize current scroll position with visible range
*/
y() {
const newAlignedScrollPos = Math.round(this.b[this.o]) - this.g;
if (newAlignedScrollPos > this.d) {
this.d = newAlignedScrollPos;
this.i();
} else if (newAlignedScrollPos < this.d) {
this.d = newAlignedScrollPos;
this.L();
}
setContainer(t) {
t !== this.R && (this.R = t, this.updateScrollerOffset());
}
H = (e) => {
this.A = e.timeStamp;
this.y();
};
/**
* Informs model about scrollable element.
* @param element - scroller element
*
* @remarks
* Must be called with `null` before killing the instance.
*/
setScroller(element) {
if (this.b) {
clearInterval(this.j);
clearTimeout(this.r);
this.w();
this.b.removeEventListener(
"scroll",
this.H
);
}
updateScrollerOffset() {
clearTimeout(this.u), this.u = setTimeout((() => {
if (this.I) {
const t =
/*#__NOINLINE__*/ o(this.I, this.R, this.i, this.o), s = t - this.S;
s && (this.S = t, this.m -= s, this.B());
}
}), 256);
this.b = element;
if (element) {
this.K();
if (isElement(element)) {
const RO = new ResizeObserver(this.n);
RO.observe(element);
this.w = () => RO.disconnect();
} else {
this.n();
addEventListener("resize", this.n);
this.w = () => removeEventListener(
"resize",
this.n
);
}
element.addEventListener("scroll", this.H, {
passive: true
});
this.updateScrollerOffset();
this.y();
}
el(t, s) {
const i = this.H.get(t);
i && (this.H.delete(t), this.F.delete(i), this.W.unobserve(i)), s && (this.F.set(s, t),
this.H.set(t, s), this.W.observe(s, m));
}
/**
* Informs model about items container element. Usually not needed.
*
* @param element - container element
*
* @remarks
* By default top/left offset between scroll container and first scrollable item is `0`.
* In this case just {@link VirtualScroller.setScroller} is needed.
* But extra element is needed when something "foreign" stands between scroll container and first scrollable item to measure distance between them.
* That extra element is represented as `ItemsContainer` on this schema:
*
* ```plaintext
* <ScrollContainer> |.|
* Some header |s|
* Another header |c|
* <ItemsContainer> |r|
* item 1 [o]
* item 2 [l]
* item 3 [l]
* ... [b]
* </ItemsContainer> |a|
* Some footer |r|
* </ScrollContainer> |.|
* ```
*
* Must be called with `null` before killing the instance.
*/
setContainer(element) {
if (element !== this.t) {
this.t = element;
this.updateScrollerOffset();
}
V(t, s) {
const i = this.K[t];
i && (this.P.unobserve(i), this.U(-this.A[t]), this.K[t] = null, this.A[t] = 0),
s && (this.K[t] = s, this.P.observe(s, m));
}
/**
* Recalculates the offset between
* {@link VirtualScroller.setScroller | scroller element} and {@link VirtualScroller.setContainer | container element}.
*
* @remarks
* By default debounced at `256` milliseconds and called automatically when:
*
* - {@link VirtualScroller.setScroller | setScroller} was called;
*
* - {@link VirtualScroller.setContainer | setContainer} was called;
*
* - {@link VirtualScroller.setScroller | scroller element} was resized.
*
* Normally this is enough, needed only if something else would trigger this offset change.
*/
updateScrollerOffset() {
clearTimeout(this.r);
this.r = setTimeout(() => {
if (this.b) {
const newScrollElementOffset = (
/*#__NOINLINE__*/
getDistanceBetween(
this.b,
this.t,
this.o,
this.q
)
);
const diff = newScrollElementOffset - this.g;
if (diff) {
this.g = newScrollElementOffset;
this.d -= diff;
this.y();
}
}
}, 256);
}
/**
* Start/finish observing size of `element` at `index`. Observing is finished if element is `null`.
* @param index - item index
* @param element - element for item
*
* @remarks
* If an item was registered like `el(5, HTMLElement)` - it must be killed with `el(5, null)` before killing the instance.
*/
el(index, element) {
const oldElement = this.v.get(index);
if (oldElement) {
this.v.delete(index);
this.u.delete(oldElement);
this.G.unobserve(oldElement);
}
setStickyHeader(t) {
this.V(0, t);
if (element) {
this.u.set(element, index);
this.v.set(index, element);
this.G.observe(element, OBSERVE_OPTIONS);
}
setStickyFooter(t) {
this.V(1, t);
}
I(i, element) {
const oldElement = this.k[i];
if (oldElement) {
this.E.unobserve(oldElement);
this.F(-this.l[i]);
this.k[i] = null;
this.l[i] = 0;
}
q() {
return this.getIndex(this.m);
if (element) {
this.k[i] = element;
this.E.observe(element, OBSERVE_OPTIONS);
}
$() {
return this.p && 1 + this.getIndex(this.m + this.T);
}
/**
* Start observing size of sticky header `element`. Observing is finished if element is `null`.
* @param element - header element
*
* @remarks
* Must be called with `null` before killing the instance.
*/
setStickyHeader(element) {
this.I(STICKY_HEADER_INDEX, element);
}
/**
* Start observing size of sticky footer `element`. Observing is finished if element is `null`.
* @param element - footer element
*
* @remarks
* Must be called with `null` before killing the instance.
*/
setStickyFooter(element) {
this.I(STICKY_FOOTER_INDEX, element);
}
/**
* Get first visible item index (without overscan)
* @returns first visible item index
*/
x() {
return this.getIndex(this.d);
}
/**
* Get last visible item index (without overscan)
* @returns last visible item index
*/
J() {
return this.e && 1 + this.getIndex(
this.d + this.f
);
}
/**
* Used to update current visible items range when scrolling down/right;
* adds overscan reserve forward to reduce rerenders quantity
*/
i() {
const exactTo = this.J();
if (exactTo > this.to) {
this.to = Math.min(this.e, exactTo + this.s);
this.from = this.x();
this.h(0 /* RANGE */);
}
G() {
const t = this.$();
t > this.to && (this.to = Math.min(this.p, t + this.M), this.from = this.q(), this.Z(0));
}
/**
* Used to update current visible items range when scrolling up/left;
* adds overscan reserve backward to reduce rerenders quantity
*/
L() {
const exactFrom = this.x();
if (exactFrom < this.from) {
this.from = Math.max(0, exactFrom - this.s);
this.to = this.J();
this.h(0 /* RANGE */);
}
D() {
const t = this.q();
t < this.from && (this.from = Math.max(0, t - this.M), this.to = this.$(), this.Z(0));
}
/**
* Scroll to pixel offset
*
* @param offset - offset to scroll to
* @param smooth - should smooth scroll be used
*/
scrollToOffset(offset, smooth) {
this.b?.scroll({
[this.q]: this.g + offset,
behavior: smooth ? "smooth" : "instant"
});
}
/**
* Scroll to item index
*
* @param index - item index to scroll to
* @param smooth - should smooth scroll be used
*
* @remarks
* Calls {@link VirtualScroller.scrollToOffset | scrollToOffset} with calcuated offset until desired scroll position is reached.
*/
scrollToIndex(index, smooth) {
clearInterval(this.j);
let attempts = 5;
this.j = setInterval(
() => {
const finishedScrolling = !smooth || performance.now() - this.A > 128;
if (finishedScrolling) {
if (!--attempts) {
clearInterval(this.j);
}
const whole = Math.trunc(index);
const desiredScrollPos = Math.min(
this.scrollSize - this.f,
this.getOffset(whole) + Math.round(this.a[whole] * (index - whole))
);
this.scrollToOffset(desiredScrollPos, smooth);
}
},
smooth ? 50 : 16
);
}
/**
* Notify model about items quantity change
* @param itemCount - new items quantity. {@link VirtualScrollerRuntimeParams.itemCount}
*/
setItemCount(itemCount) {
if (this.e !== itemCount) {
++batchLevel;
if (itemCount > MAX_INT_32) {
throw Error("itemCount must be <= " + MAX_INT_32);
}
this.e = itemCount;
this.D = itemCount && 1 << 31 - Math.clz32(itemCount);
if (itemCount > this.a.length) {
const newLen = Math.min(itemCount + ITEMS_ROOM, MAX_INT_32);
this.a = /*#__NOINLINE__*/
growTypedArray(
this.a,
newLen,
this.C || DEFAULT_ESTIMATED_ITEM_SIZE
);
this.c = new Uint32Array(newLen + 1);
syncWithArray(this.c, this.a);
}
this.scrollSize = this.getOffset(itemCount);
this.h(1 /* SCROLL_SIZE */);
if (this.to > itemCount) {
this.to = -1;
}
this.i();
batchEnd();
}
scrollToOffset(t, s) {
this.I?.scroll({
[this.o]: this.S + t,
behavior: s ? "smooth" : "instant"
});
}
/**
* Synchronize runtime parameters
* @param runtimeParams - runtime parameters
*/
set(runtimeParams) {
if (runtimeParams.estimatedItemSize) {
this.C = runtimeParams.estimatedItemSize;
}
scrollToIndex(t, s) {
clearInterval(this._);
let i = 5;
this._ = setInterval((() => {
if (!s || performance.now() - this.l > 128) {
--i || clearInterval(this._);
const e = Math.trunc(t), h = Math.min(this.scrollSize - this.T, this.getOffset(e) + Math.round(this.k[e] * (t - e)));
this.scrollToOffset(h, s);
}
}), s ? 50 : 16);
if (runtimeParams.overscanCount !== void 0) {
this.s = runtimeParams.overscanCount;
}
setItemCount(t) {
if (this.p !== t) {
if (++f, i(S >= t, "itemCount must be <= " + S), this.p = t, this.L = t && 1 << 31 - Math.clz32(t),
t > this.k.length) {
const s = Math.min(t + 32, S);
this.k = /*#__NOINLINE__*/ e(this.k, s, this.O || 40), this.C = new Uint32Array(s + 1),
/*#__NOINLINE__*/ n(this.C, this.k);
}
this.scrollSize = this.getOffset(t), this.Z(1), this.to > t && (this.to = -1), this.G(),
_();
}
if (runtimeParams.itemCount !== void 0) {
this.setItemCount(runtimeParams.itemCount);
}
set(t) {
t.estimatedItemSize && (this.O = t.estimatedItemSize), void 0 !== t.overscanCount && (this.M = t.overscanCount),
void 0 !== t.itemCount && this.setItemCount(t.itemCount);
}
}
export { p as VirtualScroller, t as VirtualScrollerEvent };
//# sourceMappingURL=index.js.map
}
};
var VirtualScroller_default = VirtualScroller;
export {
VirtualScroller_default as VirtualScroller,
VirtualScrollerEvent
};
global.ResizeObserver ||= class {
observe() {}
unobserve() {}
disconnect() {}
observe(){}
unobserve(){}
disconnect(){}
}
// src/constants/index.ts
var VirtualScrollerEvent = {
RANGE: 0,
SCROLL_SIZE: 1,
SIZES: 2
};
const t = {
RANGE: 0,
SCROLL_SIZE: 1,
SIZES: 2
}, s = t => t(), i = (t, s) => {
if (!t) throw Error(s);
}, e = (t, s, i) => {
const e = new Uint32Array(s);
return e.set(t), e.fill(i, t.length), e;
}, h = t => t instanceof HTMLElement, r = (t, s) => t.getBoundingClientRect()[s], o = (t, s, i, e) => s && t && t !== s ? t[i] + Math.round(r(s, e) - (h(t) ? r(t, e) : 0)) : 0, n = (t, s) => {
t.set(s, 1);
for (let s, i = 1, e = t.length; e > i; i++) s = i + (i & -i), e > s && (t[s] += t[i]);
}, l = (t, s, i, e) => {
for (;e > s; s += s & -s) t[s] += i;
}, c = (t, s, i) => {
for (;i > s; s += s & -s) ;
return Math.min(s, t.length);
}, a = new Set;
// src/utils/misc/index.ts
var call = (fn) => fn();
var growTypedArray = (sourceArray, newLength, fillValue) => {
const resultArray = new Uint32Array(newLength);
resultArray.set(sourceArray);
resultArray.fill(fillValue, sourceArray.length);
return resultArray;
};
var isElement = (el) => el instanceof HTMLElement;
var getElementOffset = (element, scrollToKey) => element.getBoundingClientRect()[scrollToKey];
var getDistanceBetween = (scrollElement, containerElement, scrollKey, scrollToKey) => {
if (!containerElement || !scrollElement || scrollElement === containerElement) {
return 0;
}
return scrollElement[scrollKey] + Math.round(
getElementOffset(containerElement, scrollToKey) - (isElement(scrollElement) ? getElementOffset(scrollElement, scrollToKey) : 0)
);
};
let f = 0;
// src/utils/fTree/index.ts
var syncWithArray = (fTree, sourceArray) => {
fTree.set(sourceArray, 1);
for (let i = 1, fTreeLength = fTree.length, j; i < fTreeLength; i++) {
j = i + (i & -i);
if (j < fTreeLength) {
fTree[j] += fTree[i];
}
}
};
var update = (fTree, i, delta, limitTreeLiftingIndex) => {
for (; i < limitTreeLiftingIndex; i += i & -i) {
fTree[i] += delta;
}
};
var getLiftingLimit = (fTree, from, to) => {
for (; from < to; from += from & -from)
;
return Math.min(from, fTree.length);
};
const u = () => {
--f || (a.forEach(s), a.clear());
}, _ = t => a.add(t), m = {
box: "border-box"
}, d = new Uint32Array(0), S = 2147483647, v = [ "offsetHeight", "offsetWidth", "innerHeight", "innerWidth" ], E = [ "scrollTop", "scrollLeft", "scrollY", "scrollX" ], z = [ "blockSize", "inlineSize" ], b = [ "top", "left" ], y = (t, s) => Math.round(t.borderBoxSize[0][s]);
class p {
t=v[0];
i=E[0];
h=z[0];
o=b[0];
l=0;
u=0;
_=0;
m=0;
S=0;
v=0;
p=0;
T=0;
M=6;
O=40;
I=null;
R=null;
k=d;
C=d;
L=0;
horizontal=!1;
scrollSize=0;
from=0;
to=0;
sizesHash=0;
F=new Map;
H=new Map;
K=[ null, null ];
A=[ 0, 0 ];
P=new ResizeObserver((t => {
let s = 0;
for (const i of t) {
const t = this.K.indexOf(i.target);
if (-1 !== t) {
const e = y(i, this.h) - this.A[t];
this.A[t] += e, s += e;
}
// src/models/VirtualScroller.ts
var BatchQueue = /* @__PURE__ */ new Set();
var batchLevel = 0;
var batchEnd = () => {
if (!--batchLevel) {
BatchQueue.forEach(call);
BatchQueue.clear();
}
};
var addToBatchQueue = (fn) => BatchQueue.add(fn);
var OBSERVE_OPTIONS = {
box: "border-box"
};
var ITEMS_ROOM = 32;
var DEFAULT_OVERSCAN_COUNT = 6;
var DEFAULT_ESTIMATED_WIDGET_SIZE = 200;
var DEFAULT_ESTIMATED_ITEM_SIZE = 40;
var EMPTY_TYPED_ARRAY = new Uint32Array(0);
var MAX_INT_32 = 2147483647;
var STICKY_HEADER_INDEX = 0;
var STICKY_FOOTER_INDEX = 1;
var ScrollElementSizeKeysOrdered = [
"offsetHeight" /* ELEMENT_VERTICAL */,
"offsetWidth" /* ELEMENT_HORIZONTAL */,
"innerHeight" /* WINDOW_VERTICAL */,
"innerWidth" /* WINDOW_HORIZONTAL */
];
var ScrollKeysOrdered = [
"scrollTop" /* ELEMENT_VERTICAL */,
"scrollLeft" /* ELEMENT_HORIZONTAL */,
"scrollY" /* WINDOW_VERTICAL */,
"scrollX" /* WINDOW_HORIZONTAL */
];
var ResizeObserverSizeKeysOrdered = [
"blockSize" /* VERTICAL */,
"inlineSize" /* HORIZONTAL */
];
var ScrollToKeysOrdered = [
"top" /* VERTICAL */,
"left" /* HORIZONTAL */
];
var getEntrySize = (resizeObserverEntry, sizeKey) => Math.round(resizeObserverEntry.borderBoxSize[0][sizeKey]);
var VirtualScroller = class {
z = ScrollElementSizeKeysOrdered[0];
o = ScrollKeysOrdered[0];
p = ResizeObserverSizeKeysOrdered[0];
q = ScrollToKeysOrdered[0];
A = 0;
j = 0;
r = 0;
/* It is more useful to store scrollPos - scrollElementOffset in one variable for future calculations */
d = 0;
g = 0;
B = 0;
/** {@inheritDoc VirtualScrollerRuntimeParams.itemCount} */
e = 0;
f = 0;
/** {@inheritDoc VirtualScrollerRuntimeParams.overscanCount} */
s = DEFAULT_OVERSCAN_COUNT;
/** {@inheritDoc VirtualScrollerRuntimeParams.estimatedItemSize} */
C = DEFAULT_ESTIMATED_ITEM_SIZE;
b = null;
t = null;
a = EMPTY_TYPED_ARRAY;
c = EMPTY_TYPED_ARRAY;
/**
* Most significant bit of this._itemCount;
* caching it to avoid Math.clz32 calculations on every getIndex call
*/
D = 0;
/** @readonly {@inheritDoc VirtualScrollerInitialParams.horizontal} */
horizontal = false;
/**
* @readonly
* Sum of all item sizes */
scrollSize = 0;
/**
* @readonly
* Items range start with {@link VirtualScrollerRuntimeParams.overscanCount | overscanCount} included */
from = 0;
/**
* @readonly
* Items range end with {@link VirtualScrollerRuntimeParams.overscanCount | overscanCount} included */
to = 0;
/**
* @readonly
* Hash of item sizes. Changed when at least one visible item is resized */
sizesHash = 0;
u = /* @__PURE__ */ new Map();
v = /* @__PURE__ */ new Map();
/* header and footer; lengths are hardcoded */
k = [null, null];
l = [0, 0];
E = new ResizeObserver((entries) => {
let buff = 0;
for (const entry of entries) {
const index = this.k.indexOf(entry.target);
if (index !== -1) {
const diff = getEntrySize(entry, this.p) - this.l[index];
this.l[index] += diff;
buff += diff;
}
}
this.F(buff);
});
G = new ResizeObserver((entries) => {
let buff = 0, wasAtLeastOneSizeChanged = false;
const lim = (
/*#__NOINLINE__*/
getLiftingLimit(
this.c,
this.from + 1,
this.to
)
);
for (const entry of entries) {
const index = this.u.get(entry.target);
if (index < lim) {
const diff = getEntrySize(entry, this.p) - this.a[index];
if (diff) {
wasAtLeastOneSizeChanged = true;
this.a[index] += diff;
buff += diff;
update(this.c, index + 1, diff, lim);
}
this.U(s);
}));
W=new ResizeObserver((t => {
let s = 0, i = !1;
const e = /*#__NOINLINE__*/ c(this.C, this.from + 1, this.to);
for (const h of t) {
const t = this.F.get(h.target);
if (e > t) {
const r = y(h, this.h) - this.k[t];
r && (i = !0, this.k[t] += r, s += r, l(this.C, t + 1, r, e));
}
}
}
if (wasAtLeastOneSizeChanged) {
++batchLevel;
if (buff !== 0) {
update(this.c, lim, buff, this.c.length);
this.scrollSize += buff;
this.h(1 /* SCROLL_SIZE */);
if (buff < 0) {
this.i();
}
i && (++f, 0 !== s && (l(this.C, e, s, this.C.length), this.scrollSize += s, this.Z(1),
0 > s && this.G()), this.sizesHash = this.sizesHash + 1 & S, this.Z(2), u());
}));
N=[ [], [], [] ];
X() {
const t = this.horizontal ? 1 : 0, s = t + 2 * (h(this.I) ? 0 : 1);
this.t = v[s], this.i = E[s], this.h = z[t], this.o = b[t];
}
this.sizesHash = this.sizesHash + 1 & MAX_INT_32;
this.h(2 /* SIZES */);
batchEnd();
}
Y=() => {
const t = this.I[this.t] - this.v;
t !== this.T && (this.T = t, this.updateScrollerOffset(), this.G());
};
U(t) {
t && (this.v += t, this.T -= t, this.G());
});
/**
* Providing exact type here with 2 purposes:
* - forbid using it as an array (.map, .filter, etc.)
* - if events quantity does not match - type error would be shown
*/
m = [
[],
[],
[]
];
/**
* Update property names for resize events, dimensions and scroll position extraction
*
* @remarks
* `window.resize` event must be used for window scroller, `ResizeObserver` must be used in other cases.
* `offsetWidth` is used as item size in horizontal mode, `offsetHeight` - in vertical.
*/
K() {
const h = this.horizontal ? 1 : 0;
const w = isElement(this.b) ? 0 : 1;
const i = h + 2 * w;
this.z = ScrollElementSizeKeysOrdered[i];
this.o = ScrollKeysOrdered[i];
this.p = ResizeObserverSizeKeysOrdered[h];
this.q = ScrollToKeysOrdered[h];
}
n = () => {
const availableWidgetSize = this.b[this.z] - this.B;
if (availableWidgetSize !== this.f) {
this.f = availableWidgetSize;
this.updateScrollerOffset();
this.i();
}
j=() => {};
constructor(t) {
t && (this.horizontal = !!t.horizontal, this.S = t.estimatedScrollElementOffset || 0,
this.T = t.estimatedWidgetSize ?? 200, this.set(t));
};
F(relativeOffset) {
if (relativeOffset) {
this.B += relativeOffset;
this.f -= relativeOffset;
this.i();
}
on(t, s) {
return s.forEach((s => this.N[s].push(t))), () => s.forEach((s => this.N[s].splice(this.N[s].indexOf(t) >>> 0, 1)));
}
w = () => {
};
constructor(params) {
if (params) {
this.horizontal = !!params.horizontal;
this.g = params.estimatedScrollElementOffset || 0;
this.f = params.estimatedWidgetSize ?? DEFAULT_ESTIMATED_WIDGET_SIZE;
this.set(params);
}
Z(t) {
this.N[t].forEach(f ? _ : s);
}
/**
* Subscribe to model events
* @returns unsubscribe function
* @param callBack - event to be triggered
* @param events - events to subscribe
*/
on(callBack, events) {
events.forEach((evt) => this.m[evt].push(callBack));
return () => events.forEach(
(evt) => this.m[evt].splice(
// >>> 0 - protection against -1
this.m[evt].indexOf(callBack) >>> 0,
1
)
);
}
/**
* Call all `event` subscribers
* @param event - event to emit
*/
h(event) {
this.m[event].forEach(batchLevel ? addToBatchQueue : call);
}
/**
* Get item index by pixel offset;
* @param offset - pixel offset
* @returns item index;
*
* @remarks
* Time complexity: `O(log2(itemCount))`
*/
getIndex(offset) {
if (offset <= 0) {
return 0;
}
getIndex(t) {
if (0 >= t) return 0;
if (t >= this.scrollSize) return this.p - 1;
let s = 0;
for (let i = this.L, e = 0; i > 0; i >>= 1) e = s + i, e <= this.p && t > this.C[e] && (s = e,
t -= this.C[e]);
return s;
if (offset >= this.scrollSize) {
return this.e - 1;
}
getOffset(t) {
"production" !== process.env.NODE_ENV && i(t <= this.p, "index must not be > itemCount");
let s = 0;
for (;t > 0; t -= t & -t) s += this.C[t];
return s;
let index = 0;
for (let bitMask = this.D, tempIndex = 0; bitMask > 0; bitMask >>= 1) {
tempIndex = index + bitMask;
if (tempIndex <= this.e && offset > this.c[tempIndex]) {
index = tempIndex;
offset -= this.c[tempIndex];
}
}
getSize(t) {
return "production" !== process.env.NODE_ENV && i(t < this.k.length, "itemIndex must be < itemCount in getSize"),
this.k[t];
return index;
}
/**
* Get pixel offset by item index;
* @param index - item index
* @returns pixel offset
*
* @remarks
* Time complexity: `O(log2(itemCount))`
*/
getOffset(index) {
if (process.env.NODE_ENV !== "production") {
if (index >= this.e) {
throw Error("index must not be > itemCount");
}
}
get visibleFrom() {
const t = this.q();
return t + (this.m - this.getOffset(t)) / this.k[t];
let result = 0;
for (; index > 0; index -= index & -index) {
result += this.c[index];
}
B() {
const t = Math.round(this.I[this.i]) - this.S;
t > this.m ? (this.m = t, this.G()) : t < this.m && (this.m = t, this.D());
return result;
}
/**
* Get last cached item size by item index
* @param itemIndex - item index;
* @returns last cached item size
*
* @remarks
* Time complexity: `O(1)`
*/
getSize(itemIndex) {
if (process.env.NODE_ENV !== "production") {
if (itemIndex >= this.a.length) {
throw Error("itemIndex must be < itemCount in getSize");
}
}
J=t => {
this.l = t.timeStamp, this.B();
};
setScroller(t) {
if (this.I && (clearInterval(this.u), clearTimeout(this._), this.j(), this.I.removeEventListener("scroll", this.J)),
this.I = t, t) {
if (this.X(), h(t)) {
const s = new ResizeObserver(this.Y);
s.observe(t), this.j = () => s.disconnect();
} else this.Y(), addEventListener("resize", this.Y), this.j = () => removeEventListener("resize", this.Y);
t.addEventListener("scroll", this.J, {
passive: !0
}), this.updateScrollerOffset(), this.B();
}
return this.a[itemIndex];
}
/**
* Returns snapshot of current scroll position.
*
* @remarks
* {@link VirtualScrollerExactPosition}
*
* @privateRemarks
* "returns" tag is missed by api-extractor for getters (for now).
* So using Regular description + type link.
*/
get visibleFrom() {
const firstVisibleIndex = this.x();
return firstVisibleIndex + (this.d - this.getOffset(firstVisibleIndex)) / this.a[firstVisibleIndex];
}
/**
* Synchronize current scroll position with visible range
*/
y() {
const newAlignedScrollPos = Math.round(this.b[this.o]) - this.g;
if (newAlignedScrollPos > this.d) {
this.d = newAlignedScrollPos;
this.i();
} else if (newAlignedScrollPos < this.d) {
this.d = newAlignedScrollPos;
this.L();
}
setContainer(t) {
t !== this.R && (this.R = t, this.updateScrollerOffset());
}
H = (e) => {
this.A = e.timeStamp;
this.y();
};
/**
* Informs model about scrollable element.
* @param element - scroller element
*
* @remarks
* Must be called with `null` before killing the instance.
*/
setScroller(element) {
if (this.b) {
clearInterval(this.j);
clearTimeout(this.r);
this.w();
this.b.removeEventListener(
"scroll",
this.H
);
}
updateScrollerOffset() {
clearTimeout(this._), this._ = setTimeout((() => {
if (this.I) {
const t =
/*#__NOINLINE__*/ o(this.I, this.R, this.i, this.o), s = t - this.S;
s && (this.S = t, this.m -= s, this.B());
}
}), 256);
this.b = element;
if (element) {
this.K();
if (isElement(element)) {
const RO = new ResizeObserver(this.n);
RO.observe(element);
this.w = () => RO.disconnect();
} else {
this.n();
addEventListener("resize", this.n);
this.w = () => removeEventListener(
"resize",
this.n
);
}
element.addEventListener("scroll", this.H, {
passive: true
});
this.updateScrollerOffset();
this.y();
}
el(t, s) {
const i = this.H.get(t);
i && (this.H.delete(t), this.F.delete(i), this.W.unobserve(i)), s && (this.F.set(s, t),
this.H.set(t, s), this.W.observe(s, m));
}
/**
* Informs model about items container element. Usually not needed.
*
* @param element - container element
*
* @remarks
* By default top/left offset between scroll container and first scrollable item is `0`.
* In this case just {@link VirtualScroller.setScroller} is needed.
* But extra element is needed when something "foreign" stands between scroll container and first scrollable item to measure distance between them.
* That extra element is represented as `ItemsContainer` on this schema:
*
* ```plaintext
* <ScrollContainer> |.|
* Some header |s|
* Another header |c|
* <ItemsContainer> |r|
* item 1 [o]
* item 2 [l]
* item 3 [l]
* ... [b]
* </ItemsContainer> |a|
* Some footer |r|
* </ScrollContainer> |.|
* ```
*
* Must be called with `null` before killing the instance.
*/
setContainer(element) {
if (element !== this.t) {
this.t = element;
this.updateScrollerOffset();
}
V(t, s) {
const i = this.K[t];
i && (this.P.unobserve(i), this.U(-this.A[t]), this.K[t] = null, this.A[t] = 0),
s && (this.K[t] = s, this.P.observe(s, m));
}
/**
* Recalculates the offset between
* {@link VirtualScroller.setScroller | scroller element} and {@link VirtualScroller.setContainer | container element}.
*
* @remarks
* By default debounced at `256` milliseconds and called automatically when:
*
* - {@link VirtualScroller.setScroller | setScroller} was called;
*
* - {@link VirtualScroller.setContainer | setContainer} was called;
*
* - {@link VirtualScroller.setScroller | scroller element} was resized.
*
* Normally this is enough, needed only if something else would trigger this offset change.
*/
updateScrollerOffset() {
clearTimeout(this.r);
this.r = setTimeout(() => {
if (this.b) {
const newScrollElementOffset = (
/*#__NOINLINE__*/
getDistanceBetween(
this.b,
this.t,
this.o,
this.q
)
);
const diff = newScrollElementOffset - this.g;
if (diff) {
this.g = newScrollElementOffset;
this.d -= diff;
this.y();
}
}
}, 256);
}
/**
* Start/finish observing size of `element` at `index`. Observing is finished if element is `null`.
* @param index - item index
* @param element - element for item
*
* @remarks
* If an item was registered like `el(5, HTMLElement)` - it must be killed with `el(5, null)` before killing the instance.
*/
el(index, element) {
const oldElement = this.v.get(index);
if (oldElement) {
this.v.delete(index);
this.u.delete(oldElement);
this.G.unobserve(oldElement);
}
setStickyHeader(t) {
this.V(0, t);
if (element) {
this.u.set(element, index);
this.v.set(index, element);
this.G.observe(element, OBSERVE_OPTIONS);
}
setStickyFooter(t) {
this.V(1, t);
}
I(i, element) {
const oldElement = this.k[i];
if (oldElement) {
this.E.unobserve(oldElement);
this.F(-this.l[i]);
this.k[i] = null;
this.l[i] = 0;
}
q() {
return this.getIndex(this.m);
if (element) {
this.k[i] = element;
this.E.observe(element, OBSERVE_OPTIONS);
}
$() {
return this.p && 1 + this.getIndex(this.m + this.T);
}
/**
* Start observing size of sticky header `element`. Observing is finished if element is `null`.
* @param element - header element
*
* @remarks
* Must be called with `null` before killing the instance.
*/
setStickyHeader(element) {
this.I(STICKY_HEADER_INDEX, element);
}
/**
* Start observing size of sticky footer `element`. Observing is finished if element is `null`.
* @param element - footer element
*
* @remarks
* Must be called with `null` before killing the instance.
*/
setStickyFooter(element) {
this.I(STICKY_FOOTER_INDEX, element);
}
/**
* Get first visible item index (without overscan)
* @returns first visible item index
*/
x() {
return this.getIndex(this.d);
}
/**
* Get last visible item index (without overscan)
* @returns last visible item index
*/
J() {
return this.e && 1 + this.getIndex(
this.d + this.f
);
}
/**
* Used to update current visible items range when scrolling down/right;
* adds overscan reserve forward to reduce rerenders quantity
*/
i() {
const exactTo = this.J();
if (exactTo > this.to) {
this.to = Math.min(this.e, exactTo + this.s);
this.from = this.x();
this.h(0 /* RANGE */);
}
G() {
const t = this.$();
t > this.to && (this.to = Math.min(this.p, t + this.M), this.from = this.q(), this.Z(0));
}
/**
* Used to update current visible items range when scrolling up/left;
* adds overscan reserve backward to reduce rerenders quantity
*/
L() {
const exactFrom = this.x();
if (exactFrom < this.from) {
this.from = Math.max(0, exactFrom - this.s);
this.to = this.J();
this.h(0 /* RANGE */);
}
D() {
const t = this.q();
t < this.from && (this.from = Math.max(0, t - this.M), this.to = this.$(), this.Z(0));
}
/**
* Scroll to pixel offset
*
* @param offset - offset to scroll to
* @param smooth - should smooth scroll be used
*/
scrollToOffset(offset, smooth) {
this.b?.scroll({
[this.q]: this.g + offset,
behavior: smooth ? "smooth" : "instant"
});
}
/**
* Scroll to item index
*
* @param index - item index to scroll to
* @param smooth - should smooth scroll be used
*
* @remarks
* Calls {@link VirtualScroller.scrollToOffset | scrollToOffset} with calcuated offset until desired scroll position is reached.
*/
scrollToIndex(index, smooth) {
clearInterval(this.j);
let attempts = 5;
this.j = setInterval(
() => {
const finishedScrolling = !smooth || performance.now() - this.A > 128;
if (finishedScrolling) {
if (!--attempts) {
clearInterval(this.j);
}
const whole = Math.trunc(index);
const desiredScrollPos = Math.min(
this.scrollSize - this.f,
this.getOffset(whole) + Math.round(this.a[whole] * (index - whole))
);
this.scrollToOffset(desiredScrollPos, smooth);
}
},
smooth ? 50 : 16
);
}
/**
* Notify model about items quantity change
* @param itemCount - new items quantity. {@link VirtualScrollerRuntimeParams.itemCount}
*/
setItemCount(itemCount) {
if (this.e !== itemCount) {
++batchLevel;
if (itemCount > MAX_INT_32) {
throw Error("itemCount must be <= " + MAX_INT_32);
}
this.e = itemCount;
this.D = itemCount && 1 << 31 - Math.clz32(itemCount);
if (itemCount > this.a.length) {
const newLen = Math.min(itemCount + ITEMS_ROOM, MAX_INT_32);
this.a = /*#__NOINLINE__*/
growTypedArray(
this.a,
newLen,
this.C || DEFAULT_ESTIMATED_ITEM_SIZE
);
this.c = new Uint32Array(newLen + 1);
syncWithArray(this.c, this.a);
}
this.scrollSize = this.getOffset(itemCount);
this.h(1 /* SCROLL_SIZE */);
if (this.to > itemCount) {
this.to = -1;
}
this.i();
batchEnd();
}
scrollToOffset(t, s) {
this.I?.scroll({
[this.o]: this.S + t,
behavior: s ? "smooth" : "instant"
});
}
/**
* Synchronize runtime parameters
* @param runtimeParams - runtime parameters
*/
set(runtimeParams) {
if (runtimeParams.estimatedItemSize) {
this.C = runtimeParams.estimatedItemSize;
}
scrollToIndex(t, s) {
clearInterval(this.u);
let i = 5;
this.u = setInterval((() => {
if (!s || performance.now() - this.l > 128) {
--i || clearInterval(this.u);
const e = Math.trunc(t), h = Math.min(this.scrollSize - this.T, this.getOffset(e) + Math.round(this.k[e] * (t - e)));
this.scrollToOffset(h, s);
}
}), s ? 50 : 16);
if (runtimeParams.overscanCount !== void 0) {
this.s = runtimeParams.overscanCount;
}
setItemCount(t) {
if (this.p !== t) {
if (++f, i(S >= t, "itemCount must be <= " + S), this.p = t, this.L = t && 1 << 31 - Math.clz32(t),
t > this.k.length) {
const s = Math.min(t + 32, S);
this.k = /*#__NOINLINE__*/ e(this.k, s, this.O || 40), this.C = new Uint32Array(s + 1),
/*#__NOINLINE__*/ n(this.C, this.k);
}
this.scrollSize = this.getOffset(t), this.Z(1), this.to > t && (this.to = -1), this.G(),
u();
}
if (runtimeParams.itemCount !== void 0) {
this.setItemCount(runtimeParams.itemCount);
}
set(t) {
t.estimatedItemSize && (this.O = t.estimatedItemSize), void 0 !== t.overscanCount && (this.M = t.overscanCount),
void 0 !== t.itemCount && this.setItemCount(t.itemCount);
}
}
export { p as VirtualScroller, t as VirtualScrollerEvent };
}
};
var VirtualScroller_default = VirtualScroller;
export {
VirtualScroller_default as VirtualScroller,
VirtualScrollerEvent
};
{
"name": "@af-utils/virtual-core",
"private": false,
"version": "0.0.22",
"version": "0.0.23",
"description": "Model for rendering large scrollable data",

@@ -11,5 +11,9 @@ "repository": {

},
"homepage": "https://af-utils.vercel.app/virtual",
"homepage": "https://af-utils.com/virtual",
"bugs": "https://github.com/nowaalex/af-utils/issues",
"author": "Alex Fomin <nowaalex@gmail.com> (https://github.com/nowaalex/)",
"author": {
"name": "Alex Fomin",
"email": "nowaalex@gmail.com",
"url": "https://github.com/nowaalex/"
},
"license": "MIT",

@@ -24,3 +28,6 @@ "sideEffects": false,

"exports": {
"./lib/bundlesize.index.js": "./lib/bundlesize.index.js",
"./bundlesizes": {
"types": "./lib/bundlesizes.d.ts",
"default": "./lib/bundlesizes.js"
},
".": {

@@ -51,7 +58,6 @@ "types": "./lib/index.d.ts",

"devDependencies": {
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^11.1.6",
"rollup": "^4.13.0",
"esbuild": "^0.20.2",
"typescript": "^5.4.5",
"ts-jest": "^29.1.2",
"@af-utils/rollup-plugin-export-bundle-size": "0.0.5"
"@af-utils/export-bundle-size": "0.0.5"
},

@@ -62,6 +68,11 @@ "publishConfig": {

"scripts": {
"dev": "rollup -w -c rollup.config.ts --configPlugin typescript & tsc-alias -w",
"build": "rollup -c rollup.config.ts --configPlugin typescript && tsc-alias && api-extractor run && rm -r lib/types",
"dev": "pnpm build:types && pnpm esbuild:dev",
"build": "pnpm build:types && pnpm esbuild:build && pnpm export-bundle-size && pnpm api-extractor",
"build:types": "tsc && tsc-alias",
"esbuild:build": "node build.mjs prod",
"esbuild:dev": "node build.mjs",
"api-extractor": "api-extractor run && rm -r lib/types",
"export-bundle-size": "export-bundle-size -i ./lib/index.js -o ./lib/bundlesizes.js",
"test": "jest"
}
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc