From 65e2bd011f5e585a5eb8ac818ecd59f4449f7373 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 7 May 2018 14:13:14 -0400 Subject: [PATCH 1/4] use tap-diff for reporting --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 4bbd2fb..3b8f0da 100644 --- a/package.json +++ b/package.json @@ -9,12 +9,11 @@ "build": "rollup -c", "dev": "rollup -cw", "prepublishOnly": "npm test", - "test": "node test/runner.js", + "test": "node test/runner.js | tap-diff", "test:browser": "npm run build && serve test/public", "pretest": "npm run build" }, "devDependencies": { - "faucet": "^0.0.1", "port-authority": "^1.0.3", "puppeteer": "^1.2.0", "rollup": "^0.58.2", @@ -23,6 +22,7 @@ "rollup-plugin-svelte": "^4.1.0", "serve": "^6.5.3", "svelte": "^2.1.0", + "tap-diff": "^0.1.1", "tap-dot": "^1.0.5", "tape-modern": "^1.0.0" }, From 7dceeea34096053c691c6859eb132d238ea4e89d Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 7 May 2018 14:27:36 -0400 Subject: [PATCH 2/4] update tests --- test/src/index.js | 97 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 88 insertions(+), 9 deletions(-) diff --git a/test/src/index.js b/test/src/index.js index cb093d7..a2a2330 100644 --- a/test/src/index.js +++ b/test/src/index.js @@ -5,6 +5,45 @@ import { assert, test, done } from 'tape-modern'; // setup const target = document.querySelector('main'); +function indent(node, spaces) { + if (node.childNodes.length === 0) return; + + if (node.childNodes.length > 1 || node.childNodes[0].nodeType !== 3) { + const first = node.childNodes[0]; + const last = node.childNodes[node.childNodes.length - 1]; + + const head = `\n${spaces} `; + const tail = `\n${spaces}`; + + if (first.nodeType === 3) { + first.data = `${head}${first.data}`; + } else { + node.insertBefore(document.createTextNode(head), first); + } + + if (last.nodeType === 3) { + last.data = `${last.data}${tail}`; + } else { + node.appendChild(document.createTextNode(tail)); + } + + let lastType = null; + for (let i = 0; i < node.childNodes.length; i += 1) { + const child = node.childNodes[i]; + if (child.nodeType === 1) { + indent(node.childNodes[i], `${spaces} `); + + if (lastType === 1) { + node.insertBefore(document.createTextNode(head), child); + i += 1; + } + } + + lastType = child.nodeType; + } + } +} + function normalize(html) { const div = document.createElement('div'); div.innerHTML = html @@ -15,6 +54,8 @@ function normalize(html) { .replace(/>\s+/g, '>') .replace(/\s+ { test('updates when items change', t => { const Row = svelte.create(` - {foo} +
{foo}
`); const list = new VirtualList({ target, data: { items: [{ foo: 'bar'}], - component: Row + component: Row, + height: '100px' } }); t.htmlEqual(target.innerHTML, ` -
+
- bar +
bar
@@ -161,18 +203,55 @@ test('updates when items change', t => { }); t.htmlEqual(target.innerHTML, ` -
-
+
+
- bar +
bar
- baz +
baz
+
+
+ `); + + list.destroy(); +}); +test('updates when items change from an empty list', t => { + const Row = svelte.create(` +
{foo}
+ `); + + const list = new VirtualList({ + target, + data: { + items: [], + component: Row, + height: '100px' + } + }); + + t.htmlEqual(target.innerHTML, ` +
+
+
+ `); + + list.set({ + items: [{ foo: 'bar'}, { foo: 'baz'}, { foo: 'qux'}] + }); + + t.htmlEqual(target.innerHTML, ` +
+
- qux +
bar
+
+ +
+
baz
From 611afebef9c98d98fa686688999328f917fdf063 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 7 May 2018 14:31:36 -0400 Subject: [PATCH 3/4] disable tap-diff for now, it is exiting with an error code prematurely --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3b8f0da..f479d96 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "build": "rollup -c", "dev": "rollup -cw", "prepublishOnly": "npm test", - "test": "node test/runner.js | tap-diff", + "test": "node test/runner.js", "test:browser": "npm run build && serve test/public", "pretest": "npm run build" }, From 62bc6acbeaacb1acecb58ca4deb3d0865465f2d5 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 7 May 2018 15:14:33 -0400 Subject: [PATCH 4/4] handle changes from zero to some items --- src/VirtualList.html | 86 +++++++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 33 deletions(-) diff --git a/src/VirtualList.html b/src/VirtualList.html index 6048bd2..0d8c3c3 100644 --- a/src/VirtualList.html +++ b/src/VirtualList.html @@ -34,9 +34,8 @@ }, oncreate() { - const { items, _props, itemHeight } = this.get(); - const { viewport, container } = this.refs; - const viewportHeight = viewport.offsetHeight; + const { items, _props } = this.get(); + const { container } = this.refs; const keys = Object.keys(this.options.data).filter(key => key !== 'items' && key !== 'component' && key !== 'height' && key !== 'itemHeight'); if (keys.length) { @@ -47,45 +46,31 @@ this.set({ _props }); } + this.heightMap = []; this.rows = container.getElementsByClassName('row'); - if (itemHeight) { - this.heightMap = items.map(item => itemHeight); - this.set({ - end: Math.min(items.length, Math.ceil(viewportHeight / itemHeight)), - bottom: items.length * itemHeight - }); - } else { - this.heightMap = []; - - let height = 0; - let i = 0; - - while (height < viewportHeight && i < items.length) { - this.set({ end: i + 1 }); - - const rowHeight = this.heightMap[i] = this.rows[i].offsetHeight; - height += rowHeight; - - i += 1; - } - - const end = i; - const avg = Math.round(height / i); - - for (; i < items.length; i += 1) this.heightMap[i] = avg; - - this.set({ - bottom: (items.length - end) * avg - }); + if (items.length > 0) { + this.initialise(); } this.on('state', ({ changed, previous, current }) => { if (changed.items || changed.height || changed.itemHeight) { - if (current.itemHeight && (changed.itemHeight || current.items.length !== this.heightMap.length)) { + if (current.itemHeight && (changed.itemHeight || current.items.length > this.heightMap.length)) { this.heightMap = current.items.map(() => current.itemHeight); } + else if (current.items.length > this.heightMap.length) { + if (this.heightMap.length === 0) { + this.initialise(); + } else { + let height = 0; + let i = 0; + for (; i < this.heightMap.length; i += 1) height += this.heightMap[i]; + const avg = height / this.heightMap.length; + for (; i < current.items.length; i += 1) this.heightMap[i] = avg; + } + } + this.refresh(); } @@ -100,6 +85,41 @@ }, methods: { + initialise() { + const { items, itemHeight } = this.get(); + const { viewport } = this.refs; + const viewportHeight = viewport.offsetHeight; + + if (itemHeight) { + this.heightMap = items.map(item => itemHeight); + this.set({ + end: Math.min(items.length, Math.ceil(viewportHeight / itemHeight)), + bottom: items.length * itemHeight + }); + } else { + let height = 0; + let i = 0; + + while (height < viewportHeight && i < items.length) { + this.set({ end: i + 1 }); + + const rowHeight = this.heightMap[i] = this.rows[i].offsetHeight; + height += rowHeight; + + i += 1; + } + + const end = i; + const avg = Math.round(height / i); + + for (; i < items.length; i += 1) this.heightMap[i] = avg; + + this.set({ + bottom: (items.length - end) * avg + }); + } + }, + refresh() { const { items, start, end, itemHeight } = this.get(); const { offsetHeight, scrollTop } = this.refs.viewport;