Chromium Code Reviews| Index: appengine/swarming/elements/res/imp/common/dynamic-table.html |
| diff --git a/appengine/swarming/elements/res/imp/common/dynamic-table.html b/appengine/swarming/elements/res/imp/common/dynamic-table.html |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..4cb7712d935647c27cc8b94cc82ce6867f389ff6 |
| --- /dev/null |
| +++ b/appengine/swarming/elements/res/imp/common/dynamic-table.html |
| @@ -0,0 +1,201 @@ |
| +<!-- |
| + Copyright 2016 The LUCI Authors. All rights reserved. |
| + Use of this source code is governed under the Apache License, Version 2.0 |
| + that can be found in the LICENSE file. |
| + |
| + This file contains most of the logic needed to create a dynamic table. It is roken up into two parts, a style dom-module called "dynamic-table-style" and a behavior called SwarmingBehaviors.DynamicTableBehavior. This behavior ties together filtering, sorting and column content. It also offers a few utilities to make creating the table easier. A client of these two parts needs to create the templates to actually draw the <table>,<tr> and so on. See bot-list.html and task-list.html for examples. |
|
jcgregorio
2016/08/16 12:09:27
Line wrap at 100 chars. Also check other lines bel
kjlubick
2016/08/16 12:29:46
Whoops, forgot to do this. Done.
|
| + |
| + A client should use the provided style set as follows: |
| + |
| + <link rel="import" href="/res/imp/common/dynamic-table.html"> |
| + ... |
| + <template> |
| + <style include="dynamic-table-style"> |
| + ... |
| + |
| + This behavior has already defined the following properties, which a client should bind to: |
| + _columns, Array<String>, The columns that should be shown. |
| + _items, Array<Object>, Those elements that may be displayed and/or sorted, depending on the settings. |
| + _filter, Function, Given an element from _items, return a boolean if the item should be shown. This function will be bound to this element. |
| + _sortstr, String, A String representation of the current state of sorting like [name]:["asc", "desc"]. |
| + _verbose, Boolean, If the verbose contents of the table should be shown. |
| + |
| + A client must define the following properties: |
| + _columnMap: Object, a mapping of column name to a function that will return the content for a given bot. These functions are bound to this element. If a column is not listed here, a sane default will be used (see _column()). |
| + _headerMap: Object, a mapping of column name to the displayed text for a column. |
| + _specialColumns, Array<String> A list of "special" column names, that is, columns which will have html in them, provided by the client. non-special (i.e. plain colunns) just contain text and will have their content provided by _attribute (see below). |
| + _specialSort, Object, A mapping of column name to a function that implements custom sorting rules. The function will be given (dir, a, b) and is expected to return an int, as a normal sort comparison function would. Otherwise, natural comparison of a and b is used (see _compare()). |
| + |
| + A client must define the following methods: |
| + _attribute(i, col, default): Given the item i, return an array of values for the column "col", or an array containting just the default, if not. This is only used as a default when a column does not appear in _columnMap. |
| + |
| + This behavior provides the following properties: |
| + _filteredSortedItems, Array<Object>, The list of items that should shown, after filtering and sorting. |
| + _plainColumns, Array<String>, the list of columns with any special columns stripped out. |
| + |
| + This behavior provides the following methods: |
| + _column(col, item): Return the text content of item for a column. |
| + _header(col): Return the header for a column, defaulting to the column name. |
| + _hide(col): Return a boolean based on whether to hide this column. |
| + _sortChange(event): Update the sorting based on an event created by sort-toggle. |
| + --> |
| + |
| +<dom-module id="dynamic-table-style"> |
| + <template> |
| + <style> |
| + table { |
| + border-collapse: collapse; |
| + margin-left: 5px; |
| + } |
| + td, th { |
| + border: 1px solid #DDD; |
| + padding: 5px; |
| + } |
| + th { |
| + position: relative; |
| + } |
| + sort-toggle { |
| + position: absolute; |
| + right: 0; |
| + top: 0.4em; |
| + } |
| + </style> |
| + |
| + </template> |
| +</dom-module> |
| + |
| +<script> |
| + window.SwarmingBehaviors = window.SwarmingBehaviors || {}; |
| + (function(){ |
| + // This behavior wraps up all the shared swarming functionality. |
| + SwarmingBehaviors.DynamicTableBehavior = { |
| + |
| + properties: { |
| + |
| + _columns: { |
| + type: Array, |
| + }, |
| + |
| + _filter: { |
| + type: Function, |
| + }, |
| + |
| + _filteredSortedItems: { |
| + type: Array, |
| + computed: "_filterAndSort(_items,_filter.*,_sort.*)" |
| + }, |
| + |
| + _items: { |
| + type: Array, |
| + }, |
| + |
| + _plainColumns: { |
| + type: Array, |
| + computed: "_stripSpecial(_columns.*)", |
| + }, |
| + |
| + // _sort is an Object {name:String, direction:String}. |
| + _sort: { |
| + type: Object, |
| + computed: "_makeSortObject(_sortstr)", |
| + }, |
| + |
| + _sortstr: { |
| + type: String, |
| + }, |
| + |
| + _verbose: { |
| + type: Boolean, |
| + } |
| + }, |
| + |
| + _column: function(col, key) { |
| + var f = this._columnMap[col]; |
| + if (!f) { |
| + f = function(key) { |
| + var c = this._attribute(key, col, "none"); |
| + if (this._verbose) { |
| + return c.join(" | "); |
| + } |
| + return c[0]; |
| + } |
| + } |
| + return f.bind(this)(key); |
| + }, |
| + |
| + _compare: function(a, b) { |
| + if (!this._sort) { |
| + return 0; |
| + } |
| + var dir = 1; |
| + if (this._sort.direction === "desc") { |
| + dir = -1; |
| + } |
| + var sort = this._specialSort[this._sort.name]; |
| + if (sort) { |
| + return sort.bind(this)(dir, a, b); |
| + } |
| + // Default to a natural compare of the columns. |
| + var aCol = this._column(this._sort.name, a); |
| + var bCol = this._column(this._sort.name, b); |
| + |
| + return dir * swarming.naturalCompare(aCol, bCol); |
| + }, |
| + |
| + _filterAndSort: function() { |
| + // We intentionally sort this._items (and not a copy) to allow users to |
| + // "chain" sorts, that is, sort by one thing and then another, and |
| + // have both orderings properly impact the list. |
| + swarming.stableSort(this._items, this._compare.bind(this)); |
| + var items = this._items; |
| + if (this._filter) { |
| + items = items.filter(this._filter.bind(this)); |
| + } |
| + |
| + return items; |
| + }, |
| + |
| + _header: function(col){ |
| + return this._headerMap[col] || col; |
| + }, |
| + |
| + _hide: function(col) { |
| + return this._columns.indexOf(col) === -1; |
| + }, |
| + |
| + _makeSortObject: function(sortstr){ |
| + if (!sortstr) { |
| + return undefined; |
| + } |
| + var pieces = sortstr.split(":"); |
| + if (pieces.length != 2) { |
| + // fail safe |
| + return {name: "id", direction: "asc"}; |
| + } |
| + return { |
| + name: pieces[0], |
| + direction: pieces[1], |
| + } |
| + }, |
| + |
| + _sortChange: function(e) { |
| + // The event we get from sort-toggle tells us the name of what needs |
| + // to be sorting and how to sort it. |
| + if (!(e && e.detail && e.detail.name)) { |
| + return; |
| + } |
| + // should trigger the computation of _sort and __filterAndSort |
| + this.set("_sortstr", e.detail.name +":"+e.detail.direction); |
|
jcgregorio
2016/08/16 12:09:27
Spaces around plus:
+ ":" + e.
kjlubick
2016/08/16 12:29:46
Done.
|
| + }, |
| + // _stripSpecial removes the special columns and sorts the remaining |
| + // columns so they always appear in the same order, regardless of |
| + // the order they are added. |
| + _stripSpecial: function(){ |
| + return this._columns.filter(function(c){ |
|
jcgregorio
2016/08/16 12:09:27
function(c) {
kjlubick
2016/08/16 12:29:46
Done.
|
| + return this._specialColumns.indexOf(c) === -1; |
| + }.bind(this)).sort(); |
| + }, |
| + |
| + }; |
| + })(); |
| +</script> |