jquery.dgtable 0.6.0 → 0.6.2

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.
package/README.md CHANGED
@@ -73,7 +73,7 @@ To create a new table, just use `var myTable = new DGTable(INIT_OPTIONS)`.
73
73
  * `DGTable.Width.AUTO`: Sets the width automatically
74
74
  * `DGTable.Width.SCROLL`: Creates a horizontal scroll when required
75
75
  * **virtualTable**: `boolean=true` When set, the table will work in virtual mode, which means only the visible rows are rendered. Rows must have fixed height in this mode.
76
- * **estimatedRowHeight**: `number=40` Sets the estimated row height for the table. This is used for virtual table mode, to calculate the estimated scroll size.
76
+ * **estimatedRowHeight**: `number?` Sets the estimated row height for the table. This is used for virtual table mode, to calculate the estimated scroll size. Will be auto calculated by default.
77
77
  * **resizableColumns**: `boolean=true` Turns on or off the resizable columns globally.
78
78
  * **movableColumns**: `boolean=true` Turns on or off the movable columns globally.
79
79
  * **sortableColumns**: `number=1` How many columns can you sort by, one after another?
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * jquery.dgtable 0.6.0
2
+ * jquery.dgtable 0.6.2
3
3
  * git://github.com/danielgindi/jquery.dgtable.git
4
4
  */
5
5
  'use strict';
