├── .gitignore ├── .npmignore ├── index.html ├── demo ├── index.html ├── miscellaneous │ ├── bwt-datatable-edit-dialog.html │ ├── icons.html │ ├── light.html │ ├── events.html │ └── theming.html ├── libs │ └── relativeDate.js ├── paper-datatable │ ├── race.html │ ├── sorting.html │ ├── mobile.html │ ├── index.html │ ├── bindings.html │ ├── switching-datas.html │ ├── filter.html │ ├── playground.html │ ├── editable.html │ ├── dynamic-columns.html │ └── resize-behavior.html ├── data │ ├── fakeDB.js │ └── users.json └── paper-datatable-card │ ├── playground.html │ ├── index.html │ └── full-implementation.html ├── images ├── icon.png ├── screenshot.png ├── header-fixed.gif └── bwt-datatable-mobile.png ├── src ├── whatever.js ├── collectionHelpers.js └── weakCache.js ├── docs ├── api.html ├── events.md ├── browser-support.md ├── installation.md ├── getting-started-with-data-table-cards.md ├── docs.html ├── styling.md ├── getting-started.md └── documentation-menu.html ├── test ├── index.html └── bwt-datatable.html ├── bwt-datatable-styles.js ├── bower.json ├── datatable-icons.js ├── package.json ├── bwt-datatable-edit-dialog.js ├── README.md ├── examples └── your-datatable-implementation.js ├── bwt-datatable-column.js └── bwt-datatable-card.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | bower_components -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | bower_components -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluewatertracks/bwt-datatable/HEAD/images/icon.png -------------------------------------------------------------------------------- /images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluewatertracks/bwt-datatable/HEAD/images/screenshot.png -------------------------------------------------------------------------------- /images/header-fixed.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluewatertracks/bwt-datatable/HEAD/images/header-fixed.gif -------------------------------------------------------------------------------- /images/bwt-datatable-mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluewatertracks/bwt-datatable/HEAD/images/bwt-datatable-mobile.png -------------------------------------------------------------------------------- /src/whatever.js: -------------------------------------------------------------------------------- 1 | export const Whenever = () => { 2 | var callbacks = []; 3 | var ready = false; 4 | var args; 5 | return { 6 | get state(){ 7 | return { 8 | ready: ready, 9 | args: args, 10 | pendingCallbacks: callbacks.length 11 | }; 12 | }, 13 | ready() { 14 | args = arguments; 15 | callbacks.forEach(function(callback){ 16 | callback.apply(this, args); 17 | }); 18 | callbacks = []; 19 | ready = true; 20 | }, 21 | whenReady(callback) { 22 | if(ready){ 23 | callback.apply(this, args); 24 | }else{ 25 | callbacks.push(callback); 26 | } 27 | } 28 | } 29 | }; -------------------------------------------------------------------------------- /docs/api.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | API Reference 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | bwt-datatable tests 16 | 17 | 18 | 19 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /demo/miscellaneous/bwt-datatable-edit-dialog.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 25 | 31 | 32 | -------------------------------------------------------------------------------- /src/collectionHelpers.js: -------------------------------------------------------------------------------- 1 | export class CollectionHelpers { 2 | constructor(data) { 3 | this.store = data.slice(); 4 | this._initMap(); 5 | } 6 | 7 | getItem (key) { 8 | if (key = this._parseKey(key)) { 9 | return this.store[key]; 10 | } 11 | } 12 | 13 | getKey (item) { 14 | let key; 15 | if (item && typeof item == 'object') { 16 | key = this.omap.get(item); 17 | } else { 18 | key = this.pmap[item]; 19 | } 20 | if (key != undefined) { 21 | return '#' + key; 22 | } 23 | } 24 | 25 | getKeys () { 26 | return Object.keys(this.store).map((key) => { 27 | return '#' + key; 28 | }); 29 | } 30 | 31 | _initMap () { 32 | this.omap = new WeakMap(); 33 | this.pmap = {}; 34 | var s = this.store; 35 | this.store.forEach((item, index)=>{ 36 | if (item && typeof item == 'object') { 37 | this.omap.set(item, index); 38 | } else { 39 | this.pmap[item] = index; 40 | } 41 | }); 42 | } 43 | 44 | _parseKey (key) { 45 | if (key && key[0] === '#') { 46 | return key.slice(1); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /src/weakCache.js: -------------------------------------------------------------------------------- 1 | /* 2 | IMPORTANT: Right now this Cache is **not** weak in the sense that it discards items based memory pressure, however 3 | it is written in a way that will allow this later to be added without rewriting the rest of the code and right now 4 | 'emulates' the behaviour by having a fixed maximum of items. 5 | */ 6 | export class WeakCache { 7 | constructor(limit) { 8 | this.limit = limit; 9 | this.values = []; 10 | this.keys = []; 11 | } 12 | set (key, obj) { 13 | var index = this.keys.indexOf(key); 14 | if(index > -1){ 15 | this.values[index] = obj; 16 | this.values.splice(this.values.length, 0, this.values.splice(index, 1)[0]); 17 | this.keys.splice(this.keys.length, 0, this.keys.splice(index, 1)[0]); 18 | }else{ 19 | this.keys.push(key); 20 | this.values.push(obj); 21 | if(this.keys.length > this.limit){ 22 | this.keys.shift(); 23 | this.values.shift(); 24 | } 25 | } 26 | } 27 | has (key){ 28 | return this.keys.indexOf(key) > -1; 29 | } 30 | get (key){ 31 | var index = this.keys.indexOf(key); 32 | return this.values[index]; 33 | } 34 | delete (key){ 35 | var index = this.keys.indexOf(key); 36 | this.values.splice(index, 1); 37 | this.keys.splice(index, 1); 38 | } 39 | } -------------------------------------------------------------------------------- /docs/events.md: -------------------------------------------------------------------------------- 1 | Events 2 | === 3 | All properties with a `notifies` label in the API references will trigger a `propertyName-changed` event. Beyond that the 4 | element also exposes a few custom events which are documented here. One of the best ways to find events is 5 | simply be exploring the [event explorer](../demo/events.html). 6 | 7 | `` and `` 8 | === 9 | Taps 10 | --- 11 | 12 | - `row-tap` is triggered when a user taps anywhere on a row. The `item` of the row can be found in the `detail` object. 13 | This event can be cancelled using `event.preventDefault()` which will prevent the row from getting selected. 14 | - `cell-tap` on `` is triggered whenever any cell is tapped containing both the `item` and `column` in 15 | the `detail` object. 16 | - `cell-tap` on `` is triggered whenever a cell in that column is tapped. It contains only the 17 | `item` of the current row. 18 | 19 | Selections 20 | --- 21 | 22 | - `selection-changed` is a generic event when the selection changes and exposes added or removed items from the selection. 23 | - `toggle-all` is triggered when the 'toggle all' checkbox is tapped and can be cancelled. 24 | 25 | Other 26 | --- 27 | - `sort` is triggered when a column is tapped and can be cancelled. 28 | 29 | `` 30 | === 31 | Doesn't expose any special events beyond the standard Polymer `*-changed` events which you can find in the API reference. -------------------------------------------------------------------------------- /demo/libs/relativeDate.js: -------------------------------------------------------------------------------- 1 | /* from https://github.com/azer/relative-date */ 2 | 3 | var relativeDate = (function(undefined){ 4 | 5 | var SECOND = 1000, 6 | MINUTE = 60 * SECOND, 7 | HOUR = 60 * MINUTE, 8 | DAY = 24 * HOUR, 9 | WEEK = 7 * DAY, 10 | YEAR = DAY * 365, 11 | MONTH = YEAR / 12; 12 | 13 | var formats = [ 14 | [ 0.7 * MINUTE, 'just now' ], 15 | [ 1.5 * MINUTE, 'a minute ago' ], 16 | [ 60 * MINUTE, 'minutes ago', MINUTE ], 17 | [ 1.5 * HOUR, 'an hour ago' ], 18 | [ DAY, 'hours ago', HOUR ], 19 | [ 2 * DAY, 'yesterday' ], 20 | [ 7 * DAY, 'days ago', DAY ], 21 | [ 1.5 * WEEK, 'a week ago'], 22 | [ MONTH, 'weeks ago', WEEK ], 23 | [ 1.5 * MONTH, 'a month ago' ], 24 | [ YEAR, 'months ago', MONTH ], 25 | [ 1.5 * YEAR, 'a year ago' ], 26 | [ Number.MAX_VALUE, 'years ago', YEAR ] 27 | ]; 28 | 29 | function relativeDate(input,reference){ 30 | !reference && ( reference = (new Date).getTime() ); 31 | reference instanceof Date && ( reference = reference.getTime() ); 32 | input instanceof Date && ( input = input.getTime() ); 33 | 34 | var delta = reference - input, 35 | format, i, len; 36 | 37 | for(i = -1, len=formats.length; ++i < len; ){ 38 | format = formats[i]; 39 | if(delta < format[0]){ 40 | return format[2] == undefined ? format[1] : Math.round(delta/format[2]) + ' ' + format[1]; 41 | } 42 | }; 43 | } 44 | 45 | return relativeDate; 46 | 47 | })(); 48 | 49 | if(typeof module != 'undefined' && module.exports){ 50 | module.exports = relativeDate; 51 | } -------------------------------------------------------------------------------- /docs/browser-support.md: -------------------------------------------------------------------------------- 1 | Supported browsers 2 | === 3 | First of all let's be clear about one thing here: This data table is in certain ways more complex than anything out there. 4 | To make this component possible at all cutting edge libraries like Polymer were used and support were limited to 5 | the newest version of browsers that actually care about implementing new technologies, so this left us the following 6 | browsers: 7 | 8 | - Chrome (Primary target) 9 | - Edge 10 | - Firefox 11 | - Opera 12 | 13 | Which covers more than three quarters of users and seems quite acceptable as data tables are mostly used on 14 | back end/admin systems where it's relatively more OK to instruct users to use certain browsers. Notable not supported 15 | browsers are IE and Safari. IE because it doesn't support *a lot* of new technologies and Edge is practically the newest 16 | version of IE. Safari because it's consistently lagging behind other browsers in technology adoption1 17 | and because buying Apple products is a conscious decision to buy into a closed ecosystem on which outsiders can not 18 | develop and test. 19 | 20 | 1 Some stats to back this up: caniuse.com is a site which covers technology adoption in browsers. Considering JS API's 21 | Safari only supports 50% (like IE) of the covered API's compared to the next worst browser (Edge) which covers 71%. 22 | And just for comparisons sake the most recent versions of Firefox cover 89% and Chrome 82%. That's simply a HUGE 23 | difference. -------------------------------------------------------------------------------- /demo/miscellaneous/icons.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Custom datatable `iron-iconset` 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 33 | 34 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /docs/installation.md: -------------------------------------------------------------------------------- 1 | paper Datatable 2 | === 3 | An implementation of [material design data tables](https://www.google.com/design/spec/components/data-tables.html) 4 | for [modern web browsers](?browser-support) and inherited from [David Mulder paper-datatable](http://github.com/David-Mulder/paper-datatable). It can be found on GitHub over 5 | [here](https://github.com/bluewatertracks/bwt-datatable). 6 | 7 | Installation 8 | === 9 | The element can be installed using bower using 10 | 11 | bower install --save bwt-datatable 12 | 13 | Important: If you wish to use `` you need the paper elements listed in `devDependencies` as well. They are not listed as normal dependencies to prevent them from being pulled in on production if you do not need them. 14 | 15 | License 16 | === 17 | 18 | This program is free software: you can redistribute it and/or modify 19 | it under the terms of the GNU General Public License version 3 of the License as published by 20 | the Free Software Foundation. 21 | 22 | This program is distributed in the hope that it will be useful, 23 | but WITHOUT ANY WARRANTY; without even the implied warranty of 24 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 | GNU General Public License for more details. 26 | 27 | This project uses a fair share license construction, if you wish to use this project commercially you will likely want to 28 | continue reading [here](https://github.com/David-Mulder/fair-share-license/blob/master/CONTRIBUTING.md). If you wish to 29 | donate please contact me personally. 30 | -------------------------------------------------------------------------------- /bwt-datatable-styles.js: -------------------------------------------------------------------------------- 1 | import '../@polymer/polymer/polymer-legacy.js'; 2 | import '../@polymer/font-roboto/roboto.js'; 3 | import '../@polymer/iron-flex-layout/iron-flex-layout-classes.js'; 4 | const $_documentContainer = document.createElement('template'); 5 | 6 | $_documentContainer.innerHTML = ` 7 | 49 | `; 50 | 51 | document.head.appendChild($_documentContainer.content); 52 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bwt-datatable", 3 | "version": "3.0.0", 4 | "authors": [ 5 | "Blue Water Tracks" 6 | ], 7 | "description": "Material design data table.", 8 | "keywords": [ 9 | "bwt-datatable", 10 | "paper-datatable", 11 | "web-components", 12 | "polymer", 13 | "data-table" 14 | ], 15 | "main": [ 16 | "bwt-datatable.html", 17 | "bwt-datatable-card.html" 18 | ], 19 | "license": "http://www.gnu.org/licenses/gpl-3.0.en.html", 20 | "homepage": "https://github.com/bluewatertracks/bwt-datatable", 21 | "ignore": [ 22 | "/.*", 23 | "/test/" 24 | ], 25 | "dependencies": { 26 | "polymer": "Polymer/polymer#^2.0.0", 27 | "paper-material": "^2.0.0", 28 | "paper-item": "^2.0.0", 29 | "paper-icon-button": "^2.0.1", 30 | "paper-checkbox": "^2.0.1", 31 | "paper-tooltip": "^2.0.1", 32 | "paper-styles": "^2.0.0", 33 | "whenever.js": "~0.0.1", 34 | "iron-form": "^2.0.1", 35 | "iron-media-query": "^2.0.0", 36 | "iron-scroll-target-behavior": "^2.0.0", 37 | "paper-listbox": "^2.0.0", 38 | "paper-dropdown-menu": "^2.0.0", 39 | "paper-progress": "^2.0.1" 40 | }, 41 | "devDependencies": { 42 | "iron-component-page": "PolymerElements/iron-component-page#^1.0.0", 43 | "paper-card": "PolymerElements/paper-card#~1.0.7", 44 | "web-component-tester": "*", 45 | "paper-input": "PolymerElements/paper-input#~1.0.18", 46 | "paper-header-panel": "^1.1.7", 47 | "paper-drawer-panel": "PolymerElements/paper-drawer-panel#^1.0.11", 48 | "paper-dialog": "PolymerElements/paper-dialog#^1.1.0", 49 | "paper-dialog-scrollable": "PolymerElements/paper-dialog-scrollable#^1.1.5", 50 | "google-map": "GoogleWebComponents/google-map#^1.2.0" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /datatable-icons.js: -------------------------------------------------------------------------------- 1 | import '@polymer/iron-icons/iron-icons.js'; 2 | import '@polymer/iron-iconset-svg/iron-iconset-svg.js'; 3 | const $_documentContainer = document.createElement('template'); 4 | 5 | $_documentContainer.innerHTML = ` 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | `; 28 | 29 | document.head.appendChild($_documentContainer.content); 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Material design data table.", 3 | "keywords": [ 4 | "bwt-datatable", 5 | "paper-datatable", 6 | "web-components", 7 | "polymer", 8 | "data-table" 9 | ], 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/bluewatertracks/bwt-datatable" 13 | }, 14 | "homepage": "https://github.com/bluewatertracks/bwt-datatable", 15 | "name": "bwt-datatable", 16 | "version": "3.0.0", 17 | "author": "Blue Water Tracks", 18 | "license": "AGPL-3.0-only", 19 | "files": [], 20 | "main": "bwt-datatable.js", 21 | "dependencies": { 22 | "@polymer/polymer": "^3.0.0", 23 | "@polymer/paper-material": "^3.0.0-pre.18", 24 | "@polymer/paper-item": "^3.0.0-pre.18", 25 | "@polymer/paper-icon-button": "^3.0.0-pre.18", 26 | "@polymer/paper-checkbox": "^3.0.0-pre.18", 27 | "@polymer/paper-tooltip": "^3.0.0-pre.18", 28 | "@polymer/paper-styles": "^3.0.0-pre.18", 29 | "@polymer/iron-form": "^3.0.0-pre.18", 30 | "@polymer/iron-media-query": "^3.0.0-pre.18", 31 | "@polymer/iron-scroll-target-behavior": "^3.0.0-pre.18", 32 | "@polymer/paper-listbox": "^3.0.0-pre.18", 33 | "@polymer/paper-dropdown-menu": "^3.0.0-pre.18", 34 | "@polymer/paper-progress": "^3.0.0-pre.18" 35 | }, 36 | "devDependencies": { 37 | "@polymer/iron-component-page": "^3.0.0-pre.18", 38 | "@polymer/paper-card": "^3.0.0-pre.18", 39 | "wct-browser-legacy": "^1.0.1", 40 | "@polymer/paper-input": "^3.0.0-pre.18", 41 | "@polymer/paper-header-panel": "^3.0.0-pre.18", 42 | "@polymer/paper-drawer-panel": "^3.0.0-pre.18", 43 | "@polymer/paper-dialog": "^3.0.0-pre.18", 44 | "@polymer/paper-dialog-scrollable": "^3.0.0-pre.18" 45 | }, 46 | "resolutions": { 47 | "inherits": "2.0.3", 48 | "samsam": "1.1.3", 49 | "supports-color": "3.1.2", 50 | "type-detect": "1.0.0" 51 | } 52 | } -------------------------------------------------------------------------------- /demo/miscellaneous/light.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Lightweight CSS-only MD tables 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 |
Column 1Column 2
Cell 1Cell 2
Cell 1Cell 2
73 | 74 | 75 | The above is a simple <table> element after including 76 | paper-datable-styles using: 77 | 78 |
<style is="custom-style" include="paper-datatable-styles"></style>
79 | 80 | with class="card" added for a simple MD card design. 81 |
82 |
83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /demo/paper-datatable/race.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 54 | 55 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /docs/getting-started-with-data-table-cards.md: -------------------------------------------------------------------------------- 1 | Getting started with data tables in cards 2 | === 3 | This documentation still isn't as complete as I would like it to be, so if anything is unclear please start with checking 4 | out the source code from the demo's. 5 | 6 | Data sources 7 | --- 8 | This component uses an object with a set number of functions to retrieve and save data. A `data-source` looks like 9 | 10 | { 11 | get: function(sort, page, pageSize){ return new Promise(...) }, 12 | set: function(item, property, value){ return new Promise(...) }, 13 | length: Number 14 | } 15 | 16 | Alternatively you can use `queryForIds(sort, page, pageSize)` which returns an array of `ids` + `getByIds(ids)` instead 17 | of a simple `get`. Advantages are better caching, better cross page selections, but the big disadvantage is that you 18 | have two do two AJAX requests and the added complexity of the API calls. 19 | 20 | Note: If you wonder why the functions aren't directly available to the element and instead are grouped like this, the 21 | reason for this is that this allows you to create a 22 | 23 | 24 | 25 | object which in turn you bind to this element. I have however been considering allowing manual binding directly as well. 26 | 27 | Using `` without a `data-source` 28 | === 29 | Sometimes it might make sense to do your own implementation of pagination. Sadly extending custom elements is not possible 30 | yet, but you can simply use a `` without a `data-source` and next listen for events like 31 | `page-changed`, `sort`, etc. to handle all the data switchery. It's good to note that a lot of events (like `sort`) do 32 | support `event.preventDefault()`. 33 | 34 | Toolbars 35 | === 36 | Main toolbar 37 | --- 38 | The main toolbar is located to the right of the heading and can be filled by specifying an element with `slot="toolbar-main"`. 39 | 40 |
41 | 42 | 43 |
44 | 45 | Selection toolbar 46 | --- 47 | The selection toolbar is shown when any items are selected and shows the number of selected items. Items in the toolbar 48 | can be specified by adding an element with `slot="toolbar-select"`. Additionally it's often required to show different icons 49 | when a single item is selected from when multiple items are selected. For this there exists `slot="toolbar-select-single"` 50 | and `slot="toolbar-select-multi"` 51 | 52 |
53 | 54 |
55 |
56 | 57 |
58 | 59 | Of course you do not need to use this and you can simply check the `length` of the elements `selectedIds` as well. -------------------------------------------------------------------------------- /demo/paper-datatable/sorting.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Sorting and Type system 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 57 | 58 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /demo/paper-datatable/mobile.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | `[editable]` datatable that support Mobile devices 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 58 | 59 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /demo/paper-datatable/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello `datatable` 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 58 | 59 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /demo/paper-datatable/bindings.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Magic of Data Bindings 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 70 | 71 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /demo/data/fakeDB.js: -------------------------------------------------------------------------------- 1 | /* 2 | JSON.stringify(data.map(function(item){ 3 | item.name = { 4 | first: item.first, 5 | last: item.last 6 | }; 7 | delete item.last; 8 | delete item.first; 9 | item.location = item.location.split(", "); 10 | item.location = { 11 | lat: parseFloat(item.location[0]), 12 | lng: parseFloat(item.location[1]) 13 | }; 14 | return item; 15 | })); 16 | */ 17 | 18 | 19 | var data = new Whenever(); 20 | 21 | if(localStorage.getItem('users') !== null){ 22 | data.ready(JSON.parse(localStorage.users)); 23 | }else{ 24 | httpRequest = new XMLHttpRequest(); 25 | httpRequest.onreadystatechange = function(){ 26 | if (httpRequest.readyState === XMLHttpRequest.DONE) { 27 | if (httpRequest.status === 200) { 28 | data.ready(JSON.parse(httpRequest.responseText)); 29 | } 30 | } 31 | } 32 | httpRequest.open('GET', '../data/userData.js'); 33 | httpRequest.send(); 34 | } 35 | 36 | 37 | window.db = { 38 | count: function(columns, like, orderBy, skip, limit){ 39 | return new Promise(function(resolve, reject){ 40 | data.whenReady(function (users) { 41 | if (like.length > 0) { 42 | searchedUsers = users.filter(function (user) { 43 | if (user.name.first.toLowerCase().indexOf(like.toLowerCase()) > -1) return true; 44 | if (user.name.last.toLowerCase().indexOf(like.toLowerCase()) > -1) return true; 45 | if (user.about.toLowerCase().indexOf(like.toLowerCase()) > -1) return true; 46 | }); 47 | } else { 48 | searchedUsers = users; 49 | } 50 | resolve(searchedUsers.length); 51 | }); 52 | }); 53 | }, 54 | select: function(columns, like, orderBy, skip, limit){ 55 | if(typeof like === 'object'){ 56 | return new Promise(function (resolve, reject) { 57 | data.whenReady(function (users) { 58 | setTimeout(function(){ 59 | resolve(users.filter((user) => like.indexOf(user.id) > -1 )); 60 | }, Math.random() * 500); 61 | }); 62 | }); 63 | }else{ 64 | return new Promise(function (resolve, reject) { 65 | data.whenReady(function (users) { 66 | setTimeout(function () { 67 | if (like.length > 0) { 68 | searchedUsers = users.filter(function (user) { 69 | if (user.name.first.toLowerCase().indexOf(like.toLowerCase()) > -1) return true; 70 | if (user.name.last.toLowerCase().indexOf(like.toLowerCase()) > -1) return true; 71 | if (user.about.toLowerCase().indexOf(like.toLowerCase()) > -1) return true; 72 | }); 73 | } else { 74 | searchedUsers = users; 75 | } 76 | 77 | if (orderBy) { 78 | 79 | searchedUsers.sort(function (a, b) { 80 | if (orderBy.direction == 'desc') { 81 | var c = a; 82 | a = b; 83 | b = c; 84 | } 85 | 86 | if (orderBy.property == 'name') { 87 | a = a[orderBy.property].last + ' ' + a[orderBy.property].first; 88 | b = b[orderBy.property].last + ' ' + b[orderBy.property].first; 89 | } else { 90 | a = a[orderBy.property]; 91 | b = b[orderBy.property]; 92 | } 93 | 94 | a = typeof a === 'string' ? a.toLowerCase() : a; 95 | b = typeof b === 'string' ? b.toLowerCase() : b; 96 | 97 | if (a > b) return 1; 98 | if (a < b) return -1; 99 | return 0; 100 | }); 101 | } 102 | if (columns === 'id') { 103 | resolve(searchedUsers.slice(skip, limit).map((item) => item.id)); 104 | } else if (columns === '*') { 105 | resolve(searchedUsers.slice(skip, limit)); 106 | } else { 107 | console.warn('This is a *fake* db, remember'); 108 | } 109 | }, Math.random() * 100); 110 | }); 111 | }); 112 | } 113 | }, 114 | update: function(id, property, value){ 115 | return new Promise(function(resolve, reject){ 116 | data.whenReady(function (users) { 117 | setTimeout(function () { 118 | var myItem = users.find(function (thisItem) { 119 | return thisItem.id == id; 120 | }); 121 | myItem[property] = value; 122 | localStorage.users = JSON.stringify(users); 123 | resolve(true); 124 | }, Math.random() * 100); 125 | }); 126 | }); 127 | } 128 | }; -------------------------------------------------------------------------------- /demo/paper-datatable/switching-datas.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 54 | 55 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /bwt-datatable-edit-dialog.js: -------------------------------------------------------------------------------- 1 | import { PolymerElement, html } from '../@polymer/polymer'; 2 | import '../@polymer/paper-material/paper-material.js'; 3 | import '../@polymer/iron-form/iron-form.js'; 4 | import '../@polymer/iron-flex-layout/iron-flex-layout-classes.js'; 5 | import { IronResizableBehavior } from '../@polymer/iron-resizable-behavior/iron-resizable-behavior.js'; 6 | import { mixinBehaviors } from '../@polymer/polymer/lib/legacy/class.js'; 7 | import { dom } from '../@polymer/polymer/lib/legacy/polymer.dom.js'; 8 | 9 | class PaperDatatableEditDialog extends mixinBehaviors([IronResizableBehavior], PolymerElement) { 10 | static get template() { 11 | return html` 12 | 13 | 36 | 37 | 38 |
39 | 40 |
41 |
42 |
43 | `; 44 | } 45 | static get is() { 46 | return 'paper-datatable-edit-dialog'; 47 | } 48 | 49 | static get properties() { 50 | return { 51 | positionedRelativeTo: { 52 | type: Element, 53 | observer: 'setLocationRelativeTo' 54 | }, 55 | visible: { 56 | type: Boolean, 57 | reflectToAttribute: true 58 | } 59 | } 60 | } 61 | 62 | 63 | // listeners: { 64 | // 'iron-resize': 'setLocationRelativeTo' 65 | // }, 66 | 67 | ready() { 68 | super.ready(); 69 | this.addEventListener('keyup', (ev) => { 70 | var genericEvent = ev; 71 | if (ev.keyCode == 13 && genericEvent.path[0].nodeName.toLowerCase() !== 'textarea') { 72 | this.dismiss(); 73 | } 74 | }); 75 | document.body.addEventListener('click', (ev) => { 76 | var path = ev.path; 77 | if (this.positionedRelativeTo && path.indexOf(this) == -1 && path.indexOf(this.positionedRelativeTo) == -1) { 78 | this.dismiss(ev); 79 | } 80 | }); 81 | } 82 | 83 | dismiss(ev) { 84 | this.set('visible', undefined); 85 | this.positionedRelativeTo = undefined; 86 | if (ev) 87 | ev.preventDefault(); 88 | } 89 | 90 | findFocus() { 91 | var paperInput = this.shadowRoot.querySelector('paper-input') 92 | if (paperInput) { 93 | paperInput.$.input.focus(); 94 | } 95 | var paperInput = this.shadowRoot.querySelector('paper-textarea') 96 | if (paperInput) { 97 | var position = paperInput.$.input.$.textarea.value.length; 98 | paperInput.$.input.$.textarea.focus(); 99 | paperInput.$.input.$.textarea.setSelectionRange(position, position); 100 | } 101 | var input = this.shadowRoot.querySelector('input') 102 | if (input) { 103 | input.focus(); 104 | } 105 | } 106 | 107 | setLocationRelativeTo() { 108 | if (this.positionedRelativeTo) { 109 | this.set('visible', true) 110 | this.revealTime = Date.now(); 111 | var relativeToParent = this.parentNode; 112 | while (relativeToParent !== window) { 113 | if (relativeToParent.nodeName == '#document-fragment') { 114 | relativeToParent = relativeToParent.host; 115 | } else { 116 | if (getComputedStyle(relativeToParent).position == 'relative' || getComputedStyle(relativeToParent).position == 'absolute') { 117 | break; 118 | } 119 | relativeToParent = dom(relativeToParent).parentNode; 120 | } 121 | } 122 | var parent = relativeToParent.getBoundingClientRect(); 123 | var child = this.positionedRelativeTo.getBoundingClientRect(); 124 | this.style.top = child.top - 2 - parent.top + "px"; 125 | this.style.left = child.left - parent.left + "px"; 126 | this.style.right = Math.max(parent.right - child.right, 0) + "px"; 127 | this.$.material.style.minHeight = child.height + 2 + "px"; 128 | } 129 | } 130 | } 131 | window.customElements.define(PaperDatatableEditDialog.is, PaperDatatableEditDialog); 132 | -------------------------------------------------------------------------------- /demo/paper-datatable/filter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Filter attribute 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 61 | 62 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /docs/docs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Documentation 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 112 | 113 | 114 | 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /docs/styling.md: -------------------------------------------------------------------------------- 1 | Styling 2 | === 3 | The biggest difficulty is styling, because the way Polymer and the shadow DOM works it's incredibly hard to style elements that are hidden in the shadow DOM, as is the case for the cells. Dynamically setting up mixins isn't possible either however, so in the end the following compromise was reached: 4 | 5 | Header cell and Row cell styling 6 | --- 7 | It is possible to define a css string that will be applied to the header cell and all cells in a column like 8 | 9 | 10 | 11 | Which would make the header `green` and the content in each cell in the column `red`. This is especially useful to set a `min-width` on the header. There is also a convenience attribute `align='center'` to center columns (both the header and cells) and a convenience attribute `width` to set the minimum width. 12 | 13 | Tip: Check the `theming.html` demo for a live demonstration. 14 | 15 | Styling content inside `