Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 22 additions & 11 deletions examples/perspective.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ async function sortHandler(regularTable, event) {
const sort_method = event.shiftKey ? append_sort : override_sort;
const sort = sort_method.call(this, column_name);
this._view = this._table.view({...this._config, sort: sort});
await createViewCache(this._table, this._view, this);
await createViewCache(regularTable, this._table, this._view, this);
await regularTable.draw();
}

Expand Down Expand Up @@ -334,7 +334,8 @@ async function dataListener(x0, y0, x1, y1) {
const column_headers = [];
for (const path of this._column_paths.slice(x0, x1)) {
const path_parts = path.split("|");
data.push(columns[path].map((x) => _format.call(this, path_parts, x)));
const column = columns[path] || [];
data.push(column.map((x) => _format.call(this, path_parts, x)));
column_headers.push(path_parts);
}

Expand All @@ -348,11 +349,15 @@ async function dataListener(x0, y0, x1, y1) {
}
```

Create a model state object.
Create a model state object. This object will memoize everything that a
`regular-table` will need in one place, minimizing recalculation later when
a re-render is requested. Because some operations like _sorting_ can actually
instantiate a view (and this call `createViewCache()` themselves), we must
memoize both the `Table` and `View` objects.

```javascript
async function createViewCache(table, view, extend = {}) {
return Object.assign(extend, {
async function createViewCache(regular, table, view, extend = {}) {
const model = Object.assign(extend, {
_view: view,
_table: table,
_table_schema: await table.schema(),
Expand All @@ -363,24 +368,31 @@ async function createViewCache(table, view, extend = {}) {
return path !== "__ROW_PATH__" && path !== "__ID__";
}),
});
regular.setDataListener(dataListener.bind(model));
return model;
}
```

```javascript
async function configureRegularTable(regular, model) {
regular.setDataListener(dataListener.bind(model));

regular.addStyleListener(typeStyleListener.bind(model, regular));
regular.addStyleListener(treeStyleListener.bind(model, regular));
regular.addStyleListener(groupHeaderStyleListener.bind(model, regular));
regular.addStyleListener(columnHeaderStyleListener.bind(model, regular));

regular.addEventListener("mousedown", mousedownListener.bind(model, regular));

await regular.draw();
}
```

The functions `configureRegularTable()` and `createViewCache()` are all that's
needed to wire a `Table` to a `regular-table`, so we'll export these for
convenient inclusion in a module-aware Javascript project.

```javascript
exports.createViewCache = createViewCache;
exports.configureRegularTable = configureRegularTable;
```

## Perspective

```html
Expand All @@ -403,9 +415,8 @@ async function configureRegularTable(regular, model) {
columns: ["Sales", "Profit"],
});

const model = await createViewCache(table, view);

const regular = document.getElementsByTagName("regular-table")[0];
const model = await createViewCache(regular, table, view);
await configureRegularTable(regular, model);
});
</script>
Expand Down
4 changes: 2 additions & 2 deletions src/js/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
export const METADATA_MAP = new WeakMap();

// Output runtime debug info like FPS.
export const DEBUG = true;
export const DEBUG = false;

// Double buffer when the viewport scrolls columns, rows or when the
// view is recreated. Reduces render draw-in on some browsers, at the
// expense of performance.
export const DOUBLE_BUFFER_COLUMN = false;
export const DOUBLE_BUFFER_ROW = false;
export const DOUBLE_BUFFER_RECREATE = false;
export const DOUBLE_BUFFER_RECREATE = true;

