Skip to content

Commit

Permalink
[restructure] Simplify and flatten caching
Browse files Browse the repository at this point in the history
This is some of the precursor work to the new API:

1. Simplify the cell cache in general
2. Wrap columns and rows in proxies that add `meta` properties
3. Yield `value cell column row` as described in the new API
4. Have CollapseTree return the list of parents, which allows us to
correctly figure out selection state in tree tables (more work and tests
on that coming in future PRs)
5. Create a "row wrapper" component. This is necessary for wrapping the
rows in proxy objects, since it should be done _after_ VC has done its
work in order to remain stable, but before we yield to the `tr` because
we don't want to complicate the `tr` logic (its been a struggling
point on the refactor branch)
  • Loading branch information
pzuraq committed Apr 24, 2018
1 parent a9c5e2f commit c3c0000
Show file tree
Hide file tree
Showing 22 changed files with 270 additions and 209 deletions.
27 changes: 15 additions & 12 deletions addon/-private/collapse-tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,10 +232,10 @@ class Node {
every `objectAt` call.
@param {number} index - the index to find
@param {number} depth - the depth of the current node in iteration
@return {{ value: object, depth: number }}
@param {Array<object>} parents - the parents of the current node in the traversal
@return {{ value: object, parents: Array<object> }}
*/
objectAt(index, depth) {
objectAt(index, parents) {
assert(
'index must be gte than 0 and less than the length of the node',
index >= 0 && index < get(this, 'length')
Expand All @@ -245,17 +245,17 @@ class Node {
if (index === 0) {
let value = get(this, 'value');

return { value, depth, toggleCollapse: this.toggleCollapse };
return { value, parents, isCollapsed: get(this, 'collapsed'), toggleCollapse: this.toggleCollapse };
}

// Passed this node, remove it from the index and go one level deeper
index = index - 1;
depth = depth + 1;
parents.push(this);

if (get(this, 'isLeaf')) {
let value = objectAt(get(this, 'value.children'), index);

return { value, depth };
return { value, parents, isCollapsed: false };
}

let children = get(this, 'children');
Expand All @@ -267,10 +267,10 @@ class Node {
let child = children[offsetIndex];

if (Array.isArray(child)) {
return { value: child[index], depth };
return { value: child[index], parents };
}

return child.objectAt(index, depth);
return child.objectAt(index, parents);
}

toggleCollapse = () => {
Expand Down Expand Up @@ -361,19 +361,22 @@ export default class CollapseTree {
/**
@param {number} index - the index to find
@return {{ value: object, depth: number }}
@return {{ value: object, parents: Array<object> }}
*/
objectAt(index) {
if (index >= get(this, 'length') || index < 0) {
return undefined;
}
if (this.rootIsArray) {
// If the root was an array, we added a "fake" top level node. Skip this node
// by adding one to the index, and "subtracting" one from the depth.
return this.root.objectAt(index + 1, -1);
// by adding one to the index, and shifting the first parent off the parent list.
let result = this.root.objectAt(index + 1, []);
result.parents.shift();

return result;
}

return this.root.objectAt(index, 0);
return this.root.objectAt(index, []);
}

/**
Expand Down
16 changes: 16 additions & 0 deletions addon/-private/meta-cache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import ObjectProxy from '@ember/object/proxy';
import EmberObject from '@ember/object';

const META_CACHE_MAP = new WeakMap();

export function metaCacheFor(obj, Class) {
if (META_CACHE_MAP.has(obj) === false) {
META_CACHE_MAP.set(obj, Class.create ? Class.create() : new Class());
}

return META_CACHE_MAP.get(obj);
}

export default class MetaProxy extends ObjectProxy {
meta = metaCacheFor(this.content, EmberObject);
}
Empty file removed addon/.gitkeep
Empty file.
105 changes: 105 additions & 0 deletions addon/components/-private/row-wrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import Component from '@ember/component';
import hbs from 'htmlbars-inline-precompile';

import EmberObject, { get, set } from '@ember/object';
import { A as emberA } from '@ember/array';
import ObjectProxy from '@ember/object/proxy';

import { tagName } from '@ember-decorators/component';
import { argument } from '@ember-decorators/argument';

import { computed } from '@ember-decorators/object';
import { readOnly } from '@ember-decorators/object/computed';

import { metaCacheFor } from '../../-private/meta-cache';
import CellProxy from '../../utils/cell-proxy';

class TableRowMeta extends EmberObject {
@readOnly('_parents.length') depth;

@computed('_value', '_selectedRows.[]', '_parents.[]')
get isSelected() {
let selectedRows = get(this, '_selectedRows');
return selectedRows.includes(this.get('_value')) || this.get('_parents').some((row) => selectedRows.includes(row));
}
}

class TableRowProxy extends ObjectProxy {
@computed('content')
get meta() {
return metaCacheFor(this.get('content'), TableRowMeta);
}
}

@tagName('')
export default class RowWrapper extends Component {
layout = hbs`
{{yield proxy cells}}
`;

@argument row;

@argument rowIndex;

@argument selectedRows;

@argument columns;

_cells = emberA();
_proxy = TableRowProxy.create();

@computed('row', 'selectedRows.[]')
get proxy() {
let proxy = this._proxy;

let { value: rowValue, parents, isCollapsed } = this.get('row');
let rowIndex = this.get('rowIndex');

proxy.set('content', rowValue);

let meta = proxy.get('meta');

set(meta, '_selectedRows', this.get('selectedRows'));
set(meta, '_value', rowValue);
set(meta, '_parents', parents);

set(meta, 'index', rowIndex);
set(meta, 'isCollapsed', isCollapsed);

return proxy;
}

@computed('row.value', 'columns.[]')
get cells() {
let row = this.get('proxy');
let columns = this.get('columns');
let numColumns = get(columns, 'length');

let { _cells } = this;

if (numColumns !== _cells.length) {
while (_cells.length < numColumns) {
_cells.push(CellProxy.create());
}

while (_cells.length > numColumns) {
_cells.pop();
}
}

return columns.map((column, i) => {
let valuePath = get(column, 'valuePath');

let cell = _cells[i];
set(cell, 'row', row);
set(cell, 'valuePath', valuePath);

return {
value: get(row, valuePath),
cellValue: cell,
columnValue: column,
rowValue: row
};
});
}
}
4 changes: 4 additions & 0 deletions addon/components/ember-table-base-cell.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ export default class EmberTableCell extends Component {
@type(optional('number'))
rowIndex;

@argument
@type(optional('object'))
api;

@argument
@type('object')
column;
Expand Down
6 changes: 3 additions & 3 deletions addon/components/ember-table-cell.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ export default class EmberTableCell extends EmberTableBaseCell {
onChecked;

click() {
let rowValue = this.get('cell.row.value');
let rowIndex = this.get('cell.row.index');
let api = this.get('cell.row.api');
let rowValue = this.get('rowValue');
let rowIndex = this.get('rowIndex');
let api = this.get('api');

if (this.get('columnIndex') === 0 && Array.isArray(get(rowValue, 'children'))) {
api.toggleRowCollapse(rowIndex);
Expand Down
74 changes: 10 additions & 64 deletions addon/components/ember-table-row.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import { get, set } from '@ember/object';
import Component from '@ember/component';
import { action, computed } from '@ember-decorators/object';
import { action } from '@ember-decorators/object';
import { readOnly } from '@ember-decorators/object/computed';
import { attribute, className, classNames, tagName } from '@ember-decorators/component';
import { className, classNames, tagName } from '@ember-decorators/component';

import { argument } from '@ember-decorators/argument';
import { required } from '@ember-decorators/argument/validation';
import { type } from '@ember-decorators/argument/type';

import layout from '../templates/components/ember-table-row';
import { A as emberA } from '@ember/array';
import { isNone } from '@ember/utils';

@tagName('tr')
@classNames('et-tr')
Expand All @@ -22,77 +19,26 @@ export default class EmberTableRow extends Component {
* rendered cell view.
*/
_outerCellComponent = 'ember-table-cell';
_cells = null;

@argument
@required
@type('object')
row;

@readOnly('row.api') api;
@readOnly('row.rowValue') rowValue;
@readOnly('row.rowValue.meta.index') rowIndex;

@readOnly('row.value') rowValue;
@readOnly('row.index') rowIndex;
@className
@readOnly('row.rowValue.meta.isSelected')
isSelected;

@readOnly('api.columns') columns;
@readOnly('api.cellProxyClass') cellProxyClass;
@readOnly('api.cellCache') cellCache;
@readOnly('row.api') api;

@readOnly('row.cells') cells;
@readOnly('api.numFixedColumns') numFixedColumns;
@readOnly('api.selectedRows') selectedRows;

constructor() {
super(...arguments);

this._cells = emberA();
}

@computed('columns.[]')
get cells() {
let CellProxyClass = this.get('cellProxyClass');

let _rowComponent = this;
let _cache = this.get('cellCache');
let columns = this.get('columns');
let numColumns = get(columns, 'length');

let { _cells } = this;

if (numColumns !== _cells.length) {
while (_cells.length < numColumns) {
_cells.push(CellProxyClass.create({ _cache, _rowComponent }));
}

while (_cells.length > numColumns) {
_cells.pop();
}
}

for (let i = 0; i < numColumns; i++) {
let cell = _cells[i];
let column = columns.objectAt !== undefined ? columns.objectAt(i) : columns[i];

set(cell, 'column', column);
set(cell, 'columnIndex', i);
set(cell, 'row', this.get('row'));
}

return _cells;
}

@className
@computed('selectedRows.[]', 'rowValue')
get isSelected() {
return this.get('selectedRows').indexOf(this.get('rowValue')) >= 0;
}

@attribute
@computed('row.api.staticRowHeight')
get style() {
let staticRowHeight = this.get('row.api.staticRowHeight');
if (!isNone(staticRowHeight)) {
return `height: ${staticRowHeight}px;`;
}
return '';
}

click(event) {
Expand Down
Loading

0 comments on commit c3c0000

Please sign in to comment.