jquery.dgtable 0.6.15 → 0.6.16

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,5 +1,5 @@
1
1
  /*!
2
- * jquery.dgtable 0.6.15
2
+ * jquery.dgtable 0.6.16
3
3
  * git://github.com/danielgindi/jquery.dgtable.git
4
4
  */
5
5
  'use strict';
@@ -7,7 +7,7 @@
7
7
  var jQuery = require('jquery');
8
8
  var ScrollHelper_js = require('@danielgindi/dom-utils/lib/ScrollHelper.js');
9
9
  var Css_js = require('@danielgindi/dom-utils/lib/Css.js');
10
- var Css = require('@danielgindi/dom-utils/lib/Css');
10
+ var VirtualListHelper = require('@danielgindi/virtual-list-helper');
11
11
 
12
12
  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
13
13
 
@@ -2348,1463 +2348,6 @@ class SelectionHelper {
2348
2348
  }
2349
2349
  }
2350
2350
 
2351
- /**
2352
- * @typedef {function(index: number):(number|undefined)} VirtualListHelper~ItemHeightEstimatorFunction
2353
- */
2354
-
2355
- /**
2356
- * @typedef {function():Element} VirtualListHelper~ItemElementCreatorFunction
2357
- */
2358
-
2359
- /**
2360
- * @typedef {function(itemEl: Element, index: number)} VirtualListHelper~ItemRenderFunction
2361
- */
2362
-
2363
- /**
2364
- * @typedef {function(itemEl: Element)} VirtualListHelper~ItemUnrenderFunction
2365
- */
2366
-
2367
- /**
2368
- * @typedef {Object} VirtualListHelper~Options
2369
- * @property {Element} list - the main element to operate inside of
2370
- * @property {Element?} [itemsParent] - the element to use as parent for the items (automatically created in virtual mode, uses parent by default in non-virtual mode)
2371
- * @property {boolean} [autoVirtualWrapperWidth=true] automatically set the width of the virtual wrapper
2372
- * @property {boolean} [hookScrollEvent=true] automatically hook scroll event as needed
2373
- * @property {number} [count=0] the item count
2374
- * @property {boolean} [virtual=true] is virtual mode on?
2375
- * @property {number} [estimatedItemHeight=20] estimated item height
2376
- * @property {number} [buffer=5] the amount of buffer items to keep on each end of the list
2377
- * @property {VirtualListHelper~ItemHeightEstimatorFunction} [itemHeightEstimatorFn] an optional function for providing item height estimations
2378
- * @property {VirtualListHelper~ItemElementCreatorFunction} [itemElementCreatorFn] an optional function for providing fresh item elements (default creates `<li />`s)
2379
- * @property {VirtualListHelper~ItemRenderFunction} [onItemRender] a function for rendering element content based on item index
2380
- * @property {VirtualListHelper~ItemUnrenderFunction} [onItemUnrender] a function for freeing resources in an item element
2381
- * @property {function(height: number)} [onScrollHeightChange] a function to be notified when scroll height changes
2382
- *
2383
- */
2384
-
2385
- /** */
2386
-
2387
- const hasOwnProperty$1 = Object.prototype.hasOwnProperty;
2388
-
2389
- const hasInsertAdjacentElement = Element.prototype.insertAdjacentElement !== undefined;
2390
-
2391
- function insertBefore(el, before, parent) {
2392
- if (!before)
2393
- parent.appendChild(el);else
2394
- if (hasInsertAdjacentElement === false || el instanceof DocumentFragment)
2395
- parent.insertBefore(el, before);else
2396
- before.insertAdjacentElement('beforebegin', el);
2397
- }
2398
-
2399
- /**
2400
- *
2401
- * @param {Element} itemEl
2402
- * @param {DocumentFragment|null} fragment
2403
- * @param {Node|undefined} before
2404
- * @param {Element} itemParent
2405
- * @returns {DocumentFragment|null}
2406
- */
2407
- function insertBeforeWithFragment(itemEl, fragment, before, itemParent) {
2408
- if (itemEl.parentNode !== itemParent) {
2409
- if (!fragment)
2410
- fragment = document.createDocumentFragment();
2411
- fragment.appendChild(itemEl);
2412
- } else {
2413
- // insert fragment
2414
- if (fragment && fragment.childNodes.length > 0) {
2415
- insertBefore(fragment, before, itemParent);
2416
- fragment = null;
2417
- }
2418
-
2419
- // insert element
2420
- if (itemEl.nextSibling !== before) {
2421
- insertBefore(itemEl, before, itemParent);
2422
- }
2423
- }
2424
-
2425
- return fragment;
2426
- }
2427
-
2428
- class VirtualListHelper {
2429
- /**
2430
- * @param {VirtualListHelper~Options} opts
2431
- */
2432
- constructor(opts) {var _opts$autoVirtualWrap;
2433
- /** @private */
2434
- const p = this._p = {
2435
- // these come from options:
2436
-
2437
- list: opts.list || null,
2438
- hookScrollEvent: opts.hookScrollEvent === undefined ? true : !!opts.hookScrollEvent,
2439
- count: opts.count || 0,
2440
- virtual: opts.virtual === undefined ? true : !!opts.virtual,
2441
- userItemsParent: opts.itemsParent || null,
2442
- setVirtualWrapperWidth: (_opts$autoVirtualWrap = opts.autoVirtualWrapperWidth) !== null && _opts$autoVirtualWrap !== void 0 ? _opts$autoVirtualWrap : true,
2443
- estimatedItemHeight: 20,
2444
- buffer: 5,
2445
-
2446
- /** @type VirtualListHelper~ItemHeightEstimatorFunction|null */
2447
- itemHeightEstimatorFn: null,
2448
-
2449
- /** @type VirtualListHelper~ItemElementCreatorFunction|null */
2450
- itemElementCreatorFn: defaultElementCreator,
2451
-
2452
- /** @type VirtualListHelper~ItemRenderFunction|null */
2453
- onItemRender: null,
2454
-
2455
- /** @type VirtualListHelper~ItemUnrenderFunction|null */
2456
- onItemUnrender: null,
2457
-
2458
- /** @type funct(height: number)|null */
2459
- onScrollHeightChange: null,
2460
-
2461
- // internal:
2462
-
2463
- /** @type Element|null */
2464
- virtualWrapper: null,
2465
-
2466
- /** @type Element|null */
2467
- currentItemsParent: null,
2468
-
2469
- /** @type (number|undefined)[] */
2470
- cachedItemHeights: [],
2471
-
2472
- /** @type (number|undefined)[] */
2473
- cachedItemEstimatedHeights: [],
2474
-
2475
- /** @type (number|undefined)[] */
2476
- cachedItemPositions: [],
2477
-
2478
- /** @type number */
2479
- itemPositionsNeedsUpdate: 0,
2480
-
2481
- /** @type function */
2482
- boundRender: this.render.bind(this),
2483
-
2484
- /** @type Element[] */
2485
- existingEls: []
2486
- };
2487
-
2488
- p.currentItemsParent = p.userItemsParent || p.list;
2489
-
2490
- if (typeof opts.hookScrollEvent === 'boolean')
2491
- this.setHookScrollEvent(opts.hookScrollEvent);else
2492
- this._hookEvents();
2493
-
2494
- if (typeof opts.count === 'number')
2495
- this.setCount(opts.count);
2496
-
2497
- if (typeof opts.virtual === 'boolean')
2498
- this.setVirtual(opts.virtual);
2499
-
2500
- if (typeof opts.estimatedItemHeight === 'number')
2501
- this.setEstimatedItemHeight(opts.estimatedItemHeight);
2502
-
2503
- if (typeof opts.buffer === 'number')
2504
- this.setBuffer(opts.buffer);
2505
-
2506
- if (typeof opts.itemHeightEstimatorFn === 'function')
2507
- this.setItemHeightEstimatorFn(opts.itemHeightEstimatorFn);
2508
-
2509
- if (typeof opts.itemElementCreatorFn === 'function')
2510
- this.setItemElementCreatorFn(opts.itemElementCreatorFn);
2511
-
2512
- if (typeof opts.onItemRender === 'function')
2513
- this.setOnItemRender(opts.onItemRender);
2514
-
2515
- if (typeof opts.onItemUnrender === 'function')
2516
- this.setOnItemUnrender(opts.onItemUnrender);
2517
-
2518
- if (typeof opts.onScrollHeightChange === 'function')
2519
- this.setOnScrollHeightChange(opts.onScrollHeightChange);
2520
- }
2521
-
2522
- /**
2523
- * Clean up and free up all resources.
2524
- */
2525
- destroy() {
2526
- this._unhookEvents().invalidate()._destroyElements();
2527
- }
2528
-
2529
- /**
2530
- * Sets whether 'scroll' event on the list should be hooked automatically.
2531
- * @param {boolean} enabled
2532
- * @returns {VirtualListHelper}
2533
- */
2534
- setHookScrollEvent(enabled) {
2535
- const p = this._p;
2536
- enabled = enabled === undefined ? true : !!enabled;
2537
-
2538
- if (p.hookScrollEvent === enabled)
2539
- return this;
2540
-
2541
- p.hookScrollEvent = enabled;
2542
-
2543
- this._unhookEvents()._hookEvents();
2544
-
2545
- return this;
2546
- }
2547
-
2548
- /**
2549
- * @returns {boolean} whether 'scroll' event on the list should be hooked automatically
2550
- */
2551
- isHookScrollEventEnabled() {
2552
- const p = this._p;
2553
- return p.hookScrollEvent;
2554
- }
2555
-
2556
- /**
2557
- * Sets the list item count. <br />
2558
- * You should probably call `render()` after this.
2559
- * @param {number} count
2560
- * @returns {VirtualListHelper}
2561
- */
2562
- setCount(count) {
2563
- const p = this._p;
2564
- p.count = count;
2565
-
2566
- return this.invalidate();
2567
- }
2568
-
2569
- /**
2570
- * @returns {number} current item count
2571
- */
2572
- getCount() {
2573
- const p = this._p;
2574
- return p.count;
2575
- }
2576
-
2577
- /**
2578
- * Switches between virtual and non-virtual mode. <br />
2579
- * The list is invalidated automatically. <br />
2580
- * You should call `render()` to update the view.
2581
- * @param {boolean} enabled
2582
- * @returns {VirtualListHelper}
2583
- */
2584
- setVirtual(enabled) {
2585
- const p = this._p;
2586
- enabled = enabled === undefined ? true : !!enabled;
2587
-
2588
- if (p.virtual === enabled)
2589
- return this;
2590
-
2591
- p.virtual = enabled;
2592
-
2593
- this._hookEvents().invalidate()._destroyElements();
2594
-
2595
- return this;
2596
- }
2597
-
2598
- /**
2599
- * @returns {boolean} virtual mode
2600
- */
2601
- isVirtual() {
2602
- const p = this._p;
2603
- return p.virtual;
2604
- }
2605
-
2606
- /**
2607
- * Sets estimated item height. <br />
2608
- * No need to be accurate. <br />
2609
- * The better the estimation - the better the scrollbar behavior will be. <br />
2610
- * Applicable for virtual-mode only. <br />
2611
- * You should `invalidate` if you want this to take effect on the existing rendering.
2612
- * @param {number} height - a positive number representing estimated item height.
2613
- * @returns {VirtualListHelper}
2614
- */
2615
- setEstimatedItemHeight(height) {
2616
- const p = this._p;
2617
- p.estimatedItemHeight = Math.abs((typeof height === 'number' ? height : Number(height)) || 20);
2618
- return this;
2619
- }
2620
-
2621
- /**
2622
- * @returns {number} current item height estimation
2623
- */
2624
- getEstimatedItemHeight() {
2625
- const p = this._p;
2626
- return p.estimatedItemHeight;
2627
- }
2628
-
2629
- /**
2630
- * Sets whether the virtual wrapper width should be set automatically. <br />
2631
- * @param {boolean} enabled
2632
- * @returns {VirtualListHelper}
2633
- */
2634
- setAutoVirtualWrapperWidth(enabled) {
2635
- const p = this._p;
2636
- p.autoVirtualWrapperWidth = enabled === undefined ? true : !!enabled;
2637
-
2638
- if (p.virtualWrapper) {
2639
- if (p.autoVirtualWrapperWidth !== p.virtualWrapperWidthWasSet) {
2640
- p.virtualWrapper.style.width = p.autoVirtualWrapperWidth ? '100%' : '';
2641
- p.virtualWrapperWidthWasSet = p.autoVirtualWrapperWidth;
2642
- }
2643
- }
2644
-
2645
- return this;
2646
- }
2647
-
2648
- /**
2649
- * @returns {boolean} whether the virtual wrapper width should be set automatically
2650
- */
2651
- isAutoVirtualWrapperWidth() {
2652
- const p = this._p;
2653
- return p.autoVirtualWrapperWidth;
2654
- }
2655
-
2656
- /**
2657
- * Sets the amount of buffer items to keep on each end of the list. <br />
2658
- * Applicable for virtual-mode only.
2659
- * @param {number} buffer - a positive value representing the count of buffer items for each end.
2660
- * @returns {VirtualListHelper}
2661
- */
2662
- setBuffer(buffer) {
2663
- const p = this._p;
2664
- p.buffer = Math.abs(typeof buffer === 'number' ? buffer : Number(buffer) || 5);
2665
- return this;
2666
- }
2667
-
2668
- /**
2669
- * @returns {number} current buffer value
2670
- */
2671
- getBuffer() {
2672
- const p = this._p;
2673
- return p.buffer;
2674
- }
2675
-
2676
- /**
2677
- * The `itemHeightEstimatorFn` is an alternative to `estimatedItemHeight` to give better estimations for specific item. <br/>
2678
- * It's optional, and if it's present - it should return either a numeric height estimation,
2679
- * or `undefined` to fall back to the default estimation. <br />
2680
- * You should `invalidate` if you want this to take effect on the existing rendering.
2681
- * @param {VirtualListHelper~ItemHeightEstimatorFunction} fn
2682
- * @returns {VirtualListHelper}
2683
- */
2684
- setItemHeightEstimatorFn(fn) {
2685
- const p = this._p;
2686
- p.itemHeightEstimatorFn = fn;
2687
- return this;
2688
- }
2689
-
2690
- /**
2691
- * The `itemElementCreatorFn` is a function creating a basic item element, that will be possibly reused later. <br />
2692
- * It has no association with a specific item index. <br />
2693
- * You should `invalidate` if you want this to take effect on the existing rendering.
2694
- * @param {VirtualListHelper~ItemElementCreatorFunction} fn
2695
- * @returns {VirtualListHelper}
2696
- */
2697
- setItemElementCreatorFn(fn) {
2698
- const p = this._p;
2699
- p.itemElementCreatorFn = fn || defaultElementCreator;
2700
- return this;
2701
- }
2702
-
2703
- /**
2704
- * The `onItemRender` is a function called for rendering the contents of an item. <br />
2705
- * It's passed an `Element` and an item index. <br />
2706
- * You should `invalidate` if you want this to take effect on the existing rendering.
2707
- * @param {VirtualListHelper~ItemRenderFunction} fn
2708
- * @returns {VirtualListHelper}
2709
- */
2710
- setOnItemRender(fn) {
2711
- const p = this._p;
2712
- p.onItemRender = fn;
2713
- return this;
2714
- }
2715
-
2716
- /**
2717
- * The `onItemUnrender` is a function called for freeing resources in an item element,
2718
- * if you've attached something that needs to be explicitly freed. <br />
2719
- * It's passed an `Element` only, and has no association with a specific index,
2720
- * as by the time it's called - the indexes are probably not valid anymore.
2721
- * @param {VirtualListHelper~ItemUnrenderFunction} fn
2722
- * @returns {VirtualListHelper}
2723
- */
2724
- setOnItemUnrender(fn) {
2725
- const p = this._p;
2726
- p.onItemUnrender = fn;
2727
- return this;
2728
- }
2729
-
2730
- /**
2731
- * The `onScrollHeightChange` is a function called when the scroll height changes.
2732
- * @param {function(height: number)} fn
2733
- * @returns {VirtualListHelper}
2734
- */
2735
- setOnScrollHeightChange(fn) {
2736
- const p = this._p;
2737
- p.onScrollHeightChange = fn;
2738
- return this;
2739
- }
2740
-
2741
- /**
2742
- * Estimates the full scroll height. This gets better as more renderings occur.
2743
- * @returns {number}
2744
- */
2745
- estimateFullHeight() {
2746
- const p = this._p;
2747
-
2748
- if (p.count === 0)
2749
- return 0;
2750
-
2751
- if (p.virtual) {
2752
- return this._calculateItemPosition(p.count) || 0;
2753
- } else {
2754
- const existingEls = p.existingEls;
2755
- if (p.count === existingEls.length) {
2756
- let rect1 = existingEls[0].getBoundingClientRect();
2757
- let rect2 = existingEls[existingEls.length - 1].getBoundingClientRect();
2758
- return rect2.top - rect1.top + rect2.height;
2759
- }
2760
-
2761
- return this._calculateItemPosition(p.count) || 0;
2762
- }
2763
- }
2764
-
2765
- /**
2766
- * States that the cached positions/heights are invalid,
2767
- * and needs to be completely re-calculated.<br />
2768
- * You should probably call `render()` after this.
2769
- * @returns {VirtualListHelper}
2770
- */
2771
- invalidatePositions() {
2772
- const p = this._p;
2773
-
2774
- p.itemPositionsNeedsUpdate = 0;
2775
- p.cachedItemHeights = [];
2776
- p.cachedItemEstimatedHeights = [];
2777
- p.cachedItemPositions = [];
2778
- p.cachedItemHeights.length = p.count;
2779
- p.cachedItemEstimatedHeights.length = p.count;
2780
- p.cachedItemPositions.length = p.count;
2781
-
2782
- return this;
2783
- }
2784
-
2785
- /**
2786
- * States that the indexes/item count/rendered content are invalid,
2787
- * and needs to be completely re-calculated and re-rendered. <br />
2788
- * You should probably call `render()` after this.
2789
- * @returns {VirtualListHelper}
2790
- */
2791
- invalidate() {
2792
- const p = this._p;
2793
-
2794
- this.invalidatePositions();
2795
-
2796
- if (!p.virtual) {
2797
- this._destroyElements();
2798
- }
2799
-
2800
- return this;
2801
- }
2802
-
2803
- /**
2804
- * Renders the current viewport. <br />
2805
- * Call this after making changes to the list. <br />
2806
- * In virtual mode, this is called automatically for every scroll event.
2807
- */
2808
- render() {
2809
- const p = this._p;
2810
- const list = p.list;
2811
- const virtual = p.virtual;
2812
- let virtualWrapper = p.virtualWrapper;
2813
- let itemParent = p.currentItemsParent;
2814
- let scrollTop = list.scrollTop;
2815
- let visibleHeight = list.clientHeight;
2816
- let visibleBottom = scrollTop + visibleHeight;
2817
- let count = p.count;
2818
- let buffer = p.buffer;
2819
- let onItemUnrender = p.onItemUnrender;
2820
- let existingEls = p.existingEls;
2821
- let existingCount = existingEls.length;
2822
-
2823
- if (virtual) {
2824
- const originalWidth = list.clientWidth;
2825
-
2826
- if (!virtualWrapper) {
2827
- virtualWrapper = p.virtualWrapper = p.userItemsParent;
2828
- if (!virtualWrapper) {
2829
- virtualWrapper = p.virtualWrapper = document.createElement('div');
2830
- list.appendChild(virtualWrapper);
2831
- }
2832
-
2833
- this._resetCurrentItemsParent();
2834
- itemParent = p.currentItemsParent;
2835
-
2836
- if (p.autoVirtualWrapperWidth) {
2837
- virtualWrapper.style.width = '100%';
2838
- p.virtualWrapperWidthWasSet = true;
2839
- } else {
2840
- p.virtualWrapperWidthWasSet = false;
2841
- }
2842
- }
2843
-
2844
- // Mark all of them for potential reuse
2845
- for (let i = 0; i < existingCount; i++) {
2846
- existingEls[i][ReuseElSymbol] = true;
2847
- }
2848
-
2849
- // Make sure we have at least estimated positions for all items so we can translate scroll position
2850
- this._calculateItemPosition(p.count - 1);
2851
-
2852
- // Find existing elements index range
2853
- let existingRange = this._getExistingElsRange();
2854
-
2855
- // Find first visible element
2856
- let firstVisibleIndex = binarySearchPosition(p.cachedItemPositions, scrollTop);
2857
- let firstRenderIndex = Math.max(0, firstVisibleIndex - buffer);
2858
-
2859
- // Iterate over viewport
2860
- let index = firstRenderIndex;
2861
- let renderPos = this._calculateItemPosition(index);
2862
- let bufferEnd = buffer;
2863
-
2864
- // we want to render until viewport's bottom + buffer items
2865
- let maxIndexToRender = Math.max(index, binarySearchPosition(p.cachedItemPositions, visibleBottom - 1) + 1 + buffer);
2866
-
2867
- let insertedItems = [];
2868
-
2869
- /** @type DocumentFragment|null */
2870
- let fragment = null;
2871
-
2872
- // Find the element to insert before
2873
- let before = virtualWrapper.childNodes[0];
2874
-
2875
- const findElementToReuse = function (index) {
2876
- // Find existing element to reuse
2877
- /** @type Element|undefined */
2878
- let existingEl = undefined;
2879
-
2880
- if (existingRange.firstIndex !== -1 && index >= existingRange.firstIndex && index <= existingRange.lastIndex) {
2881
- existingEl = existingEls.find((x) => x[ItemIndexSymbol] === index && x[ReuseElSymbol] === true);
2882
- }
2883
-
2884
- if (existingEl === undefined) {
2885
- existingEl = (existingRange.firstIndex < firstRenderIndex || existingRange.firstValidArrayIndex > 0 ?
2886
- existingEls.find((x) =>
2887
- (x[ItemIndexSymbol] < firstRenderIndex || false === hasOwnProperty$1.call(x, ItemIndexSymbol)) &&
2888
- x[ReuseElSymbol] === true) :
2889
- undefined) ||
2890
- findLast(existingEls, (x) => x[ReuseElSymbol] === true);
2891
- }
2892
-
2893
- if (existingEl !== undefined) {
2894
- delete existingEl[ReuseElSymbol];
2895
- }
2896
-
2897
- return existingEl;
2898
- };
2899
-
2900
- // First we iterate and try to add all at once in a fragment, as much as we can.
2901
- // And then reflow the at once.
2902
- for (; index < count && index < maxIndexToRender; index++) {
2903
- let existingEl = findElementToReuse(index);
2904
-
2905
- if (before && before === existingEl)
2906
- before = before.nextSibling;
2907
-
2908
- // Dequeue the element by reusing or creating a new one
2909
- const itemEl = this._dequeueElementForIndex(existingEl, index, before, true);
2910
- insertedItems.push([itemEl, index]);
2911
-
2912
- fragment = insertBeforeWithFragment(itemEl, fragment, before, itemParent);
2913
- }
2914
-
2915
- // Insert any remaining fragment
2916
- if (fragment && fragment.childNodes.length > 0) {
2917
- insertBefore(fragment, before, itemParent);
2918
- }
2919
-
2920
- // Iterate on inserted items and reflow them
2921
- for (let item of insertedItems) {
2922
- const index = item[1];
2923
- this._insertItemAndFlow(item[0], index, false /* inserted already */);
2924
- renderPos = p.cachedItemPositions[index] + p.cachedItemHeights[index];
2925
- }
2926
-
2927
- // See if we still need to insert more items
2928
- if (renderPos < visibleBottom) {
2929
- for (; (renderPos < visibleBottom || bufferEnd-- > 0) && index < count; index++) {
2930
- let existingEl = findElementToReuse(index);
2931
-
2932
- if (before && before === existingEl)
2933
- before = before.nextSibling;
2934
-
2935
- // Dequeue the element by reusing or creating a new one
2936
- this._dequeueElementForIndex(existingEl, index, before, false);
2937
-
2938
- // Increment pointers
2939
- renderPos = p.cachedItemPositions[index] + p.cachedItemHeights[index];
2940
- }
2941
- }
2942
-
2943
- // Calculate up-to-date scroll height
2944
- let scrollHeight = this.estimateFullHeight();
2945
- let scrollHeightPx = scrollHeight + 'px';
2946
-
2947
- if (virtualWrapper.style.height !== scrollHeightPx) {var _p$onScrollHeightChan;
2948
- p.virtualWrapper.style.height = scrollHeightPx;
2949
- (_p$onScrollHeightChan = p.onScrollHeightChange) === null || _p$onScrollHeightChan === void 0 || _p$onScrollHeightChan.call(p, scrollHeight);
2950
- }
2951
-
2952
- if (originalWidth !== list.clientWidth)
2953
- this.render();
2954
- } else {// non-virtual
2955
- if (count !== existingEls.length) {
2956
- for (let i = 0; i < existingCount; i++) {
2957
- existingEls[i][ReuseElSymbol] = true;
2958
- }
2959
-
2960
- // Find the element to insert before
2961
- let before = itemParent.childNodes[0];
2962
-
2963
- /** @type DocumentFragment|null */
2964
- let fragment = null;
2965
-
2966
- for (let index = 0; index < count; index++) {
2967
- // Find existing element to reuse
2968
- let existingEl = existingEls.find((x) => x[ItemIndexSymbol] === index && x[ReuseElSymbol] === true);
2969
-
2970
- if (existingEl !== undefined) {
2971
- delete existingEl[ReuseElSymbol];
2972
- }
2973
-
2974
- if (before && before === existingEl)
2975
- before = before.nextSibling;
2976
-
2977
- // Dequeue the element by reusing or creating a new one
2978
- const itemEl = this._dequeueElementForIndex(existingEl, index, before, true);
2979
-
2980
- fragment = insertBeforeWithFragment(itemEl, fragment, before, itemParent);
2981
- }
2982
-
2983
- // Insert any remaining fragment
2984
- if (fragment && fragment.childNodes.length > 0) {
2985
- insertBefore(fragment, before, itemParent);
2986
- }
2987
- }
2988
- }
2989
-
2990
- // Cleanup extra unused elements
2991
- existingCount = existingEls.length; // May have changed
2992
- for (let i = 0; i < existingCount; i++) {
2993
- const el = existingEls[i];
2994
- if (el[ReuseElSymbol] !== true) continue;
2995
-
2996
- let parent = el.parentNode;
2997
- if (parent)
2998
- parent.removeChild(el);
2999
- if (onItemUnrender && el[ItemIndexSymbol] !== undefined)
3000
- onItemUnrender(el);
3001
- existingEls.splice(i, 1);
3002
-
3003
- i--;
3004
- existingCount--;
3005
- }
3006
- }
3007
-
3008
- /**
3009
- * States that items were added at a certain position in the list. <br />
3010
- * Virtual mode: Call `render()` to update the view after making changes.
3011
- * @param {number} count
3012
- * @param {number} [atIndex=-1]
3013
- * @returns {VirtualListHelper}
3014
- */
3015
- addItemsAt(count, atIndex = -1) {
3016
- if (typeof count !== 'number' || count <= 0)
3017
- return this;
3018
-
3019
- const p = this._p;
3020
-
3021
- if (atIndex < 0 || atIndex >= p.count)
3022
- atIndex = p.count;
3023
-
3024
- p.count += count;
3025
-
3026
- if (p.virtual) {
3027
- if (atIndex >= 0 && atIndex < p.count) {
3028
- this._invalidateItemIndexesAt(atIndex, -1);
3029
- }
3030
- } else
3031
- {// non-virtual
3032
- let existingEls = p.existingEls;
3033
- let existingCount = existingEls.length;
3034
- if (existingCount !== p.count - count)
3035
- return this;
3036
-
3037
- let existingRange = this._getExistingElsRange();
3038
- if (existingRange.firstValidArrayIndex === -1)
3039
- return this;
3040
-
3041
- const itemParent = p.currentItemsParent;
3042
-
3043
- let startIndex = existingRange.firstValidArrayIndex + atIndex - existingRange.firstIndex;
3044
-
3045
- this._pushItemIndexesAt(atIndex, count);
3046
-
3047
- /** @type Node|undefined */
3048
- let before = existingEls[startIndex - 1] ?
3049
- existingEls[startIndex - 1].nextSibling :
3050
- existingEls[0];
3051
-
3052
- /** @type DocumentFragment|null */
3053
- let fragment = null;
3054
-
3055
- for (let index = atIndex, end = atIndex + count; index < end; index++) {
3056
- const itemEl = this._dequeueElementForIndex(undefined, index, before, true);
3057
- fragment = insertBeforeWithFragment(itemEl, fragment, before, itemParent);
3058
- }
3059
-
3060
- // Insert any remaining fragment
3061
- if (fragment && fragment.childNodes.length > 0) {
3062
- insertBefore(fragment, before, itemParent);
3063
- }
3064
- }
3065
-
3066
- return this;
3067
- }
3068
-
3069
- /**
3070
- * States that items were removed at a certain position in the list. <br />
3071
- * Virtual mode: Call `render()` to update the view after making changes.
3072
- * @param {number} count
3073
- * @param {number} atIndex
3074
- * @returns {VirtualListHelper}
3075
- */
3076
- removeItemsAt(count, atIndex) {
3077
- const p = this._p;
3078
-
3079
- if (typeof count !== 'number' || typeof atIndex !== 'number' || count <= 0 || atIndex < 0 || atIndex >= p.count)
3080
- return this;
3081
-
3082
- p.count -= Math.min(count, p.count - atIndex);
3083
-
3084
- if (p.virtual) {
3085
- this._invalidateItemIndexesAt(atIndex, -1);
3086
- } else
3087
- {// non-virtual
3088
- let existingEls = p.existingEls;
3089
- let existingCount = existingEls.length;
3090
- if (existingCount !== p.count + count)
3091
- return this;
3092
-
3093
- let existingRange = this._getExistingElsRange();
3094
- if (existingRange.firstValidArrayIndex === -1)
3095
- return this;
3096
-
3097
- this._pushItemIndexesAt(atIndex + count, -count);
3098
-
3099
- const onItemUnrender = p.onItemUnrender;
3100
- let index = existingRange.firstValidArrayIndex + atIndex - existingRange.firstIndex;
3101
- if (index < existingEls.length) {
3102
- for (let i = 0; i < count; i++) {
3103
- let itemEl = existingEls[index + i];
3104
-
3105
- let parent = itemEl.parentNode;
3106
- if (parent)
3107
- parent.removeChild(itemEl);
3108
- if (onItemUnrender && itemEl[ItemIndexSymbol] !== undefined)
3109
- onItemUnrender(itemEl);
3110
- }
3111
- existingEls.splice(index, count);
3112
- }
3113
- }
3114
-
3115
- return this;
3116
- }
3117
-
3118
- /**
3119
- * Mark an element for a re-render. <br />
3120
- * Virtual mode: Call `render()` to update the view after making changes. <br />
3121
- * Non-virtual mode - the element is re-rendered immediately.
3122
- * @param {number} index - the index of the element to re-render
3123
- * @returns {VirtualListHelper}
3124
- */
3125
- refreshItemAt(index) {
3126
- const p = this._p;
3127
-
3128
- if (typeof index !== 'number' || index < 0 || index >= p.count)
3129
- return this;
3130
-
3131
- if (p.virtual) {
3132
- this._invalidateItemIndexesAt(index, 1);
3133
- } else
3134
- {// non-virtual
3135
- let existingEls = p.existingEls;
3136
- let existingCount = existingEls.length;
3137
- if (existingCount !== p.count)
3138
- return this;
3139
-
3140
- let existingRange = this._getExistingElsRange();
3141
-
3142
- if (index >= existingRange.firstIndex && index <= existingRange.lastIndex) {
3143
- let itemEl = existingEls[existingRange.firstValidArrayIndex + index - existingRange.firstIndex];
3144
- delete itemEl[ItemIndexSymbol];
3145
- this._dequeueElementForIndex(itemEl, index, itemEl.nextSibling, false);
3146
- }
3147
- }
3148
-
3149
- return this;
3150
- }
3151
-
3152
- /**
3153
- * Tests whether an item at the specified index is rendered.
3154
- * @param {number} index - the index to test
3155
- * @returns {boolean}
3156
- */
3157
- isItemRendered(index) {
3158
- const p = this._p;
3159
-
3160
- if (typeof index !== 'number' || index < 0 || index >= p.count)
3161
- return false;
3162
-
3163
- let existingRange = this._getExistingElsRange();
3164
-
3165
- return index >= existingRange.firstIndex && index <= existingRange.lastIndex;
3166
- }
3167
-
3168
- /**
3169
- * Retrieves DOM element for the item at the specified index - if it's currently rendered.
3170
- * @param {number} index - the index to retrieve
3171
- * @returns {Element|undefined}
3172
- */
3173
- getItemElementAt(index) {
3174
- const p = this._p;
3175
-
3176
- if (typeof index !== 'number' || index < 0 || index >= p.count)
3177
- return undefined;
3178
-
3179
- let existingEls = p.existingEls;
3180
- let existingRange = this._getExistingElsRange();
3181
-
3182
- if (index >= existingRange.firstIndex && index <= existingRange.lastIndex) {
3183
- return existingEls[existingRange.firstValidArrayIndex + index - existingRange.firstIndex];
3184
- }
3185
-
3186
- return undefined;
3187
- }
3188
-
3189
- /**
3190
- * Retrieves the position for the specified index. <br />
3191
- * Can be used to scroll to a specific item.
3192
- * @param {number} index
3193
- * @returns {number|undefined}
3194
- */
3195
- getItemPosition(index) {
3196
- const p = this._p;
3197
-
3198
- if (typeof index !== 'number' || index < 0 || index >= p.count)
3199
- return undefined;
3200
-
3201
- if (p.virtual) {
3202
- return this._calculateItemPosition(index);
3203
- } else {
3204
- let itemEl = this.getItemElementAt(index);
3205
- if (itemEl === undefined)
3206
- return undefined;
3207
-
3208
- const list = p.list;
3209
- return Css.getElementOffset(itemEl).top - Css.getElementOffset(list).top + list.scrollTop;
3210
- }
3211
- }
3212
-
3213
- /**
3214
- * Retrieves the item index for the specified element
3215
- * @param {Element} el
3216
- * @returns {number|undefined}
3217
- */
3218
- getItemIndexFromElement(el) {
3219
- return el ? el[ItemIndexSymbol] : undefined;
3220
- }
3221
-
3222
- /**
3223
- * Retrieves the size (or estimated size, if unknown) for the specified index. <br />
3224
- * @param {number} index
3225
- * @returns {number|undefined}
3226
- */
3227
- getItemSize(index) {
3228
- const p = this._p;
3229
-
3230
- if (typeof index !== 'number' || index < 0 || index >= p.count)
3231
- return undefined;
3232
-
3233
- let height = p.cachedItemHeights[index - 1]; // already calculated
3234
-
3235
- if (height === undefined) {
3236
- height = p.itemHeightEstimatorFn ? p.itemHeightEstimatorFn(index - 1) : null; // estimated per item
3237
-
3238
- if (typeof height !== 'number')
3239
- height = p.estimatedItemHeight; // estimated
3240
-
3241
- p.cachedItemEstimatedHeights[index - 1] = height;
3242
- }
3243
-
3244
- return height;
3245
- }
3246
-
3247
- /**
3248
- * Retrieves the number of items that fit into the current viewport.
3249
- * @returns {number}
3250
- */
3251
- getVisibleItemCount() {
3252
- const p = this._p,list = p.list;
3253
-
3254
- let scrollTop = list.scrollTop;
3255
- let visibleHeight = list.clientHeight;
3256
- let firstVisibleIndex, lastVisibleIndex;
3257
-
3258
- if (p.virtual) {
3259
- firstVisibleIndex = binarySearchPosition(p.cachedItemPositions, scrollTop);
3260
- lastVisibleIndex = binarySearchPosition(p.cachedItemPositions, scrollTop + visibleHeight, firstVisibleIndex);
3261
- } else
3262
- {
3263
- const retriever = (i) => {
3264
- let pos = this.getItemPosition(i);
3265
- if (pos === undefined)
3266
- pos = Infinity;
3267
- return pos;
3268
- };
3269
-
3270
- firstVisibleIndex = binarySearchPositionByFn(p.count, retriever, scrollTop);
3271
- lastVisibleIndex = binarySearchPositionByFn(p.count, retriever, scrollTop + visibleHeight, firstVisibleIndex);
3272
- }
3273
-
3274
- if (this.getItemPosition(lastVisibleIndex) === scrollTop + visibleHeight)
3275
- lastVisibleIndex--;
3276
- return lastVisibleIndex - firstVisibleIndex + 1;
3277
- }
3278
-
3279
- /**
3280
- * Renders a temporary ghost item. Can be used for testings several aspects of a proposed element, i.e measurements.
3281
- * @param {*} ghostIndex - the value to pass as the index for the renderer function
3282
- * @param {boolean} append - whether to append the item element to the DOM
3283
- * @param {function(itemEl: Element)} ghostTester - the function that will receive the element, called synchronously.
3284
- */
3285
- createGhostItemElement(ghostIndex, append, ghostTester) {
3286
- const p = this._p;
3287
-
3288
- let itemEl = this._dequeueElementForIndex(null, ghostIndex, false, true);
3289
- try {
3290
- if (append) {
3291
- p.currentItemsParent.appendChild(itemEl);
3292
- }
3293
- ghostTester(itemEl);
3294
- } finally {
3295
- if (append) {
3296
- let parent = itemEl.parentNode;
3297
- if (parent)
3298
- parent.removeChild(itemEl);
3299
- }
3300
- if (p.onItemUnrender)
3301
- p.onItemUnrender(itemEl);
3302
- }
3303
- }
3304
-
3305
- /**
3306
- * Reset the pointer to the current items wrapper
3307
- * @private
3308
- */
3309
- _resetCurrentItemsParent() {var _ref, _p$virtualWrapper;
3310
- const p = this._p;
3311
- p.currentItemsParent = (_ref = (_p$virtualWrapper = p.virtualWrapper) !== null && _p$virtualWrapper !== void 0 ? _p$virtualWrapper : p.userItemsParent) !== null && _ref !== void 0 ? _ref : p.list;
3312
- }
3313
-
3314
- /**
3315
- * Destroy all created elements, for cleanup
3316
- * @returns {VirtualListHelper}
3317
- * @private
3318
- */
3319
- _destroyElements() {
3320
- const p = this._p;
3321
- const onItemUnrender = p.onItemUnrender;
3322
- const existingEls = p.existingEls;
3323
-
3324
- for (let i = 0; i < existingEls.length; i++) {
3325
- const el = existingEls[i];
3326
-
3327
- let parent = el.parentNode;
3328
- if (parent)
3329
- parent.removeChild(el);
3330
- if (onItemUnrender && el[ItemIndexSymbol] !== undefined)
3331
- onItemUnrender(el);
3332
- }
3333
-
3334
- existingEls.length = 0;
3335
-
3336
- if (p.virtualWrapper) {
3337
- if (p.virtualWrapper !== p.userItemsParent) {
3338
- if (p.virtualWrapper.parentNode) {
3339
- p.virtualWrapper.parentNode.removeChild(p.virtualWrapper);
3340
- }
3341
- }
3342
- p.virtualWrapper = null;
3343
- this._resetCurrentItemsParent();
3344
- }
3345
-
3346
- return this;
3347
- }
3348
-
3349
- /**
3350
- * Marks (an) item(s) at specific index(es) as to be re-rendered. <br />
3351
- * Applicable for virtual mode only.
3352
- * @param {number} index
3353
- * @param {number} count
3354
- * @private
3355
- */
3356
- _invalidateItemIndexesAt(index, count) {
3357
- const p = this._p;
3358
-
3359
- this._setItemPositionsNeedsUpdate(index);
3360
-
3361
- let existingEls = p.existingEls;
3362
- let existingCount = existingEls.length;
3363
- let existingRange = this._getExistingElsRange();
3364
-
3365
- if (existingRange.firstValidArrayIndex === -1)
3366
- return;
3367
-
3368
- if (count === -1)
3369
- count = existingEls.length;
3370
-
3371
- // Clean
3372
- if (index >= existingRange.firstIndex && index <= existingRange.lastIndex) {
3373
- for (let i = existingRange.firstValidArrayIndex + index - existingRange.firstIndex,
3374
- c = 0;
3375
- i < existingCount && c < count;
3376
- i++, c++)
3377
- delete existingEls[i][ItemIndexSymbol];
3378
- }
3379
- }
3380
-
3381
- /**
3382
- * In/decrement the item-index marker for specific item(s). <br />
3383
- * Used for inserting/removing items in the middle of the list, without re-rendering everything. <br />
3384
- * Applicable for non-virtual mode only.
3385
- * @param {number} index
3386
- * @param {number} count
3387
- * @private
3388
- */
3389
- _pushItemIndexesAt(index, count) {
3390
- const p = this._p;
3391
-
3392
- let existingEls = p.existingEls;
3393
- let existingCount = existingEls.length;
3394
- let existingRange = this._getExistingElsRange();
3395
-
3396
- if (existingRange.firstValidArrayIndex === -1)
3397
- return;
3398
-
3399
- // Clean
3400
- if (index >= existingRange.firstIndex && index <= existingRange.lastIndex) {
3401
- for (let i = existingRange.firstValidArrayIndex + index - existingRange.firstIndex;
3402
- i < existingCount;
3403
- i++)
3404
- existingEls[i][ItemIndexSymbol] += count;
3405
- }
3406
- }
3407
-
3408
- /**
3409
- * Hook relevant events
3410
- * @returns {VirtualListHelper}
3411
- * @private
3412
- */
3413
- _hookEvents() {
3414
- const p = this._p;
3415
-
3416
- this._unhookEvents();
3417
-
3418
- if (p.virtual && p.hookScrollEvent) {
3419
- p.list && p.list.addEventListener('scroll', /**@type Function*/p.boundRender);
3420
- }
3421
-
3422
- return this;
3423
- }
3424
-
3425
- /**
3426
- * Unhook previously hooked events
3427
- * @returns {VirtualListHelper}
3428
- * @private
3429
- */
3430
- _unhookEvents() {
3431
- const p = this._p;
3432
-
3433
- p.list && p.list.removeEventListener('scroll', /**@type Function*/p.boundRender);
3434
-
3435
- return this;
3436
- }
3437
-
3438
- /**
3439
- * Mark item index from which the positions are not considered valid anymore. <br />
3440
- * Applicable for virtual mode only.
3441
- * @param {number} value
3442
- * @private
3443
- */
3444
- _setItemPositionsNeedsUpdate(value) {
3445
- const p = this._p;
3446
-
3447
- if (value < p.itemPositionsNeedsUpdate) {
3448
- p.itemPositionsNeedsUpdate = value;
3449
- }
3450
- }
3451
-
3452
- /**
3453
- * Calculates an item's top position (and stores in the private `cachedItemPositions` array). <br />
3454
- * Allows calculating last+1 index too, to get the bottom-most position. <br />
3455
- * Applicable for non-virtual mode only.
3456
- * @param {number} index
3457
- * @returns {number|undefined}
3458
- * @private
3459
- */
3460
- _calculateItemPosition(index) {
3461
- const p = this._p;
3462
-
3463
- const cachedItemPositions = p.cachedItemPositions;
3464
-
3465
- if (index >= p.itemPositionsNeedsUpdate) {
3466
- const count = p.count;
3467
- const cachedItemHeights = p.cachedItemHeights;
3468
- const cachedItemEstimatedHeights = p.cachedItemEstimatedHeights;
3469
- const estimatedItemHeight = p.estimatedItemHeight;
3470
- const itemHeightEstimatorFn = p.itemHeightEstimatorFn;
3471
-
3472
- if (cachedItemHeights.length !== count) {
3473
- cachedItemHeights.length = count;
3474
- cachedItemEstimatedHeights.length = count;
3475
- cachedItemPositions.length = count;
3476
- }
3477
-
3478
- let fromIndex = p.itemPositionsNeedsUpdate;
3479
- let toIndex = Math.min(index, count);
3480
-
3481
- let pos = 0;
3482
-
3483
- if (fromIndex > 0) {
3484
- pos = cachedItemPositions[fromIndex - 1];
3485
- }
3486
-
3487
- for (let i = fromIndex; i <= toIndex; i++) {
3488
- if (i === 0) {
3489
- cachedItemPositions[i] = pos;
3490
- continue;
3491
- }
3492
-
3493
- const prevIndex = i - 1;
3494
-
3495
- let height = cachedItemHeights[prevIndex]; // already calculated
3496
-
3497
- if (height === undefined) {
3498
- height = itemHeightEstimatorFn ? itemHeightEstimatorFn(prevIndex) : null; // estimated per item
3499
-
3500
- if (typeof height !== 'number')
3501
- height = estimatedItemHeight; // estimated
3502
-
3503
- cachedItemEstimatedHeights[prevIndex] = height;
3504
- }
3505
-
3506
- pos += height;
3507
- cachedItemPositions[i] = pos;
3508
- }
3509
-
3510
- p.itemPositionsNeedsUpdate = toIndex + 1;
3511
- }
3512
-
3513
- // item after the last (calculate full height)
3514
- if (index > 0 && index === p.count) {
3515
- let height = p.cachedItemHeights[index - 1]; // already calculated
3516
-
3517
- if (height === undefined) {
3518
- height = p.itemHeightEstimatorFn ? p.itemHeightEstimatorFn(index - 1) : null; // estimated per item
3519
-
3520
- if (typeof height !== 'number')
3521
- height = p.estimatedItemHeight; // estimated
3522
-
3523
- p.cachedItemEstimatedHeights[index - 1] = height;
3524
- }
3525
-
3526
- return cachedItemPositions[index - 1] + height;
3527
- }
3528
-
3529
- return cachedItemPositions[index];
3530
- }
3531
-
3532
- /**
3533
- * Create (or reuse an existing) element for an item at the specified index,
3534
- * and insert physically at specified position. <br />
3535
- * This will also update the element's position in the `existingEls` array.
3536
- * @param {Element|undefined} itemEl
3537
- * @param {number} index
3538
- * @param {Node|boolean|undefined} insertBefore
3539
- * @param {boolean|undefined} avoidDomReflow
3540
- * @returns {Element}
3541
- * @private
3542
- */
3543
- _dequeueElementForIndex(itemEl, index, insertBefore, avoidDomReflow) {
3544
- const p = this._p;
3545
- const virtualWrapper = p.virtualWrapper;
3546
- p.currentItemsParent;
3547
- const existingEls = p.existingEls;
3548
- const onItemRender = p.onItemRender;
3549
- const onItemUnrender = p.onItemUnrender;
3550
- const isNew = !itemEl;
3551
- const shouldReRender = isNew || index !== itemEl[ItemIndexSymbol];
3552
-
3553
- if (itemEl) {
3554
- if (onItemUnrender && shouldReRender) {
3555
- onItemUnrender(itemEl);
3556
- }
3557
- } else {
3558
- itemEl = p.itemElementCreatorFn();
3559
-
3560
- if (virtualWrapper && insertBefore !== false) {
3561
- (/**@type ElementCSSInlineStyle*/itemEl).style.position = 'absolute';
3562
- (/**@type ElementCSSInlineStyle*/itemEl).style.top = '0';
3563
- (/**@type ElementCSSInlineStyle*/itemEl).style.left = '0';
3564
- (/**@type ElementCSSInlineStyle*/itemEl).style.right = '0';
3565
- }
3566
- }
3567
-
3568
- // Render only if it's a new item element
3569
- // OR the index of the existing element is not the same of the index to render
3570
- if (shouldReRender) {
3571
- itemEl.innerHTML = ''; // Basic cleanup
3572
-
3573
- if (onItemRender)
3574
- onItemRender(itemEl, index);
3575
- }
3576
-
3577
- if (insertBefore !== false) {
3578
- if (!(insertBefore instanceof Node))
3579
- insertBefore = null;
3580
-
3581
- // Remove from existing list
3582
- if (!isNew) {
3583
- let i = existingEls.indexOf(itemEl);
3584
- if (i !== -1)
3585
- existingEls.splice(i, 1);
3586
- }
3587
-
3588
- // Insert into existing list
3589
- let beforeIndex = insertBefore ? existingEls.indexOf(/**@type Element*/insertBefore) : -1;
3590
- if (beforeIndex === -1) {
3591
- existingEls.push(itemEl);
3592
- } else {
3593
- existingEls.splice(beforeIndex, 0, itemEl);
3594
- }
3595
-
3596
- if (!avoidDomReflow) {
3597
- this._insertItemAndFlow(itemEl, index, insertBefore);
3598
- }
3599
- }
3600
-
3601
- // Add index metadata to item
3602
- itemEl[ItemIndexSymbol] = index;
3603
-
3604
- return itemEl;
3605
- }
3606
-
3607
- /**
3608
- * Insert item element into the DOM, set it's flow in the DOM, and update the item's position. <br />
3609
- * @param {Element|undefined} itemEl
3610
- * @param {number} index
3611
- * @param {Node|boolean|undefined} before
3612
- * @private
3613
- */
3614
- _insertItemAndFlow(itemEl, index, before) {
3615
- const p = this._p;
3616
- const virtualWrapper = p.virtualWrapper;
3617
- const itemParent = p.currentItemsParent;
3618
-
3619
- if (before !== false) {
3620
- if (!(before instanceof Node))
3621
- before = null;
3622
-
3623
- // Insert into DOM
3624
- if (itemEl.parentNode !== itemParent ||
3625
- itemEl.nextSibling !== before) {
3626
- insertBefore(itemEl, before, itemParent);
3627
- }
3628
- }
3629
-
3630
- if (virtualWrapper) {
3631
- // Calculate height
3632
- let itemHeight = itemEl.getBoundingClientRect().height;
3633
-
3634
- // Put calculated height into cache, and invalidate positions if it's different
3635
- let cachedItemHeight = p.cachedItemHeights[index];
3636
- if (cachedItemHeight !== itemHeight) {
3637
- p.cachedItemHeights[index] = itemHeight;
3638
- }
3639
-
3640
- if (cachedItemHeight !== undefined && itemHeight !== cachedItemHeight ||
3641
- cachedItemHeight === undefined && itemHeight !== p.cachedItemEstimatedHeights[index]) {
3642
- this._setItemPositionsNeedsUpdate(index + 1);
3643
- }
3644
-
3645
- // Set item top position
3646
- let pos = this._calculateItemPosition(index);
3647
- const supportedTransform = getSupportedTransform();
3648
-
3649
- if (supportedTransform === false) {
3650
- (/**@type ElementCSSInlineStyle*/itemEl).style.top = `${pos}px`;
3651
- } else {
3652
- (/**@type ElementCSSInlineStyle*/itemEl).style[supportedTransform] = `translateY(${pos}px)`;
3653
- }
3654
- }
3655
- }
3656
-
3657
- /**
3658
- * Fetches valid range of existingEls
3659
- * @returns {{firstIndex: (*|number), firstValidArrayIndex: number, lastValidArrayIndex: number, lastIndex: (*|number)}}
3660
- * @private
3661
- */
3662
- _getExistingElsRange() {
3663
- const p = this._p,existingEls = p.existingEls;
3664
-
3665
- let firstValidArrayIndex = -1,lastValidArrayIndex = -1;
3666
-
3667
- for (let i = 0, len = existingEls.length; i < len; i++) {
3668
- if (false === hasOwnProperty$1.call(existingEls[i], ItemIndexSymbol))
3669
- continue;
3670
- firstValidArrayIndex = i;
3671
- break;
3672
- }
3673
-
3674
- for (let i = existingEls.length - 1; i >= 0; i--) {
3675
- if (false === hasOwnProperty$1.call(existingEls[i], ItemIndexSymbol))
3676
- continue;
3677
- lastValidArrayIndex = i;
3678
- break;
3679
- }
3680
-
3681
- let firstIndex = firstValidArrayIndex !== -1 ? existingEls[firstValidArrayIndex][ItemIndexSymbol] : -1;
3682
- let lastIndex = lastValidArrayIndex !== -1 ? existingEls[lastValidArrayIndex][ItemIndexSymbol] : -1;
3683
-
3684
- return {
3685
- firstValidArrayIndex: firstValidArrayIndex,
3686
- lastValidArrayIndex: lastValidArrayIndex,
3687
- firstIndex: firstIndex,
3688
- lastIndex: lastIndex
3689
- };
3690
- }
3691
- }
3692
-
3693
- /** Marks the item index associated with an item element */
3694
- const ItemIndexSymbol = Symbol('index');
3695
-
3696
- /** Marks an element for reuse */
3697
- const ReuseElSymbol = Symbol('reuse');
3698
-
3699
- /**
3700
- * The default element creator
3701
- * @returns {HTMLLIElement}
3702
- */
3703
- const defaultElementCreator = () => {
3704
- return document.createElement('li');
3705
- };
3706
-
3707
- /**
3708
- * Will look for the index in the `positions` array closest to the specified `pos` value (<= pos).
3709
- * @param {number[]} positions
3710
- * @param {number} pos
3711
- * @param {number} [start=0]
3712
- * @param {number} [end=-1]
3713
- * @returns {number}
3714
- */
3715
- const binarySearchPosition = (positions, pos, start = 0, end = -1) => {
3716
- let total = positions.length;
3717
- if (end < 0)
3718
- end += total;
3719
- if (end <= start) return end; // 0 or 1 length array
3720
-
3721
- while (start <= end) {
3722
- let mid = Math.floor(start + (end - start) / 2);
3723
- let midPos = positions[mid];
3724
-
3725
- if (midPos === pos || midPos <= pos && mid < total && positions[mid + 1] > pos) {
3726
- while (mid > 0 && positions[mid - 1] === midPos) // avoid bugs on 0-height items
3727
- mid--;
3728
-
3729
- return mid;
3730
- }
3731
-
3732
- if (midPos < pos)
3733
- start = mid + 1;else
3734
-
3735
- end = mid - 1;
3736
- }
3737
-
3738
- return end === -1 ? 0 : total - 1;
3739
- };
3740
-
3741
- /**
3742
- * Will look for the index in a virtual list of positions supplied by `total` and `fn`,
3743
- * closest to the specified `pos` value (<= pos).
3744
- * @param {number} total
3745
- * @param {function(index: number):number} fn
3746
- * @param {number} pos
3747
- * @param {number} [start=0]
3748
- * @param {number} [end=-1]
3749
- * @returns {number}
3750
- */
3751
- const binarySearchPositionByFn = (total, fn, pos, start = 0, end = -1) => {
3752
- if (end < 0)
3753
- end += total;
3754
- if (end <= start) return end; // 0 or 1 length array
3755
-
3756
- while (start <= end) {
3757
- let mid = Math.floor(start + (end - start) / 2);
3758
- let midPos = fn(mid);
3759
-
3760
- if (midPos === pos || midPos <= pos && mid < total && fn(mid + 1) > pos) {
3761
- while (mid > 0 && fn(mid - 1) === midPos) // avoid bugs on 0-height items
3762
- mid--;
3763
-
3764
- return mid;
3765
- }
3766
-
3767
- if (midPos < pos)
3768
- start = mid + 1;else
3769
-
3770
- end = mid - 1;
3771
- }
3772
-
3773
- return end === -1 ? 0 : fn(total - 1);
3774
- };
3775
-
3776
- /**
3777
- * Finds the last item in the array for which `fn` returns a truthy value
3778
- * @param {Array} array
3779
- * @param {Function} fn
3780
- * @returns {undefined|*}
3781
- */
3782
- const findLast = (array, fn) => {
3783
- for (let i = array.length - 1; i >= 0; i--) {
3784
- if (fn(array[i])) {
3785
- return array[i];
3786
- }
3787
- }
3788
- return undefined;
3789
- };
3790
-
3791
- let _isTransformSupported = null;
3792
-
3793
- const getSupportedTransform = () => {
3794
- if (_isTransformSupported === null) {
3795
- let prefixes = ['transform', 'WebkitTransform', 'MozTransform', 'OTransform', 'msTransform'];
3796
- let div = document.createElement('div');
3797
- _isTransformSupported = false;
3798
- for (let item of prefixes) {
3799
- if (div && div.style[item] !== undefined) {
3800
- _isTransformSupported = item;
3801
- break;
3802
- }
3803
- }
3804
- }
3805
- return _isTransformSupported;
3806
- };
3807
-
3808
2351
  function ByColumnFilter(row, args) {
3809
2352
 
3810
2353
  let column = args.column;