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

[FEATURE] Resizable Columns #98

Merged
merged 6 commits into from
Jul 20, 2016
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
7 changes: 7 additions & 0 deletions addon/classes/Column.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ export default class Column extends Ember.Object.extend({
*/
sortable: true,

/**
* @property resizable
* @type {Boolean}
* @default false
*/
resizable: false,

/**
* @property sorted
* @type {Boolean}
Expand Down
11 changes: 9 additions & 2 deletions addon/components/columns/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ const Column = Component.extend({
tagName: 'th',
classNames: ['lt-column'],
attributeBindings: ['width', 'colspan', 'rowspan'],
classNameBindings: ['align', 'isGroupColumn:lt-group-column', 'isHideable', 'isSortable', 'isSorted', 'column.classNames'],
classNameBindings: ['align', 'isGroupColumn:lt-group-column', 'isHideable', 'isSortable', 'isSorted', 'isResizable', 'column.classNames'],

width: computed.readOnly('column.width'),
isGroupColumn: computed.readOnly('column.isGroupColumn'),
isSortable: computed.readOnly('column.sortable'),
isSorted: computed.readOnly('column.sorted'),
isHideable: computed.readOnly('column.hideable'),
isResizable: computed.readOnly('column.resizable'),

align: computed('column.align', function () {
return `align-${this.get('column.align')}`;
Expand Down Expand Up @@ -62,7 +63,13 @@ const Column = Component.extend({
rowspan: computed('column.visibleSubColumns.[]', function () {
let subColumns = this.get('column.visibleSubColumns');
return !isEmpty(subColumns) ? 1 : 2;
})
}),

actions: {
columnResized(width) {
this.sendAction('columnResized', this.get('column'), width);
}
}
});

Column.reopenClass({
Expand Down
68 changes: 68 additions & 0 deletions addon/components/lt-column-resize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import Ember from 'ember';
import layout from '../templates/components/lt-column-resize';

const {
$
} = Ember;

export default Ember.Component.extend({
layout,
classNameBindings: [':lt-column-resize', 'isResizing'],
column: null,
isResizing: false,

startWidth: null,
startX: null,

didInsertElement() {
this._super(...arguments);

this.__mouseMove = this._mouseMove.bind(this);
this.__mouseUp = this._mouseUp.bind(this);

$(document).on('mousemove', this.__mouseMove);
$(document).on('mouseup', this.__mouseUp);
},

willDestroyElement() {
this._super(...arguments);

$(document).off('mousemove', this.__mouseMove);
$(document).off('mouseup', this.__mouseUp);
},

mouseDown(e) {
const $column = $(this.get('element')).parent('th');

e.preventDefault();
e.stopPropagation();

this.setProperties({
isResizing: true,
startWidth: $column.width(),
startX: e.pageX
});
},

_mouseUp(e) {
if(this.get('isResizing')) {
e.preventDefault();
e.stopPropagation();

this.set('isResizing', false);
this.sendAction('columnResized', this.get('column.width'));
}
},

_mouseMove(e) {
if(this.get('isResizing')) {
e.preventDefault();
e.stopPropagation();

const { startX, startWidth } = this.getProperties(['startX', 'startWidth']);
const width = startWidth + (e.pageX - startX);

this.set('column.width', `${width}px`);
}
}
});
11 changes: 11 additions & 0 deletions addon/mixins/table-header.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,5 +118,16 @@ export default Ember.Mixin.create({
onColumnDoubleClick(/* column */) {
callAction(this, 'onColumnDoubleClick', ...arguments);
},

/**
* onColumnResized action.
*
* @method onColumnResized
* @param {Column} column The column that was resized
* @param {String} width The final width of the column
*/
onColumnResized(/* column, width */) {
callAction(this, 'onColumnResized', ...arguments);
}
}
});
14 changes: 14 additions & 0 deletions addon/styles/addon.css
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
flex: 1 0 auto;
}

.ember-light-table .lt-column {
position: relative;
}

.ember-light-table .lt-scrollable {
width: 100%;
flex: 1 0 auto;
Expand All @@ -63,6 +67,16 @@
cursor: pointer;
}

.ember-light-table .lt-column .lt-column-resize {
width: 5px;
cursor: col-resize;
height: 100%;
background: transparent;
position: absolute;
right: 0;
top: 0;
}

