From ae70b5daeda74a656af7a6b7119170390aa56469 Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Wed, 21 Aug 2019 18:52:36 +0200 Subject: [PATCH 01/37] flexbox tables with dummy app to play with --- addon/components/upf-table/index.js | 7 ++ app/templates/components/upf-table.hbs | 102 +++------------------ package.json | 1 + tests/dummy/app/controllers/application.js | 22 +++++ tests/dummy/app/routes/application.js | 33 +++++++ tests/dummy/app/styles/app.less | 24 +++++ tests/dummy/app/templates/application.hbs | 3 +- tests/dummy/config/optional-features.json | 2 +- 8 files changed, 103 insertions(+), 91 deletions(-) create mode 100644 tests/dummy/app/controllers/application.js create mode 100644 tests/dummy/app/routes/application.js diff --git a/addon/components/upf-table/index.js b/addon/components/upf-table/index.js index fa3dbba05..52e850ef6 100644 --- a/addon/components/upf-table/index.js +++ b/addon/components/upf-table/index.js @@ -2,6 +2,7 @@ import Component from '@ember/component'; import EmberObject, { observer, computed } from '@ember/object'; import { filterBy } from '@ember/object/computed'; import { run } from '@ember/runloop'; +import { isEmpty } from '@ember/utils'; export default Component.extend({ classNames: ['upf-table__container'], @@ -76,6 +77,12 @@ export default Component.extend({ run.debounce(this, this._bubbleSearch, 100); }), + didReceiveAttrs() { + if (isEmpty(this.columns)) { + throw new Error('[component][upf-table] Missing columns'); + } + }, + actions: { toggleDisplayedColumnVisibilityPanel() { this.toggleProperty('displayedColumnsPanel'); diff --git a/app/templates/components/upf-table.hbs b/app/templates/components/upf-table.hbs index 30139765a..11ed6bb90 100644 --- a/app/templates/components/upf-table.hbs +++ b/app/templates/components/upf-table.hbs @@ -43,94 +43,18 @@ {{/each}} - - - {{#upf-table/row isHeaderRow=true}} - {{#if hasSelection}} - {{#unless contentChanging}} - - {{/unless}} - {{/if}} - - {{#each _columns as |column|}} - {{#unless (eq column.visible false)}} - {{upf-table/header_cell column=column - click=(action "onClickHeader" column)}} - {{/unless}} - {{/each}} - {{/upf-table/row}} - - - - {{#if isLoading}} - - - - {{else}} - {{#if contentChanging}} - {{#each _contentPlaceholder}} - {{#upf-table/row}} - - - {{/upf-table/row}} - {{/each}} - {{else}} - {{#each collection as |item index|}} - {{#upf-table/row ref=item action='callOnRowClickCallback' - hasPolymorphicColumns=hasPolymorphicColumns - onRowClick=onRowClick}} - {{#if hasSelection}} - - {{/if}} +
+ {{#each _columns as |column|}} +
+
+ {{column.title}} +
- {{#each _columns as |column|}} - {{#unless (eq column.visible false)}} - {{#upf-table/column ref=column editable=column.editable - classNames=column.additionalClasses}} - {{#if column.component}} - {{component column.component item=item column=column}} - {{else}} - {{#if (eq column.helper "money")}} - {{format-money (get item column.property) (get item column.currency)}} - {{else if (eq column.helper "numeric")}} - {{format-numeric (get item column.property)}} - {{else}} - {{get item column.property}} - {{/if}} - {{/if}} - {{/upf-table/column}} - {{/unless}} - {{/each}} - {{/upf-table/row}} - {{else}} -
- - +
+ {{#each collection as |item|}} +
{{get item column.property}}
{{/each}} - - {{#if (and hasPagination isCompact)}} -
- - - {{/if}} - {{/if}} - {{/if}} - -
- {{upf-checkbox value=allRowsSelected}} -
- {{loading-state}} -
-
-
-
-
- {{upf-checkbox value=item.selected}} -
- {{yield}} -
- {{upf-table/pagination currentPage=currentPage perPage=perPage - totalPages=totalPages itemTotal=itemTotal - itemCount=itemCount itemName=itemName}} -
+ + + {{/each}} + diff --git a/package.json b/package.json index e6cbb3927..59a9d024a 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "test:all": "ember try:each" }, "dependencies": { + "@ember/jquery": "^0.6.1", "babel-plugin-debug-macros": "^0.3.1", "ember-cli-babel": "^7.1.2", "ember-cli-htmlbars": "^3.0.0", diff --git a/tests/dummy/app/controllers/application.js b/tests/dummy/app/controllers/application.js new file mode 100644 index 000000000..be9f8a895 --- /dev/null +++ b/tests/dummy/app/controllers/application.js @@ -0,0 +1,22 @@ +import Controller from '@ember/controller'; + +export default Controller.extend({ + columns: [ + { + title: 'Plan Name', + property: 'name' + }, + { + title: 'Plan Price', + property: 'price' + }, + { + title: 'Users Count', + property: 'usersCount' + }, + { + title: 'Bulk Emails', + property: 'bulkEmailsCount' + } + ] +}); diff --git a/tests/dummy/app/routes/application.js b/tests/dummy/app/routes/application.js new file mode 100644 index 000000000..910951bc5 --- /dev/null +++ b/tests/dummy/app/routes/application.js @@ -0,0 +1,33 @@ +import Route from '@ember/routing/route'; +import EmberObject from '@ember/object'; + +export default Route.extend({ + model() { + return [ + EmberObject.create({ + name: 'Bronze', + price: '99', + usersCount: 1, + bulkEmailsCount: 0, + }), + EmberObject.create({ + name: 'Silver', + price: '195', + usersCount: 1, + bulkEmailsCount: 100, + }), + EmberObject.create({ + name: 'Gold', + price: '495', + usersCount: 1, + bulkEmailsCount: 300, + }), + EmberObject.create({ + name: 'Enterprise', + price: 'Talk to us', + usersCount: 'Custom', + bulkEmailsCount: 'Custom', + }) + ]; + } +}); diff --git a/tests/dummy/app/styles/app.less b/tests/dummy/app/styles/app.less index 8b1378917..bdf5cf7ae 100644 --- a/tests/dummy/app/styles/app.less +++ b/tests/dummy/app/styles/app.less @@ -1 +1,25 @@ +@import 'oss-components'; +.table-v2 { + display: flex; + flex-direction: row; +} + +.table-v2__column { + margin-left: @spacing-xxx-sm; + border-left: 1px solid @upf-gray; + + &:first-child { + border-left: none; + } +} + +.table-v2__body { + display: flex; + flex-direction: column; +} + +.table-v2__cell { + padding: @spacing-xxx-sm; + vertical-align: middle; +} diff --git a/tests/dummy/app/templates/application.hbs b/tests/dummy/app/templates/application.hbs index f0097571c..25e925df5 100644 --- a/tests/dummy/app/templates/application.hbs +++ b/tests/dummy/app/templates/application.hbs @@ -1 +1,2 @@ -this is upfluence oss component +{{upf-table + columns=columns collection=model}} diff --git a/tests/dummy/config/optional-features.json b/tests/dummy/config/optional-features.json index b1902623a..21f1dc719 100644 --- a/tests/dummy/config/optional-features.json +++ b/tests/dummy/config/optional-features.json @@ -1,3 +1,3 @@ { - "jquery-integration": false + "jquery-integration": true } From 2c2b9c4f00fae6a5dd520a747cfbc6075c72648c Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Fri, 23 Aug 2019 11:16:22 +0200 Subject: [PATCH 02/37] add component representing a single cell in the table --- addon/components/upf-table/cell.js | 13 +++++++++++++ app/components/upf-table/cell.js | 1 + app/templates/components/upf-table/cell.hbs | 5 +++++ 3 files changed, 19 insertions(+) create mode 100644 addon/components/upf-table/cell.js create mode 100644 app/components/upf-table/cell.js create mode 100644 app/templates/components/upf-table/cell.hbs diff --git a/addon/components/upf-table/cell.js b/addon/components/upf-table/cell.js new file mode 100644 index 000000000..4bc0c9b42 --- /dev/null +++ b/addon/components/upf-table/cell.js @@ -0,0 +1,13 @@ +import Component from '@ember/component'; +import { observer } from '@ember/object'; +import { run } from '@ember/runloop'; + +export default Component.extend({ + classNames: ['table-v2__cell'], + classNameBindings: [ + 'header:table-v2__cell--header', + 'item.selected:table-v2__cell--selected' + ], + + header: false +}); diff --git a/app/components/upf-table/cell.js b/app/components/upf-table/cell.js new file mode 100644 index 000000000..763ad29bf --- /dev/null +++ b/app/components/upf-table/cell.js @@ -0,0 +1 @@ +export { default } from 'oss-components/components/upf-table/cell'; diff --git a/app/templates/components/upf-table/cell.hbs b/app/templates/components/upf-table/cell.hbs new file mode 100644 index 000000000..dde7acff1 --- /dev/null +++ b/app/templates/components/upf-table/cell.hbs @@ -0,0 +1,5 @@ +{{#if (has-block)}} + {{yield}} +{{else}} + {{get item property}} +{{/if}} From d6bcdc2bd8cfba693eff23b6e5cb3c4573665f06 Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Fri, 23 Aug 2019 11:22:56 +0200 Subject: [PATCH 03/37] setup basic table with drag and droppable columns --- addon/components/upf-table/index.js | 195 +++++++++++++------------ app/templates/components/upf-table.hbs | 70 ++------- package.json | 1 + tests/dummy/app/styles/app.less | 25 +++- 4 files changed, 136 insertions(+), 155 deletions(-) diff --git a/addon/components/upf-table/index.js b/addon/components/upf-table/index.js index 52e850ef6..37b6808db 100644 --- a/addon/components/upf-table/index.js +++ b/addon/components/upf-table/index.js @@ -6,36 +6,38 @@ import { isEmpty } from '@ember/utils'; export default Component.extend({ classNames: ['upf-table__container'], - classNameBindings: ['isCompact:upf-table__container--compact'], - - hasSelection: false, - hasPagination: false, - hasSearch: false, - hasPolymorphicColumns: false, - hasToggleableColumns: true, - isCompact: false, - - allRowsSelected: false, - displayedColumnsPanel: false, - isLoading: false, - contentChanging: false, - _contentPlaceholder: new Array(3), - - _searchQuery: null, - searchInputPlaceholder: 'Search...', - - currentPage: 1, - totalPages: 1, - perPage: 1, - itemTotal: 0, - itemCount: 0, - itemName: '', - - init() { - this._super(); - - this.set('_searchQuery', this.get('searchQuery')); - }, + //classNameBindings: ['isCompact:upf-table__container--compact'], + + // Rows Selection + //hasSelection: false, + //allRowsSelected: false, + + //hasPagination: false, + //hasSearch: false, + //hasPolymorphicColumns: false, + //hasToggleableColumns: true, + //isCompact: false, + + //displayedColumnsPanel: false, + //isLoading: false, + //contentChanging: false, + //_contentPlaceholder: new Array(3), + + //_searchQuery: null, + //searchInputPlaceholder: 'Search...', + +/* currentPage: 1,*/ + //totalPages: 1, + //perPage: 1, + //itemTotal: 0, + //itemCount: 0, + /*itemName: '',*/ + + //init() { + //this._super(); + + //this.set('_searchQuery', this.get('searchQuery')); + /*},*/ _columns: computed('columns', function() { return this.get('columns').map((column) => { @@ -48,34 +50,34 @@ export default Component.extend({ }); }), - _initiallyDisplayedColumns: filterBy('_columns', 'visible'), - _fullSizeColumnColspan: computed('_initiallyDisplayedColumns', function() { - if (this.get('hasSelection')) { - return this.get('_initiallyDisplayedColumns').length + 1; - } - - return this.get('_initiallyDisplayedColumns').length; - }), - - _selectAllObserver: observer('allRowsSelected', function() { - this.get('collection').map((item) => { - if (this.get('allRowsSelected')) { - item.set('selected', true); - } else { - item.set('selected', false); - } - }); - }), - - _bubbleSearch: function() { - if (this.performSearch) { - this.performSearch(this.get('_searchQuery')); - } - }, - - _searchQueryObserver: observer('_searchQuery', function() { - run.debounce(this, this._bubbleSearch, 100); - }), +/* _initiallyDisplayedColumns: filterBy('_columns', 'visible'),*/ + //_fullSizeColumnColspan: computed('_initiallyDisplayedColumns', function() { + //if (this.get('hasSelection')) { + //return this.get('_initiallyDisplayedColumns').length + 1; + //} + + //return this.get('_initiallyDisplayedColumns').length; + /*}),*/ + +/* _selectAllObserver: observer('allRowsSelected', function() {*/ + //this.get('collection').forEach((item) => { + //if (this.get('allRowsSelected')) { + //item.set('selected', true); + //} else { + //item.set('selected', false); + //} + //}); + /*}),*/ + +/* _bubbleSearch: function() {*/ + //if (this.performSearch) { + //this.performSearch(this.get('_searchQuery')); + //} + //}, + + //_searchQueryObserver: observer('_searchQuery', function() { + //run.debounce(this, this._bubbleSearch, 100); + //}), didReceiveAttrs() { if (isEmpty(this.columns)) { @@ -84,42 +86,47 @@ export default Component.extend({ }, actions: { - toggleDisplayedColumnVisibilityPanel() { - this.toggleProperty('displayedColumnsPanel'); - }, - - callOnRowClickCallback(action, record) { - this.onRowClick(record); - }, - - didPageChange(page) { - this.sendAction('didPageChange', page); - }, - - onClickHeader(column) { - if (!column.get('sortKey')) { - return; - } - - if (!column.get('sorted')) { - this.get('_columns').forEach((c) => c.set('sorted', false)); - // default direction - column.set('sorted', true); - column.set('direction', 'desc'); - } else { - let direction = column.get('direction') === 'desc' ? 'asc' : 'desc'; - column.set('direction', direction); - } - - this.sendAction('didSortColumn', column); + reorderColumns(_, itemModels) { + this.set('columns', itemModels); + console.log(itemModels); + window.sessionStorage.setItem('columns', itemModels.map((x) => x.property)); }, - - triggerObjectCreation() { - this.triggerAction({ action: 'handleObjectCreation'}); - }, - - setContentChanging(contentChanging) { - this.set('contentChanging', contentChanging); - } +/* toggleDisplayedColumnVisibilityPanel() {*/ + //this.toggleProperty('displayedColumnsPanel'); + //}, + + //callOnRowClickCallback(action, record) { + //this.onRowClick(record); + //}, + + //didPageChange(page) { + //this.sendAction('didPageChange', page); + /*},*/ + + //onClickHeader(column) { + //if (!column.get('sortKey')) { + //return; + //} + + //if (!column.get('sorted')) { + //this.get('_columns').forEach((c) => c.set('sorted', false)); + //// default direction + //column.set('sorted', true); + //column.set('direction', 'desc'); + //} else { + //let direction = column.get('direction') === 'desc' ? 'asc' : 'desc'; + //column.set('direction', direction); + //} + + //this.sendAction('didSortColumn', column); + /*},*/ + + //triggerObjectCreation() { + //this.triggerAction({ action: 'handleObjectCreation'}); + //}, + + //setContentChanging(contentChanging) { + //this.set('contentChanging', contentChanging); + //} } }); diff --git a/app/templates/components/upf-table.hbs b/app/templates/components/upf-table.hbs index 11ed6bb90..ab480ce30 100644 --- a/app/templates/components/upf-table.hbs +++ b/app/templates/components/upf-table.hbs @@ -1,60 +1,16 @@ -
    - {{#if contextualActionsComponent}} - {{component contextualActionsComponent collection=collection}} - {{/if}} +{{#sortable-group + direction="x" model=_columns onChange=(action "reorderColumns") as |group|}} +
    + {{#each _columns as |column|}} + {{#sortable-item classNames="table-v2__column" model=column group=group}} + {{#upf-table/cell header=true}} + {{column.title}} + {{/upf-table/cell}} -
  • - {{#if hasToggleableColumns}} - -   - {{#if displayedColumnsPanel}} - - {{else}} - - {{/if}} - - {{/if}} - - {{#if hasPagination}} - {{#unless isCompact}} - {{upf-table/pagination currentPage=currentPage perPage=perPage - totalPages=totalPages itemTotal=itemTotal - itemCount=itemCount itemName=itemName}} - {{/unless}} - {{/if}} - - {{#if hasSearch}} - {{expanding-search classNames="margin-left-xx-sm" searchQuery=_searchQuery - placeholder=searchInputPlaceholder small=true}} - {{/if}} -
  • -
- -
- {{#each _columns as |column|}} - {{#unless column.unhideable}} -
- {{#upf-checkbox value=column.visible hasLabel=true}} - {{column.title}} - {{/upf-checkbox}} -
- {{/unless}} - {{/each}} -
- -
- {{#each _columns as |column|}} -
-
- {{column.title}} -
- -
{{#each collection as |item|}} -
{{get item column.property}}
+ {{upf-table/cell item=item property=column.property}} {{/each}} -
-
- {{/each}} -
+ {{/sortable-item}} + {{/each}} + +{{/sortable-group}} diff --git a/package.json b/package.json index 59a9d024a..309edf911 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "ember-maybe-import-regenerator": "^0.1.6", "ember-qunit": "^3.4.1", "ember-resolver": "^5.0.1", + "ember-sortable": "^1.12.9", "ember-source": "~3.8.0", "ember-source-channel-url": "^1.1.0", "ember-try": "^1.0.0", diff --git a/tests/dummy/app/styles/app.less b/tests/dummy/app/styles/app.less index bdf5cf7ae..ddde15358 100644 --- a/tests/dummy/app/styles/app.less +++ b/tests/dummy/app/styles/app.less @@ -3,15 +3,28 @@ .table-v2 { display: flex; flex-direction: row; + width: 100%; + justify-content: space-between; } .table-v2__column { - margin-left: @spacing-xxx-sm; - border-left: 1px solid @upf-gray; + border-left: 1px solid lighten(@upf-gray, 15%); + width: 100%; &:first-child { border-left: none; } + + // Animate Columns Drag & Drop + &.is-dragging { + transition: background .5s; + background: red; + } + + &.is-dropping { + transition: background .5s; + background: blue; + } } .table-v2__body { @@ -20,6 +33,10 @@ } .table-v2__cell { - padding: @spacing-xxx-sm; - vertical-align: middle; + align-items: center; + border-bottom: 1px solid lighten(@upf-gray, 15%); + display: flex; + height: 35px; + max-height: 35px; + padding: @spacing-xx-sm; } From c2c8a7366ed3b9c8c96d7ca7f3b6ecb26baa4fa9 Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Fri, 23 Aug 2019 11:24:22 +0200 Subject: [PATCH 04/37] add back support for row selection --- addon/components/upf-table/index.js | 22 +++++++++++----------- app/templates/components/upf-table.hbs | 14 ++++++++++++++ tests/dummy/app/routes/application.js | 9 +++++++-- tests/dummy/app/styles/app.less | 13 +++++++++++++ tests/dummy/app/templates/application.hbs | 2 +- 5 files changed, 46 insertions(+), 14 deletions(-) diff --git a/addon/components/upf-table/index.js b/addon/components/upf-table/index.js index 37b6808db..8bd0fc420 100644 --- a/addon/components/upf-table/index.js +++ b/addon/components/upf-table/index.js @@ -9,8 +9,8 @@ export default Component.extend({ //classNameBindings: ['isCompact:upf-table__container--compact'], // Rows Selection - //hasSelection: false, - //allRowsSelected: false, + hasSelection: false, + allRowsSelected: false, //hasPagination: false, //hasSearch: false, @@ -59,15 +59,15 @@ export default Component.extend({ //return this.get('_initiallyDisplayedColumns').length; /*}),*/ -/* _selectAllObserver: observer('allRowsSelected', function() {*/ - //this.get('collection').forEach((item) => { - //if (this.get('allRowsSelected')) { - //item.set('selected', true); - //} else { - //item.set('selected', false); - //} - //}); - /*}),*/ + _selectAllObserver: observer('allRowsSelected', function() { + this.get('collection').forEach((item) => { + if (this.get('allRowsSelected')) { + item.set('selected', true); + } else { + item.set('selected', false); + } + }); + }), /* _bubbleSearch: function() {*/ //if (this.performSearch) { diff --git a/app/templates/components/upf-table.hbs b/app/templates/components/upf-table.hbs index ab480ce30..f289df5c9 100644 --- a/app/templates/components/upf-table.hbs +++ b/app/templates/components/upf-table.hbs @@ -1,6 +1,20 @@ {{#sortable-group direction="x" model=_columns onChange=(action "reorderColumns") as |group|}}
+ {{#if hasSelection}} +
+
+ {{upf-checkbox value=allRowsSelected}} +
+ + {{#each collection as |item|}} +
+ {{upf-checkbox value=item.selected}} +
+ {{/each}} +
+ {{/if}} + {{#each _columns as |column|}} {{#sortable-item classNames="table-v2__column" model=column group=group}} {{#upf-table/cell header=true}} diff --git a/tests/dummy/app/routes/application.js b/tests/dummy/app/routes/application.js index 910951bc5..78568f1b7 100644 --- a/tests/dummy/app/routes/application.js +++ b/tests/dummy/app/routes/application.js @@ -1,33 +1,38 @@ +import { A } from '@ember/array'; import Route from '@ember/routing/route'; import EmberObject from '@ember/object'; export default Route.extend({ model() { - return [ + return A([ EmberObject.create({ name: 'Bronze', price: '99', usersCount: 1, bulkEmailsCount: 0, + selected: false }), EmberObject.create({ name: 'Silver', price: '195', usersCount: 1, bulkEmailsCount: 100, + selected: false }), EmberObject.create({ name: 'Gold', price: '495', usersCount: 1, bulkEmailsCount: 300, + selected: false }), EmberObject.create({ name: 'Enterprise', price: 'Talk to us', usersCount: 'Custom', bulkEmailsCount: 'Custom', + selected: false }) - ]; + ]); } }); diff --git a/tests/dummy/app/styles/app.less b/tests/dummy/app/styles/app.less index ddde15358..262ed3896 100644 --- a/tests/dummy/app/styles/app.less +++ b/tests/dummy/app/styles/app.less @@ -27,6 +27,15 @@ } } +.table-v2__column--selection { + width: 40px; + + .table-v2__cell { + display: flex; + justify-content: space-around; + } +} + .table-v2__body { display: flex; flex-direction: column; @@ -40,3 +49,7 @@ max-height: 35px; padding: @spacing-xx-sm; } + +.table-v2__cell--selected { + background-color: @upf-gray-light; +} diff --git a/tests/dummy/app/templates/application.hbs b/tests/dummy/app/templates/application.hbs index 25e925df5..dc4973ba9 100644 --- a/tests/dummy/app/templates/application.hbs +++ b/tests/dummy/app/templates/application.hbs @@ -1,2 +1,2 @@ {{upf-table - columns=columns collection=model}} + columns=columns collection=model hasSelection=true}} From dd62a39faaf1d45563926cf44c8c6ee84e4a565a Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Fri, 23 Aug 2019 11:32:27 +0200 Subject: [PATCH 05/37] table columns are only draggable by their header --- app/templates/components/upf-table.hbs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/templates/components/upf-table.hbs b/app/templates/components/upf-table.hbs index f289df5c9..639086282 100644 --- a/app/templates/components/upf-table.hbs +++ b/app/templates/components/upf-table.hbs @@ -16,7 +16,9 @@ {{/if}} {{#each _columns as |column|}} - {{#sortable-item classNames="table-v2__column" model=column group=group}} + {{#sortable-item + classNames="table-v2__column" model=column group=group + handle=".table-v2__cell--header"}} {{#upf-table/cell header=true}} {{column.title}} {{/upf-table/cell}} From 7a2b000a314f85f79cd1103806a1fc2fa8ed1a18 Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Fri, 23 Aug 2019 15:40:47 +0200 Subject: [PATCH 06/37] higher cells --- tests/dummy/app/styles/app.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/dummy/app/styles/app.less b/tests/dummy/app/styles/app.less index 262ed3896..504d7c964 100644 --- a/tests/dummy/app/styles/app.less +++ b/tests/dummy/app/styles/app.less @@ -45,8 +45,8 @@ align-items: center; border-bottom: 1px solid lighten(@upf-gray, 15%); display: flex; - height: 35px; - max-height: 35px; + height: 45px; + max-height: 45px; padding: @spacing-xx-sm; } From ba7f4ddc4efd71b155a911a3556916d575c25202 Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Fri, 23 Aug 2019 15:41:49 +0200 Subject: [PATCH 07/37] better transition when swapping columns (at least for now) --- tests/dummy/app/styles/app.less | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/dummy/app/styles/app.less b/tests/dummy/app/styles/app.less index 504d7c964..233b2d041 100644 --- a/tests/dummy/app/styles/app.less +++ b/tests/dummy/app/styles/app.less @@ -9,7 +9,9 @@ .table-v2__column { border-left: 1px solid lighten(@upf-gray, 15%); + opacity: 1; width: 100%; + transition: opacity .5s; &:first-child { border-left: none; @@ -17,13 +19,11 @@ // Animate Columns Drag & Drop &.is-dragging { - transition: background .5s; - background: red; + opacity: .75; } &.is-dropping { - transition: background .5s; - background: blue; + opacity: 1; } } From c5bf9c704b4430ecad7d709ac0e0427a43a64d3b Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Fri, 23 Aug 2019 15:48:31 +0200 Subject: [PATCH 08/37] add icon commands to the headers, always displayed for now --- app/templates/components/upf-table.hbs | 5 +++++ tests/dummy/app/styles/app.less | 30 ++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/app/templates/components/upf-table.hbs b/app/templates/components/upf-table.hbs index 639086282..41e2c4314 100644 --- a/app/templates/components/upf-table.hbs +++ b/app/templates/components/upf-table.hbs @@ -21,6 +21,11 @@ handle=".table-v2__cell--header"}} {{#upf-table/cell header=true}} {{column.title}} + +
+ + +
{{/upf-table/cell}} {{#each collection as |item|}} diff --git a/tests/dummy/app/styles/app.less b/tests/dummy/app/styles/app.less index 233b2d041..e612a63f1 100644 --- a/tests/dummy/app/styles/app.less +++ b/tests/dummy/app/styles/app.less @@ -50,6 +50,36 @@ padding: @spacing-xx-sm; } +.table-v2__cell.table-v2__cell--header { + background-color: @upf-gray-light; + color: @upf-primary-rock-blue; + justify-content: space-between; + align-items: center; + transition: opacity .5s; + + & > .icon-commands { + color: @color-text-lighter; + display: flex; + flex-direction: row; + align-items: center; + + i { + margin-left: @spacing-xx-sm; + } + + i:hover, i:active { + color: @color-text; + cursor: pointer; + } + + i.fa-bars { cursor: move; } + } + + &:hover .icon-commands { + opacity: 1; + } +} + .table-v2__cell--selected { background-color: @upf-gray-light; } From 049ae2cc0f71a555a5a18628d87eed7abcc43bb8 Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Mon, 26 Aug 2019 10:41:20 +0200 Subject: [PATCH 09/37] add only display columns that are supposed to be --- addon/components/upf-table/index.js | 4 +--- app/templates/components/upf-table.hbs | 30 ++++++++++++++------------ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/addon/components/upf-table/index.js b/addon/components/upf-table/index.js index 8bd0fc420..8f366bc9f 100644 --- a/addon/components/upf-table/index.js +++ b/addon/components/upf-table/index.js @@ -88,9 +88,7 @@ export default Component.extend({ actions: { reorderColumns(_, itemModels) { this.set('columns', itemModels); - console.log(itemModels); - window.sessionStorage.setItem('columns', itemModels.map((x) => x.property)); - }, + } /* toggleDisplayedColumnVisibilityPanel() {*/ //this.toggleProperty('displayedColumnsPanel'); //}, diff --git a/app/templates/components/upf-table.hbs b/app/templates/components/upf-table.hbs index 41e2c4314..21c5195ba 100644 --- a/app/templates/components/upf-table.hbs +++ b/app/templates/components/upf-table.hbs @@ -16,22 +16,24 @@ {{/if}} {{#each _columns as |column|}} - {{#sortable-item - classNames="table-v2__column" model=column group=group - handle=".table-v2__cell--header"}} - {{#upf-table/cell header=true}} - {{column.title}} + {{#if column.visible}} + {{#sortable-item + classNames="table-v2__column" model=column group=group + handle=".table-v2__cell--header"}} + {{#upf-table/cell header=true}} + {{column.title}} -
- - -
- {{/upf-table/cell}} +
+ + +
+ {{/upf-table/cell}} - {{#each collection as |item|}} - {{upf-table/cell item=item property=column.property}} - {{/each}} - {{/sortable-item}} + {{#each collection as |item|}} + {{upf-table/cell item=item property=column.property}} + {{/each}} + {{/sortable-item}} + {{/if}} {{/each}}
{{/sortable-group}} From 2014393b628b9b10476471a51193cf9cc1036e8d Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Mon, 26 Aug 2019 10:54:56 +0200 Subject: [PATCH 10/37] transform columns config to EmberObjects from the start --- addon/components/upf-table/index.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/addon/components/upf-table/index.js b/addon/components/upf-table/index.js index 8f366bc9f..fc5ff92f6 100644 --- a/addon/components/upf-table/index.js +++ b/addon/components/upf-table/index.js @@ -41,12 +41,14 @@ export default Component.extend({ _columns: computed('columns', function() { return this.get('columns').map((column) => { - column.visible = column.visible !== false; - column.sorted = column.sorted === true; - column.editable = column.editable !== false; - column.unhideable = column.unhideable === true; + column = EmberObject.create(column); - return EmberObject.create(column); + column.set('visible', column.visible !== false); + column.set('sorted', column.sorted === true); + column.set('editable', column.editable !== false); + column.set('unhideable', column.unhideable === true); + + return column; }); }), From b6fe401dddf24188ee24a9991888d8003febffd7 Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Mon, 26 Aug 2019 10:55:45 +0200 Subject: [PATCH 11/37] allow the callee app to handle the columns update --- addon/components/upf-table/index.js | 7 +++++++ tests/dummy/app/controllers/application.js | 8 +++++++- tests/dummy/app/templates/application.hbs | 3 ++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/addon/components/upf-table/index.js b/addon/components/upf-table/index.js index fc5ff92f6..0c7435c1e 100644 --- a/addon/components/upf-table/index.js +++ b/addon/components/upf-table/index.js @@ -71,6 +71,13 @@ export default Component.extend({ }); }), + _columnsChanged: observer('_columns', function() { + if (this.columnsChanged) { + this.columnsChanged(this._columns); + } + }), + + /* _bubbleSearch: function() {*/ //if (this.performSearch) { //this.performSearch(this.get('_searchQuery')); diff --git a/tests/dummy/app/controllers/application.js b/tests/dummy/app/controllers/application.js index be9f8a895..62159910a 100644 --- a/tests/dummy/app/controllers/application.js +++ b/tests/dummy/app/controllers/application.js @@ -18,5 +18,11 @@ export default Controller.extend({ title: 'Bulk Emails', property: 'bulkEmailsCount' } - ] + ], + + actions: { + columnsChanged(layout) { + console.log(layout.map((x) => x.property)); + } + } }); diff --git a/tests/dummy/app/templates/application.hbs b/tests/dummy/app/templates/application.hbs index dc4973ba9..046ee88cf 100644 --- a/tests/dummy/app/templates/application.hbs +++ b/tests/dummy/app/templates/application.hbs @@ -1,2 +1,3 @@ {{upf-table - columns=columns collection=model hasSelection=true}} + columns=columns collection=model hasSelection=true + columnsChanged=(action "columnsChanged")}} From 991b44e70b4316b8ea0b39af6d0cb100638b5ac3 Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Mon, 26 Aug 2019 14:37:25 +0200 Subject: [PATCH 12/37] mock columns config being sent out by the backend --- tests/dummy/app/controllers/application.js | 41 ++++++------ tests/dummy/app/routes/application.js | 34 ---------- tests/dummy/app/services/plans-fetcher.js | 77 ++++++++++++++++++++++ tests/dummy/app/templates/application.hbs | 2 +- 4 files changed, 100 insertions(+), 54 deletions(-) create mode 100644 tests/dummy/app/services/plans-fetcher.js diff --git a/tests/dummy/app/controllers/application.js b/tests/dummy/app/controllers/application.js index 62159910a..1d9e61827 100644 --- a/tests/dummy/app/controllers/application.js +++ b/tests/dummy/app/controllers/application.js @@ -1,28 +1,31 @@ import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; export default Controller.extend({ - columns: [ - { - title: 'Plan Name', - property: 'name' - }, - { - title: 'Plan Price', - property: 'price' - }, - { - title: 'Users Count', - property: 'usersCount' - }, - { - title: 'Bulk Emails', - property: 'bulkEmailsCount' - } - ], + plansFetcher: service(), + + collection: [], + columns: [], + + init() { + this._super(); + this._fetchPlans(); + }, + + _fetchPlans(columnsLayout) { + this.set('refreshing', true); + + this.plansFetcher.fetch().then((data) => { + this.set('collection', data.items); + this.set('columns', data.meta.columns); + }).finally(() => { + this.set('refreshing', false); + }); + }, actions: { columnsChanged(layout) { - console.log(layout.map((x) => x.property)); + this.plansFetcher.fetch(layout); } } }); diff --git a/tests/dummy/app/routes/application.js b/tests/dummy/app/routes/application.js index 78568f1b7..6c74252aa 100644 --- a/tests/dummy/app/routes/application.js +++ b/tests/dummy/app/routes/application.js @@ -1,38 +1,4 @@ -import { A } from '@ember/array'; import Route from '@ember/routing/route'; -import EmberObject from '@ember/object'; export default Route.extend({ - model() { - return A([ - EmberObject.create({ - name: 'Bronze', - price: '99', - usersCount: 1, - bulkEmailsCount: 0, - selected: false - }), - EmberObject.create({ - name: 'Silver', - price: '195', - usersCount: 1, - bulkEmailsCount: 100, - selected: false - }), - EmberObject.create({ - name: 'Gold', - price: '495', - usersCount: 1, - bulkEmailsCount: 300, - selected: false - }), - EmberObject.create({ - name: 'Enterprise', - price: 'Talk to us', - usersCount: 'Custom', - bulkEmailsCount: 'Custom', - selected: false - }) - ]); - } }); diff --git a/tests/dummy/app/services/plans-fetcher.js b/tests/dummy/app/services/plans-fetcher.js new file mode 100644 index 000000000..955229f70 --- /dev/null +++ b/tests/dummy/app/services/plans-fetcher.js @@ -0,0 +1,77 @@ +import { A } from '@ember/array'; +import EmberObject from '@ember/object'; +import Service from '@ember/service'; + +const MOCK_DATA = A([ + EmberObject.create({ + name: 'Bronze', + price: '99', + usersCount: 1, + bulkEmailsCount: 0, + selected: false + }), + EmberObject.create({ + name: 'Silver', + price: '195', + usersCount: 1, + bulkEmailsCount: 100, + selected: false + }), + EmberObject.create({ + name: 'Gold', + price: '495', + usersCount: 1, + bulkEmailsCount: 300, + selected: false + }), + EmberObject.create({ + name: 'Enterprise', + price: 'Talk to us', + usersCount: 'Custom', + bulkEmailsCount: 'Custom', + selected: false + }) +]); + +const DEFAULT_COLUMNS = [ + { + title: 'Plan Name', + property: 'name' + }, + { + title: 'Plan Price', + property: 'price' + }, + { + title: 'Users Count', + property: 'usersCount' + }, + { + title: 'Bulk Emails', + property: 'bulkEmailsCount' + } +]; + +export default Service.extend({ + fetch(columnsLayout) { + return new Promise((resolve, reject) => { + let columns = DEFAULT_COLUMNS; + + if (columnsLayout) { + columns = columnsLayout; + window.sessionStorage.setItem('columns', JSON.stringify(columns)); + } else if (!columnsLayout && window.sessionStorage.getItem('columns')) { + columns = JSON.parse(window.sessionStorage.getItem('columns')); + } + + setTimeout(() => { + resolve({ + items: MOCK_DATA, + meta: { + columns: columns + } + }); + }, 1500) + }); + } +}); diff --git a/tests/dummy/app/templates/application.hbs b/tests/dummy/app/templates/application.hbs index 046ee88cf..369fe01ce 100644 --- a/tests/dummy/app/templates/application.hbs +++ b/tests/dummy/app/templates/application.hbs @@ -1,3 +1,3 @@ {{upf-table - columns=columns collection=model hasSelection=true + columns=columns collection=collection hasSelection=true columnsChanged=(action "columnsChanged")}} From 007a9a3e948b94053ed6269bbe9e2255ff83bbef Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Tue, 27 Aug 2019 16:09:48 +0200 Subject: [PATCH 13/37] order available columns alphabetically and avoid stripping hidden columns from the availables when reordering --- addon/components/upf-table/index.js | 40 ++++++++++------ app/templates/components/upf-table.hbs | 19 +++++++- tests/dummy/app/styles/app.less | 63 +++++++++++++++++++++++++- 3 files changed, 105 insertions(+), 17 deletions(-) diff --git a/addon/components/upf-table/index.js b/addon/components/upf-table/index.js index 0c7435c1e..a7dcc1e8f 100644 --- a/addon/components/upf-table/index.js +++ b/addon/components/upf-table/index.js @@ -1,6 +1,6 @@ import Component from '@ember/component'; import EmberObject, { observer, computed } from '@ember/object'; -import { filterBy } from '@ember/object/computed'; +import { filterBy, sort } from '@ember/object/computed'; import { run } from '@ember/runloop'; import { isEmpty } from '@ember/utils'; @@ -10,7 +10,7 @@ export default Component.extend({ // Rows Selection hasSelection: false, - allRowsSelected: false, + _allRowsSelected: false, //hasPagination: false, //hasSearch: false, @@ -18,7 +18,7 @@ export default Component.extend({ //hasToggleableColumns: true, //isCompact: false, - //displayedColumnsPanel: false, + _availableColumnsPanel: false, //isLoading: false, //contentChanging: false, //_contentPlaceholder: new Array(3), @@ -52,6 +52,9 @@ export default Component.extend({ }); }), + _columnsSortingOrder: ['title'], + _orderedColumns: sort('_columns', '_columnsSortingOrder'), + /* _initiallyDisplayedColumns: filterBy('_columns', 'visible'),*/ //_fullSizeColumnColspan: computed('_initiallyDisplayedColumns', function() { //if (this.get('hasSelection')) { @@ -61,9 +64,9 @@ export default Component.extend({ //return this.get('_initiallyDisplayedColumns').length; /*}),*/ - _selectAllObserver: observer('allRowsSelected', function() { + _selectAllObserver: observer('_allRowsSelected', function() { this.get('collection').forEach((item) => { - if (this.get('allRowsSelected')) { + if (this.get('_allRowsSelected')) { item.set('selected', true); } else { item.set('selected', false); @@ -71,12 +74,14 @@ export default Component.extend({ }); }), - _columnsChanged: observer('_columns', function() { - if (this.columnsChanged) { - this.columnsChanged(this._columns); + _columnsChanged: observer( + '_columns', '_columns.@each.visible', + function() { + if (this.columnsChanged) { + this.columnsChanged(this._columns); + } } - }), - + ), /* _bubbleSearch: function() {*/ //if (this.performSearch) { @@ -95,12 +100,17 @@ export default Component.extend({ }, actions: { - reorderColumns(_, itemModels) { - this.set('columns', itemModels); + reorderColumns(x, itemModels, y) { + this.set('columns', itemModels.concat(x.filter(x => !x.visible))); + }, + + openAvailableColumns() { + this.toggleProperty('_availableColumnsPanel'); + }, + + toggleColumnVisibility(column) { + column.toggleProperty('visible'); } -/* toggleDisplayedColumnVisibilityPanel() {*/ - //this.toggleProperty('displayedColumnsPanel'); - //}, //callOnRowClickCallback(action, record) { //this.onRowClick(record); diff --git a/app/templates/components/upf-table.hbs b/app/templates/components/upf-table.hbs index 21c5195ba..03e0d28d2 100644 --- a/app/templates/components/upf-table.hbs +++ b/app/templates/components/upf-table.hbs @@ -1,10 +1,27 @@ +
+ + + {{#if _availableColumnsPanel}} +
    + {{#each _orderedColumns as |column|}} +
  • + {{column.title}} +
  • + {{/each}} +
+ {{/if}} +
+ {{#sortable-group direction="x" model=_columns onChange=(action "reorderColumns") as |group|}}
{{#if hasSelection}}
- {{upf-checkbox value=allRowsSelected}} + {{upf-checkbox value=_allRowsSelected}}
{{#each collection as |item|}} diff --git a/tests/dummy/app/styles/app.less b/tests/dummy/app/styles/app.less index e612a63f1..13944843f 100644 --- a/tests/dummy/app/styles/app.less +++ b/tests/dummy/app/styles/app.less @@ -7,6 +7,64 @@ justify-content: space-between; } +.table-v2__actions { + align-items: center; + display: flex; + justify-content: flex-end; + + padding: @spacing-xxx-sm; + width: 100%; +} + +.table-v2__available-columns { + background-color: @upf-gray-light; + border: 1px solid lighten(@upf-gray, 15%); + border-radius: @default-radius; + list-style-type: none; + + padding: @spacing-xxx-sm 0; + position: absolute; + top: 4em; + right: 3em; + + width: 200px; + + li { + padding: @spacing-xxx-sm; + + &:hover { + background-color: lighten(@upf-gray, 15%); + cursor: pointer; + } + } + + li:not(.visible) { + color: @color-text-lighter; + } + + &:before, + &:after { + content:''; + position: absolute; + bottom: 100%; + left: 80%; + width: 0; + height: 0; + border-style: solid; + } + + &:after { + border-color: transparent transparent @upf-gray-light transparent; + border-width: 8px; + } + + &:before { + left: 79%; + border-color: transparent transparent lighten(@upf-gray, 15%) transparent; + border-width: 10px; + } +} + .table-v2__column { border-left: 1px solid lighten(@upf-gray, 15%); opacity: 1; @@ -53,8 +111,11 @@ .table-v2__cell.table-v2__cell--header { background-color: @upf-gray-light; color: @upf-primary-rock-blue; - justify-content: space-between; + cursor: pointer; + align-items: center; + justify-content: space-between; + transition: opacity .5s; & > .icon-commands { From 88bcd916dd0aa2de43a03468fbcc01ed55fd297a Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Wed, 28 Aug 2019 14:47:46 +0200 Subject: [PATCH 14/37] hardset an order on columns --- addon/components/upf-table/index.js | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/addon/components/upf-table/index.js b/addon/components/upf-table/index.js index a7dcc1e8f..2526d0048 100644 --- a/addon/components/upf-table/index.js +++ b/addon/components/upf-table/index.js @@ -1,5 +1,5 @@ import Component from '@ember/component'; -import EmberObject, { observer, computed } from '@ember/object'; +import EmberObject, { computed, observer } from '@ember/object'; import { filterBy, sort } from '@ember/object/computed'; import { run } from '@ember/runloop'; import { isEmpty } from '@ember/utils'; @@ -10,6 +10,7 @@ export default Component.extend({ // Rows Selection hasSelection: false, + _allRowsSelected: false, //hasPagination: false, @@ -55,15 +56,6 @@ export default Component.extend({ _columnsSortingOrder: ['title'], _orderedColumns: sort('_columns', '_columnsSortingOrder'), -/* _initiallyDisplayedColumns: filterBy('_columns', 'visible'),*/ - //_fullSizeColumnColspan: computed('_initiallyDisplayedColumns', function() { - //if (this.get('hasSelection')) { - //return this.get('_initiallyDisplayedColumns').length + 1; - //} - - //return this.get('_initiallyDisplayedColumns').length; - /*}),*/ - _selectAllObserver: observer('_allRowsSelected', function() { this.get('collection').forEach((item) => { if (this.get('_allRowsSelected')) { @@ -101,7 +93,9 @@ export default Component.extend({ actions: { reorderColumns(x, itemModels, y) { - this.set('columns', itemModels.concat(x.filter(x => !x.visible))); + let _cs = itemModels.concat(x.filter(x => !x.visible)) + _cs.forEach((c, i) => c.set('order', i)) + this.set('columns', _cs); }, openAvailableColumns() { From d1472f853943e1a18a09300c5d52b7659bc52d5f Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Wed, 28 Aug 2019 15:14:36 +0200 Subject: [PATCH 15/37] setup basic support for column data types --- addon/components/upf-table/cell.js | 34 ++++++++++++++++--- .../upf-table/cell/renderers/money.js | 14 ++++++++ .../upf-table/cell/renderers/numeric.js | 4 +++ .../upf-table/cell/renderers/text.js | 4 +++ addon/components/upf-table/index.js | 1 + addon/helpers/format-money.js | 7 ++-- .../upf-table/cell/renderers/money.js | 1 + .../upf-table/cell/renderers/numeric.js | 1 + .../upf-table/cell/renderers/text.js | 1 + app/helpers/format-numeric.js | 24 ++----------- app/templates/components/upf-table.hbs | 3 +- app/templates/components/upf-table/cell.hbs | 2 +- .../upf-table/cell/renderers/money.hbs | 1 + .../upf-table/cell/renderers/numeric.hbs | 1 + .../upf-table/cell/renderers/text.hbs | 1 + tests/dummy/app/services/plans-fetcher.js | 16 ++++++--- tests/dummy/app/styles/app.less | 6 +++- 17 files changed, 83 insertions(+), 38 deletions(-) create mode 100644 addon/components/upf-table/cell/renderers/money.js create mode 100644 addon/components/upf-table/cell/renderers/numeric.js create mode 100644 addon/components/upf-table/cell/renderers/text.js create mode 100644 app/components/upf-table/cell/renderers/money.js create mode 100644 app/components/upf-table/cell/renderers/numeric.js create mode 100644 app/components/upf-table/cell/renderers/text.js create mode 100644 app/templates/components/upf-table/cell/renderers/money.hbs create mode 100644 app/templates/components/upf-table/cell/renderers/numeric.hbs create mode 100644 app/templates/components/upf-table/cell/renderers/text.hbs diff --git a/addon/components/upf-table/cell.js b/addon/components/upf-table/cell.js index 4bc0c9b42..4b8f417e5 100644 --- a/addon/components/upf-table/cell.js +++ b/addon/components/upf-table/cell.js @@ -1,13 +1,39 @@ import Component from '@ember/component'; -import { observer } from '@ember/object'; -import { run } from '@ember/runloop'; +import { computed, defineProperty } from '@ember/object'; +import { capitalize } from '@ember/string'; + +const AVAILABLE_RENDERERS = [ + 'text', 'numeric', 'money' +]; export default Component.extend({ classNames: ['table-v2__cell'], classNameBindings: [ 'header:table-v2__cell--header', - 'item.selected:table-v2__cell--selected' + 'item.selected:table-v2__cell--selected', + 'isNumeric:table-v2__cell--numeric', + 'isMoney:table-v2__cell--numeric' ], - header: false + header: false, + + _renderingComponent: computed('column.type', function() { + if (AVAILABLE_RENDERERS.includes(this.column.type)) { + return `upf-table/cell/renderers/${this.column.type}`; + } + }), + + didReceiveAttrs() { + if (this.column) { + AVAILABLE_RENDERERS.forEach((rendererType) => { + defineProperty( + this, + `is${capitalize(rendererType)}`, + computed('column.type', function() { + return this.column.type === rendererType; + }) + ); + }); + } + } }); diff --git a/addon/components/upf-table/cell/renderers/money.js b/addon/components/upf-table/cell/renderers/money.js new file mode 100644 index 000000000..9c2690d79 --- /dev/null +++ b/addon/components/upf-table/cell/renderers/money.js @@ -0,0 +1,14 @@ +import Component from '@ember/component'; +import { computed } from '@ember/object'; + +export default Component.extend({ + currency: computed('column', 'column.currency_key', function() { + if (this.column && !this.column.currency_key) { + throw new Error( + '[upf-table/cell-renderers][money] You are trying to render Money without a currency' + ); + } + + return this.item.get(this.column.currency_key); + }), +}); diff --git a/addon/components/upf-table/cell/renderers/numeric.js b/addon/components/upf-table/cell/renderers/numeric.js new file mode 100644 index 000000000..bb93d73f3 --- /dev/null +++ b/addon/components/upf-table/cell/renderers/numeric.js @@ -0,0 +1,4 @@ +import Component from '@ember/component'; + +export default Component.extend({ +}); diff --git a/addon/components/upf-table/cell/renderers/text.js b/addon/components/upf-table/cell/renderers/text.js new file mode 100644 index 000000000..bb93d73f3 --- /dev/null +++ b/addon/components/upf-table/cell/renderers/text.js @@ -0,0 +1,4 @@ +import Component from '@ember/component'; + +export default Component.extend({ +}); diff --git a/addon/components/upf-table/index.js b/addon/components/upf-table/index.js index 2526d0048..66c307e6c 100644 --- a/addon/components/upf-table/index.js +++ b/addon/components/upf-table/index.js @@ -48,6 +48,7 @@ export default Component.extend({ column.set('sorted', column.sorted === true); column.set('editable', column.editable !== false); column.set('unhideable', column.unhideable === true); + column.set('type', column.type || 'text'); return column; }); diff --git a/addon/helpers/format-money.js b/addon/helpers/format-money.js index 2f9d3a57b..623869d86 100644 --- a/addon/helpers/format-money.js +++ b/addon/helpers/format-money.js @@ -9,11 +9,8 @@ var _getFormatter = function(currency) { }; var _formatMoney = function(amount, currency) { - if (amount > 0) { - return _getFormatter(currency).format(parseFloat(amount)); - } else { - return '--'; - } + if (isNaN(parseInt(amount))) return amount; + return _getFormatter(currency).format(parseFloat(amount)); }; diff --git a/app/components/upf-table/cell/renderers/money.js b/app/components/upf-table/cell/renderers/money.js new file mode 100644 index 000000000..ab154483e --- /dev/null +++ b/app/components/upf-table/cell/renderers/money.js @@ -0,0 +1 @@ +export { default } from 'oss-components/components/upf-table/cell/renderers/money'; diff --git a/app/components/upf-table/cell/renderers/numeric.js b/app/components/upf-table/cell/renderers/numeric.js new file mode 100644 index 000000000..2c10de031 --- /dev/null +++ b/app/components/upf-table/cell/renderers/numeric.js @@ -0,0 +1 @@ +export { default } from 'oss-components/components/upf-table/cell/renderers/numeric'; diff --git a/app/components/upf-table/cell/renderers/text.js b/app/components/upf-table/cell/renderers/text.js new file mode 100644 index 000000000..01b732de1 --- /dev/null +++ b/app/components/upf-table/cell/renderers/text.js @@ -0,0 +1 @@ +export { default } from 'oss-components/components/upf-table/cell/renderers/text'; diff --git a/app/helpers/format-numeric.js b/app/helpers/format-numeric.js index f87b44829..c23a6b436 100644 --- a/app/helpers/format-numeric.js +++ b/app/helpers/format-numeric.js @@ -1,22 +1,5 @@ import Helper from '@ember/component/helper'; -var _getFormatter = function(currency) { - return Intl.NumberFormat(['en-EN', 'fr-FR'], { - style: 'currency', - currency: currency, - minimumFractionDigits: 0, // show decimals only if there are ones - }); -}; - -var _formatMoney = function(amount, currency) { - if (amount > 0) { - return _getFormatter(currency).format(parseFloat(amount)); - } else { - return '--'; - } -}; - - export function formatNumericHelper(params) { let [number] = params; let formatter = Intl.NumberFormat(['en-EN', 'fr-FR'], { @@ -24,11 +7,8 @@ export function formatNumericHelper(params) { minimumFractionDigits: 0, // show decimals only if there are ones }); - if(number > 0) { - return formatter.format(number); - } else { - return '--'; - } + if (isNaN(parseInt(number))) return number; + return formatter.format(number); } export default Helper.helper(formatNumericHelper); diff --git a/app/templates/components/upf-table.hbs b/app/templates/components/upf-table.hbs index 03e0d28d2..0c9971d12 100644 --- a/app/templates/components/upf-table.hbs +++ b/app/templates/components/upf-table.hbs @@ -47,7 +47,8 @@ {{/upf-table/cell}} {{#each collection as |item|}} - {{upf-table/cell item=item property=column.property}} + {{upf-table/cell + item=item column=column}} {{/each}} {{/sortable-item}} {{/if}} diff --git a/app/templates/components/upf-table/cell.hbs b/app/templates/components/upf-table/cell.hbs index dde7acff1..44aba8669 100644 --- a/app/templates/components/upf-table/cell.hbs +++ b/app/templates/components/upf-table/cell.hbs @@ -1,5 +1,5 @@ {{#if (has-block)}} {{yield}} {{else}} - {{get item property}} + {{component _renderingComponent item=item column=column}} {{/if}} diff --git a/app/templates/components/upf-table/cell/renderers/money.hbs b/app/templates/components/upf-table/cell/renderers/money.hbs new file mode 100644 index 000000000..a40bd64c6 --- /dev/null +++ b/app/templates/components/upf-table/cell/renderers/money.hbs @@ -0,0 +1 @@ +{{format-money (get item column.property) currency}} diff --git a/app/templates/components/upf-table/cell/renderers/numeric.hbs b/app/templates/components/upf-table/cell/renderers/numeric.hbs new file mode 100644 index 000000000..7e0cde8f8 --- /dev/null +++ b/app/templates/components/upf-table/cell/renderers/numeric.hbs @@ -0,0 +1 @@ +{{format-numeric (get item column.property)}} diff --git a/app/templates/components/upf-table/cell/renderers/text.hbs b/app/templates/components/upf-table/cell/renderers/text.hbs new file mode 100644 index 000000000..943bf383f --- /dev/null +++ b/app/templates/components/upf-table/cell/renderers/text.hbs @@ -0,0 +1 @@ +{{get item column.property}} diff --git a/tests/dummy/app/services/plans-fetcher.js b/tests/dummy/app/services/plans-fetcher.js index 955229f70..5ac91416c 100644 --- a/tests/dummy/app/services/plans-fetcher.js +++ b/tests/dummy/app/services/plans-fetcher.js @@ -6,6 +6,7 @@ const MOCK_DATA = A([ EmberObject.create({ name: 'Bronze', price: '99', + currency: 'EUR', usersCount: 1, bulkEmailsCount: 0, selected: false @@ -13,6 +14,7 @@ const MOCK_DATA = A([ EmberObject.create({ name: 'Silver', price: '195', + currency: 'EUR', usersCount: 1, bulkEmailsCount: 100, selected: false @@ -20,6 +22,7 @@ const MOCK_DATA = A([ EmberObject.create({ name: 'Gold', price: '495', + currency: 'EUR', usersCount: 1, bulkEmailsCount: 300, selected: false @@ -36,19 +39,24 @@ const MOCK_DATA = A([ const DEFAULT_COLUMNS = [ { title: 'Plan Name', - property: 'name' + property: 'name', + type: 'text' }, { title: 'Plan Price', - property: 'price' + property: 'price', + type: 'money', + currency_key: 'currency' }, { title: 'Users Count', - property: 'usersCount' + property: 'usersCount', + type: 'numeric' }, { title: 'Bulk Emails', - property: 'bulkEmailsCount' + property: 'bulkEmailsCount', + type: 'numeric' } ]; diff --git a/tests/dummy/app/styles/app.less b/tests/dummy/app/styles/app.less index 13944843f..295327c01 100644 --- a/tests/dummy/app/styles/app.less +++ b/tests/dummy/app/styles/app.less @@ -141,6 +141,10 @@ } } -.table-v2__cell--selected { +.table-v2__cell.table-v2__cell--selected { background-color: @upf-gray-light; } + +.table-v2__cell.table-v2__cell--numeric { + justify-content: flex-end; +} From 9a089dee8664a263c457e3c3222f035d81a0c61a Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Tue, 3 Sep 2019 15:56:33 +0200 Subject: [PATCH 16/37] skip wraping div on money and numeric cell renderers --- addon/components/upf-table/cell/renderers/money.js | 2 ++ addon/components/upf-table/cell/renderers/numeric.js | 1 + 2 files changed, 3 insertions(+) diff --git a/addon/components/upf-table/cell/renderers/money.js b/addon/components/upf-table/cell/renderers/money.js index 9c2690d79..7e7cce644 100644 --- a/addon/components/upf-table/cell/renderers/money.js +++ b/addon/components/upf-table/cell/renderers/money.js @@ -2,6 +2,8 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; export default Component.extend({ + tagName: '', + currency: computed('column', 'column.currency_key', function() { if (this.column && !this.column.currency_key) { throw new Error( diff --git a/addon/components/upf-table/cell/renderers/numeric.js b/addon/components/upf-table/cell/renderers/numeric.js index bb93d73f3..0a447bae1 100644 --- a/addon/components/upf-table/cell/renderers/numeric.js +++ b/addon/components/upf-table/cell/renderers/numeric.js @@ -1,4 +1,5 @@ import Component from '@ember/component'; export default Component.extend({ + tagName: '' }); From 37029102c9f911ab7d5275c5b81ef2699219f03a Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Tue, 3 Sep 2019 16:37:27 +0200 Subject: [PATCH 17/37] rework the datatable with fixed header and sticky first column support --- addon/components/upf-checkbox.js | 7 +- addon/components/upf-table/cell.js | 10 +- addon/components/upf-table/index.js | 2 +- app/templates/components/upf-table.hbs | 130 +++++++++----- tests/dummy/app/services/plans-fetcher.js | 32 +++- tests/dummy/app/styles/app.less | 205 ++++++++++++++-------- tests/dummy/app/templates/application.hbs | 8 +- 7 files changed, 264 insertions(+), 130 deletions(-) diff --git a/addon/components/upf-checkbox.js b/addon/components/upf-checkbox.js index 59f805a75..e6fb99832 100644 --- a/addon/components/upf-checkbox.js +++ b/addon/components/upf-checkbox.js @@ -1,6 +1,11 @@ import Component from '@ember/component'; +import { equal } from '@ember/object/computed'; export default Component.extend({ classNames: ['upf-checkbox'], - classNameBindings: ['hasLabel:upf-checkbox--has-label'] + classNameBindings: [ + 'hasLabel:upf-checkbox--has-label', 'sizeSmall:upf-checkbox--sm' + ], + + sizeSmall: equal('size', 'sm') }); diff --git a/addon/components/upf-table/cell.js b/addon/components/upf-table/cell.js index 4b8f417e5..9323da6be 100644 --- a/addon/components/upf-table/cell.js +++ b/addon/components/upf-table/cell.js @@ -7,12 +7,12 @@ const AVAILABLE_RENDERERS = [ ]; export default Component.extend({ - classNames: ['table-v2__cell'], + classNames: ['upf-hypertable__cell'], classNameBindings: [ - 'header:table-v2__cell--header', - 'item.selected:table-v2__cell--selected', - 'isNumeric:table-v2__cell--numeric', - 'isMoney:table-v2__cell--numeric' + 'header:upf-hypertable__cell--header', + 'item.selected:upf-hypertable__cell--selected', + 'isNumeric:upf-hypertable__cell--numeric', + 'isMoney:upf-hypertable__cell--numeric' ], header: false, diff --git a/addon/components/upf-table/index.js b/addon/components/upf-table/index.js index 66c307e6c..a3621ae6d 100644 --- a/addon/components/upf-table/index.js +++ b/addon/components/upf-table/index.js @@ -94,7 +94,7 @@ export default Component.extend({ actions: { reorderColumns(x, itemModels, y) { - let _cs = itemModels.concat(x.filter(x => !x.visible)) + let _cs = [x[0]].concat(itemModels.concat(x.filter(x => !x.visible))) _cs.forEach((c, i) => c.set('order', i)) this.set('columns', _cs); }, diff --git a/app/templates/components/upf-table.hbs b/app/templates/components/upf-table.hbs index 0c9971d12..6e10fd8b9 100644 --- a/app/templates/components/upf-table.hbs +++ b/app/templates/components/upf-table.hbs @@ -1,57 +1,91 @@ -
- - - {{#if _availableColumnsPanel}} -
    - {{#each _orderedColumns as |column|}} -
  • - {{column.title}} -
  • - {{/each}} -
- {{/if}} -
+
+
+ -{{#sortable-group - direction="x" model=_columns onChange=(action "reorderColumns") as |group|}} -
- {{#if hasSelection}} -
-
- {{upf-checkbox value=_allRowsSelected}} + {{#if _availableColumnsPanel}} +
+
+
+
All Fields
+
+
- {{#each collection as |item|}} -
- {{upf-checkbox value=item.selected}} -
- {{/each}} +
+ {{#each _orderedColumns as |column|}} +
+ {{column.title}} +
+ {{/each}} +
{{/if}} +
+ + +
+ {{#sortable-group + direction="x" model=_columns onChange=(action "reorderColumns") + tagName=null + classNames="upf-hypertable" as |group|}} + {{#each _columns as |column index|}} + {{#if (eq index 0)}} +
+ {{#if hasSelection}} +
+ {{#upf-table/cell header=true}} + {{upf-checkbox value=_allRowsSelected size="sm"}} + {{/upf-table/cell}} - {{#each _columns as |column|}} - {{#if column.visible}} - {{#sortable-item - classNames="table-v2__column" model=column group=group - handle=".table-v2__cell--header"}} - {{#upf-table/cell header=true}} - {{column.title}} - -
- - + {{#each collection as |item|}} +
+ {{upf-checkbox value=item.selected size="sm"}} +
+ {{/each}} +
+ {{/if}} + +
+ {{#upf-table/cell header=true}} + {{column.title}} + +
+ + +
+ {{/upf-table/cell}} + + {{#each collection as |item|}} + {{upf-table/cell item=item column=column}} + {{/each}}
- {{/upf-table/cell}} +
+ {{/if}} + {{/each}} - {{#each collection as |item|}} - {{upf-table/cell - item=item column=column}} - {{/each}} - {{/sortable-item}} - {{/if}} - {{/each}} + {{#each _columns as |column index|}} + {{#if (and column.visible (gt index 0))}} + {{#sortable-item + classNames="upf-hypertable__column" model=column group=group + handle=".upf-hypertable__cell--header"}} + {{#upf-table/cell header=true}} + {{column.title}} + +
+ + +
+ {{/upf-table/cell}} + + {{#each collection as |item|}} + {{upf-table/cell item=item column=column}} + {{/each}} + {{/sortable-item}} + {{/if}} + {{/each}} + {{/sortable-group}}
-{{/sortable-group}} +
diff --git a/tests/dummy/app/services/plans-fetcher.js b/tests/dummy/app/services/plans-fetcher.js index 5ac91416c..13b407b71 100644 --- a/tests/dummy/app/services/plans-fetcher.js +++ b/tests/dummy/app/services/plans-fetcher.js @@ -57,7 +57,37 @@ const DEFAULT_COLUMNS = [ title: 'Bulk Emails', property: 'bulkEmailsCount', type: 'numeric' - } + }, + { + title: 'Data 1', + property: 'data1', + type: 'numeric' + }, + { + title: 'Data 2', + property: 'data2', + type: 'numeric' + }, + { + title: 'Data 3', + property: 'data3', + type: 'numeric' + }, + { + title: 'Data 4', + property: 'data4', + type: 'numeric' + }, + { + title: 'Data 5', + property: 'data5', + type: 'numeric' + }, + { + title: 'Data 6', + property: 'data6', + type: 'numeric' + }, ]; export default Service.extend({ diff --git a/tests/dummy/app/styles/app.less b/tests/dummy/app/styles/app.less index 295327c01..9bf35fa07 100644 --- a/tests/dummy/app/styles/app.less +++ b/tests/dummy/app/styles/app.less @@ -1,74 +1,138 @@ @import 'oss-components'; -.table-v2 { - display: flex; - flex-direction: row; +@cell-height: 45px; + +.upf-checkbox--sm { + width: 15px; + + .upf-checkbox__fake-checkbox { + width: 15px; + height: 15px; + } + + .upf-checkbox__input:checked + .upf-checkbox__fake-checkbox:after { + top: 6px; + left: 4px; + height: 4px; + width: 7px; + } +} + +.upf-hypertable-container { + border: 1px solid lighten(@upf-gray, 15%); + border-radius: @default-radius; width: 100%; - justify-content: space-between; } -.table-v2__actions { - align-items: center; + +.upf-hypertable { + display: flex; +} + +.upf-hypertable__upper-header { + padding: @spacing-xx-sm; display: flex; justify-content: flex-end; + position: relative; - padding: @spacing-xxx-sm; - width: 100%; -} -.table-v2__available-columns { - background-color: @upf-gray-light; - border: 1px solid lighten(@upf-gray, 15%); - border-radius: @default-radius; - list-style-type: none; + .available-columns { + background-color: #fff; + border: 1px solid lighten(@upf-gray, 15%); + border-radius: @default-radius; - padding: @spacing-xxx-sm 0; - position: absolute; - top: 4em; - right: 3em; + display: flex; + flex-direction: row; - width: 200px; + position: absolute; + top: 65px; + right: 0; + z-index: 99999; + + .available-columns__categories { + background-color: @upf-gray-light; + border-right: 1px solid lighten(@upf-gray, 15%); + min-width: 150px; + + .field-category { + background-color: #fff; + border-bottom: 1px solid lighten(@upf-gray, 15%); + font-size: 16/@rem; + + display: flex; + align-items: center; + justify-content: space-between; + + padding: @spacing-sm @spacing-xx-sm; + } + } - li { - padding: @spacing-xxx-sm; + .available-columns__fields { + flex-direction: column; + flex-grow: 1; + padding: @spacing-xx-sm; + + .field { + border-radius: @default-radius; + color: @color-text; + min-width: 200px; + padding: @spacing-xxx-sm; + + &:hover { + background-color: @upf-gray-light; + cursor: pointer; + } + + &.field:not(.visible) { + color: @color-text-lighter; + } + } + } - &:hover { - background-color: lighten(@upf-gray, 15%); - cursor: pointer; + &:before, + &:after { + content:''; + position: absolute; + bottom: 100%; + left: 80%; + width: 0; + height: 0; + border-style: solid; } - } - li:not(.visible) { - color: @color-text-lighter; - } + &:after { + border-color: transparent transparent @upf-gray-light transparent; + border-width: 8px; + } - &:before, - &:after { - content:''; - position: absolute; - bottom: 100%; - left: 80%; - width: 0; - height: 0; - border-style: solid; + &:before { + left: 79%; + border-color: transparent transparent lighten(@upf-gray, 15%) transparent; + border-width: 10px; + } } +} - &:after { - border-color: transparent transparent @upf-gray-light transparent; - border-width: 8px; - } +.upf-hypertable__table { + height: 700px; + overflow: auto; +} - &:before { - left: 79%; - border-color: transparent transparent lighten(@upf-gray, 15%) transparent; - border-width: 10px; - } +.upf-hypertable__sticky-columns { + position: sticky; + left: 0; + z-index: 9999; + background: white; + display: flex; } -.table-v2__column { +.upf-hypertable__column { border-left: 1px solid lighten(@upf-gray, 15%); + + min-width: 200px; + flex-grow: 200; + opacity: 1; - width: 100%; transition: opacity .5s; &:first-child { @@ -85,38 +149,41 @@ } } -.table-v2__column--selection { +.upf-hypertable__column--selection { width: 40px; - - .table-v2__cell { - display: flex; - justify-content: space-around; - } + min-width: 40px; + flex-grow: 40; } -.table-v2__body { - display: flex; - flex-direction: column; -} - -.table-v2__cell { - align-items: center; +.upf-hypertable__cell { + border-right: 1px solid lighten(@upf-gray, 15%); border-bottom: 1px solid lighten(@upf-gray, 15%); + display: flex; - height: 45px; - max-height: 45px; + align-items: center; + + height: @cell-height; + max-height: @cell-height; padding: @spacing-xx-sm; + width: 100%; + + &.upf-hypertable__cell--numeric { + justify-content: flex-end; + } } -.table-v2__cell.table-v2__cell--header { +.upf-hypertable__cell--header { background-color: @upf-gray-light; color: @upf-primary-rock-blue; cursor: pointer; - align-items: center; justify-content: space-between; + position: sticky; + top: 0; - transition: opacity .5s; + & > .column-name { + display: flex; + } & > .icon-commands { color: @color-text-lighter; @@ -141,10 +208,6 @@ } } -.table-v2__cell.table-v2__cell--selected { +.upf-hypertable__cell--selected { background-color: @upf-gray-light; } - -.table-v2__cell.table-v2__cell--numeric { - justify-content: flex-end; -} diff --git a/tests/dummy/app/templates/application.hbs b/tests/dummy/app/templates/application.hbs index 369fe01ce..b5cb372f2 100644 --- a/tests/dummy/app/templates/application.hbs +++ b/tests/dummy/app/templates/application.hbs @@ -1,3 +1,5 @@ -{{upf-table - columns=columns collection=collection hasSelection=true - columnsChanged=(action "columnsChanged")}} +
+ {{upf-table + columns=columns collection=collection hasSelection=true + columnsChanged=(action "columnsChanged")}} +
From c3ff3d87e801f4d543c5303280fa755254db5805 Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Wed, 4 Sep 2019 15:58:55 +0200 Subject: [PATCH 18/37] add support for searching through the available columns --- addon/components/upf-table/index.js | 41 ++++---- app/templates/components/upf-table.hbs | 131 +++++++++++++------------ tests/dummy/app/styles/app.less | 55 ++++++++--- 3 files changed, 133 insertions(+), 94 deletions(-) diff --git a/addon/components/upf-table/index.js b/addon/components/upf-table/index.js index a3621ae6d..eb213b02d 100644 --- a/addon/components/upf-table/index.js +++ b/addon/components/upf-table/index.js @@ -5,10 +5,9 @@ import { run } from '@ember/runloop'; import { isEmpty } from '@ember/utils'; export default Component.extend({ - classNames: ['upf-table__container'], + classNames: ['upf-hypertable-container'], //classNameBindings: ['isCompact:upf-table__container--compact'], - // Rows Selection hasSelection: false, _allRowsSelected: false, @@ -20,6 +19,7 @@ export default Component.extend({ //isCompact: false, _availableColumnsPanel: false, + _availableColumnsKeyword: '', //isLoading: false, //contentChanging: false, //_contentPlaceholder: new Array(3), @@ -34,12 +34,6 @@ export default Component.extend({ //itemCount: 0, /*itemName: '',*/ - //init() { - //this._super(); - - //this.set('_searchQuery', this.get('searchQuery')); - /*},*/ - _columns: computed('columns', function() { return this.get('columns').map((column) => { column = EmberObject.create(column); @@ -54,8 +48,20 @@ export default Component.extend({ }); }), - _columnsSortingOrder: ['title'], - _orderedColumns: sort('_columns', '_columnsSortingOrder'), + _orderedFilteredColumns: computed( + '_columns', + '_availableColumnsKeyword', + function() { + let columns = Ember.A(this._columns); + + if (!isEmpty(this._availableColumnsKeyword)) { + let reg = RegExp(this._availableColumnsKeyword, 'i'); + columns = Ember.A(columns.filter((x) => reg.test(x.title))); + } + + return columns.sortBy('title'); + } + ), _selectAllObserver: observer('_allRowsSelected', function() { this.get('collection').forEach((item) => { @@ -86,12 +92,11 @@ export default Component.extend({ //run.debounce(this, this._bubbleSearch, 100); //}), - didReceiveAttrs() { - if (isEmpty(this.columns)) { - throw new Error('[component][upf-table] Missing columns'); - } - }, - + //didReceiveAttrs() { + //if (isEmpty(this.columns)) { + //throw new Error('[component][upf-table] Missing columns'); + //} + //}, actions: { reorderColumns(x, itemModels, y) { let _cs = [x[0]].concat(itemModels.concat(x.filter(x => !x.visible))) @@ -101,10 +106,6 @@ export default Component.extend({ openAvailableColumns() { this.toggleProperty('_availableColumnsPanel'); - }, - - toggleColumnVisibility(column) { - column.toggleProperty('visible'); } //callOnRowClickCallback(action, record) { diff --git a/app/templates/components/upf-table.hbs b/app/templates/components/upf-table.hbs index 6e10fd8b9..1d122e377 100644 --- a/app/templates/components/upf-table.hbs +++ b/app/templates/components/upf-table.hbs @@ -1,76 +1,60 @@ -
-
- +
+ - {{#if _availableColumnsPanel}} -
-
-
-
All Fields
-
-
+ {{#if _availableColumnsPanel}} +
+
+
+
All Fields
+
+
-
- {{#each _orderedColumns as |column|}} -
- {{column.title}} +
+ + +
+ {{#each _orderedFilteredColumns as |column|}} +
+ {{upf-checkbox value=column.visible size="sm"}} +
{{column.title}}
{{/each}}
- {{/if}} -
- - -
- {{#sortable-group - direction="x" model=_columns onChange=(action "reorderColumns") - tagName=null - classNames="upf-hypertable" as |group|}} - {{#each _columns as |column index|}} - {{#if (eq index 0)}} -
- {{#if hasSelection}} -
- {{#upf-table/cell header=true}} - {{upf-checkbox value=_allRowsSelected size="sm"}} - {{/upf-table/cell}} - - {{#each collection as |item|}} -
- {{upf-checkbox value=item.selected size="sm"}} -
- {{/each}} -
- {{/if}} +
+ {{/if}} +
-
+
+ {{#sortable-group + direction="x" model=_columns onChange=(action "reorderColumns") + tagName=null + classNames="upf-hypertable" as |group|}} + {{#each _columns as |column index|}} + {{#if (eq index 0)}} +
+ {{#if hasSelection}} +
{{#upf-table/cell header=true}} - {{column.title}} - -
- - -
+ {{upf-checkbox value=_allRowsSelected size="sm"}} {{/upf-table/cell}} {{#each collection as |item|}} - {{upf-table/cell item=item column=column}} +
+ {{upf-checkbox value=item.selected size="sm"}} +
{{/each}}
-
- {{/if}} - {{/each}} + {{/if}} - {{#each _columns as |column index|}} - {{#if (and column.visible (gt index 0))}} - {{#sortable-item - classNames="upf-hypertable__column" model=column group=group - handle=".upf-hypertable__cell--header"}} +
{{#upf-table/cell header=true}} {{column.title}} @@ -83,9 +67,30 @@ {{#each collection as |item|}} {{upf-table/cell item=item column=column}} {{/each}} - {{/sortable-item}} - {{/if}} - {{/each}} - {{/sortable-group}} -
+
+
+ {{/if}} + {{/each}} + + {{#each _columns as |column index|}} + {{#if (and column.visible (gt index 0))}} + {{#sortable-item + classNames="upf-hypertable__column" model=column group=group + handle=".upf-hypertable__cell--header"}} + {{#upf-table/cell header=true}} + {{column.title}} + +
+ + +
+ {{/upf-table/cell}} + + {{#each collection as |item|}} + {{upf-table/cell item=item column=column}} + {{/each}} + {{/sortable-item}} + {{/if}} + {{/each}} + {{/sortable-group}}
diff --git a/tests/dummy/app/styles/app.less b/tests/dummy/app/styles/app.less index 9bf35fa07..5fe8fb7c4 100644 --- a/tests/dummy/app/styles/app.less +++ b/tests/dummy/app/styles/app.less @@ -29,6 +29,13 @@ display: flex; } +/* + * Hypertable's Upper Header + * ======================== + * Access to actions allowing the user to modify the current state of the + * datatable. + * + */ .upf-hypertable__upper-header { padding: @spacing-xx-sm; display: flex; @@ -36,9 +43,15 @@ position: relative; + /* + * Available Columns + * ================= + * Access all available columns and toggle their visibility in the table + * + */ .available-columns { background-color: #fff; - border: 1px solid lighten(@upf-gray, 15%); + border: 2px solid lighten(@upf-gray, 15%); border-radius: @default-radius; display: flex; @@ -72,21 +85,42 @@ flex-grow: 1; padding: @spacing-xx-sm; - .field { - border-radius: @default-radius; - color: @color-text; - min-width: 200px; + .search { + background-color: #fff; padding: @spacing-xxx-sm; + position: sticky; - &:hover { - background-color: @upf-gray-light; - cursor: pointer; + input { + width: 100%; } + } - &.field:not(.visible) { - color: @color-text-lighter; + .fields-list { + height: 200px; + max-height: 200px; + overflow: hidden scroll; + + .field { + border-radius: @default-radius; + color: @color-text; + min-width: 200px; + padding: @spacing-xxx-sm; + display: flex; + + &:hover { + background-color: @upf-gray-light; + cursor: pointer; + } + + &.field:not(.visible) { + color: @color-text-lighter; + } } } + + ::-webkit-scrollbar { + display: none; + } } &:before, @@ -156,7 +190,6 @@ } .upf-hypertable__cell { - border-right: 1px solid lighten(@upf-gray, 15%); border-bottom: 1px solid lighten(@upf-gray, 15%); display: flex; From 79b5ef9e5ecd7a90cea8280224e90fa63c139004 Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Wed, 4 Sep 2019 16:30:14 +0200 Subject: [PATCH 19/37] add empty states for the data cell renderers --- addon/components/upf-table/cell/renderers/money.js | 7 +++++++ addon/components/upf-table/cell/renderers/numeric.js | 10 +++++++++- addon/components/upf-table/cell/renderers/text.js | 7 +++++++ .../components/upf-table/cell/renderers/money.hbs | 6 +++++- .../components/upf-table/cell/renderers/numeric.hbs | 6 +++++- .../components/upf-table/cell/renderers/text.hbs | 6 +++++- 6 files changed, 38 insertions(+), 4 deletions(-) diff --git a/addon/components/upf-table/cell/renderers/money.js b/addon/components/upf-table/cell/renderers/money.js index 7e7cce644..533301e03 100644 --- a/addon/components/upf-table/cell/renderers/money.js +++ b/addon/components/upf-table/cell/renderers/money.js @@ -1,5 +1,6 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; +import { empty } from '@ember/object/computed'; export default Component.extend({ tagName: '', @@ -13,4 +14,10 @@ export default Component.extend({ return this.item.get(this.column.currency_key); }), + + amount: computed('item', 'column.property', function() { + return this.item.get(this.column.property); + }), + + emptyAmount: empty('amount') }); diff --git a/addon/components/upf-table/cell/renderers/numeric.js b/addon/components/upf-table/cell/renderers/numeric.js index 0a447bae1..d8471b335 100644 --- a/addon/components/upf-table/cell/renderers/numeric.js +++ b/addon/components/upf-table/cell/renderers/numeric.js @@ -1,5 +1,13 @@ import Component from '@ember/component'; +import { computed } from '@ember/object'; +import { empty } from '@ember/object/computed'; export default Component.extend({ - tagName: '' + tagName: '', + + value: computed('item', 'column.property', function() { + return this.item.get(this.column.property); + }), + + emptyValue: empty('value') }); diff --git a/addon/components/upf-table/cell/renderers/text.js b/addon/components/upf-table/cell/renderers/text.js index bb93d73f3..8d9485d5c 100644 --- a/addon/components/upf-table/cell/renderers/text.js +++ b/addon/components/upf-table/cell/renderers/text.js @@ -1,4 +1,11 @@ import Component from '@ember/component'; +import { computed } from '@ember/object'; +import { empty } from '@ember/object/computed'; export default Component.extend({ + value: computed('item', 'column.property', function() { + return this.item.get(this.column.property); + }), + + emptyValue: empty('value') }); diff --git a/app/templates/components/upf-table/cell/renderers/money.hbs b/app/templates/components/upf-table/cell/renderers/money.hbs index a40bd64c6..3897c0dd4 100644 --- a/app/templates/components/upf-table/cell/renderers/money.hbs +++ b/app/templates/components/upf-table/cell/renderers/money.hbs @@ -1 +1,5 @@ -{{format-money (get item column.property) currency}} +{{#if emptyAmount}} + — +{{else}} + {{format-money amount currency}} +{{/if}} diff --git a/app/templates/components/upf-table/cell/renderers/numeric.hbs b/app/templates/components/upf-table/cell/renderers/numeric.hbs index 7e0cde8f8..f6ecf9401 100644 --- a/app/templates/components/upf-table/cell/renderers/numeric.hbs +++ b/app/templates/components/upf-table/cell/renderers/numeric.hbs @@ -1 +1,5 @@ -{{format-numeric (get item column.property)}} +{{#if emptyValue}} + — +{{else}} + {{format-numeric value}} +{{/if}} diff --git a/app/templates/components/upf-table/cell/renderers/text.hbs b/app/templates/components/upf-table/cell/renderers/text.hbs index 943bf383f..d9fc5d395 100644 --- a/app/templates/components/upf-table/cell/renderers/text.hbs +++ b/app/templates/components/upf-table/cell/renderers/text.hbs @@ -1 +1,5 @@ -{{get item column.property}} +{{#if emptyValue}} + — +{{else}} + {{get item column.property}} +{{/if}} From b7de4777efb3ddf3b947e1fdc3a375bf3203865c Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Wed, 4 Sep 2019 16:30:27 +0200 Subject: [PATCH 20/37] more fake data --- tests/dummy/app/services/plans-fetcher.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/dummy/app/services/plans-fetcher.js b/tests/dummy/app/services/plans-fetcher.js index 13b407b71..5f1c147ea 100644 --- a/tests/dummy/app/services/plans-fetcher.js +++ b/tests/dummy/app/services/plans-fetcher.js @@ -61,7 +61,7 @@ const DEFAULT_COLUMNS = [ { title: 'Data 1', property: 'data1', - type: 'numeric' + type: 'text' }, { title: 'Data 2', @@ -104,7 +104,7 @@ export default Service.extend({ setTimeout(() => { resolve({ - items: MOCK_DATA, + items: MOCK_DATA.concat(MOCK_DATA).concat(MOCK_DATA).concat(MOCK_DATA).concat(MOCK_DATA).concat(MOCK_DATA).concat(MOCK_DATA).concat(MOCK_DATA), meta: { columns: columns } From b904ef9987214fd89da07cbf85afb43c186ea7a7 Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Thu, 5 Sep 2019 15:00:18 +0200 Subject: [PATCH 21/37] use a service to handle the table's state --- addon/components/upf-table/index.js | 23 +++++++--------------- addon/services/upf-table-state.js | 21 ++++++++++++++++++++ app/services/upf-table-state.js | 1 + tests/dummy/app/controllers/application.js | 3 ++- tests/dummy/app/templates/application.hbs | 2 +- 5 files changed, 32 insertions(+), 18 deletions(-) create mode 100644 addon/services/upf-table-state.js create mode 100644 app/services/upf-table-state.js diff --git a/addon/components/upf-table/index.js b/addon/components/upf-table/index.js index eb213b02d..55acf29e6 100644 --- a/addon/components/upf-table/index.js +++ b/addon/components/upf-table/index.js @@ -1,10 +1,13 @@ import Component from '@ember/component'; import EmberObject, { computed, observer } from '@ember/object'; -import { filterBy, sort } from '@ember/object/computed'; +import { alias, filterBy, sort } from '@ember/object/computed'; import { run } from '@ember/runloop'; +import { inject as service } from '@ember/service'; import { isEmpty } from '@ember/utils'; export default Component.extend({ + upfTableState: service(), + classNames: ['upf-hypertable-container'], //classNameBindings: ['isCompact:upf-table__container--compact'], @@ -34,19 +37,7 @@ export default Component.extend({ //itemCount: 0, /*itemName: '',*/ - _columns: computed('columns', function() { - return this.get('columns').map((column) => { - column = EmberObject.create(column); - - column.set('visible', column.visible !== false); - column.set('sorted', column.sorted === true); - column.set('editable', column.editable !== false); - column.set('unhideable', column.unhideable === true); - column.set('type', column.type || 'text'); - - return column; - }); - }), + _columns: alias('upfTableState.columns'), _orderedFilteredColumns: computed( '_columns', @@ -74,7 +65,7 @@ export default Component.extend({ }), _columnsChanged: observer( - '_columns', '_columns.@each.visible', + '_columns', '_columns.@each.{visible,sortBy}', function() { if (this.columnsChanged) { this.columnsChanged(this._columns); @@ -101,7 +92,7 @@ export default Component.extend({ reorderColumns(x, itemModels, y) { let _cs = [x[0]].concat(itemModels.concat(x.filter(x => !x.visible))) _cs.forEach((c, i) => c.set('order', i)) - this.set('columns', _cs); + this.upfTableState.updateColumns(_cs); }, openAvailableColumns() { diff --git a/addon/services/upf-table-state.js b/addon/services/upf-table-state.js new file mode 100644 index 000000000..9fb916812 --- /dev/null +++ b/addon/services/upf-table-state.js @@ -0,0 +1,21 @@ +import Service from '@ember/service'; +import EmberObject from '@ember/object'; +import { typeOf } from '@ember/utils'; + +export default Service.extend({ + columns: [], + + updateColumns(columns) { + this.set('columns', columns.map((column) => { + if (typeOf(column) !== 'instance') { + column = EmberObject.create(column); + } + + column.set('visible', column.visible !== false); + column.set('sortBy', column.sortBy || null); + column.set('type', column.type || 'text'); + + return column; + })); + } +}); diff --git a/app/services/upf-table-state.js b/app/services/upf-table-state.js new file mode 100644 index 000000000..7b92da7b3 --- /dev/null +++ b/app/services/upf-table-state.js @@ -0,0 +1 @@ +export { default } from 'oss-components/services/upf-table-state'; diff --git a/tests/dummy/app/controllers/application.js b/tests/dummy/app/controllers/application.js index 1d9e61827..236db3a67 100644 --- a/tests/dummy/app/controllers/application.js +++ b/tests/dummy/app/controllers/application.js @@ -3,6 +3,7 @@ import { inject as service } from '@ember/service'; export default Controller.extend({ plansFetcher: service(), + upfTableState: service(), collection: [], columns: [], @@ -17,7 +18,7 @@ export default Controller.extend({ this.plansFetcher.fetch().then((data) => { this.set('collection', data.items); - this.set('columns', data.meta.columns); + this.upfTableState.updateColumns(data.meta.columns); }).finally(() => { this.set('refreshing', false); }); diff --git a/tests/dummy/app/templates/application.hbs b/tests/dummy/app/templates/application.hbs index b5cb372f2..54d5d6d59 100644 --- a/tests/dummy/app/templates/application.hbs +++ b/tests/dummy/app/templates/application.hbs @@ -1,5 +1,5 @@
{{upf-table - columns=columns collection=collection hasSelection=true + columns=upfTableState.columns collection=collection hasSelection=true columnsChanged=(action "columnsChanged")}}
From ef43cdf201939211da1494f503110b150c6b8edb Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Fri, 6 Sep 2019 11:49:47 +0200 Subject: [PATCH 22/37] add support for sorting on a column --- addon/components/upf-table/cell.js | 43 ++++++++++++++- .../upf-table/cell/filters-renderers/money.js | 17 ++++++ .../cell/filters-renderers/numeric.js | 17 ++++++ .../upf-table/cell/filters-renderers/text.js | 17 ++++++ addon/components/upf-table/index.js | 9 ++-- addon/services/upf-table-state.js | 5 ++ .../upf-table/cell/filters-renderers/money.js | 1 + .../cell/filters-renderers/numeric.js | 1 + .../upf-table/cell/filters-renderers/text.js | 1 + app/templates/components/upf-table.hbs | 20 ++----- app/templates/components/upf-table/cell.hbs | 25 +++++++-- .../cell/filters-renderers/money.hbs | 10 ++++ .../cell/filters-renderers/numeric.hbs | 10 ++++ .../upf-table/cell/filters-renderers/text.hbs | 10 ++++ tests/dummy/app/controllers/application.js | 6 ++- tests/dummy/app/services/plans-fetcher.js | 30 +++++++++-- tests/dummy/app/styles/app.less | 54 ++++++++++++++----- tests/dummy/app/templates/application.hbs | 2 +- 18 files changed, 233 insertions(+), 45 deletions(-) create mode 100644 addon/components/upf-table/cell/filters-renderers/money.js create mode 100644 addon/components/upf-table/cell/filters-renderers/numeric.js create mode 100644 addon/components/upf-table/cell/filters-renderers/text.js create mode 100644 app/components/upf-table/cell/filters-renderers/money.js create mode 100644 app/components/upf-table/cell/filters-renderers/numeric.js create mode 100644 app/components/upf-table/cell/filters-renderers/text.js create mode 100644 app/templates/components/upf-table/cell/filters-renderers/money.hbs create mode 100644 app/templates/components/upf-table/cell/filters-renderers/numeric.hbs create mode 100644 app/templates/components/upf-table/cell/filters-renderers/text.hbs diff --git a/addon/components/upf-table/cell.js b/addon/components/upf-table/cell.js index 9323da6be..c41879e16 100644 --- a/addon/components/upf-table/cell.js +++ b/addon/components/upf-table/cell.js @@ -1,21 +1,30 @@ import Component from '@ember/component'; -import { computed, defineProperty } from '@ember/object'; +import { computed, defineProperty, observer } from '@ember/object'; +import { inject as service } from '@ember/service'; import { capitalize } from '@ember/string'; +import { isEmpty } from '@ember/utils'; const AVAILABLE_RENDERERS = [ 'text', 'numeric', 'money' ]; export default Component.extend({ + upfTableState: service(), + classNames: ['upf-hypertable__cell'], classNameBindings: [ 'header:upf-hypertable__cell--header', 'item.selected:upf-hypertable__cell--selected', + '_sorted:upf-hypertable__cell--sorted', 'isNumeric:upf-hypertable__cell--numeric', 'isMoney:upf-hypertable__cell--numeric' ], header: false, + selection: false, + + _sorted: false, + _showFiltersPanel: false, _renderingComponent: computed('column.type', function() { if (AVAILABLE_RENDERERS.includes(this.column.type)) { @@ -23,6 +32,22 @@ export default Component.extend({ } }), + _filtersRenderingComponent: computed('column.type', function() { + if (AVAILABLE_RENDERERS.includes(this.column.type)) { + return `upf-table/cell/filters-renderers/${this.column.type}`; + } + }), + + _tableStateListener() { + this.set('_sorted', !isEmpty(this.column.sortBy)); + }, + + init() { + this._super(); + + this.get('upfTableState'); + }, + didReceiveAttrs() { if (this.column) { AVAILABLE_RENDERERS.forEach((rendererType) => { @@ -34,6 +59,22 @@ export default Component.extend({ }) ); }); + + if (this.header) { + this.addObserver( + 'upfTableState.columns.@each.sortBy', + this, + this._tableStateListener + ); + } + + this.set('_sorted', !isEmpty(this.column.sortBy)); + } + }, + + actions: { + toggleFiltersPanel() { + this.toggleProperty('showFiltersPanel'); } } }); diff --git a/addon/components/upf-table/cell/filters-renderers/money.js b/addon/components/upf-table/cell/filters-renderers/money.js new file mode 100644 index 000000000..1f10fa367 --- /dev/null +++ b/addon/components/upf-table/cell/filters-renderers/money.js @@ -0,0 +1,17 @@ +import Component from '@ember/component'; +import { inject as service } from '@ember/service'; + +export default Component.extend({ + upfTableState: service(), + + sortingOptions: { + '0 — 9': 'alphanumerical:asc', + '9 — 0': 'alphanumerical:desc' + }, + + actions: { + sortingOptionChanged(value) { + this.upfTableState.updateSortBy(this.column, value); + } + } +}); diff --git a/addon/components/upf-table/cell/filters-renderers/numeric.js b/addon/components/upf-table/cell/filters-renderers/numeric.js new file mode 100644 index 000000000..1f10fa367 --- /dev/null +++ b/addon/components/upf-table/cell/filters-renderers/numeric.js @@ -0,0 +1,17 @@ +import Component from '@ember/component'; +import { inject as service } from '@ember/service'; + +export default Component.extend({ + upfTableState: service(), + + sortingOptions: { + '0 — 9': 'alphanumerical:asc', + '9 — 0': 'alphanumerical:desc' + }, + + actions: { + sortingOptionChanged(value) { + this.upfTableState.updateSortBy(this.column, value); + } + } +}); diff --git a/addon/components/upf-table/cell/filters-renderers/text.js b/addon/components/upf-table/cell/filters-renderers/text.js new file mode 100644 index 000000000..57b4d8969 --- /dev/null +++ b/addon/components/upf-table/cell/filters-renderers/text.js @@ -0,0 +1,17 @@ +import Component from '@ember/component'; +import { inject as service } from '@ember/service'; + +export default Component.extend({ + upfTableState: service(), + + sortingOptions: { + 'A — Z': 'alphanumerical:asc', + 'Z — A': 'alphanumerical:desc' + }, + + actions: { + sortingOptionChanged(value) { + this.upfTableState.updateSortBy(this.column, value); + } + } +}); diff --git a/addon/components/upf-table/index.js b/addon/components/upf-table/index.js index 55acf29e6..cac3a15b1 100644 --- a/addon/components/upf-table/index.js +++ b/addon/components/upf-table/index.js @@ -1,3 +1,4 @@ +import { A } from '@ember/array'; import Component from '@ember/component'; import EmberObject, { computed, observer } from '@ember/object'; import { alias, filterBy, sort } from '@ember/object/computed'; @@ -43,11 +44,11 @@ export default Component.extend({ '_columns', '_availableColumnsKeyword', function() { - let columns = Ember.A(this._columns); + let columns = A(this._columns); if (!isEmpty(this._availableColumnsKeyword)) { let reg = RegExp(this._availableColumnsKeyword, 'i'); - columns = Ember.A(columns.filter((x) => reg.test(x.title))); + columns = A(columns.filter((x) => reg.test(x.title))); } return columns.sortBy('title'); @@ -67,8 +68,8 @@ export default Component.extend({ _columnsChanged: observer( '_columns', '_columns.@each.{visible,sortBy}', function() { - if (this.columnsChanged) { - this.columnsChanged(this._columns); + if (this.onColumnsChange) { + this.onColumnsChange(this._columns); } } ), diff --git a/addon/services/upf-table-state.js b/addon/services/upf-table-state.js index 9fb916812..85a2d1719 100644 --- a/addon/services/upf-table-state.js +++ b/addon/services/upf-table-state.js @@ -5,6 +5,11 @@ import { typeOf } from '@ember/utils'; export default Service.extend({ columns: [], + updateSortBy(column, sortBy) { + this.columns.forEach((c) => c.set('sortBy', null)); + column.set('sortBy', sortBy); + }, + updateColumns(columns) { this.set('columns', columns.map((column) => { if (typeOf(column) !== 'instance') { diff --git a/app/components/upf-table/cell/filters-renderers/money.js b/app/components/upf-table/cell/filters-renderers/money.js new file mode 100644 index 000000000..1873f436d --- /dev/null +++ b/app/components/upf-table/cell/filters-renderers/money.js @@ -0,0 +1 @@ +export { default } from 'oss-components/components/upf-table/cell/filters-renderers/money'; diff --git a/app/components/upf-table/cell/filters-renderers/numeric.js b/app/components/upf-table/cell/filters-renderers/numeric.js new file mode 100644 index 000000000..80141e186 --- /dev/null +++ b/app/components/upf-table/cell/filters-renderers/numeric.js @@ -0,0 +1 @@ +export { default } from 'oss-components/components/upf-table/cell/filters-renderers/numeric'; diff --git a/app/components/upf-table/cell/filters-renderers/text.js b/app/components/upf-table/cell/filters-renderers/text.js new file mode 100644 index 000000000..f8da34f64 --- /dev/null +++ b/app/components/upf-table/cell/filters-renderers/text.js @@ -0,0 +1 @@ +export { default } from 'oss-components/components/upf-table/cell/filters-renderers/text'; diff --git a/app/templates/components/upf-table.hbs b/app/templates/components/upf-table.hbs index 1d122e377..554a043f4 100644 --- a/app/templates/components/upf-table.hbs +++ b/app/templates/components/upf-table.hbs @@ -42,7 +42,7 @@
{{#if hasSelection}}
- {{#upf-table/cell header=true}} + {{#upf-table/cell header=true selection=true}} {{upf-checkbox value=_allRowsSelected size="sm"}} {{/upf-table/cell}} @@ -55,14 +55,7 @@ {{/if}}
- {{#upf-table/cell header=true}} - {{column.title}} - -
- - -
- {{/upf-table/cell}} + {{upf-table/cell column=column header=true}} {{#each collection as |item|}} {{upf-table/cell item=item column=column}} @@ -77,14 +70,7 @@ {{#sortable-item classNames="upf-hypertable__column" model=column group=group handle=".upf-hypertable__cell--header"}} - {{#upf-table/cell header=true}} - {{column.title}} - -
- - -
- {{/upf-table/cell}} + {{upf-table/cell column=column header=true}} {{#each collection as |item|}} {{upf-table/cell item=item column=column}} diff --git a/app/templates/components/upf-table/cell.hbs b/app/templates/components/upf-table/cell.hbs index 44aba8669..91de2625e 100644 --- a/app/templates/components/upf-table/cell.hbs +++ b/app/templates/components/upf-table/cell.hbs @@ -1,5 +1,24 @@ -{{#if (has-block)}} - {{yield}} +{{#if header}} + {{#if selection}} + {{yield}} + {{else}} + {{column.title}} + +
+ + +
+ + {{#if showFiltersPanel}} +
+ {{component _filtersRenderingComponent item=item column=column}} +
+ {{/if}} + {{/if}} {{else}} - {{component _renderingComponent item=item column=column}} + {{#if (has-block)}} + {{yield}} + {{else}} + {{component _renderingComponent item=item column=column}} + {{/if}} {{/if}} diff --git a/app/templates/components/upf-table/cell/filters-renderers/money.hbs b/app/templates/components/upf-table/cell/filters-renderers/money.hbs new file mode 100644 index 000000000..6cc0831e5 --- /dev/null +++ b/app/templates/components/upf-table/cell/filters-renderers/money.hbs @@ -0,0 +1,10 @@ +{{#input-wrapper}} + +
+ {{#each-in sortingOptions as |label value|}} + {{radio-button + value=value currentValue=column.sortBy label=label + options=sortingOptions onCheck="sortingOptionChanged"}} + {{/each-in}} +
+{{/input-wrapper}} diff --git a/app/templates/components/upf-table/cell/filters-renderers/numeric.hbs b/app/templates/components/upf-table/cell/filters-renderers/numeric.hbs new file mode 100644 index 000000000..6cc0831e5 --- /dev/null +++ b/app/templates/components/upf-table/cell/filters-renderers/numeric.hbs @@ -0,0 +1,10 @@ +{{#input-wrapper}} + +
+ {{#each-in sortingOptions as |label value|}} + {{radio-button + value=value currentValue=column.sortBy label=label + options=sortingOptions onCheck="sortingOptionChanged"}} + {{/each-in}} +
+{{/input-wrapper}} diff --git a/app/templates/components/upf-table/cell/filters-renderers/text.hbs b/app/templates/components/upf-table/cell/filters-renderers/text.hbs new file mode 100644 index 000000000..6cc0831e5 --- /dev/null +++ b/app/templates/components/upf-table/cell/filters-renderers/text.hbs @@ -0,0 +1,10 @@ +{{#input-wrapper}} + +
+ {{#each-in sortingOptions as |label value|}} + {{radio-button + value=value currentValue=column.sortBy label=label + options=sortingOptions onCheck="sortingOptionChanged"}} + {{/each-in}} +
+{{/input-wrapper}} diff --git a/tests/dummy/app/controllers/application.js b/tests/dummy/app/controllers/application.js index 236db3a67..e345b6a60 100644 --- a/tests/dummy/app/controllers/application.js +++ b/tests/dummy/app/controllers/application.js @@ -13,7 +13,7 @@ export default Controller.extend({ this._fetchPlans(); }, - _fetchPlans(columnsLayout) { + _fetchPlans() { this.set('refreshing', true); this.plansFetcher.fetch().then((data) => { @@ -26,7 +26,9 @@ export default Controller.extend({ actions: { columnsChanged(layout) { - this.plansFetcher.fetch(layout); + this.plansFetcher.fetch(layout).then((data) => { + this.set('collection', data.items); + }); } } }); diff --git a/tests/dummy/app/services/plans-fetcher.js b/tests/dummy/app/services/plans-fetcher.js index 5f1c147ea..d72cd3114 100644 --- a/tests/dummy/app/services/plans-fetcher.js +++ b/tests/dummy/app/services/plans-fetcher.js @@ -1,6 +1,7 @@ import { A } from '@ember/array'; import EmberObject from '@ember/object'; import Service from '@ember/service'; +import { isEmpty } from '@ember/utils'; const MOCK_DATA = A([ EmberObject.create({ @@ -9,7 +10,8 @@ const MOCK_DATA = A([ currency: 'EUR', usersCount: 1, bulkEmailsCount: 0, - selected: false + selected: false, + data1: 'A', }), EmberObject.create({ name: 'Silver', @@ -17,7 +19,8 @@ const MOCK_DATA = A([ currency: 'EUR', usersCount: 1, bulkEmailsCount: 100, - selected: false + selected: false, + data1: 'B', }), EmberObject.create({ name: 'Gold', @@ -25,14 +28,16 @@ const MOCK_DATA = A([ currency: 'EUR', usersCount: 1, bulkEmailsCount: 300, - selected: false + selected: false, + data1: 'C', }), EmberObject.create({ name: 'Enterprise', price: 'Talk to us', usersCount: 'Custom', bulkEmailsCount: 'Custom', - selected: false + selected: false, + data1: 'D', }) ]); @@ -94,6 +99,9 @@ export default Service.extend({ fetch(columnsLayout) { return new Promise((resolve, reject) => { let columns = DEFAULT_COLUMNS; + let data = A( + MOCK_DATA.concat(MOCK_DATA).concat(MOCK_DATA).concat(MOCK_DATA) + ); if (columnsLayout) { columns = columnsLayout; @@ -102,9 +110,21 @@ export default Service.extend({ columns = JSON.parse(window.sessionStorage.getItem('columns')); } + let sortedColumn = columns.find((x) => !isEmpty(x.sortBy)); + + if (sortedColumn) { + let [sortType, sortDirection] = sortedColumn.sortBy.split(':'); + + data = data.sortBy(sortedColumn.property); + + if (sortDirection === 'desc') { + data.reverse(); + } + } + setTimeout(() => { resolve({ - items: MOCK_DATA.concat(MOCK_DATA).concat(MOCK_DATA).concat(MOCK_DATA).concat(MOCK_DATA).concat(MOCK_DATA).concat(MOCK_DATA).concat(MOCK_DATA), + items: data, meta: { columns: columns } diff --git a/tests/dummy/app/styles/app.less b/tests/dummy/app/styles/app.less index 5fe8fb7c4..5afff0d3d 100644 --- a/tests/dummy/app/styles/app.less +++ b/tests/dummy/app/styles/app.less @@ -58,14 +58,14 @@ flex-direction: row; position: absolute; - top: 65px; + top: 55px; right: 0; z-index: 99999; .available-columns__categories { background-color: @upf-gray-light; border-right: 1px solid lighten(@upf-gray, 15%); - min-width: 150px; + min-width: 200px; .field-category { background-color: #fff; @@ -103,7 +103,7 @@ .field { border-radius: @default-radius; color: @color-text; - min-width: 200px; + min-width: 270px; padding: @spacing-xxx-sm; display: flex; @@ -128,21 +128,21 @@ content:''; position: absolute; bottom: 100%; - left: 80%; + right: 30px; width: 0; height: 0; border-style: solid; } &:after { - border-color: transparent transparent @upf-gray-light transparent; + border-color: transparent transparent lighten(@upf-gray, 15%) transparent; border-width: 8px; } &:before { - left: 79%; border-color: transparent transparent lighten(@upf-gray, 15%) transparent; border-width: 10px; + right: 28.5px } } } @@ -156,7 +156,7 @@ position: sticky; left: 0; z-index: 9999; - background: white; + background-color: white; display: flex; } @@ -190,8 +190,6 @@ } .upf-hypertable__cell { - border-bottom: 1px solid lighten(@upf-gray, 15%); - display: flex; align-items: center; @@ -200,7 +198,20 @@ padding: @spacing-xx-sm; width: 100%; - &.upf-hypertable__cell--numeric { + .available-filters { + background-color: #fff; + border: 2px solid lighten(@upf-gray, 15%); + border-radius: @default-radius; + + min-width: 300px; + width: 300px; + + position: absolute; + padding: @spacing-xx-sm; + top: 50px; + } + + &.upf-hypertable__cell--numeric:not(.upf-hypertable__cell--header) { justify-content: flex-end; } } @@ -214,17 +225,29 @@ position: sticky; top: 0; + &.upf-hypertable__cell--sorted { + .icon-commands .fa.fa-filter { + display: block; + font-weight: bold; + opacity: 1; + } + } + & > .column-name { display: flex; } & > .icon-commands { color: @color-text-lighter; + display: flex; flex-direction: row; align-items: center; i { + opacity: 0; + display: none; + font-size: 16/@rem; margin-left: @spacing-xx-sm; } @@ -236,11 +259,18 @@ i.fa-bars { cursor: move; } } - &:hover .icon-commands { - opacity: 1; + &:hover { + background-color: lighten(@upf-gray, 15%); + + .icon-commands i { + opacity: 1; + display: block; + } } } + + .upf-hypertable__cell--selected { background-color: @upf-gray-light; } diff --git a/tests/dummy/app/templates/application.hbs b/tests/dummy/app/templates/application.hbs index 54d5d6d59..df9d84244 100644 --- a/tests/dummy/app/templates/application.hbs +++ b/tests/dummy/app/templates/application.hbs @@ -1,5 +1,5 @@
{{upf-table columns=upfTableState.columns collection=collection hasSelection=true - columnsChanged=(action "columnsChanged")}} + onColumnsChange=(action "columnsChanged")}}
From dadd9a17ab44193d349af7190b647550c495e5bc Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Fri, 6 Sep 2019 17:39:59 +0200 Subject: [PATCH 23/37] add back support for search and contextual actions --- addon/components/upf-table/index.js | 34 ++++--- app/templates/components/loading-state.hbs | 10 +-- app/templates/components/upf-table.hbs | 65 +++++++++----- .../app/components/contextual-actions.js | 3 + tests/dummy/app/services/plans-fetcher.js | 7 +- tests/dummy/app/styles/app.less | 89 ++++++++++++++++++- tests/dummy/app/templates/application.hbs | 4 +- .../components/contextual-actions.hbs | 3 + 8 files changed, 168 insertions(+), 47 deletions(-) create mode 100644 tests/dummy/app/components/contextual-actions.js create mode 100644 tests/dummy/app/templates/components/contextual-actions.hbs diff --git a/addon/components/upf-table/index.js b/addon/components/upf-table/index.js index cac3a15b1..3a3e12460 100644 --- a/addon/components/upf-table/index.js +++ b/addon/components/upf-table/index.js @@ -13,11 +13,12 @@ export default Component.extend({ //classNameBindings: ['isCompact:upf-table__container--compact'], hasSelection: false, + hasSearch: false, + contextualActions: null, _allRowsSelected: false, //hasPagination: false, - //hasSearch: false, //hasPolymorphicColumns: false, //hasToggleableColumns: true, //isCompact: false, @@ -28,7 +29,7 @@ export default Component.extend({ //contentChanging: false, //_contentPlaceholder: new Array(3), - //_searchQuery: null, + _searchQuery: null, //searchInputPlaceholder: 'Search...', /* currentPage: 1,*/ @@ -39,6 +40,7 @@ export default Component.extend({ /*itemName: '',*/ _columns: alias('upfTableState.columns'), + _selectedItems: filterBy('collection', 'selected', true), _orderedFilteredColumns: computed( '_columns', @@ -74,15 +76,27 @@ export default Component.extend({ } ), -/* _bubbleSearch: function() {*/ - //if (this.performSearch) { - //this.performSearch(this.get('_searchQuery')); - //} - //}, + _selectedItemsChanged: observer('_selectedItems', function() { + if (this.contextualActions) { + let ca = document.querySelector('.contextual-actions'); + + if (this._selectedItems.length > 0) { + ca.classList.remove('contextual-actions--no-animation'); + ca.classList.add('contextual-actions--visible'); + } else { + ca.classList.remove('contextual-actions--visible'); + ca.classList.add('contextual-actions--hidden'); + } + } + }), - //_searchQueryObserver: observer('_searchQuery', function() { - //run.debounce(this, this._bubbleSearch, 100); - //}), + _searchQueryObserver: observer('_searchQuery', function() { + run.debounce(this, () => { + if (this.onSearchQueryChange) { + this.onSearchQueryChange(this.get('_searchQuery')); + } + }, 1000); + }), //didReceiveAttrs() { //if (isEmpty(this.columns)) { diff --git a/app/templates/components/loading-state.hbs b/app/templates/components/loading-state.hbs index 47d01c29c..53e68d0ef 100644 --- a/app/templates/components/loading-state.hbs +++ b/app/templates/components/loading-state.hbs @@ -1,7 +1,7 @@ -
-
-
-
-
+
+
+
+
+
diff --git a/app/templates/components/upf-table.hbs b/app/templates/components/upf-table.hbs index 554a043f4..bc2300e48 100644 --- a/app/templates/components/upf-table.hbs +++ b/app/templates/components/upf-table.hbs @@ -1,35 +1,52 @@
- +
+ {{#if hasSearch}} + {{input + type="text" placeholder="Search..." value=_searchQuery + class="form-control upf-input upf-input--small"}} + {{/if}} - {{#if _availableColumnsPanel}} -
-
-
-
All Fields
-
-
+ {{#if contextualActions}} +
+ {{component contextualActions selectedItems=_selectedItems}}
+ {{/if}} +
+ +
+ -
-
diff --git a/tests/dummy/app/components/contextual-actions.js b/tests/dummy/app/components/contextual-actions.js new file mode 100644 index 000000000..8429b51a1 --- /dev/null +++ b/tests/dummy/app/components/contextual-actions.js @@ -0,0 +1,3 @@ +import Component from '@ember/component'; + +export default Component.extend({}) diff --git a/tests/dummy/app/services/plans-fetcher.js b/tests/dummy/app/services/plans-fetcher.js index d72cd3114..c46f5f8ac 100644 --- a/tests/dummy/app/services/plans-fetcher.js +++ b/tests/dummy/app/services/plans-fetcher.js @@ -6,7 +6,7 @@ import { isEmpty } from '@ember/utils'; const MOCK_DATA = A([ EmberObject.create({ name: 'Bronze', - price: '99', + price: 99, currency: 'EUR', usersCount: 1, bulkEmailsCount: 0, @@ -15,7 +15,7 @@ const MOCK_DATA = A([ }), EmberObject.create({ name: 'Silver', - price: '195', + price: 195, currency: 'EUR', usersCount: 1, bulkEmailsCount: 100, @@ -24,7 +24,7 @@ const MOCK_DATA = A([ }), EmberObject.create({ name: 'Gold', - price: '495', + price: 495, currency: 'EUR', usersCount: 1, bulkEmailsCount: 300, @@ -114,7 +114,6 @@ export default Service.extend({ if (sortedColumn) { let [sortType, sortDirection] = sortedColumn.sortBy.split(':'); - data = data.sortBy(sortedColumn.property); if (sortDirection === 'desc') { diff --git a/tests/dummy/app/styles/app.less b/tests/dummy/app/styles/app.less index 5afff0d3d..443a7c458 100644 --- a/tests/dummy/app/styles/app.less +++ b/tests/dummy/app/styles/app.less @@ -39,9 +39,61 @@ .upf-hypertable__upper-header { padding: @spacing-xx-sm; display: flex; - justify-content: flex-end; + justify-content: space-between; position: relative; + .left-side { + display: flex; + flex-grow: 1; + } + + /* + * Search + * ====== + * + */ + .left-side { + input { + max-width: 250px; + } + } + + /* + * Contextual Actions + * =========== + * Contextual actions that show up to act on the selected items + * + */ + .left-side .contextual-actions { + border-left: 1px solid lighten(@upf-gray, 15%); + opacity: 0; + z-index: -1; + animation: none; + + margin-left: @spacing-sm; + padding: 0 @spacing-sm; + + &--no-search-sibling { + border-left: 0; + margin-left: 0; + padding: 0; + } + + &--no-animation { + display: none !important; + } + + &--hidden { + animation: hide-contextual-actions 0.5s 1; + } + + &--visible { + animation: show-contextual-actions 0.5s 1; + z-index: 1; + opacity: 1; + display: block; + } + } /* * Available Columns @@ -190,6 +242,7 @@ } .upf-hypertable__cell { + border-bottom: 1px solid lighten(@upf-gray, 15%); display: flex; align-items: center; @@ -227,6 +280,7 @@ &.upf-hypertable__cell--sorted { .icon-commands .fa.fa-filter { + color: @color-text; display: block; font-weight: bold; opacity: 1; @@ -269,8 +323,37 @@ } } - - .upf-hypertable__cell--selected { background-color: @upf-gray-light; } + +/* + * Animations used throughout the datatable + * ======================================= + * + */ +@keyframes show-contextual-actions { + 0% { + display: block; + opacity: 0; + transform: translateX(-50px); + } + + 100% { + opacity: 1; + transform: translateX(0); + } +} + + +@keyframes hide-contextual-actions { + 0% { + opacity: 1; + transform: translateX(0); + } + + 100% { + opacity: 0; + transform: translateX(-50px); + } +} diff --git a/tests/dummy/app/templates/application.hbs b/tests/dummy/app/templates/application.hbs index df9d84244..7d8cedfad 100644 --- a/tests/dummy/app/templates/application.hbs +++ b/tests/dummy/app/templates/application.hbs @@ -1,5 +1,7 @@
{{upf-table columns=upfTableState.columns collection=collection hasSelection=true - onColumnsChange=(action "columnsChanged")}} + hasSearch=true onColumnsChange=(action "columnsChanged") + onSearchQueryChange=(action "performSearch") + refreshing=refreshing contextualActions="contextual-actions"}}
diff --git a/tests/dummy/app/templates/components/contextual-actions.hbs b/tests/dummy/app/templates/components/contextual-actions.hbs new file mode 100644 index 000000000..db44ea3c0 --- /dev/null +++ b/tests/dummy/app/templates/components/contextual-actions.hbs @@ -0,0 +1,3 @@ + From 685bef8fe66348c68682a8207c8419384f0175f6 Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Mon, 9 Sep 2019 10:51:00 +0200 Subject: [PATCH 24/37] avoid displaying multiple filters panels simultaneously --- addon/components/upf-table/cell.js | 13 +++++++++++++ addon/components/upf-table/index.js | 6 +++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/addon/components/upf-table/cell.js b/addon/components/upf-table/cell.js index c41879e16..a1c0f85d3 100644 --- a/addon/components/upf-table/cell.js +++ b/addon/components/upf-table/cell.js @@ -42,6 +42,12 @@ export default Component.extend({ this.set('_sorted', !isEmpty(this.column.sortBy)); }, + _filtersPanelListener() { + if (this.column.property !== this.upfTableState.applyingFiltersOn) { + this.set('showFiltersPanel', false); + } + }, + init() { this._super(); @@ -66,6 +72,12 @@ export default Component.extend({ this, this._tableStateListener ); + + this.addObserver( + 'upfTableState.applyingFiltersOn', + this, + this._filtersPanelListener + ); } this.set('_sorted', !isEmpty(this.column.sortBy)); @@ -75,6 +87,7 @@ export default Component.extend({ actions: { toggleFiltersPanel() { this.toggleProperty('showFiltersPanel'); + this.set('upfTableState.applyingFiltersOn', this.column.property); } } }); diff --git a/addon/components/upf-table/index.js b/addon/components/upf-table/index.js index 3a3e12460..23c69ae2f 100644 --- a/addon/components/upf-table/index.js +++ b/addon/components/upf-table/index.js @@ -1,7 +1,7 @@ import { A } from '@ember/array'; import Component from '@ember/component'; -import EmberObject, { computed, observer } from '@ember/object'; -import { alias, filterBy, sort } from '@ember/object/computed'; +import { computed, observer } from '@ember/object'; +import { alias, filterBy } from '@ember/object/computed'; import { run } from '@ember/runloop'; import { inject as service } from '@ember/service'; import { isEmpty } from '@ember/utils'; @@ -104,7 +104,7 @@ export default Component.extend({ //} //}, actions: { - reorderColumns(x, itemModels, y) { + reorderColumns(x, itemModels, _) { let _cs = [x[0]].concat(itemModels.concat(x.filter(x => !x.visible))) _cs.forEach((c, i) => c.set('order', i)) this.upfTableState.updateColumns(_cs); From 270671817dbdf5c5f9d1a866337a7ae19cfb0183 Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Mon, 9 Sep 2019 14:36:50 +0200 Subject: [PATCH 25/37] add support for date type columns --- addon/components/upf-table/cell.js | 2 +- .../upf-table/cell/filters-renderers/date.js | 17 ++++++++++++++ .../upf-table/cell/renderers/date.js | 22 +++++++++++++++++++ .../upf-table/cell/filters-renderers/date.js | 1 + .../upf-table/cell/renderers/date.js | 1 + .../upf-table/cell/filters-renderers/date.hbs | 10 +++++++++ .../upf-table/cell/renderers/date.hbs | 5 +++++ package.json | 2 ++ tests/dummy/app/services/plans-fetcher.js | 6 ++++- tests/dummy/app/styles/app.less | 4 ++-- 10 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 addon/components/upf-table/cell/filters-renderers/date.js create mode 100644 addon/components/upf-table/cell/renderers/date.js create mode 100644 app/components/upf-table/cell/filters-renderers/date.js create mode 100644 app/components/upf-table/cell/renderers/date.js create mode 100644 app/templates/components/upf-table/cell/filters-renderers/date.hbs create mode 100644 app/templates/components/upf-table/cell/renderers/date.hbs diff --git a/addon/components/upf-table/cell.js b/addon/components/upf-table/cell.js index a1c0f85d3..c1de17e90 100644 --- a/addon/components/upf-table/cell.js +++ b/addon/components/upf-table/cell.js @@ -5,7 +5,7 @@ import { capitalize } from '@ember/string'; import { isEmpty } from '@ember/utils'; const AVAILABLE_RENDERERS = [ - 'text', 'numeric', 'money' + 'text', 'numeric', 'money', 'date' ]; export default Component.extend({ diff --git a/addon/components/upf-table/cell/filters-renderers/date.js b/addon/components/upf-table/cell/filters-renderers/date.js new file mode 100644 index 000000000..1423f5d85 --- /dev/null +++ b/addon/components/upf-table/cell/filters-renderers/date.js @@ -0,0 +1,17 @@ +import Component from '@ember/component'; +import { inject as service } from '@ember/service'; + +export default Component.extend({ + upfTableState: service(), + + sortingOptions: { + 'Oldest — Newest': 'alphanumerical:asc', + 'Newest — Oldest': 'alphanumerical:desc' + }, + + actions: { + sortingOptionChanged(value) { + this.upfTableState.updateSortBy(this.column, value); + } + } +}); diff --git a/addon/components/upf-table/cell/renderers/date.js b/addon/components/upf-table/cell/renderers/date.js new file mode 100644 index 000000000..3f8d6f490 --- /dev/null +++ b/addon/components/upf-table/cell/renderers/date.js @@ -0,0 +1,22 @@ +import Component from '@ember/component'; +import { computed } from '@ember/object'; +import { empty, or } from '@ember/object/computed'; + +import moment from 'moment'; + +export default Component.extend({ + tagName: '', + + value: computed('item', 'column.property', function() { + return this.item.get(this.column.property); + }), + + emptyValue: empty('value'), + + _defaultDateFormat: 'MM/DD/YYYY', + _dateFormat: or('column.date_format', '_defaultDateFormat'), + + _formattedDate: computed('value', '_dateFormat', function() { + return moment.unix(this.value).format(this._defaultDateFormat); + }) +}); diff --git a/app/components/upf-table/cell/filters-renderers/date.js b/app/components/upf-table/cell/filters-renderers/date.js new file mode 100644 index 000000000..aec5d59bc --- /dev/null +++ b/app/components/upf-table/cell/filters-renderers/date.js @@ -0,0 +1 @@ +export { default } from 'oss-components/components/upf-table/cell/filters-renderers/date'; diff --git a/app/components/upf-table/cell/renderers/date.js b/app/components/upf-table/cell/renderers/date.js new file mode 100644 index 000000000..9498690ea --- /dev/null +++ b/app/components/upf-table/cell/renderers/date.js @@ -0,0 +1 @@ +export { default } from 'oss-components/components/upf-table/cell/renderers/date'; diff --git a/app/templates/components/upf-table/cell/filters-renderers/date.hbs b/app/templates/components/upf-table/cell/filters-renderers/date.hbs new file mode 100644 index 000000000..6cc0831e5 --- /dev/null +++ b/app/templates/components/upf-table/cell/filters-renderers/date.hbs @@ -0,0 +1,10 @@ +{{#input-wrapper}} + +
+ {{#each-in sortingOptions as |label value|}} + {{radio-button + value=value currentValue=column.sortBy label=label + options=sortingOptions onCheck="sortingOptionChanged"}} + {{/each-in}} +
+{{/input-wrapper}} diff --git a/app/templates/components/upf-table/cell/renderers/date.hbs b/app/templates/components/upf-table/cell/renderers/date.hbs new file mode 100644 index 000000000..bbd9f418f --- /dev/null +++ b/app/templates/components/upf-table/cell/renderers/date.hbs @@ -0,0 +1,5 @@ +{{#if emptyValue}} + — +{{else}} + {{_formattedDate}} +{{/if}} diff --git a/package.json b/package.json index 309edf911..fbccf9d79 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "ember-cli-eslint": "^4.2.3", "ember-cli-ifa": ">= 0.4.1", "ember-cli-inject-live-reload": "^1.8.2", + "ember-cli-moment-shim": "^3.7.1", "ember-cli-sri": "^2.1.1", "ember-cli-template-lint": "^1.0.0-beta.1", "ember-cli-uglify": "^2.1.0", @@ -49,6 +50,7 @@ "ember-faker": "^1.1.1", "ember-load-initializers": "^1.1.0", "ember-maybe-import-regenerator": "^0.1.6", + "ember-moment": "^8.0.0", "ember-qunit": "^3.4.1", "ember-resolver": "^5.0.1", "ember-sortable": "^1.12.9", diff --git a/tests/dummy/app/services/plans-fetcher.js b/tests/dummy/app/services/plans-fetcher.js index c46f5f8ac..c36910154 100644 --- a/tests/dummy/app/services/plans-fetcher.js +++ b/tests/dummy/app/services/plans-fetcher.js @@ -12,6 +12,7 @@ const MOCK_DATA = A([ bulkEmailsCount: 0, selected: false, data1: 'A', + data2: 1620563995 }), EmberObject.create({ name: 'Silver', @@ -21,6 +22,7 @@ const MOCK_DATA = A([ bulkEmailsCount: 100, selected: false, data1: 'B', + data2: 1554800754 }), EmberObject.create({ name: 'Gold', @@ -30,6 +32,7 @@ const MOCK_DATA = A([ bulkEmailsCount: 300, selected: false, data1: 'C', + data2: 1365498354 }), EmberObject.create({ name: 'Enterprise', @@ -38,6 +41,7 @@ const MOCK_DATA = A([ bulkEmailsCount: 'Custom', selected: false, data1: 'D', + data2: 1383987954 }) ]); @@ -71,7 +75,7 @@ const DEFAULT_COLUMNS = [ { title: 'Data 2', property: 'data2', - type: 'numeric' + type: 'date' }, { title: 'Data 3', diff --git a/tests/dummy/app/styles/app.less b/tests/dummy/app/styles/app.less index 443a7c458..9949b2d13 100644 --- a/tests/dummy/app/styles/app.less +++ b/tests/dummy/app/styles/app.less @@ -84,11 +84,11 @@ } &--hidden { - animation: hide-contextual-actions 0.5s 1; + animation: hide-contextual-actions .5s 1; } &--visible { - animation: show-contextual-actions 0.5s 1; + animation: show-contextual-actions .5s 1; z-index: 1; opacity: 1; display: block; From 6b49dbc1f399582884867714dcba69b06ab8144b Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Tue, 10 Sep 2019 14:53:01 +0200 Subject: [PATCH 26/37] add support for date filtering by presets --- .../upf-table/cell/filters-renderers/date.js | 50 +++++++++++++++++++ .../upf-table/cell/filters-renderers/money.js | 2 + .../cell/filters-renderers/numeric.js | 2 + .../upf-table/cell/filters-renderers/text.js | 2 + .../upf-table/cell/renderers/money.js | 2 +- addon/components/upf-table/index.js | 2 +- addon/services/upf-table-state.js | 26 ++++++++-- app/templates/components/upf-table/cell.hbs | 4 +- .../upf-table/cell/filters-renderers/date.hbs | 25 ++++++++++ tests/dummy/app/styles/app.less | 17 +++++++ 10 files changed, 124 insertions(+), 8 deletions(-) diff --git a/addon/components/upf-table/cell/filters-renderers/date.js b/addon/components/upf-table/cell/filters-renderers/date.js index 1423f5d85..a4ab65b72 100644 --- a/addon/components/upf-table/cell/filters-renderers/date.js +++ b/addon/components/upf-table/cell/filters-renderers/date.js @@ -1,7 +1,10 @@ import Component from '@ember/component'; +import { computed } from '@ember/object'; import { inject as service } from '@ember/service'; export default Component.extend({ + classNames: ['available-filters'], + upfTableState: service(), sortingOptions: { @@ -9,9 +12,56 @@ export default Component.extend({ 'Newest — Oldest': 'alphanumerical:desc' }, + filteringOptions: { + 'Fixed': 'fixed', + 'Moving': 'moving' + }, + + filterOption: 'moving', // or 'moving' + + currentMovingDateOption: computed('column.filters.@each.value.alias', function() { + let filter = this.column.filters.find((f) => f.type === 'range'); + + return (filter) ? filter.value.alias : null; + }), + + movingDateOptions: { + 'Today': 'today', + 'Yesterday': 'yesterday' + }, + + _buildDateRange(from) { + switch(from) { + case 'today': + return { + alias: 'today', + from: moment().startOf('day').format('X'), + to: moment().endOf('day').format('X') + } + case 'yesterday': + return { + alias: 'yesterday', + from: moment().subtract(1, 'day').startOf('day').format('X'), + to: moment().subtract(1, 'day').endOf('day').format('X') + } + default: + break; + } + }, + actions: { sortingOptionChanged(value) { this.upfTableState.updateSortBy(this.column, value); + }, + + filterOptionChanged(value) { + this.set('filterOption', value); + }, + + selectMovingDate(value) { + this.upfTableState.addFilters( + this.column, 'range', this._buildDateRange(value) + ); } } }); diff --git a/addon/components/upf-table/cell/filters-renderers/money.js b/addon/components/upf-table/cell/filters-renderers/money.js index 1f10fa367..0f50dab29 100644 --- a/addon/components/upf-table/cell/filters-renderers/money.js +++ b/addon/components/upf-table/cell/filters-renderers/money.js @@ -2,6 +2,8 @@ import Component from '@ember/component'; import { inject as service } from '@ember/service'; export default Component.extend({ + classNames: ['available-filters'], + upfTableState: service(), sortingOptions: { diff --git a/addon/components/upf-table/cell/filters-renderers/numeric.js b/addon/components/upf-table/cell/filters-renderers/numeric.js index 1f10fa367..0f50dab29 100644 --- a/addon/components/upf-table/cell/filters-renderers/numeric.js +++ b/addon/components/upf-table/cell/filters-renderers/numeric.js @@ -2,6 +2,8 @@ import Component from '@ember/component'; import { inject as service } from '@ember/service'; export default Component.extend({ + classNames: ['available-filters'], + upfTableState: service(), sortingOptions: { diff --git a/addon/components/upf-table/cell/filters-renderers/text.js b/addon/components/upf-table/cell/filters-renderers/text.js index 57b4d8969..6c364728d 100644 --- a/addon/components/upf-table/cell/filters-renderers/text.js +++ b/addon/components/upf-table/cell/filters-renderers/text.js @@ -2,6 +2,8 @@ import Component from '@ember/component'; import { inject as service } from '@ember/service'; export default Component.extend({ + classNames: ['available-filters'], + upfTableState: service(), sortingOptions: { diff --git a/addon/components/upf-table/cell/renderers/money.js b/addon/components/upf-table/cell/renderers/money.js index 533301e03..2fa97f2b5 100644 --- a/addon/components/upf-table/cell/renderers/money.js +++ b/addon/components/upf-table/cell/renderers/money.js @@ -1,5 +1,5 @@ import Component from '@ember/component'; -import { computed } from '@ember/object'; +import { computed } from '@ember/object'; import { empty } from '@ember/object/computed'; export default Component.extend({ diff --git a/addon/components/upf-table/index.js b/addon/components/upf-table/index.js index 23c69ae2f..f63adf2d7 100644 --- a/addon/components/upf-table/index.js +++ b/addon/components/upf-table/index.js @@ -68,7 +68,7 @@ export default Component.extend({ }), _columnsChanged: observer( - '_columns', '_columns.@each.{visible,sortBy}', + '_columns', '_columns.@each.{visible,sortBy,filters}', function() { if (this.onColumnsChange) { this.onColumnsChange(this._columns); diff --git a/addon/services/upf-table-state.js b/addon/services/upf-table-state.js index 85a2d1719..63913ca87 100644 --- a/addon/services/upf-table-state.js +++ b/addon/services/upf-table-state.js @@ -1,6 +1,7 @@ +import { A } from '@ember/array'; import Service from '@ember/service'; import EmberObject from '@ember/object'; -import { typeOf } from '@ember/utils'; +import { isEmpty, typeOf } from '@ember/utils'; export default Service.extend({ columns: [], @@ -10,6 +11,21 @@ export default Service.extend({ column.set('sortBy', sortBy); }, + addFilters(column, type, value) { + if (isEmpty(column.filters)) { + let f = EmberObject.create({ type, value }) + column.set('filters', [f]); + } else { + column.set('filters', column.filters.map((f) => { + if (f.type === type) { + f.set('value', value); + } + + return f; + })); + } + }, + updateColumns(columns) { this.set('columns', columns.map((column) => { if (typeOf(column) !== 'instance') { @@ -17,8 +33,12 @@ export default Service.extend({ } column.set('visible', column.visible !== false); - column.set('sortBy', column.sortBy || null); - column.set('type', column.type || 'text'); + column.set('sortBy', column.sortBy || null); + column.set( + 'filters', + (column.filters || []).map((x) => EmberObject.create(x)) + ); + column.set('type', column.type || 'text'); return column; })); diff --git a/app/templates/components/upf-table/cell.hbs b/app/templates/components/upf-table/cell.hbs index 91de2625e..0dce641c2 100644 --- a/app/templates/components/upf-table/cell.hbs +++ b/app/templates/components/upf-table/cell.hbs @@ -10,9 +10,7 @@
{{#if showFiltersPanel}} -
- {{component _filtersRenderingComponent item=item column=column}} -
+ {{component _filtersRenderingComponent item=item column=column}} {{/if}} {{/if}} {{else}} diff --git a/app/templates/components/upf-table/cell/filters-renderers/date.hbs b/app/templates/components/upf-table/cell/filters-renderers/date.hbs index 6cc0831e5..107151ab3 100644 --- a/app/templates/components/upf-table/cell/filters-renderers/date.hbs +++ b/app/templates/components/upf-table/cell/filters-renderers/date.hbs @@ -8,3 +8,28 @@ {{/each-in}}
{{/input-wrapper}} + +
+ + +
+ {{#each-in filteringOptions as |label value|}} + {{radio-button + value=value currentValue=filterOption label=label + options=filteringOptions onCheck="filterOptionChanged"}} + {{/each-in}} +
+ +
+ {{#if (eq filterOption "fixed")}} + Date Range + {{else}} + {{#each-in movingDateOptions as |label value|}} +
+ {{label}} +
+ {{/each-in}} + {{/if}} +
+
diff --git a/tests/dummy/app/styles/app.less b/tests/dummy/app/styles/app.less index 9949b2d13..14cc1a1bd 100644 --- a/tests/dummy/app/styles/app.less +++ b/tests/dummy/app/styles/app.less @@ -262,6 +262,23 @@ position: absolute; padding: @spacing-xx-sm; top: 50px; + + .filters { + padding-top: @spacing-xx-sm; + + .filters__option { + padding: @spacing-xxx-sm; + + &:hover { + background-color: @upf-gray-light; + cursor: pointer; + } + } + + .filters__option--active { + background-color: @upf-gray-light; + } + } } &.upf-hypertable__cell--numeric:not(.upf-hypertable__cell--header) { From 7b608cc91b36445cece3b26e328452b1d5e07c42 Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Tue, 10 Sep 2019 15:04:14 +0200 Subject: [PATCH 27/37] map the display of the filters icon on filters presence, and same for sorters (with synced icon direction) --- addon/components/upf-table/cell.js | 12 ++++++++++++ app/templates/components/upf-table/cell.hbs | 7 ++++++- tests/dummy/app/styles/app.less | 9 ++++++++- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/addon/components/upf-table/cell.js b/addon/components/upf-table/cell.js index c1de17e90..5345497f4 100644 --- a/addon/components/upf-table/cell.js +++ b/addon/components/upf-table/cell.js @@ -16,6 +16,7 @@ export default Component.extend({ 'header:upf-hypertable__cell--header', 'item.selected:upf-hypertable__cell--selected', '_sorted:upf-hypertable__cell--sorted', + '_filtered:upf-hypertable__cell--filtered', 'isNumeric:upf-hypertable__cell--numeric', 'isMoney:upf-hypertable__cell--numeric' ], @@ -24,8 +25,17 @@ export default Component.extend({ selection: false, _sorted: false, + _filtered: false, _showFiltersPanel: false, + _sortingIconClass: computed('_sorted', function() { + if (this._sorted) { + let [_, direction] = this.column.sortBy.split(':'); + + return (direction === 'asc') ? 'fa-long-arrow-up' : 'fa-long-arrow-down'; + } + }), + _renderingComponent: computed('column.type', function() { if (AVAILABLE_RENDERERS.includes(this.column.type)) { return `upf-table/cell/renderers/${this.column.type}`; @@ -40,6 +50,7 @@ export default Component.extend({ _tableStateListener() { this.set('_sorted', !isEmpty(this.column.sortBy)); + this.set('_filtered', !isEmpty(this.column.filters)); }, _filtersPanelListener() { @@ -81,6 +92,7 @@ export default Component.extend({ } this.set('_sorted', !isEmpty(this.column.sortBy)); + this.set('_filtered', !isEmpty(this.column.filters)); } }, diff --git a/app/templates/components/upf-table/cell.hbs b/app/templates/components/upf-table/cell.hbs index 0dce641c2..3da966756 100644 --- a/app/templates/components/upf-table/cell.hbs +++ b/app/templates/components/upf-table/cell.hbs @@ -2,7 +2,12 @@ {{#if selection}} {{yield}} {{else}} - {{column.title}} +
+ {{column.title}} + {{#if _sorted }} +   + {{/if}} +
diff --git a/tests/dummy/app/styles/app.less b/tests/dummy/app/styles/app.less index 14cc1a1bd..103458bb5 100644 --- a/tests/dummy/app/styles/app.less +++ b/tests/dummy/app/styles/app.less @@ -296,8 +296,15 @@ top: 0; &.upf-hypertable__cell--sorted { + .fa.fa-long-arrow-up, .fa.fa-long-arrow-down { + color: @color-text-light; + font-weight: bold; + } + } + + &.upf-hypertable__cell--filtered { .icon-commands .fa.fa-filter { - color: @color-text; + color: @color-text-light; display: block; font-weight: bold; opacity: 1; From 92b227b549123ce6f6f8be82fbe60f93d8270b8f Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Tue, 10 Sep 2019 15:16:48 +0200 Subject: [PATCH 28/37] move table related css to the package's css so it's available for testing --- app/styles/oss-components.less | 383 ++++++++++++++++++++++++++++++++ tests/dummy/app/styles/app.less | 382 ------------------------------- 2 files changed, 383 insertions(+), 382 deletions(-) diff --git a/app/styles/oss-components.less b/app/styles/oss-components.less index 253a0cb9f..093f1e5ea 100644 --- a/app/styles/oss-components.less +++ b/app/styles/oss-components.less @@ -1,3 +1,386 @@ @import "bootstrap.less"; @import "font-awesome"; @import "upfluence-oss"; + + +@cell-height: 45px; + +.upf-checkbox--sm { + width: 15px; + + .upf-checkbox__fake-checkbox { + width: 15px; + height: 15px; + } + + .upf-checkbox__input:checked + .upf-checkbox__fake-checkbox:after { + top: 6px; + left: 4px; + height: 4px; + width: 7px; + } +} + +.upf-hypertable-container { + border: 1px solid lighten(@upf-gray, 15%); + border-radius: @default-radius; + width: 100%; +} + + +.upf-hypertable { + display: flex; +} + +/* + * Hypertable's Upper Header + * ======================== + * Access to actions allowing the user to modify the current state of the + * datatable. + * + */ +.upf-hypertable__upper-header { + padding: @spacing-xx-sm; + display: flex; + justify-content: space-between; + position: relative; + + .left-side { + display: flex; + flex-grow: 1; + } + + /* + * Search + * ====== + * + */ + .left-side { + input { + max-width: 250px; + } + } + + /* + * Contextual Actions + * =========== + * Contextual actions that show up to act on the selected items + * + */ + .left-side .contextual-actions { + border-left: 1px solid lighten(@upf-gray, 15%); + opacity: 0; + z-index: -1; + animation: none; + + margin-left: @spacing-sm; + padding: 0 @spacing-sm; + + &--no-search-sibling { + border-left: 0; + margin-left: 0; + padding: 0; + } + + &--no-animation { + display: none !important; + } + + &--hidden { + animation: hide-contextual-actions .5s 1; + } + + &--visible { + animation: show-contextual-actions .5s 1; + z-index: 1; + opacity: 1; + display: block; + } + } + + /* + * Available Columns + * ================= + * Access all available columns and toggle their visibility in the table + * + */ + .available-columns { + background-color: #fff; + border: 2px solid lighten(@upf-gray, 15%); + border-radius: @default-radius; + + display: flex; + flex-direction: row; + + position: absolute; + top: 55px; + right: 0; + z-index: 99999; + + .available-columns__categories { + background-color: @upf-gray-light; + border-right: 1px solid lighten(@upf-gray, 15%); + min-width: 200px; + + .field-category { + background-color: #fff; + border-bottom: 1px solid lighten(@upf-gray, 15%); + font-size: 16/@rem; + + display: flex; + align-items: center; + justify-content: space-between; + + padding: @spacing-sm @spacing-xx-sm; + } + } + + .available-columns__fields { + flex-direction: column; + flex-grow: 1; + padding: @spacing-xx-sm; + + .search { + background-color: #fff; + padding: @spacing-xxx-sm; + position: sticky; + + input { + width: 100%; + } + } + + .fields-list { + height: 200px; + max-height: 200px; + overflow: hidden scroll; + + .field { + border-radius: @default-radius; + color: @color-text; + min-width: 270px; + padding: @spacing-xxx-sm; + display: flex; + + &:hover { + background-color: @upf-gray-light; + cursor: pointer; + } + + &.field:not(.visible) { + color: @color-text-lighter; + } + } + } + + ::-webkit-scrollbar { + display: none; + } + } + + &:before, + &:after { + content:''; + position: absolute; + bottom: 100%; + right: 30px; + width: 0; + height: 0; + border-style: solid; + } + + &:after { + border-color: transparent transparent lighten(@upf-gray, 15%) transparent; + border-width: 8px; + } + + &:before { + border-color: transparent transparent lighten(@upf-gray, 15%) transparent; + border-width: 10px; + right: 28.5px + } + } +} + +.upf-hypertable__table { + height: 700px; + overflow: auto; +} + +.upf-hypertable__sticky-columns { + position: sticky; + left: 0; + z-index: 9999; + background-color: white; + display: flex; +} + +.upf-hypertable__column { + border-left: 1px solid lighten(@upf-gray, 15%); + + min-width: 200px; + flex-grow: 200; + + opacity: 1; + transition: opacity .5s; + + &:first-child { + border-left: none; + } + + // Animate Columns Drag & Drop + &.is-dragging { + opacity: .75; + } + + &.is-dropping { + opacity: 1; + } +} + +.upf-hypertable__column--selection { + width: 40px; + min-width: 40px; + flex-grow: 40; +} + +.upf-hypertable__cell { + border-bottom: 1px solid lighten(@upf-gray, 15%); + display: flex; + align-items: center; + + height: @cell-height; + max-height: @cell-height; + padding: @spacing-xx-sm; + width: 100%; + + .available-filters { + background-color: #fff; + border: 2px solid lighten(@upf-gray, 15%); + border-radius: @default-radius; + + min-width: 300px; + width: 300px; + + position: absolute; + padding: @spacing-xx-sm; + top: 50px; + + .filters { + padding-top: @spacing-xx-sm; + + .filters__option { + padding: @spacing-xxx-sm; + + &:hover { + background-color: @upf-gray-light; + cursor: pointer; + } + } + + .filters__option--active { + background-color: @upf-gray-light; + } + } + } + + &.upf-hypertable__cell--numeric:not(.upf-hypertable__cell--header) { + justify-content: flex-end; + } +} + +.upf-hypertable__cell--header { + background-color: @upf-gray-light; + color: @upf-primary-rock-blue; + cursor: pointer; + + justify-content: space-between; + position: sticky; + top: 0; + + &.upf-hypertable__cell--sorted { + .fa.fa-long-arrow-up, .fa.fa-long-arrow-down { + color: @color-text-light; + font-weight: bold; + } + } + + &.upf-hypertable__cell--filtered { + .icon-commands .fa.fa-filter { + color: @color-text-light; + display: block; + font-weight: bold; + opacity: 1; + } + } + + & > .column-name { + display: flex; + } + + & > .icon-commands { + color: @color-text-lighter; + + display: flex; + flex-direction: row; + align-items: center; + + i { + opacity: 0; + display: none; + font-size: 16/@rem; + margin-left: @spacing-xx-sm; + } + + i:hover, i:active { + color: @color-text; + cursor: pointer; + } + + i.fa-bars { cursor: move; } + } + + &:hover { + background-color: lighten(@upf-gray, 15%); + + .icon-commands i { + opacity: 1; + display: block; + } + } +} + +.upf-hypertable__cell--selected { + background-color: @upf-gray-light; +} + +/* + * Animations used throughout the datatable + * ======================================= + * + */ +@keyframes show-contextual-actions { + 0% { + display: block; + opacity: 0; + transform: translateX(-50px); + } + + 100% { + opacity: 1; + transform: translateX(0); + } +} + + +@keyframes hide-contextual-actions { + 0% { + opacity: 1; + transform: translateX(0); + } + + 100% { + opacity: 0; + transform: translateX(-50px); + } +} diff --git a/tests/dummy/app/styles/app.less b/tests/dummy/app/styles/app.less index 103458bb5..36282f18b 100644 --- a/tests/dummy/app/styles/app.less +++ b/tests/dummy/app/styles/app.less @@ -1,383 +1 @@ @import 'oss-components'; - -@cell-height: 45px; - -.upf-checkbox--sm { - width: 15px; - - .upf-checkbox__fake-checkbox { - width: 15px; - height: 15px; - } - - .upf-checkbox__input:checked + .upf-checkbox__fake-checkbox:after { - top: 6px; - left: 4px; - height: 4px; - width: 7px; - } -} - -.upf-hypertable-container { - border: 1px solid lighten(@upf-gray, 15%); - border-radius: @default-radius; - width: 100%; -} - - -.upf-hypertable { - display: flex; -} - -/* - * Hypertable's Upper Header - * ======================== - * Access to actions allowing the user to modify the current state of the - * datatable. - * - */ -.upf-hypertable__upper-header { - padding: @spacing-xx-sm; - display: flex; - justify-content: space-between; - position: relative; - - .left-side { - display: flex; - flex-grow: 1; - } - - /* - * Search - * ====== - * - */ - .left-side { - input { - max-width: 250px; - } - } - - /* - * Contextual Actions - * =========== - * Contextual actions that show up to act on the selected items - * - */ - .left-side .contextual-actions { - border-left: 1px solid lighten(@upf-gray, 15%); - opacity: 0; - z-index: -1; - animation: none; - - margin-left: @spacing-sm; - padding: 0 @spacing-sm; - - &--no-search-sibling { - border-left: 0; - margin-left: 0; - padding: 0; - } - - &--no-animation { - display: none !important; - } - - &--hidden { - animation: hide-contextual-actions .5s 1; - } - - &--visible { - animation: show-contextual-actions .5s 1; - z-index: 1; - opacity: 1; - display: block; - } - } - - /* - * Available Columns - * ================= - * Access all available columns and toggle their visibility in the table - * - */ - .available-columns { - background-color: #fff; - border: 2px solid lighten(@upf-gray, 15%); - border-radius: @default-radius; - - display: flex; - flex-direction: row; - - position: absolute; - top: 55px; - right: 0; - z-index: 99999; - - .available-columns__categories { - background-color: @upf-gray-light; - border-right: 1px solid lighten(@upf-gray, 15%); - min-width: 200px; - - .field-category { - background-color: #fff; - border-bottom: 1px solid lighten(@upf-gray, 15%); - font-size: 16/@rem; - - display: flex; - align-items: center; - justify-content: space-between; - - padding: @spacing-sm @spacing-xx-sm; - } - } - - .available-columns__fields { - flex-direction: column; - flex-grow: 1; - padding: @spacing-xx-sm; - - .search { - background-color: #fff; - padding: @spacing-xxx-sm; - position: sticky; - - input { - width: 100%; - } - } - - .fields-list { - height: 200px; - max-height: 200px; - overflow: hidden scroll; - - .field { - border-radius: @default-radius; - color: @color-text; - min-width: 270px; - padding: @spacing-xxx-sm; - display: flex; - - &:hover { - background-color: @upf-gray-light; - cursor: pointer; - } - - &.field:not(.visible) { - color: @color-text-lighter; - } - } - } - - ::-webkit-scrollbar { - display: none; - } - } - - &:before, - &:after { - content:''; - position: absolute; - bottom: 100%; - right: 30px; - width: 0; - height: 0; - border-style: solid; - } - - &:after { - border-color: transparent transparent lighten(@upf-gray, 15%) transparent; - border-width: 8px; - } - - &:before { - border-color: transparent transparent lighten(@upf-gray, 15%) transparent; - border-width: 10px; - right: 28.5px - } - } -} - -.upf-hypertable__table { - height: 700px; - overflow: auto; -} - -.upf-hypertable__sticky-columns { - position: sticky; - left: 0; - z-index: 9999; - background-color: white; - display: flex; -} - -.upf-hypertable__column { - border-left: 1px solid lighten(@upf-gray, 15%); - - min-width: 200px; - flex-grow: 200; - - opacity: 1; - transition: opacity .5s; - - &:first-child { - border-left: none; - } - - // Animate Columns Drag & Drop - &.is-dragging { - opacity: .75; - } - - &.is-dropping { - opacity: 1; - } -} - -.upf-hypertable__column--selection { - width: 40px; - min-width: 40px; - flex-grow: 40; -} - -.upf-hypertable__cell { - border-bottom: 1px solid lighten(@upf-gray, 15%); - display: flex; - align-items: center; - - height: @cell-height; - max-height: @cell-height; - padding: @spacing-xx-sm; - width: 100%; - - .available-filters { - background-color: #fff; - border: 2px solid lighten(@upf-gray, 15%); - border-radius: @default-radius; - - min-width: 300px; - width: 300px; - - position: absolute; - padding: @spacing-xx-sm; - top: 50px; - - .filters { - padding-top: @spacing-xx-sm; - - .filters__option { - padding: @spacing-xxx-sm; - - &:hover { - background-color: @upf-gray-light; - cursor: pointer; - } - } - - .filters__option--active { - background-color: @upf-gray-light; - } - } - } - - &.upf-hypertable__cell--numeric:not(.upf-hypertable__cell--header) { - justify-content: flex-end; - } -} - -.upf-hypertable__cell--header { - background-color: @upf-gray-light; - color: @upf-primary-rock-blue; - cursor: pointer; - - justify-content: space-between; - position: sticky; - top: 0; - - &.upf-hypertable__cell--sorted { - .fa.fa-long-arrow-up, .fa.fa-long-arrow-down { - color: @color-text-light; - font-weight: bold; - } - } - - &.upf-hypertable__cell--filtered { - .icon-commands .fa.fa-filter { - color: @color-text-light; - display: block; - font-weight: bold; - opacity: 1; - } - } - - & > .column-name { - display: flex; - } - - & > .icon-commands { - color: @color-text-lighter; - - display: flex; - flex-direction: row; - align-items: center; - - i { - opacity: 0; - display: none; - font-size: 16/@rem; - margin-left: @spacing-xx-sm; - } - - i:hover, i:active { - color: @color-text; - cursor: pointer; - } - - i.fa-bars { cursor: move; } - } - - &:hover { - background-color: lighten(@upf-gray, 15%); - - .icon-commands i { - opacity: 1; - display: block; - } - } -} - -.upf-hypertable__cell--selected { - background-color: @upf-gray-light; -} - -/* - * Animations used throughout the datatable - * ======================================= - * - */ -@keyframes show-contextual-actions { - 0% { - display: block; - opacity: 0; - transform: translateX(-50px); - } - - 100% { - opacity: 1; - transform: translateX(0); - } -} - - -@keyframes hide-contextual-actions { - 0% { - opacity: 1; - transform: translateX(0); - } - - 100% { - opacity: 0; - transform: translateX(-50px); - } -} From 53c9cd0cfc84449781eb005a3f962ed2d5b9d3d2 Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Tue, 10 Sep 2019 15:18:11 +0200 Subject: [PATCH 29/37] listen to filters changes to update the current column header's state --- addon/components/upf-table/cell.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/addon/components/upf-table/cell.js b/addon/components/upf-table/cell.js index 5345497f4..7b4875699 100644 --- a/addon/components/upf-table/cell.js +++ b/addon/components/upf-table/cell.js @@ -48,6 +48,10 @@ export default Component.extend({ } }), + _filtersChanged: observer('column.filters.@each', function () { + this.set('_filtered', !isEmpty(this.column.filters)); + }), + _tableStateListener() { this.set('_sorted', !isEmpty(this.column.sortBy)); this.set('_filtered', !isEmpty(this.column.filters)); From cac3c76f21bbbb49402f52efc875305e286ae11c Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Tue, 10 Sep 2019 15:18:42 +0200 Subject: [PATCH 30/37] add support for clearing filters on a column --- addon/components/upf-table/cell/filters-renderers/date.js | 5 +++++ addon/services/upf-table-state.js | 4 ++++ .../components/upf-table/cell/filters-renderers/date.hbs | 8 +++++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/addon/components/upf-table/cell/filters-renderers/date.js b/addon/components/upf-table/cell/filters-renderers/date.js index a4ab65b72..8e409416f 100644 --- a/addon/components/upf-table/cell/filters-renderers/date.js +++ b/addon/components/upf-table/cell/filters-renderers/date.js @@ -62,6 +62,11 @@ export default Component.extend({ this.upfTableState.addFilters( this.column, 'range', this._buildDateRange(value) ); + }, + + // Mixin Candidate + clearFilters() { + this.upfTableState.clearFilters(this.column); } } }); diff --git a/addon/services/upf-table-state.js b/addon/services/upf-table-state.js index 63913ca87..eec116c6f 100644 --- a/addon/services/upf-table-state.js +++ b/addon/services/upf-table-state.js @@ -26,6 +26,10 @@ export default Service.extend({ } }, + clearFilters(column) { + column.set('filters', []); + }, + updateColumns(columns) { this.set('columns', columns.map((column) => { if (typeOf(column) !== 'instance') { diff --git a/app/templates/components/upf-table/cell/filters-renderers/date.hbs b/app/templates/components/upf-table/cell/filters-renderers/date.hbs index 107151ab3..25b31fd0a 100644 --- a/app/templates/components/upf-table/cell/filters-renderers/date.hbs +++ b/app/templates/components/upf-table/cell/filters-renderers/date.hbs @@ -9,7 +9,7 @@
{{/input-wrapper}} -
+
@@ -33,3 +33,9 @@ {{/if}}
+ +
+ +
From 076f42af3e06e11cc6e50895b141e1e223cfeee0 Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Tue, 10 Sep 2019 18:00:47 +0200 Subject: [PATCH 31/37] basic support of onBottomReached actions to lay the basis of an infinite scroll --- addon/components/upf-table/index.js | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/addon/components/upf-table/index.js b/addon/components/upf-table/index.js index f63adf2d7..9e2ed997a 100644 --- a/addon/components/upf-table/index.js +++ b/addon/components/upf-table/index.js @@ -98,11 +98,22 @@ export default Component.extend({ }, 1000); }), - //didReceiveAttrs() { - //if (isEmpty(this.columns)) { - //throw new Error('[component][upf-table] Missing columns'); - //} - //}, + didInsertElement() { + let self = this; + + this.$('.upf-hypertable__table').on('scroll', function() { + let tableHeight = $(this).innerHeight(); + let heightScrolled = $(this).scrollTop(); + let contentHeight = $(`.upf-hypertable`)[0].scrollHeight; + + if ((heightScrolled + tableHeight) >= contentHeight) { + if (self.onBottomReached) { + self.onBottomReached(); + } + } + }); + }, + actions: { reorderColumns(x, itemModels, _) { let _cs = [x[0]].concat(itemModels.concat(x.filter(x => !x.visible))) From e00d0404c7976cd833862c12d8e68f99b4875e0b Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Wed, 11 Sep 2019 09:56:18 +0200 Subject: [PATCH 32/37] move to ember-sortable to shared dependencies --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fbccf9d79..c3ee5bd23 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "ember-cli-less": "^1.5.3", "ember-cli-shims": "^1.2.0", "ember-cli-toggle": "^3.0.0", + "ember-sortable": "^1.12.9", "ember-truth-helpers": "^1.3.0", "upfluence-oss": "^1.x" }, @@ -53,7 +54,6 @@ "ember-moment": "^8.0.0", "ember-qunit": "^3.4.1", "ember-resolver": "^5.0.1", - "ember-sortable": "^1.12.9", "ember-source": "~3.8.0", "ember-source-channel-url": "^1.1.0", "ember-try": "^1.0.0", From d63c20e3b26101781d1f4d18a9f065861b375f44 Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Wed, 11 Sep 2019 11:52:53 +0200 Subject: [PATCH 33/37] add state when loading more items to the collection and do not handle bottom reached state if there is no scrollbar --- addon/components/upf-table/cell.js | 2 ++ addon/components/upf-table/index.js | 28 +++++++++++++-------- app/styles/oss-components.less | 24 ++++++++++++++++++ app/templates/components/upf-table.hbs | 18 +++++++++++++ app/templates/components/upf-table/cell.hbs | 4 ++- 5 files changed, 65 insertions(+), 11 deletions(-) diff --git a/addon/components/upf-table/cell.js b/addon/components/upf-table/cell.js index 7b4875699..7d874f389 100644 --- a/addon/components/upf-table/cell.js +++ b/addon/components/upf-table/cell.js @@ -15,6 +15,7 @@ export default Component.extend({ classNameBindings: [ 'header:upf-hypertable__cell--header', 'item.selected:upf-hypertable__cell--selected', + 'loading:upf-hypertable__cell--loading', '_sorted:upf-hypertable__cell--sorted', '_filtered:upf-hypertable__cell--filtered', 'isNumeric:upf-hypertable__cell--numeric', @@ -23,6 +24,7 @@ export default Component.extend({ header: false, selection: false, + loading: false, _sorted: false, _filtered: false, diff --git a/addon/components/upf-table/index.js b/addon/components/upf-table/index.js index 9e2ed997a..30715fa87 100644 --- a/addon/components/upf-table/index.js +++ b/addon/components/upf-table/index.js @@ -15,8 +15,10 @@ export default Component.extend({ hasSelection: false, hasSearch: false, contextualActions: null, + loadingMore: false, _allRowsSelected: false, + _hasScrollbar: false, //hasPagination: false, //hasPolymorphicColumns: false, @@ -57,6 +59,10 @@ export default Component.extend({ } ), + _loadingMore: computed('_hasScrollbar', 'loadingMore', function() { + return this.onBottomReached && this._hasScrollbar && this.loadingMore; + }), + _selectAllObserver: observer('_allRowsSelected', function() { this.get('collection').forEach((item) => { if (this.get('_allRowsSelected')) { @@ -78,15 +84,15 @@ export default Component.extend({ _selectedItemsChanged: observer('_selectedItems', function() { if (this.contextualActions) { - let ca = document.querySelector('.contextual-actions'); - - if (this._selectedItems.length > 0) { - ca.classList.remove('contextual-actions--no-animation'); - ca.classList.add('contextual-actions--visible'); - } else { - ca.classList.remove('contextual-actions--visible'); - ca.classList.add('contextual-actions--hidden'); - } + let ca = document.querySelector('.contextual-actions'); + + if (this._selectedItems.length > 0) { + ca.classList.remove('contextual-actions--no-animation'); + ca.classList.add('contextual-actions--visible'); + } else { + ca.classList.remove('contextual-actions--visible'); + ca.classList.add('contextual-actions--hidden'); + } } }), @@ -103,8 +109,10 @@ export default Component.extend({ this.$('.upf-hypertable__table').on('scroll', function() { let tableHeight = $(this).innerHeight(); + let contentHeight = $('.upf-hypertable')[0].scrollHeight; let heightScrolled = $(this).scrollTop(); - let contentHeight = $(`.upf-hypertable`)[0].scrollHeight; + + self.set('_hasScrollbar', (tableHeight <= contentHeight)); if ((heightScrolled + tableHeight) >= contentHeight) { if (self.onBottomReached) { diff --git a/app/styles/oss-components.less b/app/styles/oss-components.less index 093f1e5ea..4d7937f87 100644 --- a/app/styles/oss-components.less +++ b/app/styles/oss-components.less @@ -297,6 +297,7 @@ justify-content: space-between; position: sticky; top: 0; + z-index: 9999; &.upf-hypertable__cell--sorted { .fa.fa-long-arrow-up, .fa.fa-long-arrow-down { @@ -354,6 +355,19 @@ background-color: @upf-gray-light; } +.upf-hypertable__cell--loading .skeleton-placeholder { + height: 10px; + animation: skeleton-content 1.2s ease-in-out infinite; + background-color: #eee; + background-image: linear-gradient(90deg, #eee, #f5f5f5, #eee); + background-size: 200px 100%; + background-repeat: no-repeat; + border-radius: @default-radius; + display: inline-block; + line-height: 1; + width: 100%; +} + /* * Animations used throughout the datatable * ======================================= @@ -384,3 +398,13 @@ transform: translateX(-50px); } } + +@keyframes skeleton-content { + 0% { + background-position: -200px 0; + } + + 100% { + background-position: calc(200px + 100%) 0; + } +} diff --git a/app/templates/components/upf-table.hbs b/app/templates/components/upf-table.hbs index bc2300e48..ae294339a 100644 --- a/app/templates/components/upf-table.hbs +++ b/app/templates/components/upf-table.hbs @@ -68,6 +68,12 @@ {{upf-checkbox value=item.selected size="sm"}}
{{/each}} + + {{#if _loadingMore}} + {{upf-table/cell loading=true}} + {{upf-table/cell loading=true}} + {{upf-table/cell loading=true}} + {{/if}}
{{/if}} @@ -77,6 +83,12 @@ {{#each collection as |item|}} {{upf-table/cell item=item column=column}} {{/each}} + + {{#if _loadingMore}} + {{upf-table/cell loading=true}} + {{upf-table/cell loading=true}} + {{upf-table/cell loading=true}} + {{/if}}
{{/if}} @@ -92,6 +104,12 @@ {{#each collection as |item|}} {{upf-table/cell item=item column=column}} {{/each}} + + {{#if _loadingMore}} + {{upf-table/cell loading=true}} + {{upf-table/cell loading=true}} + {{upf-table/cell loading=true}} + {{/if}} {{/sortable-item}} {{/if}} {{/each}} diff --git a/app/templates/components/upf-table/cell.hbs b/app/templates/components/upf-table/cell.hbs index 3da966756..4fa8818ad 100644 --- a/app/templates/components/upf-table/cell.hbs +++ b/app/templates/components/upf-table/cell.hbs @@ -19,7 +19,9 @@ {{/if}} {{/if}} {{else}} - {{#if (has-block)}} + {{#if loading}} +
+ {{else if (has-block)}} {{yield}} {{else}} {{component _renderingComponent item=item column=column}} From 9a658767e93e7b5604f61956a5b221270106143e Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Wed, 11 Sep 2019 14:09:06 +0200 Subject: [PATCH 34/37] group the enabled features in a config dict --- addon/components/upf-table/index.js | 29 ++++++++++++++++++++-- app/templates/components/upf-table.hbs | 6 ++--- tests/dummy/app/controllers/application.js | 11 ++++++++ tests/dummy/app/templates/application.hbs | 4 +-- 4 files changed, 43 insertions(+), 7 deletions(-) diff --git a/addon/components/upf-table/index.js b/addon/components/upf-table/index.js index 30715fa87..491e28de4 100644 --- a/addon/components/upf-table/index.js +++ b/addon/components/upf-table/index.js @@ -6,17 +6,42 @@ import { run } from '@ember/runloop'; import { inject as service } from '@ember/service'; import { isEmpty } from '@ember/utils'; +const DEFAULT_OPTIONS = { + features: { + selection: false, + search: false + } +}; + export default Component.extend({ upfTableState: service(), classNames: ['upf-hypertable-container'], //classNameBindings: ['isCompact:upf-table__container--compact'], - hasSelection: false, - hasSearch: false, contextualActions: null, loadingMore: false, + /* + * Configuration + * ============= + * + * Define which features of the datatable should be activated. + * + */ + options: DEFAULT_OPTIONS, + + /* + * Event Hooks + * =========== + * + * Actions to be called to react to various events happening on the datatable + * + */ + onColumnsChange: null, + onBottomReached: null, + onSearchQueryChange: null, + _allRowsSelected: false, _hasScrollbar: false, diff --git a/app/templates/components/upf-table.hbs b/app/templates/components/upf-table.hbs index ae294339a..8da639569 100644 --- a/app/templates/components/upf-table.hbs +++ b/app/templates/components/upf-table.hbs @@ -1,6 +1,6 @@
- {{#if hasSearch}} + {{#if options.features.search}} {{input type="text" placeholder="Search..." value=_searchQuery class="form-control upf-input upf-input--small"}} @@ -8,7 +8,7 @@ {{#if contextualActions}}
+ {{unless options.features.search 'contextual-actions--no-search-sibling'}}"> {{component contextualActions selectedItems=_selectedItems}}
{{/if}} @@ -57,7 +57,7 @@ {{#each _columns as |column index|}} {{#if (eq index 0)}}
- {{#if hasSelection}} + {{#if options.features.selection}}
{{#upf-table/cell header=true selection=true}} {{upf-checkbox value=_allRowsSelected size="sm"}} diff --git a/tests/dummy/app/controllers/application.js b/tests/dummy/app/controllers/application.js index e345b6a60..e924050d2 100644 --- a/tests/dummy/app/controllers/application.js +++ b/tests/dummy/app/controllers/application.js @@ -8,6 +8,13 @@ export default Controller.extend({ collection: [], columns: [], + tableOptions: { + features: { + search: true, + selection: true + } + }, + init() { this._super(); this._fetchPlans(); @@ -29,6 +36,10 @@ export default Controller.extend({ this.plansFetcher.fetch(layout).then((data) => { this.set('collection', data.items); }); + }, + + performSearch(s) { + alert(`Search: ${s}`); } } }); diff --git a/tests/dummy/app/templates/application.hbs b/tests/dummy/app/templates/application.hbs index 7d8cedfad..bbac42530 100644 --- a/tests/dummy/app/templates/application.hbs +++ b/tests/dummy/app/templates/application.hbs @@ -1,7 +1,7 @@
{{upf-table - columns=upfTableState.columns collection=collection hasSelection=true - hasSearch=true onColumnsChange=(action "columnsChanged") + columns=upfTableState.columns collection=collection options=tableOptions + onColumnsChange=(action "columnsChanged") onSearchQueryChange=(action "performSearch") refreshing=refreshing contextualActions="contextual-actions"}}
From b878e4f0e61cb473ca550676af10a5ee12fe1c7e Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Wed, 11 Sep 2019 15:03:16 +0200 Subject: [PATCH 35/37] handle case where the date data received is already a date object (cf. something going through ember-data) --- addon/components/upf-table/cell/renderers/date.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/addon/components/upf-table/cell/renderers/date.js b/addon/components/upf-table/cell/renderers/date.js index 3f8d6f490..c3b60190b 100644 --- a/addon/components/upf-table/cell/renderers/date.js +++ b/addon/components/upf-table/cell/renderers/date.js @@ -1,6 +1,7 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; import { empty, or } from '@ember/object/computed'; +import { typeOf } from '@ember/utils'; import moment from 'moment'; @@ -17,6 +18,14 @@ export default Component.extend({ _dateFormat: or('column.date_format', '_defaultDateFormat'), _formattedDate: computed('value', '_dateFormat', function() { - return moment.unix(this.value).format(this._defaultDateFormat); + let _date; + + if (typeOf(this.value) === 'date') { + _date = moment(this.value); + } else { + _date = moment.unix(this.value) + } + + return _date.format(this._dateFormat); }) }); From 494f9798daf13c70745092fd75ee109e69fbffd5 Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Wed, 11 Sep 2019 15:56:49 +0200 Subject: [PATCH 36/37] allow rendering component to be overwritten in column config --- addon/components/upf-table/cell.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/addon/components/upf-table/cell.js b/addon/components/upf-table/cell.js index 7d874f389..fb53f2797 100644 --- a/addon/components/upf-table/cell.js +++ b/addon/components/upf-table/cell.js @@ -1,5 +1,6 @@ import Component from '@ember/component'; import { computed, defineProperty, observer } from '@ember/object'; +import { or } from '@ember/object/computed'; import { inject as service } from '@ember/service'; import { capitalize } from '@ember/string'; import { isEmpty } from '@ember/utils'; @@ -38,12 +39,16 @@ export default Component.extend({ } }), - _renderingComponent: computed('column.type', function() { + _typeInferredRenderingComponent: computed('column.type', function() { if (AVAILABLE_RENDERERS.includes(this.column.type)) { return `upf-table/cell/renderers/${this.column.type}`; } }), + _renderingComponent: or( + 'column.renderingComponent', '_typeInferredRenderingComponent' + ), + _filtersRenderingComponent: computed('column.type', function() { if (AVAILABLE_RENDERERS.includes(this.column.type)) { return `upf-table/cell/filters-renderers/${this.column.type}`; From fceb4311248efcd067916beedb10911e43d7a01c Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Thu, 12 Sep 2019 15:55:29 +0200 Subject: [PATCH 37/37] refactor the table service to be able to create multiple table instances --- addon/components/upf-table/cell.js | 17 ++------ .../upf-table/cell/filters-renderers/date.js | 9 ++-- .../upf-table/cell/filters-renderers/money.js | 5 +-- .../cell/filters-renderers/numeric.js | 5 +-- .../upf-table/cell/filters-renderers/text.js | 5 +-- addon/components/upf-table/index.js | 7 +-- addon/services/upf-table-state.js | 43 +++++++++++-------- app/styles/oss-components.less | 2 +- app/templates/components/upf-table.hbs | 4 +- app/templates/components/upf-table/cell.hbs | 3 +- tests/dummy/app/controllers/application.js | 3 +- tests/dummy/app/templates/application.hbs | 4 +- 12 files changed, 46 insertions(+), 61 deletions(-) diff --git a/addon/components/upf-table/cell.js b/addon/components/upf-table/cell.js index fb53f2797..a89b0f48c 100644 --- a/addon/components/upf-table/cell.js +++ b/addon/components/upf-table/cell.js @@ -1,7 +1,6 @@ import Component from '@ember/component'; import { computed, defineProperty, observer } from '@ember/object'; import { or } from '@ember/object/computed'; -import { inject as service } from '@ember/service'; import { capitalize } from '@ember/string'; import { isEmpty } from '@ember/utils'; @@ -10,8 +9,6 @@ const AVAILABLE_RENDERERS = [ ]; export default Component.extend({ - upfTableState: service(), - classNames: ['upf-hypertable__cell'], classNameBindings: [ 'header:upf-hypertable__cell--header', @@ -65,17 +62,11 @@ export default Component.extend({ }, _filtersPanelListener() { - if (this.column.property !== this.upfTableState.applyingFiltersOn) { + if (this.column.property !== this.manager.applyingFiltersOn) { this.set('showFiltersPanel', false); } }, - init() { - this._super(); - - this.get('upfTableState'); - }, - didReceiveAttrs() { if (this.column) { AVAILABLE_RENDERERS.forEach((rendererType) => { @@ -90,13 +81,13 @@ export default Component.extend({ if (this.header) { this.addObserver( - 'upfTableState.columns.@each.sortBy', + 'manager.columns.@each.sortBy', this, this._tableStateListener ); this.addObserver( - 'upfTableState.applyingFiltersOn', + 'manager.applyingFiltersOn', this, this._filtersPanelListener ); @@ -110,7 +101,7 @@ export default Component.extend({ actions: { toggleFiltersPanel() { this.toggleProperty('showFiltersPanel'); - this.set('upfTableState.applyingFiltersOn', this.column.property); + this.set('manager.applyingFiltersOn', this.column.property); } } }); diff --git a/addon/components/upf-table/cell/filters-renderers/date.js b/addon/components/upf-table/cell/filters-renderers/date.js index 8e409416f..89f38bc8c 100644 --- a/addon/components/upf-table/cell/filters-renderers/date.js +++ b/addon/components/upf-table/cell/filters-renderers/date.js @@ -1,12 +1,9 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; -import { inject as service } from '@ember/service'; export default Component.extend({ classNames: ['available-filters'], - upfTableState: service(), - sortingOptions: { 'Oldest — Newest': 'alphanumerical:asc', 'Newest — Oldest': 'alphanumerical:desc' @@ -51,7 +48,7 @@ export default Component.extend({ actions: { sortingOptionChanged(value) { - this.upfTableState.updateSortBy(this.column, value); + this.manager.updateSortBy(this.column, value); }, filterOptionChanged(value) { @@ -59,14 +56,14 @@ export default Component.extend({ }, selectMovingDate(value) { - this.upfTableState.addFilters( + this.manager.addFilters( this.column, 'range', this._buildDateRange(value) ); }, // Mixin Candidate clearFilters() { - this.upfTableState.clearFilters(this.column); + this.manager.clearFilters(this.column); } } }); diff --git a/addon/components/upf-table/cell/filters-renderers/money.js b/addon/components/upf-table/cell/filters-renderers/money.js index 0f50dab29..ac4d3f73a 100644 --- a/addon/components/upf-table/cell/filters-renderers/money.js +++ b/addon/components/upf-table/cell/filters-renderers/money.js @@ -1,11 +1,8 @@ import Component from '@ember/component'; -import { inject as service } from '@ember/service'; export default Component.extend({ classNames: ['available-filters'], - upfTableState: service(), - sortingOptions: { '0 — 9': 'alphanumerical:asc', '9 — 0': 'alphanumerical:desc' @@ -13,7 +10,7 @@ export default Component.extend({ actions: { sortingOptionChanged(value) { - this.upfTableState.updateSortBy(this.column, value); + this.manager.updateSortBy(this.column, value); } } }); diff --git a/addon/components/upf-table/cell/filters-renderers/numeric.js b/addon/components/upf-table/cell/filters-renderers/numeric.js index 0f50dab29..ac4d3f73a 100644 --- a/addon/components/upf-table/cell/filters-renderers/numeric.js +++ b/addon/components/upf-table/cell/filters-renderers/numeric.js @@ -1,11 +1,8 @@ import Component from '@ember/component'; -import { inject as service } from '@ember/service'; export default Component.extend({ classNames: ['available-filters'], - upfTableState: service(), - sortingOptions: { '0 — 9': 'alphanumerical:asc', '9 — 0': 'alphanumerical:desc' @@ -13,7 +10,7 @@ export default Component.extend({ actions: { sortingOptionChanged(value) { - this.upfTableState.updateSortBy(this.column, value); + this.manager.updateSortBy(this.column, value); } } }); diff --git a/addon/components/upf-table/cell/filters-renderers/text.js b/addon/components/upf-table/cell/filters-renderers/text.js index 6c364728d..f0e94de02 100644 --- a/addon/components/upf-table/cell/filters-renderers/text.js +++ b/addon/components/upf-table/cell/filters-renderers/text.js @@ -1,11 +1,8 @@ import Component from '@ember/component'; -import { inject as service } from '@ember/service'; export default Component.extend({ classNames: ['available-filters'], - upfTableState: service(), - sortingOptions: { 'A — Z': 'alphanumerical:asc', 'Z — A': 'alphanumerical:desc' @@ -13,7 +10,7 @@ export default Component.extend({ actions: { sortingOptionChanged(value) { - this.upfTableState.updateSortBy(this.column, value); + this.manager.updateSortBy(this.column, value); } } }); diff --git a/addon/components/upf-table/index.js b/addon/components/upf-table/index.js index 491e28de4..1e7c45a15 100644 --- a/addon/components/upf-table/index.js +++ b/addon/components/upf-table/index.js @@ -3,7 +3,6 @@ import Component from '@ember/component'; import { computed, observer } from '@ember/object'; import { alias, filterBy } from '@ember/object/computed'; import { run } from '@ember/runloop'; -import { inject as service } from '@ember/service'; import { isEmpty } from '@ember/utils'; const DEFAULT_OPTIONS = { @@ -14,8 +13,6 @@ const DEFAULT_OPTIONS = { }; export default Component.extend({ - upfTableState: service(), - classNames: ['upf-hypertable-container'], //classNameBindings: ['isCompact:upf-table__container--compact'], @@ -66,7 +63,7 @@ export default Component.extend({ //itemCount: 0, /*itemName: '',*/ - _columns: alias('upfTableState.columns'), + _columns: alias('manager.columns'), _selectedItems: filterBy('collection', 'selected', true), _orderedFilteredColumns: computed( @@ -151,7 +148,7 @@ export default Component.extend({ reorderColumns(x, itemModels, _) { let _cs = [x[0]].concat(itemModels.concat(x.filter(x => !x.visible))) _cs.forEach((c, i) => c.set('order', i)) - this.upfTableState.updateColumns(_cs); + this.manager.updateColumns(_cs); }, openAvailableColumns() { diff --git a/addon/services/upf-table-state.js b/addon/services/upf-table-state.js index eec116c6f..512b6bd22 100644 --- a/addon/services/upf-table-state.js +++ b/addon/services/upf-table-state.js @@ -3,8 +3,27 @@ import Service from '@ember/service'; import EmberObject from '@ember/object'; import { isEmpty, typeOf } from '@ember/utils'; -export default Service.extend({ +const Table = EmberObject.extend({ columns: [], + applyingFiltersOn: null, + + updateColumns(columns) { + this.set('columns', columns.map((column) => { + if (typeOf(column) !== 'instance') { + column = EmberObject.create(column); + } + + column.set('visible', column.visible !== false); + column.set('sortBy', column.sortBy || null); + column.set( + 'filters', + (column.filters || []).map((x) => EmberObject.create(x)) + ); + column.set('type', column.type || 'text'); + + return column; + })); + }, updateSortBy(column, sortBy) { this.columns.forEach((c) => c.set('sortBy', null)); @@ -28,23 +47,11 @@ export default Service.extend({ clearFilters(column) { column.set('filters', []); - }, - - updateColumns(columns) { - this.set('columns', columns.map((column) => { - if (typeOf(column) !== 'instance') { - column = EmberObject.create(column); - } - - column.set('visible', column.visible !== false); - column.set('sortBy', column.sortBy || null); - column.set( - 'filters', - (column.filters || []).map((x) => EmberObject.create(x)) - ); - column.set('type', column.type || 'text'); + } +}); - return column; - })); +export default Service.extend({ + createTable() { + return Table.create(); } }); diff --git a/app/styles/oss-components.less b/app/styles/oss-components.less index 4d7937f87..79c88db6c 100644 --- a/app/styles/oss-components.less +++ b/app/styles/oss-components.less @@ -203,7 +203,7 @@ } .upf-hypertable__table { - height: 700px; + max-height: 700px; overflow: auto; } diff --git a/app/templates/components/upf-table.hbs b/app/templates/components/upf-table.hbs index 8da639569..3a64118db 100644 --- a/app/templates/components/upf-table.hbs +++ b/app/templates/components/upf-table.hbs @@ -78,7 +78,7 @@ {{/if}}
- {{upf-table/cell column=column header=true}} + {{upf-table/cell column=column header=true manager=manager}} {{#each collection as |item|}} {{upf-table/cell item=item column=column}} @@ -99,7 +99,7 @@ {{#sortable-item classNames="upf-hypertable__column" model=column group=group handle=".upf-hypertable__cell--header"}} - {{upf-table/cell column=column header=true}} + {{upf-table/cell column=column header=true manager=manager}} {{#each collection as |item|}} {{upf-table/cell item=item column=column}} diff --git a/app/templates/components/upf-table/cell.hbs b/app/templates/components/upf-table/cell.hbs index 4fa8818ad..d0a1aefc3 100644 --- a/app/templates/components/upf-table/cell.hbs +++ b/app/templates/components/upf-table/cell.hbs @@ -15,7 +15,8 @@
{{#if showFiltersPanel}} - {{component _filtersRenderingComponent item=item column=column}} + {{component + _filtersRenderingComponent item=item column=column manager=manager}} {{/if}} {{/if}} {{else}} diff --git a/tests/dummy/app/controllers/application.js b/tests/dummy/app/controllers/application.js index e924050d2..638c058e9 100644 --- a/tests/dummy/app/controllers/application.js +++ b/tests/dummy/app/controllers/application.js @@ -25,7 +25,8 @@ export default Controller.extend({ this.plansFetcher.fetch().then((data) => { this.set('collection', data.items); - this.upfTableState.updateColumns(data.meta.columns); + this.set('tableManager', this.upfTableState.createTable()); + this.tableManager.updateColumns(data.meta.columns); }).finally(() => { this.set('refreshing', false); }); diff --git a/tests/dummy/app/templates/application.hbs b/tests/dummy/app/templates/application.hbs index bbac42530..50e60dd8e 100644 --- a/tests/dummy/app/templates/application.hbs +++ b/tests/dummy/app/templates/application.hbs @@ -1,7 +1,7 @@
{{upf-table - columns=upfTableState.columns collection=collection options=tableOptions + manager=tableManager collection=collection options=tableOptions onColumnsChange=(action "columnsChanged") onSearchQueryChange=(action "performSearch") - refreshing=refreshing contextualActions="contextual-actions"}} + contextualActions="contextual-actions"}}