@@ -1786,6 +1786,45 @@ class SelectionHelper {
1786
1786
 
1787
1787
  const hasOwnProperty$1 = Object.prototype.hasOwnProperty;
1788
1788
 
1789
+ const hasInsertAdjacentElement = Element.prototype.insertAdjacentElement !== undefined;
1790
+
1791
+ function insertBefore(el, before, parent) {
1792
+ if (!before)
1793
+ parent.appendChild(el);else
1794
+ if (hasInsertAdjacentElement === true)
1795
+ before.insertAdjacentElement('beforebegin', el);else
1796
+ parent.insertBefore(el, before);
1797
+ }
1798
+
1799
+ /**
1800
+ *
1801
+ * @param {Element} itemEl
1802
+ * @param {DocumentFragment|null} fragment
1803
+ * @param {Node|undefined} before
1804
+ * @param {Element} itemParent
1805
+ * @returns {DocumentFragment|null}
1806
+ */
1807
+ function insertBeforeWithFragment(itemEl, fragment, before, itemParent) {
1808
+ if (itemEl.parentNode !== itemParent) {
1809
+ if (!fragment)
1810
+ fragment = document.createDocumentFragment();
1811
+ fragment.appendChild(itemEl);
1812
+ } else {
1813
+ // insert fragment
1814
+ if (fragment && fragment.childNodes.length > 0) {
1815
+ insertBefore(fragment, before, itemParent);
1816
+ fragment = null;
1817
+ }
1818
+
1819
+ // insert element
1820
+ if (itemEl.nextSibling !== before) {
1821
+ insertBefore(itemEl, before, itemParent);
1822
+ }
1823
+ }
1824
+
1825
+ return fragment;
1826
+ }
1827
+
1789
1828
  class VirtualListHelper {
1790
1829
  /**
1791
1830
  * @param {VirtualListHelper~Options} opts
@@ -2164,6 +2203,7 @@ class VirtualListHelper {
2164
2203
  const list = p.list;
2165
2204
  const virtual = p.virtual;
2166
2205
  let virtualWrapper = p.virtualWrapper;
2206
+ let itemParent = p.currentItemsParent;
2167
2207
  let scrollTop = list.scrollTop;
2168
2208
  let visibleHeight = list.clientHeight;
2169
2209
  let visibleBottom = scrollTop + visibleHeight;
@@ -2184,6 +2224,7 @@ class VirtualListHelper {
2184
2224
  }
2185
2225
 
2186
2226
  this._resetCurrentItemsParent();
2227
+ itemParent = p.currentItemsParent;
2187
2228
 
2188
2229
  if (p.autoVirtualWrapperWidth) {
2189
2230
  virtualWrapper.style.width = '100%';
@@ -2213,12 +2254,18 @@ class VirtualListHelper {
2213
2254
  let renderPos = this._calculateItemPosition(index);
2214
2255
  let bufferEnd = buffer;
2215
2256
 
2216
- /** @type Node|undefined */
2217
- let lastEl = undefined;
2257
+ // we want to render until viewport's bottom + buffer items
2258
+ let maxIndexToRender = Math.max(index, binarySearchPosition(p.cachedItemPositions, visibleBottom - 1) + 1 + buffer);
2259
+
2260
+ let insertedItems = [];
2218
2261
 
2219
- // If rendering position has not reached the viewport bottom
2220
- // AND we have not rendered all the buffer items yet
2221
- while ((renderPos < visibleBottom || bufferEnd-- > 0) && index < count) {
2262
+ /** @type DocumentFragment|null */
2263
+ let fragment = null;
2264
+
2265
+ // Find the element to insert before
2266
+ let before = virtualWrapper.childNodes[0];
2267
+
2268
+ const findElementToReuse = function (index) {
2222
2269
  // Find existing element to reuse
2223
2270
  /** @type Element|undefined */
2224
2271
  let existingEl = undefined;
@@ -2240,20 +2287,53 @@ class VirtualListHelper {
2240
2287
  delete existingEl[ReuseElSymbol];
2241
2288
  }
2242
2289
 
2243
- // Find the element to insert before
2244
- let insertBefore = lastEl ? lastEl.nextSibling : virtualWrapper.childNodes[0];
2245
- if (insertBefore && insertBefore === existingEl)
2246
- insertBefore = insertBefore.nextSibling;
2290
+ return existingEl;
2291
+ };
2292
+
2293
+ // First we iterate and try to add all at once in a fragment, as much as we can.
2294
+ // And then reflow the at once.
2295
+ for (; index < count && index < maxIndexToRender; index++) {
2296
+ let existingEl = findElementToReuse(index);
2297
+
2298
+ if (before && before === existingEl)
2299
+ before = before.nextSibling;
2247
2300
 
2248
2301
  // Dequeue the element by reusing or creating a new one
2249
- lastEl = this._dequeueElementForIndex(existingEl, index, insertBefore);
2302
+ const itemEl = this._dequeueElementForIndex(existingEl, index, before, true);
2303
+ insertedItems.push([itemEl, index]);
2304
+
2305
+ fragment = insertBeforeWithFragment(itemEl, fragment, before, itemParent);
2306
+ }
2250
2307
 
2251
- // Increment pointers
2308
+ // Insert any remaining fragment
2309
+ if (fragment && fragment.childNodes.length > 0) {
2310
+ insertBefore(fragment, before, itemParent);
2311
+ }
2312
+
2313
+ // Iterate on inserted items and reflow them
2314
+ for (let item of insertedItems) {
2315
+ const index = item[1];
2316
+ this._insertItemAndFlow(item[0], index, false /* inserted already */);
2252
2317
  renderPos = p.cachedItemPositions[index] + p.cachedItemHeights[index];
2253
- index++;
2254
2318
  }
2255
2319
 
2256
- // Calculate up to date scroll height
2320
+ // See if we still need to insert more items
2321
+ if (renderPos < visibleBottom) {
2322
+ for (; (renderPos < visibleBottom || bufferEnd-- > 0) && index < count; index++) {
2323
+ let existingEl = findElementToReuse(index);
2324
+
2325
+ if (before && before === existingEl)
2326
+ before = before.nextSibling;
2327
+
2328
+ // Dequeue the element by reusing or creating a new one
2329
+ this._dequeueElementForIndex(existingEl, index, before, false);
2330
+
2331
+ // Increment pointers
2332
+ renderPos = p.cachedItemPositions[index] + p.cachedItemHeights[index];
2333
+ }
2334
+ }
2335
+
2336
+ // Calculate up-to-date scroll height
2257
2337
  let scrollHeight = this.estimateFullHeight();
2258
2338
  p.virtualWrapper.style.height = scrollHeight + 'px';
2259
2339
 
@@ -2265,8 +2345,12 @@ class VirtualListHelper {
2265
2345
  existingEls[i][ReuseElSymbol] = true;
2266
2346
  }
2267
2347
 
2268
- /** @type Node|undefined */
2269
- let lastEl = undefined;
2348
+ // Find the element to insert before
2349
+ let before = list.childNodes[0];
2350
+
2351
+ /** @type DocumentFragment|null */
2352
+ let fragment = null;
2353
+
2270
2354
  for (let index = 0; index < count; index++) {
2271
2355
  // Find existing element to reuse
2272
2356
  let existingEl = existingEls.find((x) => x[ItemIndexSymbol] === index && x[ReuseElSymbol] === true);
@@ -2275,13 +2359,18 @@ class VirtualListHelper {
2275
2359
  delete existingEl[ReuseElSymbol];
2276
2360
  }
2277
2361
 
2278
- // Find the element to insert before
2279
- let insertBefore = lastEl ? lastEl.nextSibling : list.childNodes[0];
2280
- if (insertBefore && insertBefore === existingEl)
2281
- insertBefore = insertBefore.nextSibling;
2362
+ if (before && before === existingEl)
2363
+ before = before.nextSibling;
2282
2364
 
2283
2365
  // Dequeue the element by reusing or creating a new one
2284
- lastEl = this._dequeueElementForIndex(existingEl, index, insertBefore);
2366
+ const itemEl = this._dequeueElementForIndex(existingEl, index, before, true);
2367
+
2368
+ fragment = insertBeforeWithFragment(itemEl, fragment, before, itemParent);
2369
+ }
2370
+
2371
+ // Insert any remaining fragment
2372
+ if (fragment && fragment.childNodes.length > 0) {
2373
+ insertBefore(fragment, before, itemParent);
2285
2374
  }
2286
2375
  }
2287
2376
  }
@@ -2337,16 +2426,28 @@ class VirtualListHelper {
2337
2426
  if (existingRange.firstValidArrayIndex === -1)
2338
2427
  return this;
2339
2428
 
2429
+ const itemParent = p.currentItemsParent;
2430
+
2340
2431
  let startIndex = existingRange.firstValidArrayIndex + atIndex - existingRange.firstIndex;
2341
2432
 
2342
2433
  this._pushItemIndexesAt(atIndex, count);
2343
2434
 
2344
2435
  /** @type Node|undefined */
2345
- let lastEl = existingEls[startIndex - 1];
2436
+ let before = existingEls[startIndex - 1] ?
2437
+ existingEls[startIndex - 1].nextSibling :
2438
+ existingEls[0];
2439
+
2440
+ /** @type DocumentFragment|null */
2441
+ let fragment = null;
2346
2442
 
2347
2443
  for (let index = atIndex, end = atIndex + count; index < end; index++) {
2348
- let insertBefore = lastEl ? lastEl.nextSibling : existingEls[0];
2349
- lastEl = this._dequeueElementForIndex(undefined, index, insertBefore);
2444
+ const itemEl = this._dequeueElementForIndex(undefined, index, before, true);
2445
+ fragment = insertBeforeWithFragment(itemEl, fragment, before, itemParent);
2446
+ }
2447
+
2448
+ // Insert any remaining fragment
2449
+ if (fragment && fragment.childNodes.length > 0) {
2450
+ insertBefore(fragment, before, itemParent);
2350
2451
  }
2351
2452
  }
2352
2453
 
@@ -2429,7 +2530,7 @@ class VirtualListHelper {
2429
2530
  if (index >= existingRange.firstIndex && index <= existingRange.lastIndex) {
2430
2531
  let itemEl = existingEls[existingRange.firstValidArrayIndex + index - existingRange.firstIndex];
2431
2532
  delete itemEl[ItemIndexSymbol];
2432
- this._dequeueElementForIndex(itemEl, index, itemEl.nextSibling);
2533
+ this._dequeueElementForIndex(itemEl, index, itemEl.nextSibling, false);
2433
2534
  }
2434
2535
  }
2435
2536
 
@@ -2572,7 +2673,7 @@ class VirtualListHelper {
2572
2673
  createGhostItemElement(ghostIndex, append, ghostTester) {
2573
2674
  const p = this._p;
2574
2675
 
2575
- let itemEl = this._dequeueElementForIndex(null, ghostIndex, false);
2676
+ let itemEl = this._dequeueElementForIndex(null, ghostIndex, false, true);
2576
2677
  try {
2577
2678
  if (append) {
2578
2679
  p.currentItemsParent.appendChild(itemEl);
@@ -2823,13 +2924,14 @@ class VirtualListHelper {
2823
2924
  * @param {Element|undefined} itemEl
2824
2925
  * @param {number} index
2825
2926
  * @param {Node|boolean|undefined} insertBefore
2927
+ * @param {boolean|undefined} avoidDomReflow
2826
2928
  * @returns {Element}
2827
2929
  * @private
2828
2930
  */
2829
- _dequeueElementForIndex(itemEl, index, insertBefore) {
2931
+ _dequeueElementForIndex(itemEl, index, insertBefore, avoidDomReflow) {
2830
2932
  const p = this._p;
2831
2933
  const virtualWrapper = p.virtualWrapper;
2832
- const itemParent = p.currentItemsParent;
2934
+ p.currentItemsParent;
2833
2935
  const existingEls = p.existingEls;
2834
2936
  const onItemRender = p.onItemRender;
2835
2937
  const onItemUnrender = p.onItemUnrender;
@@ -2864,12 +2966,6 @@ class VirtualListHelper {
2864
2966
  if (!(insertBefore instanceof Node))
2865
2967
  insertBefore = null;
2866
2968
 
2867
- // Insert into DOM
2868
- if (itemEl.parentNode !== itemParent ||
2869
- itemEl.nextSibling !== insertBefore) {
2870
- itemParent.insertBefore(itemEl, insertBefore);
2871
- }
2872
-
2873
2969
  // Remove from existing list
2874
2970
  if (!isNew) {
2875
2971
  let i = existingEls.indexOf(itemEl);
@@ -2885,37 +2981,65 @@ class VirtualListHelper {
2885
2981
  existingEls.splice(beforeIndex, 0, itemEl);
2886
2982
  }
2887
2983
 
2888
- if (virtualWrapper) {
2889
- // Calculate height
2890
- let itemHeight = itemEl.getBoundingClientRect().height;
2984
+ if (!avoidDomReflow) {
2985
+ this._insertItemAndFlow(itemEl, index, insertBefore);
2986
+ }
2987
+ }
2891
2988
 
2892
- // Put calculated height into cache, and invalidate positions if it's different
2893
- let cachedItemHeight = p.cachedItemHeights[index];
2894
- if (cachedItemHeight !== itemHeight) {
2895
- p.cachedItemHeights[index] = itemHeight;
2896
- }
2989
+ // Add index metadata to item
2990
+ itemEl[ItemIndexSymbol] = index;
2897
2991
 
2898
- if (cachedItemHeight !== undefined && itemHeight !== cachedItemHeight ||
2899
- cachedItemHeight === undefined && itemHeight !== p.cachedItemEstimatedHeights[index]) {
2900
- this._setItemPositionsNeedsUpdate(index + 1);
2901
- }
2992
+ return itemEl;
2993
+ }
2994
+
2995
+ /**
2996
+ * Insert item element into the DOM, set it's flow in the DOM, and update the item's position. <br />
2997
+ * @param {Element|undefined} itemEl
2998
+ * @param {number} index
2999
+ * @param {Node|boolean|undefined} before
3000
+ * @private
3001
+ */
3002
+ _insertItemAndFlow(itemEl, index, before) {
3003
+ const p = this._p;
3004
+ const virtualWrapper = p.virtualWrapper;
3005
+ const itemParent = p.currentItemsParent;
2902
3006
 
2903
- // Set item top position
2904
- let pos = this._calculateItemPosition(index);
2905
- const supportedTransform = getSupportedTransform();
3007
+ if (before !== false) {
3008
+ if (!(before instanceof Node))
3009
+ before = null;
2906
3010
 
2907
- if (supportedTransform === false) {
2908
- /**@type ElementCSSInlineStyle*/itemEl.style.top = `${pos}px`;
2909
- } else {
2910
- /**@type ElementCSSInlineStyle*/itemEl.style[supportedTransform] = `translateY(${pos}px)`;
2911
- }
3011
+ // Insert into DOM
3012
+ if (itemEl.parentNode !== itemParent ||
3013
+ itemEl.nextSibling !== before) {
3014
+ insertBefore(itemEl, before, itemParent);
2912
3015
  }
2913
3016
  }
2914
3017
 
2915
- // Add index metadata to item
2916
- itemEl[ItemIndexSymbol] = index;
3018
+ if (virtualWrapper) {
3019
+ // Calculate height
3020
+ let itemHeight = itemEl.getBoundingClientRect().height;
2917
3021
 
2918
- return itemEl;
3022
+ // Put calculated height into cache, and invalidate positions if it's different
3023
+ let cachedItemHeight = p.cachedItemHeights[index];
3024
+ if (cachedItemHeight !== itemHeight) {
3025
+ p.cachedItemHeights[index] = itemHeight;
3026
+ }
3027
+
3028
+ if (cachedItemHeight !== undefined && itemHeight !== cachedItemHeight ||
3029
+ cachedItemHeight === undefined && itemHeight !== p.cachedItemEstimatedHeights[index]) {
3030
+ this._setItemPositionsNeedsUpdate(index + 1);
3031
+ }
3032
+
3033
+ // Set item top position
3034
+ let pos = this._calculateItemPosition(index);
3035
+ const supportedTransform = getSupportedTransform();
3036
+
3037
+ if (supportedTransform === false) {
3038
+ /**@type ElementCSSInlineStyle*/itemEl.style.top = `${pos}px`;
3039
+ } else {
3040
+ /**@type ElementCSSInlineStyle*/itemEl.style[supportedTransform] = `translateY(${pos}px)`;
3041
+ }
3042
+ }
2919
3043
  }
2920
3044
 
2921
3045
  /**
@@ -3219,7 +3343,7 @@ DGTable.prototype.initialize = function (options) {
3219
3343
  /**
3220
3344
  * @private
3221
3345
  * @field {number} estimatedRowHeight */
3222
- o.estimatedRowHeight = options.estimatedRowHeight === undefined ? 40 : options.estimatedRowHeight;
3346
+ o.estimatedRowHeight = options.estimatedRowHeight || undefined;
3223
3347
 
3224
3348
  /**
3225
3349
  * @private
@@ -3481,7 +3605,7 @@ DGTable.prototype._setupVirtualTable = function () {
3481
3605
  autoVirtualWrapperWidth: false,
3482
3606
  virtual: true,
3483
3607
  buffer: o.rowsBufferSize,
3484
- estimatedItemHeight: o.estimatedRowHeight || 40,
3608
+ estimatedItemHeight: o.estimatedRowHeight ? o.estimatedRowHeight : p.virtualRowHeight || 40,
3485
3609
  itemElementCreatorFn: () => {
3486
3610
  return createElement('div');
3487
3611
  },
@@ -6417,6 +6541,41 @@ DGTable.prototype._renderSkeletonBody = function () {
6417
6541
 
6418
6542
  let tableClassName = o.tableClassName;
6419
6543
 
6544
+ // Calculate virtual row heights
6545
+ if (o.virtualTable && !p.virtualRowHeight) {
6546
+ let createDummyRow = function () {
6547
+ let row = createElement('div'),
6548
+ cell = row.appendChild(createElement('div')),
6549
+ cellInner = cell.appendChild(createElement('div'));
6550
+ row.className = tableClassName + '-row';
6551
+ cell.className = tableClassName + '-cell';
6552
+ cellInner.innerHTML = '0';
6553
+ row.style.visibility = 'hidden';
6554
+ row.style.position = 'absolute';
6555
+ return row;
6556
+ };
6557
+
6558
+ let $dummyTbody,$dummyWrapper = $('<div>').
6559
+ addClass(that.el.className).
6560
+ css({ 'z-index': -1, 'position': 'absolute', left: '0', top: '-9999px', width: '1px', overflow: 'hidden' }).
6561
+ append(
6562
+ $('<div>').addClass(tableClassName).append(
6563
+ $dummyTbody = $('<div>').addClass(tableClassName + '-body').css('width', 99999)));
6564
+
6565
+
6566
+
6567
+ $dummyWrapper.appendTo(document.body);
6568
+
6569
+ let row1 = createDummyRow(),row2 = createDummyRow(),row3 = createDummyRow();
6570
+ $dummyTbody.append(row1, row2, row3);
6571
+
6572
+ p.virtualRowHeightFirst = Css.getElementHeight(row1, true, true, true);
6573
+ p.virtualRowHeight = Css.getElementHeight(row2, true, true, true);
6574
+ p.virtualRowHeightLast = Css.getElementHeight(row3, true, true, true);
6575
+
6576
+ $dummyWrapper.remove();
6577
+ }
6578
+
6420
6579
  // Create inner table and tbody
6421
6580
  if (!p.$table) {
6422
6581