Skip to content

Commit

Permalink
Implement fixed header (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
billy-addepar authored Aug 11, 2017
1 parent 95dd768 commit 1e852cc
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 30 deletions.
88 changes: 67 additions & 21 deletions addon/components/ember-table-2.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const HEAD_ALIGN_BAR_WIDTH = 5;
export default class EmberTable2 extends Component {
@property attributeBindings = ['style:style'];
@property classNames = ['et2-outer-wrapper'];
@property tagName = 'table';

@property layout = layout;

Expand Down Expand Up @@ -61,55 +62,82 @@ export default class EmberTable2 extends Component {

/**
* A sensor object that sends events to this table component when table size changes. When table
* is resized, we update the table width variable and all other properties dependent on table
* width is updated automatically.
* is resized, table width & height are updated and other computed properties depending on them
* also get updated.
*/
@property _resizeSensor = null;
@property _tableResizeSensor = null;

/**
* A sensor object that detect header row size change.
*/
@property _headerResizeSensor = null;

/**
* A variable to store table width. This is updated when table is created or resized. We need to
* store the table width because there are several computed property dependent on the table width.
*/
@property tableWidth = 0;
@property _width = 0;

/**
* Table height. This value is also equivalent to container height.
*/
@property _height = 0;

@property _headerHeight = 0;

didInsertElement() {
super.didInsertElement(...arguments);

requestAnimationFrame(() => {
this.set('_height', this.getHeightFromParent(this.element.parentElement));
this.set('_headerHeight', this.element.getElementsByClassName('et2-thead-wrapper')[0].offsetHeight);

this.set('tableWidth', this.element.offsetWidth);
this.set('_width', this.element.offsetWidth);
this.fillupColumn();

// Sync between table & the horizontal div scroller.
const tableContainer = $(this.element).find('table');
const bodyScrollContainer = $(this.element).find('.et2-body-inner-wrapper');
const headerScrollContainer = $(this.element).find('.et2-thead');
const horizontalScrollContainer = $(this.element).find('.et2-horizontal-scroll-wrapper');
horizontalScrollContainer.scroll(function() {
tableContainer.scrollLeft(horizontalScrollContainer.scrollLeft());
});
tableContainer.scroll(function() {
horizontalScrollContainer.scrollLeft(tableContainer.scrollLeft());
});

this.syncScroll([bodyScrollContainer, headerScrollContainer, horizontalScrollContainer]);
});

this._resizeSensor = new ResizeSensor(this.element, () => {
this._tableResizeSensor = new ResizeSensor(this.element, () => {
this.set('_height', this.getHeightFromParent(this.element.parentElement));
this.set('tableWidth', this.element.offsetWidth);
this.set('_width', this.element.offsetWidth);
this.fillupColumn();
});

this._headerResizeSensor
= new ResizeSensor(this.element.getElementsByClassName('et2-thead-wrapper')[0], () => {
this.set('_headerHeight', this.element.getElementsByClassName('et2-thead-wrapper')[0].offsetHeight);
});
}

willDestroyElement() {
this.get('resizeSensor').detach(this.element);
this.get('_tableResizeSensor').detach(this.element);
this.get('_headerResizeSensor').detach(this.element.getElementsByClassName('et2-thead-wrapper')[0]);

super.willDestroyElement(...arguments);
}

/**
* Syncs horizontal scrolling between table, header, body & footer.
*/
syncScroll(scrollElements) {
for (let i = 0; i < scrollElements.length; i++) {
scrollElements[i].scroll(function() {
const scrollPosition = scrollElements[i].scrollLeft();
for (let j = 0; j < scrollElements.length; j++) {
if (scrollElements[i] !== scrollElements[j]) {
scrollElements[j].scrollLeft(scrollPosition);
}
}
});
}
}

/**
* Gets height of this table container. This is equivalent to the parent's heignt minus top and
* bottom paddings.
Expand Down Expand Up @@ -141,13 +169,31 @@ export default class EmberTable2 extends Component {
return htmlSafe(`height: ${height}px;`);
}

@computed('_width')
theadWrapperStyle() {
return htmlSafe(`width: ${this.get('_width')}px;`);
}

@computed('_height', '_headerHeight')
tBodyWrapperStyle() {
const tableHeight = this.get('_height');
const headerHeight = this.get('_headerHeight');

let height = tableHeight - headerHeight;
if (height == 0) {
height = 1; // Set height at least 1 as vertical collection disallows 0px height.
}

return htmlSafe(`margin-top: ${headerHeight}px; max-height: ${height}px;`);
}

/**
* There are cases where the sum of all column width is smaller than the container width. In this
* case, we want to auto increase width of some column. This function handles that logic.
*/
fillupColumn() {
const columns = this.get('columns');
const tableWidth = this.get('tableWidth');
const tableWidth = this.get('_width');
const sum = this.get('allColumnWidths');

if (sum < tableWidth - 1) {
Expand Down Expand Up @@ -193,10 +239,10 @@ export default class EmberTable2 extends Component {
'hasFixedColumn',
'columns.firstObject.width',
'allColumnWidths',
'tableWidth'
'_width'
) horizontalScrollWrapperStyle() {
const columns = this.get('columns');
const visibility = this.get('tableWidth') < this.get('allColumnWidths') ? 'visibility' : 'hidden';
const visibility = this.get('_width') < this.get('allColumnWidths') ? 'visibility' : 'hidden';

return htmlSafe(`visibility: ${visibility}; left: ${columns[0].width}px; right: 0px;`);
}
Expand Down Expand Up @@ -275,7 +321,7 @@ export default class EmberTable2 extends Component {

@action
onColumnReorder(columnIndex, header, deltaX) {
const [containerElement] = this.element.getElementsByClassName('et2-table-container');
const containerElement = this.element;
const tableBoundingBox = containerElement.getBoundingClientRect();
const columns = this.get('columns');

Expand Down Expand Up @@ -347,8 +393,8 @@ export default class EmberTable2 extends Component {
this._currentColumnX = -1;

// Remove the header ghost element & aligned bar.
this.element.getElementsByClassName('et2-table-container')[0].removeChild(this._headerGhostElement);
this.element.getElementsByClassName('et2-table-container')[0].removeChild(this._headerAlignBar);
this.element.removeChild(this._headerGhostElement);
this.element.removeChild(this._headerAlignBar);
this._headerGhostElement = null;
this._headerAlignBar = null;
this.element.classList.remove('et2-unselectable');
Expand Down
26 changes: 21 additions & 5 deletions addon/styles/addon.css
Original file line number Diff line number Diff line change
@@ -1,16 +1,31 @@
.et2-outer-wrapper {
width: 100%;
height: 100%;
position: relative;
}

.et2-table-container {
.et2-body-outer-wrapper {
width: 100%;
height: 100%;
overflow-y: overlay;
overflow-x: hidden;
position: relative;
font-size: 30px;
}

.et2-body-inner-wrapper {
max-width: 100%;
display: block;
overflow-x: auto;
}

.et2-thead-wrapper {
position: absolute;
overflow-x: hidden;
}

.et2-thead {
max-width: 100%;
display: block;
overflow-x: auto;
}

div {
Expand All @@ -22,13 +37,14 @@ table {
max-width: 100%;
display: block;
/*allows max-width to work*/
overflow-x: auto;
overflow-x: hidden;
box-sizing: border-box;
}

table td, table th {
border: 1px solid black;
box-sizing: border-box;
font-size: 30px;
}

.et2-table-header {
Expand Down Expand Up @@ -81,7 +97,7 @@ table td, table th {
}

/* TODO(billy): do the same thing for IE. */
table::-webkit-scrollbar {
table::-webkit-scrollbar, thead::-webkit-scrollbar, .et2-body-inner-wrapper::-webkit-scrollbar {
width: 0px; /* remove scrollbar space */
height: 0px;
background: transparent; /* optional: just make scrollbar invisible */
Expand Down
12 changes: 8 additions & 4 deletions addon/templates/components/ember-table-2.hbs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div class="et2-table-container">
<table>
<div class="et2-thead-wrapper" style={{theadWrapperStyle}}>
<thead class="et2-thead">
<tr>
{{#each columns as |column columnIndex|}}
{{#ember-table-header
Expand All @@ -17,10 +17,14 @@
{{/ember-table-header}}
{{/each}}
</tr>
</thead>
</div>

<div class="et2-body-outer-wrapper" style={{tBodyWrapperStyle}}>
<div class="et2-body-inner-wrapper">
{{#vertical-collection
items=rows
containerSelector=".et2-table-container"
containerSelector=".et2-body-outer-wrapper"
estimateHeight=20
tagName="tbody"
as |row rowIndex| }}
Expand All @@ -37,7 +41,7 @@
{{/each}}
</tr>
{{/vertical-collection}}
</table>
</div>
</div>

<div class="et2-horizontal-scroll-wrapper" style={{horizontalScrollWrapperStyle}}>
Expand Down

0 comments on commit 1e852cc

Please sign in to comment.