// The largest size virtual <div> in (px) that Chrome can support without
// glitching.
Expand Down
66 changes: 60 additions & 6 deletions src/js/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,12 @@ import {throttlePromise} from "./utils";
*/
export class RegularViewEventModel extends RegularVirtualTableViewModel {
register_listeners() {
this.addEventListener("mousedown", this._on_click.bind(this));
this.addEventListener("dblclick", this._on_dblclick.bind(this));
this.addEventListener("scroll", this._on_scroll.bind(this), {
passive: false,
});
this.addEventListener("mousewheel", this._on_mousewheel.bind(this), {
passive: false,
});
this.addEventListener("mousedown", this._on_click.bind(this));
this.addEventListener("dblclick", this._on_dblclick.bind(this));
this._register_glitch_scroll_listeners();
}

/**
Expand All @@ -41,9 +39,29 @@ export class RegularViewEventModel extends RegularVirtualTableViewModel {
this.dispatchEvent(new CustomEvent("regular-table-scroll"));
}

/**
* Modern and mobile browsers implement complex scroll behavior to
* implement fancy touch and intertia effects; these must be intercepted
* and disabled to achieve clean virtual scrolling in the presence of a
* `fixed` element.
*
* @memberof RegularViewEventModel
*/
_register_glitch_scroll_listeners() {
this.addEventListener("mousewheel", this._on_mousewheel.bind(this), {
passive: false,
});
this.addEventListener("touchstart", this._on_touchstart.bind(this), {
passive: false,
});
this.addEventListener("touchmove", this._on_touchmove.bind(this), {
passive: false,
});
}

/**
* Mousewheel must precalculate in addition to `_on_scroll` to prevent
* visual artifacts due to scrolling "inertia" on modern browsers/mobile.
* visual artifacts due to scrolling "inertia" on modern browsers.
*
* @param {*} event
* @memberof RegularViewModel
Expand All @@ -62,6 +80,42 @@ export class RegularViewEventModel extends RegularVirtualTableViewModel {
this._on_scroll(event);
}

/**
* Touchmove must precalculate in addition to `_on_scroll` to prevent
* visual artifacts due to scrolling "inertia" on mobile browsers. This has
* the unfortunate side-effect of disabling scroll intertia, but the
* alternative is a dodgy, glitchy mess.
*
* @param {*} event
* @returns
* @memberof RegularViewEventModel
*/
_on_touchmove(event) {
if (this._virtual_scrolling_disabled) {
return;
}
event.preventDefault();
event.returnValue = false;
const {clientWidth, clientHeight, scrollTop, scrollLeft} = this;
const total_scroll_height = Math.max(1, this._virtual_panel.offsetHeight - clientHeight);
const total_scroll_width = Math.max(1, this._virtual_panel.offsetWidth - clientWidth);
this.scrollTop = Math.min(total_scroll_height, scrollTop + (this._memo_touch_startY - event.touches[0].screenY));
this.scrollLeft = Math.min(total_scroll_width, scrollLeft + (this._memo_touch_startX - event.touches[0].screenX));
this._on_scroll(event);
}

/**
* Memoize `touchstart` positions to calculate deltas, since these are not
* generated on `touchmove` events.
*
* @param {*} event
* @memberof RegularViewEventModel
*/
_on_touchstart(event) {
this._memo_touch_startY = event.touches[0].screenY;
this._memo_touch_startX = event.touches[0].screenX;
}

/**
* Handles double-click header width override reset.
*
Expand Down
1 change: 1 addition & 0 deletions src/less/material.less
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ regular-table {
// firefox scrollbar styling
scrollbar-color: transparent transparent;
scrollbar-width: thin;
outline: none;
}

regular-table:hover {
Expand Down
2 changes: 1 addition & 1 deletion test/examples/area_mouse_selection.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe("area_mouse_selection.html", () => {
};

beforeAll(async () => {
await page.setViewport({width: 2500, height: 2500});
await page.setViewport({width: 300, height: 300});
await page.goto("http://localhost:8081/dist/examples/area_mouse_selection.html");
await page.waitFor("regular-table table tbody tr td");
});
Expand Down
4 changes: 2 additions & 2 deletions test/examples/spreadsheet.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ describe("spreadsheet.html", () => {
for (const td of tds) {
cells.push(await page.evaluate((td) => td.innerHTML, td));
}
expect(cells).toEqual(["", "", "", "", "", "Hello, World!"]);
expect(cells).toEqual(["", "", "", "", "Hello, World!"]);

const ths = await page.$$("regular-table tbody tr:nth-of-type(1) th");
const th_value = await page.evaluate((th) => th.innerHTML, ths[0]);
Expand Down Expand Up @@ -161,7 +161,7 @@ describe("spreadsheet.html", () => {
for (const td of tds) {
cells.push(await page.evaluate((td) => td.innerHTML, td));
}
expect(cells).toEqual(["", "", "", "", "", "Hello, World!"]);
expect(cells).toEqual(["", "", "", "Hello, World!"]);

const ths = await page.$$("regular-table tbody tr:nth-of-type(1) th");
const th = await page.evaluate((th) => th.innerHTML, ths[0]);
Expand Down