.ember-light-table .lt-row.is-expandable,
.ember-light-table .lt-row.is-selectable {
cursor: pointer;
Expand Down
4 changes: 4 additions & 0 deletions addon/templates/components/columns/base.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@
<span class="lt-sort-icon {{if column.ascending sortIcons.iconAscending sortIcons.iconDescending}}"></span>
{{/if}}
{{/if}}

{{#if isResizable}}
{{lt-column-resize column=column columnResized=(action 'columnResized')}}
{{/if}}
1 change: 1 addition & 0 deletions addon/templates/components/lt-column-resize.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{yield}}
3 changes: 2 additions & 1 deletion addon/templates/components/lt-foot.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
tableActions=tableActions
sortIcons=sortIcons
click=(action 'onColumnClick' column)
doubleClick=(action 'onColumnDoubleClick' column)}}
doubleClick=(action 'onColumnDoubleClick' column)
onColumnResized=(action 'onColumnResized')}}
{{/each}}
</tr>
{{/if}}
Expand Down
6 changes: 4 additions & 2 deletions addon/templates/components/lt-head.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
tableActions=tableActions
sortIcons=sortIcons
click=(action 'onColumnClick' column)
doubleClick=(action 'onColumnDoubleClick' column)}}
doubleClick=(action 'onColumnDoubleClick' column)
columnResized=(action 'onColumnResized')}}
{{/each}}
</tr>
<tr>
Expand All @@ -35,7 +36,8 @@
tableActions=tableActions
sortIcons=sortIcons
click=(action 'onColumnClick' column)
doubleClick=(action 'onColumnDoubleClick' column)}}
doubleClick=(action 'onColumnDoubleClick' column)
columnResized=(action 'onColumnResized')}}
{{/each}}
</tr>
{{/if}}
Expand Down
1 change: 1 addition & 0 deletions app/components/lt-column-resize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from 'ember-light-table/components/lt-column-resize';
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@
"ember-cli-babel": "^5.1.6",
"ember-cli-htmlbars": "^1.0.3",
"ember-in-viewport": "2.0.7",
"ember-wormhole": "0.4.0",
"ember-scrollable": "0.3.2",
"ember-truth-helpers": "1.2.0",
"ember-scrollable": "0.3.2"
"ember-wormhole": "0.4.0"
},
"ember-addon": {
"configPath": "tests/dummy/config",
Expand Down
1 change: 1 addition & 0 deletions tests/dummy/app/controllers/grouped.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export default TableController.extend({
label: 'User Details',
sortable: false,
align: 'center',

subColumns: [{
label: 'Avatar',
valuePath: 'avatar',
Expand Down
40 changes: 40 additions & 0 deletions tests/dummy/app/controllers/resizable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import Ember from 'ember';
import TableController from './table';

const {
computed
} = Ember;

export default TableController.extend({
columns: computed(function() {
return [{
label: 'Avatar',
valuePath: 'avatar',
width: '60px',
sortable: false,
cellComponent: 'user-avatar'
}, {
label: 'First Name',
valuePath: 'firstName',
resizable: true,
width: '150px'
}, {
label: 'Last Name',
valuePath: 'lastName',
resizable: true,
width: '150px'
}, {
label: 'Address',
valuePath: 'address',
resizable: true
}, {
label: 'State',
valuePath: 'state',
resizable: true
}, {
label: 'Country',
valuePath: 'country',
resizable: true
}];
})
});
1 change: 1 addition & 0 deletions tests/dummy/app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Router.map(function() {
this.route('grouped');
this.route('expandable');
this.route('selectable');
this.route('resizable');
});

export default Router;
4 changes: 4 additions & 0 deletions tests/dummy/app/routes/resizable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import Ember from 'ember';

export default Ember.Route.extend({
});
3 changes: 3 additions & 0 deletions tests/dummy/app/templates/application.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
{{#link-to 'grouped' tagName="li"}}
{{link-to 'Grouped Columns' 'grouped'}}
{{/link-to}}
{{#link-to 'resizable' tagName="li"}}
{{link-to 'Resizable Columns' 'resizable'}}
{{/link-to}}
{{#link-to 'expandable' tagName="li"}}
{{link-to 'Expandable Rows' 'expandable'}}
{{/link-to}}
Expand Down
51 changes: 51 additions & 0 deletions tests/dummy/app/templates/resizable.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="table-1">
<a role="button" data-toggle="collapse" data-parent="#accordion" href="#code-snippet" aria-expanded="true" aria-controls="code-snippet">
<h4 class="panel-title">Resizable Columns <span class="code-icon fa fa-code pull-right"></span></h4>
</a>
</div>
<div id="code-snippet" class="panel-collapse collapse" role="tabpanel" aria-labelledby="table-1">
<div class="panel-body code-snippet">
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active"><a href="#component" aria-controls="component" role="tab" data-toggle="tab">component.js</a></li>
<li role="presentation"><a href="#template" aria-controls="template" role="tab" data-toggle="tab">template.hbs</a></li>
<li role="presentation"><a href="#user-avatar" aria-controls="user-avatar" role="tab" data-toggle="tab">user-avatar.hbs</a></li>
<li role="presentation"><a href="#table-loader" aria-controls="table-loader" role="tab" data-toggle="tab">table-loader.hbs</a></li>
</ul>
<div class="tab-content">
<div role="tabpanel" class="tab-pane fade in active" id="component">{{code-snippet name="resizable-table.js"}}</div>
<div role="tabpanel" class="tab-pane fade" id="template">{{code-snippet name="resizable-table.hbs"}}</div>
<div role="tabpanel" class="tab-pane fade" id="user-avatar">{{code-snippet name="user-avatar.hbs"}}</div>
<div role="tabpanel" class="tab-pane fade" id="table-loader">{{code-snippet name="table-loader.hbs"}}</div>
</div>
</div>
</div>
<div class="panel-body table-container">
{{!-- BEGIN-SNIPPET resizable-table --}}
{{#light-table table height='65vh' as |t|}}

{{t.head
onColumnClick=(action 'onColumnClick')
iconAscending='fa fa-sort-asc'
iconDescending='fa fa-sort-desc'
fixed=true
}}

{{#t.body
canSelect=false
onScrolledToBottom=(action 'onScrolledToBottom')
as |body|
}}
{{#if isLoading}}
{{#body.loader}}
{{table-loader}}
{{/body.loader}}
{{/if}}
{{/t.body}}

{{/light-table}}
{{!-- END-SNIPPET --}}
</div>
</div>
</div>
Loading