Skip to content

Commit

Permalink
refactor(scheduler): Adds a simple RAF scheduler (#38)
Browse files Browse the repository at this point in the history
  • Loading branch information
pzuraq authored Sep 12, 2017
1 parent 311ebc3 commit 5fbf485
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 15 deletions.
1 change: 1 addition & 0 deletions addon/-private/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { scheduler, Token } from './scheduler';
108 changes: 108 additions & 0 deletions addon/-private/scheduler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import Ember from 'ember';
import { stripInProduction } from 'vertical-collection/-debug/helpers';

const {
run
} = Ember;

export class Token {
constructor(parent) {
this._parent = parent;
this._cancelled = false;

stripInProduction(() => {
Object.seal(this);
});
}

get cancelled() {
return this._cancelled || (this._cancelled = this._parent ? this._parent.cancelled : false);
}

cancel() {
this._cancelled = true;
}
}

function job(cb, token) {
return function execJob() {
if (token.cancelled === false) {
cb();
}
};
}

export class Scheduler {
constructor() {
this.sync = [];
this.measure = [];
this.jobs = 0;
this._nextFlush = null;
this.ticks = 0;

stripInProduction(() => {
Object.seal(this);
});
}

schedule(queueName, cb, parent) {
this.jobs++;
const token = new Token(parent);

this[queueName].push(job(cb, token));
this._flush();

return token;
}

forget(token) {
// TODO add explicit test
if (token) {
token.cancel();
}
}

_flush() {
if (this._nextFlush !== null) {
return;
}

this._nextFlush = requestAnimationFrame(() => {
this.flush();
});
}

flush() {
let i, q;
this.jobs = 0;

if (this.sync.length > 0) {
run.begin();
q = this.sync;
this.sync = [];

for (i = 0; i < q.length; i++) {
q[i]();
}
run.end();
}

if (this.measure.length > 0) {
q = this.measure;
this.measure = [];

for (i = 0; i < q.length; i++) {
q[i]();
}
}

this._nextFlush = null;
if (this.jobs > 0) {
this._flush();
}
}
}

export const scheduler = new Scheduler();

export default scheduler;
43 changes: 32 additions & 11 deletions addon/components/ember-table-base-cell.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,52 @@
import Component from '@ember/component';
import { addObserver, removeObserver } from "@ember/object/observers";

import { addObserver, removeObserver } from '@ember/object/observers';
import { htmlSafe } from '@ember/string';
import { action, computed } from 'ember-decorators/object';
import { get } from '@ember/object';
import { property } from '../utils/class';
import { scheduler, Token } from '../-private';

export default class EmberTableCell extends Component {
@property tagName = 'td';
@property attributeBindings = ['style:style'];

didInsertElement() {
requestAnimationFrame(() => this.syncChildSize());
addObserver(this, 'row.value', this, this.syncChildSize);
init() {
super.init(...arguments);

this.token = new Token();
}

willDestroyElement() {
removeObserver(this, 'row.value', this, this.syncChildSize);
willDestroy() {
this.token.cancel();
}

syncChildSize() {
didInsertElement() {
if (this.get('isFixed')) {
const { width, height } = this.element.getBoundingClientRect();
this.element.firstElementChild.style.width = `${width}px`;
this.element.firstElementChild.style.height = `${height}px`;
this.scheduleSync();
addObserver(this, 'rowValue', this, this.scheduleSync);
}
}

willDestroyElement() {
if (this.get('isFixed')) {
removeObserver(this, 'rowValue', this, this.scheduleSync);
}
}

scheduleSync() {
scheduler.schedule('sync', () => {
const { height } = this.element.getBoundingClientRect();

this.set('cellHeight', height || 0);
}, this.token);
}

@computed('columnIndex', 'numFixedColumns')
get isFixed() {
const numFixedColumns = this.get('numFixedColumns');
return this.get('columnIndex') === 0 && Number.isInteger(numFixedColumns)
&& numFixedColumns !== 0;
&& numFixedColumns !== 0;
}

@computed('row', 'column.valuePath')
Expand All @@ -46,6 +62,11 @@ export default class EmberTableCell extends Component {
return htmlSafe(`width: ${this.get('column.width')}px;`);
}

@computed('column.width', 'cellHeight')
get fixedCellStyle() {
return htmlSafe(`width: ${this.get('column.width')}px; height: ${this.get('cellHeight')}px;`);
}

@action
onCellEvent(args) {
this.sendAction('onCellEvent', args);
Expand Down
7 changes: 6 additions & 1 deletion addon/styles/addon.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,15 @@
display: inline-flex;
}

.et-td, .et-th {
// Fixed td/th elements don't have classes, so we need
// to make sure we override the base styles no matter what
td, th, .et-td, .et-th {
box-sizing: border-box;
flex: 1 1 auto;
display: block;
}

.et-td, .et-th {
position: relative;

&.is-fixed {
Expand Down
2 changes: 1 addition & 1 deletion addon/templates/components/ember-table-cell.hbs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{{#if isFixed}}
<div class="et-td is-fixed">
<div class="et-td is-fixed" style={{fixedCellStyle}}>
{{yield cell}}
</div>
{{else}}
Expand Down
2 changes: 1 addition & 1 deletion addon/templates/components/ember-table-footer.hbs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{{#if isFixed}}
<div class="et-th is-fixed">
<div class="et-th is-fixed" style={{fixedCellStyle}}>
{{yield}}
</div>
{{else}}
Expand Down
2 changes: 1 addition & 1 deletion addon/templates/components/ember-table-header.hbs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{{#if isFixed}}
<div class="et-th is-fixed">
<div class="et-th is-fixed" style={{fixedCellStyle}}>
{{#if column.headerComponent}}
{{#component column.headerComponent
column=column
Expand Down

0 comments on commit 5fbf485

Please sign in to comment.