Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[restructure] Simplify and flatten caching #513

Merged
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
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.
107 changes: 107 additions & 0 deletions addon/components/-private/row-wrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
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 = [];
_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 emberA(
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