From 7d900370d6391795ba5a89ad947e344a43b595c2 Mon Sep 17 00:00:00 2001 From: Andrew Stein Date: Mon, 2 Sep 2019 02:17:04 -0400 Subject: [PATCH 1/7] Removed "render_time" attribute --- .../perspective-viewer/src/js/viewer/perspective_element.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/perspective-viewer/src/js/viewer/perspective_element.js b/packages/perspective-viewer/src/js/viewer/perspective_element.js index 3df97fd373..30558e0888 100644 --- a/packages/perspective-viewer/src/js/viewer/perspective_element.js +++ b/packages/perspective-viewer/src/js/viewer/perspective_element.js @@ -261,7 +261,7 @@ export class PerspectiveElement extends StateElement { } finally { this.dispatchEvent(new Event("perspective-view-update")); } - }, calculate_throttle_timeout(this.getAttribute("render_time"))); + }, calculate_throttle_timeout(this.__render_time)); } } @@ -373,7 +373,7 @@ export class PerspectiveElement extends StateElement { } catch (err) { console.warn(err); } finally { - if (!this.hasAttribute("render_time")) { + if (!this.__render_time) { this.dispatchEvent(new Event("perspective-view-update")); } timer(); @@ -387,7 +387,7 @@ export class PerspectiveElement extends StateElement { _render_time() { const t = performance.now(); - return () => this.setAttribute("render_time", performance.now() - t); + return () => (this.__render_time = `${performance.now() - t}`); } _restyle_plugin() { From 38461aeed776fb426677311c3d5fcb06369b52d0 Mon Sep 17 00:00:00 2001 From: Andrew Stein Date: Mon, 2 Sep 2019 03:08:09 -0400 Subject: [PATCH 2/7] Simplified aggregate attribute --- .../src/js/viewer/dom_element.js | 8 +++++--- .../src/js/viewer/perspective_element.js | 10 +++++++++- .../src/js/viewer/state_element.js | 17 ++++++++++++++++- .../test/js/computed_columns.spec.js | 2 +- 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/packages/perspective-viewer/src/js/viewer/dom_element.js b/packages/perspective-viewer/src/js/viewer/dom_element.js index ca6894b919..57f17213b2 100644 --- a/packages/perspective-viewer/src/js/viewer/dom_element.js +++ b/packages/perspective-viewer/src/js/viewer/dom_element.js @@ -24,7 +24,9 @@ export class DomElement extends PerspectiveElement { "aggregates", JSON.stringify( aggs.reduce((obj, agg) => { - obj[agg.column] = agg.op; + if (this._aggregate_defaults[agg.column] !== agg.op) { + obj[agg.column] = agg.op; + } return obj; }, {}) ) @@ -69,8 +71,8 @@ export class DomElement extends PerspectiveElement { if (type === "string") { const view = this._table.view({row_pivots: [name], aggregates: {}}); view.to_json().then(json => { - row.choices(this._autocomplete_choices(json)) } - ); + row.choices(this._autocomplete_choices(json)); + }); view.delete(); } } diff --git a/packages/perspective-viewer/src/js/viewer/perspective_element.js b/packages/perspective-viewer/src/js/viewer/perspective_element.js index 30558e0888..b1dce2c5a5 100644 --- a/packages/perspective-viewer/src/js/viewer/perspective_element.js +++ b/packages/perspective-viewer/src/js/viewer/perspective_element.js @@ -40,6 +40,14 @@ const column_sorter = schema => (a, b) => { return r; }; +function get_aggregate_defaults(schema, cols) { + const aggregates = {}; + for (const col of cols) { + aggregates[col] = get_type_config(schema[col]).aggregate; + } + return aggregates; +} + function get_aggregates_with_defaults(aggregate_attribute, schema, cols) { const found = new Set(); const aggregates = []; @@ -147,7 +155,7 @@ export class PerspectiveElement extends StateElement { shown = this._initial_col_order; } - this.set_aggregate_attribute(aggregates); + this._aggregate_defaults = get_aggregate_defaults(schema, all_cols); for (const name of all_cols) { const aggregate = aggregates.find(a => a.column === name).op; diff --git a/packages/perspective-viewer/src/js/viewer/state_element.js b/packages/perspective-viewer/src/js/viewer/state_element.js index 9b0c7a9ac3..be22026f6e 100644 --- a/packages/perspective-viewer/src/js/viewer/state_element.js +++ b/packages/perspective-viewer/src/js/viewer/state_element.js @@ -87,6 +87,21 @@ export class StateElement extends HTMLElement { get_aggregate_attribute() { const aggs = JSON.parse(this.getAttribute("aggregates")) || {}; - return Object.keys(aggs).map(col => ({column: col, op: aggs[col]})); + const found = new Set(); + const new_aggs = Object.keys(aggs).map(col => { + found.add(col); + return {column: col, op: aggs[col]}; + }); + if (this._aggregate_defaults) { + for (const column of Object.keys(this._aggregate_defaults)) { + if (!found.has(column)) { + new_aggs.push({ + column, + op: this._aggregate_defaults[column] + }); + } + } + } + return new_aggs; } } diff --git a/packages/perspective-viewer/test/js/computed_columns.spec.js b/packages/perspective-viewer/test/js/computed_columns.spec.js index ecbc80d5fd..9e6d534940 100644 --- a/packages/perspective-viewer/test/js/computed_columns.spec.js +++ b/packages/perspective-viewer/test/js/computed_columns.spec.js @@ -39,7 +39,7 @@ const add_computed_column = async page => { ); await page.waitForSelector("perspective-viewer:not([updating])"); await page.evaluate(element => { - const aggs = JSON.parse(element.getAttribute("aggregates")); + const aggs = JSON.parse(element.getAttribute("aggregates")) || {}; aggs["new_cc"] = "dominant"; element.setAttribute("aggregates", JSON.stringify(aggs)); }, viewer); From 5b7b4a4595b32969ab6b6d72cbaf3b2d6ad522db Mon Sep 17 00:00:00 2001 From: Andrew Stein Date: Mon, 2 Sep 2019 03:50:31 -0400 Subject: [PATCH 3/7] Fixed boolean attributes --- packages/perspective-viewer/src/js/computed_column.js | 4 ++-- packages/perspective-viewer/src/js/viewer.js | 2 +- packages/perspective-viewer/src/js/viewer/action_element.js | 2 +- .../perspective-viewer/src/js/viewer/perspective_element.js | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/perspective-viewer/src/js/computed_column.js b/packages/perspective-viewer/src/js/computed_column.js index 13df3b9030..f08e3eac71 100644 --- a/packages/perspective-viewer/src/js/computed_column.js +++ b/packages/perspective-viewer/src/js/computed_column.js @@ -188,7 +188,7 @@ class ComputedColumn extends HTMLElement { drop_target.classList.add("dropping"); } if (drop_target_hover && !drop_target_hover.hasAttribute("drop-target")) { - drop_target_hover.setAttribute("drop-target", "true"); + drop_target_hover.toggleAttribute("drop-target", "true"); } if (drop_target.children.length === 2) { @@ -422,7 +422,7 @@ class ComputedColumn extends HTMLElement { // save button handlers _disable_save_button() { - this._save_button.setAttribute("disabled", "true"); + this._save_button.toggleAttribute("disabled", true); } _enable_save_button() { diff --git a/packages/perspective-viewer/src/js/viewer.js b/packages/perspective-viewer/src/js/viewer.js index 10f8871b17..3b7baf1701 100755 --- a/packages/perspective-viewer/src/js/viewer.js +++ b/packages/perspective-viewer/src/js/viewer.js @@ -75,7 +75,7 @@ class PerspectiveViewer extends ActionElement { register_debug_plugin(); } - this.setAttribute("settings", true); + this.toggleAttribute("settings", true); this._register_ids(); this._register_callbacks(); diff --git a/packages/perspective-viewer/src/js/viewer/action_element.js b/packages/perspective-viewer/src/js/viewer/action_element.js index ea4ed66ebe..2718741506 100644 --- a/packages/perspective-viewer/src/js/viewer/action_element.js +++ b/packages/perspective-viewer/src/js/viewer/action_element.js @@ -32,7 +32,7 @@ export class ActionElement extends DomElement { } else { this._side_panel.style.display = "flex"; this._top_panel.style.display = "flex"; - this.setAttribute("settings", true); + this.toggleAttribute("settings", true); } this._show_config = !this._show_config; this._plugin.resize.call(this, true); diff --git a/packages/perspective-viewer/src/js/viewer/perspective_element.js b/packages/perspective-viewer/src/js/viewer/perspective_element.js index b1dce2c5a5..f6ca18de96 100644 --- a/packages/perspective-viewer/src/js/viewer/perspective_element.js +++ b/packages/perspective-viewer/src/js/viewer/perspective_element.js @@ -428,7 +428,7 @@ export class PerspectiveElement extends StateElement { } _set_updating() { - this.setAttribute("updating", true); + this.toggleAttribute("updating", true); let resolve; this._updating_promise = new Promise(_resolve => { resolve = _resolve; From 1e7b2866b851355c5c1c80e2384fef7b74cffb38 Mon Sep 17 00:00:00 2001 From: Andrew Stein Date: Mon, 2 Sep 2019 15:05:50 -0400 Subject: [PATCH 4/7] Remove empty attributes, remove default overrides in `aggregates` attribute --- docs/static/css/material.dark.css | 8 +- packages/perspective-viewer/src/js/row.js | 7 +- packages/perspective-viewer/src/js/utils.js | 13 +- packages/perspective-viewer/src/js/viewer.js | 126 ++++++++++++++---- .../src/js/viewer/action_element.js | 21 ++- .../src/js/viewer/dom_element.js | 45 +++++-- .../src/js/viewer/dragdrop.js | 14 +- .../src/js/viewer/perspective_element.js | 7 +- .../perspective-viewer/src/less/default.less | 2 +- 9 files changed, 186 insertions(+), 57 deletions(-) diff --git a/docs/static/css/material.dark.css b/docs/static/css/material.dark.css index 464b62c749..ba4c0f768a 100644 --- a/docs/static/css/material.dark.css +++ b/docs/static/css/material.dark.css @@ -15,7 +15,7 @@ * */ @import url("https://fonts.googleapis.com/css?family=Material+Icons"); -@import url("https://fonts.googleapis.com/css?family=Open+Sans"); +@import url("https://fonts.googleapis.com/css?family=Open+Sans:400,700"); @import url("https://fonts.googleapis.com/css?family=Roboto+Mono"); perspective-viewer { --interface--font-family: "Open Sans"; @@ -33,6 +33,10 @@ perspective-viewer { --column_type-string--color: #ff8888; --config_button--before: "more_vert"; --button--font-family: "Material Icons"; + --overflow_hint--before: "error_outline"; + --overflow_hint--font-family: "Material Icons"; + --overflow_hint--color: rgba(0, 0, 0, 0.2); + --overflow_hint--padding: 0 12px 0 6px; --copy_button--before: "file_copy"; --download_button--before: "save_alt"; --reset_button--before: "refresh"; @@ -42,6 +46,7 @@ perspective-viewer { --sort-order-asc--before: "arrow_upward"; --sort-order-desc--before: "arrow_downward"; --sort-order-none--before: "remove"; + --close_button--before: "close"; --sort-order-col-asc--before: "arrow_back"; --sort-order-col-desc--before: "arrow_forward"; --sort_order-padding: 0 0 0 0px; @@ -59,7 +64,6 @@ perspective-viewer { --column_type--padding: 0px 0px 0px 0px; --column_type--width: 25px; --column-selector--font-family: "Material Icons"; - --close_button--before: "close"; --active-column-selector--height: 45px; --column_selector--width: 31px; --column_selector--font-size: 16px; diff --git a/packages/perspective-viewer/src/js/row.js b/packages/perspective-viewer/src/js/row.js index 13e4a2b0a3..6a09351966 100644 --- a/packages/perspective-viewer/src/js/row.js +++ b/packages/perspective-viewer/src/js/row.js @@ -78,7 +78,12 @@ class Row extends HTMLElement { default: } if (!this.hasAttribute("aggregate")) { - this.setAttribute("aggregate", type_config.aggregate); + this.aggregate = type_config.aggregate; + } else { + this.aggregate = this.getAttribute("aggregate"); + } + if (this.hasAttribute("filter")) { + this.filter = this.getAttribute("filter"); } let filter_operand = this.shadowRoot.querySelector("#filter_operand"); diff --git a/packages/perspective-viewer/src/js/utils.js b/packages/perspective-viewer/src/js/utils.js index ee4395afd5..be1abdec7b 100644 --- a/packages/perspective-viewer/src/js/utils.js +++ b/packages/perspective-viewer/src/js/utils.js @@ -56,6 +56,9 @@ export function registerElement(templateString, styleString, proto) { const _perspective_element = class extends proto { attributeChangedCallback(name, old, value) { + if (value === null) { + value = "null"; + } if (name[0] !== "_" && old != value) { this[name] = value; } @@ -102,7 +105,7 @@ export function registerElement(templateString, styleString, proto) { if (descriptor && descriptor.set) { let old = descriptor.set; descriptor.set = function(val) { - if (this.getAttribute(key) !== val) { + if (!this.hasAttribute(key) || this.getAttribute(key) !== val) { this.setAttribute(key, val); return; } @@ -142,20 +145,18 @@ function _attribute(_default) { desc.set = function(x) { let attr = this.getAttribute(name); try { - if (x === null || x === undefined) { + if (x === null || x === undefined || x === "") { x = _default(); } if (typeof x !== "string") { x = JSON.stringify(x); } if (x !== attr) { - this.setAttribute(name, x); attr = x; - return; } attr = JSON.parse(attr); } catch (e) { - console.error(`Invalid value for attribute "${name}": ${x}`); + console.warn(`Invalid value for attribute "${name}": ${x}`); attr = _default(); } old_set.call(this, attr); @@ -163,6 +164,8 @@ function _attribute(_default) { desc.get = function() { if (this.hasAttribute(name)) { return JSON.parse(this.getAttribute(name)); + } else { + return _default(); } }; delete desc["value"]; diff --git a/packages/perspective-viewer/src/js/viewer.js b/packages/perspective-viewer/src/js/viewer.js index 3b7baf1701..6890f14fc4 100755 --- a/packages/perspective-viewer/src/js/viewer.js +++ b/packages/perspective-viewer/src/js/viewer.js @@ -39,6 +39,8 @@ polyfill({}); * @module perspective-viewer */ +const PERSISTENT_ATTRIBUTES = ["plugin", "row-pivots", "column-pivots", "aggregates", "filters", "sort", "computed-columns", "columns"]; + /** * HTMLElement class for `` custom element. This class is * not exported, so this constructor cannot be invoked in the typical manner; @@ -82,12 +84,6 @@ class PerspectiveViewer extends ActionElement { this._register_view_options(); this._register_data_attribute(); this.toggleConfig(); - - for (let attr of ["row-pivots", "column-pivots", "filters", "sort"]) { - if (!this.hasAttribute(attr)) { - this.setAttribute(attr, "[]"); - } - } } /** @@ -107,6 +103,12 @@ class PerspectiveViewer extends ActionElement { */ @array_attribute sort(sort) { + if (sort === null || sort === undefined || sort.length === 0) { + if (this.hasAttribute("sort")) { + this.removeAttribute("sort"); + } + sort = []; + } var inner = this._sort.querySelector("ul"); this._update_column_list( sort, @@ -144,6 +146,16 @@ class PerspectiveViewer extends ActionElement { */ @array_attribute columns(show) { + if (show === null || show === undefined || show.length === 0) { + if (this.hasAttribute("columns")) { + if (this._initial_col_order) { + this.setAttribute("columns", JSON.stringify(this._initial_col_order)); + } else { + this.removeAttribute("columns"); + } + } + show = (this._initial_col_order || []).slice(); + } this._update_column_view(show, true); this.dispatchEvent(new Event("perspective-config-update")); this._debounce_update(); @@ -163,6 +175,12 @@ class PerspectiveViewer extends ActionElement { */ @array_attribute "computed-columns"(computed_columns) { + if (computed_columns === null || computed_columns === undefined || computed_columns.length === 0) { + if (this.hasAttribute("computed-columns")) { + this.removeAttribute("computed-columns"); + } + computed_columns = []; + } const resolve = this._set_updating(); this._computed_column._close_computed_column(); (async () => { @@ -202,6 +220,13 @@ class PerspectiveViewer extends ActionElement { */ @json_attribute aggregates(show) { + if (show === null || show === undefined || Object.keys(show).length === 0) { + if (this.hasAttribute("aggregates")) { + this.removeAttribute("aggregates"); + } + show = {}; + } + let lis = this._get_view_dom_columns(); lis.map(x => { let agg = show[x.getAttribute("name")]; @@ -237,7 +262,13 @@ class PerspectiveViewer extends ActionElement { */ @array_attribute filters(filters) { - if (!this._updating_filter && typeof this._table !== "undefined") { + if (filters === null || filters === undefined || filters.length === 0) { + if (this.hasAttribute("filters")) { + this.removeAttribute("filters"); + } + filters = []; + } + if (!this._updating_filter) { var inner = this._filters.querySelector("ul"); this._update_column_list( filters, @@ -269,21 +300,31 @@ class PerspectiveViewer extends ActionElement { * @fires PerspectiveViewer#perspective-config-update */ set plugin(v) { + if (v === "null" || v === null || v === undefined) { + this.setAttribute("plugin", this._vis_selector.options[0].value); + return; + } + const plugin_names = Object.keys(renderers.getInstance()); - let plugin = this.getAttribute("plugin"); - if (plugin_names.indexOf(plugin) === -1) { - const guess_plugin = plugin_names.find(x => x.indexOf(plugin) > -1); - if (guess_plugin) { - console.warn(`Unknown plugin "${plugin}", using "${guess_plugin}"`); - this.setAttribute("plugin", guess_plugin); + if (this.hasAttribute("plugin")) { + let plugin = this.getAttribute("plugin"); + if (plugin_names.indexOf(plugin) === -1) { + const guess_plugin = plugin_names.find(x => x.indexOf(plugin) > -1); + if (guess_plugin) { + console.warn(`Unknown plugin "${plugin}", using "${guess_plugin}"`); + this.setAttribute("plugin", guess_plugin); + } else { + console.error(`Unknown plugin "${plugin}"`); + this.setAttribute("plugin", this._vis_selector.options[0].value); + } } else { - console.error(`Unknown plugin "${plugin}"`); + this._vis_selector.value = plugin; + this._set_row_styles(); + this._set_column_defaults(); + this.dispatchEvent(new Event("perspective-config-update")); } } else { - this._vis_selector.value = plugin; - this._set_row_styles(); - this._set_column_defaults(); - this.dispatchEvent(new Event("perspective-config-update")); + this.setAttribute("plugin", this._vis_selector.options[0].value); } } @@ -296,6 +337,13 @@ class PerspectiveViewer extends ActionElement { */ @array_attribute "column-pivots"(pivots) { + if (pivots === null || pivots === undefined || pivots.length === 0) { + if (this.hasAttribute("column-pivots")) { + this.removeAttribute("column-pivots"); + } + pivots = []; + } + var inner = this._column_pivots.querySelector("ul"); this._update_column_list(pivots, inner, pivot => this._new_row(pivot)); this.dispatchEvent(new Event("perspective-config-update")); @@ -311,6 +359,13 @@ class PerspectiveViewer extends ActionElement { */ @array_attribute "row-pivots"(pivots) { + if (pivots === null || pivots === undefined || pivots.length === 0) { + if (this.hasAttribute("row-pivots")) { + this.removeAttribute("row-pivots"); + } + pivots = []; + } + var inner = this._row_pivots.querySelector("ul"); this._update_column_list(pivots, inner, pivot => this._new_row(pivot)); this.dispatchEvent(new Event("perspective-config-update")); @@ -429,9 +484,6 @@ class PerspectiveViewer extends ActionElement { * @param {any} widget A `` instance to clone. */ clone(widget) { - if (widget.hasAttribute("index")) { - this.setAttribute("index", widget.getAttribute("index")); - } if (this._inner_drop_target) { this._inner_drop_target.innerHTML = widget._inner_drop_target.innerHTML; } @@ -474,12 +526,17 @@ class PerspectiveViewer extends ActionElement { */ save() { let obj = {}; + const cols = new Set(PERSISTENT_ATTRIBUTES); for (let key = 0; key < this.attributes.length; key++) { let attr = this.attributes[key]; - if (["id"].indexOf(attr.name) === -1) { + if (cols.has(attr.name)) { obj[attr.name] = attr.value; + cols.delete(attr.name); } } + for (const col of cols) { + obj[col] = null; + } if (this._plugin.save) { obj.plugin_config = this._plugin.save.call(this); } @@ -498,6 +555,19 @@ class PerspectiveViewer extends ActionElement { if (typeof config === "string") { config = JSON.parse(config); } + for (const key in config) { + let val = config[key]; + if (PERSISTENT_ATTRIBUTES.indexOf(val) !== -1) { + if (val !== undefined && val !== null) { + if (typeof val !== "string") { + val = JSON.stringify(val); + } + this.setAttribute(key, val); + } else { + this.removeAttribute(key); + } + } + } for (let key in config) { let val = config[key]; if (typeof val !== "string") { @@ -505,6 +575,7 @@ class PerspectiveViewer extends ActionElement { } this.setAttribute(key, val); } + if (this._plugin.restore && config.plugin_config) { this._plugin.restore.call(this, config.plugin_config); } @@ -545,13 +616,12 @@ class PerspectiveViewer extends ActionElement { * */ reset() { - this.setAttribute("row-pivots", JSON.stringify([])); - this.setAttribute("column-pivots", JSON.stringify([])); - this.setAttribute("filters", JSON.stringify([])); - this.setAttribute("sort", JSON.stringify([])); - this.removeAttribute("index"); + this.removeAttribute("row-pivots"); + this.removeAttribute("column-pivots"); + this.removeAttribute("filters"); + this.removeAttribute("sort"); if (this._initial_col_order) { - this.setAttribute("columns", JSON.stringify(this._initial_col_order || [])); + this.setAttribute("columns", JSON.stringify(this._initial_col_order)); } else { this.removeAttribute("columns"); } diff --git a/packages/perspective-viewer/src/js/viewer/action_element.js b/packages/perspective-viewer/src/js/viewer/action_element.js index 2718741506..a37f53b7fc 100644 --- a/packages/perspective-viewer/src/js/viewer/action_element.js +++ b/packages/perspective-viewer/src/js/viewer/action_element.js @@ -193,9 +193,24 @@ export class ActionElement extends DomElement { // edits state _transpose() { - let row_pivots = this.getAttribute("row-pivots"); - this.setAttribute("row-pivots", this.getAttribute("column-pivots")); - this.setAttribute("column-pivots", row_pivots); + const has_row = this.hasAttribute("row-pivots"); + const has_col = this.hasAttribute("column-pivots"); + if (has_row && has_col) { + let row_pivots = this.getAttribute("row-pivots"); + this.setAttribute("row-pivots", this.getAttribute("column-pivots")); + this.setAttribute("column-pivots", row_pivots); + } else if (has_row) { + let row_pivots = this.getAttribute("row-pivots"); + this.removeAttribute("row-pivots"); + this.setAttribute("column-pivots", row_pivots); + } else if (has_col) { + let column_pivots = this.getAttribute("column-pivots"); + this.removeAttribute("column-pivots"); + this.setAttribute("row-pivots", column_pivots); + } else { + this.removeAttribute("column-pivots"); + this.removeAttribute("row-pivots"); + } } _resize_sidepanel(event) { diff --git a/packages/perspective-viewer/src/js/viewer/dom_element.js b/packages/perspective-viewer/src/js/viewer/dom_element.js index 57f17213b2..23b317a967 100644 --- a/packages/perspective-viewer/src/js/viewer/dom_element.js +++ b/packages/perspective-viewer/src/js/viewer/dom_element.js @@ -20,17 +20,36 @@ export class DomElement extends PerspectiveElement { } set_aggregate_attribute(aggs) { - this.setAttribute( - "aggregates", - JSON.stringify( - aggs.reduce((obj, agg) => { - if (this._aggregate_defaults[agg.column] !== agg.op) { - obj[agg.column] = agg.op; - } - return obj; - }, {}) - ) - ); + let is_set = false; + let aggregates = aggs.reduce((obj, agg) => { + if (this._aggregate_defaults[agg.column] !== agg.op) { + obj[agg.column] = agg.op; + is_set = true; + } + return obj; + }, {}); + if (is_set) { + this.setAttribute("aggregates", JSON.stringify(aggregates)); + } else { + this.removeAttribute("aggregates"); + } + } + + _set_row_type(row, type) { + if (!type) { + let all = this._get_view_dom_columns("#inactive_columns perspective-row"); + if (all.length > 0) { + type = all.find(x => x.getAttribute("name") === name); + if (type) { + type = type.getAttribute("type"); + } else { + type = "integer"; + } + } else { + type = ""; + } + } + row.setAttribute("type", type); } // Generates a new row in state + DOM @@ -160,7 +179,9 @@ export class DomElement extends PerspectiveElement { } } else if (typeof name === "undefined") { container.removeChild(col); - } else if (!accessor(name, col)) { + } else if (accessor(name, col)) { + this._set_row_type(col); + } else { if (next_col && accessor(name, next_col)) { container.removeChild(col); i++; diff --git a/packages/perspective-viewer/src/js/viewer/dragdrop.js b/packages/perspective-viewer/src/js/viewer/dragdrop.js index d3ea0dd6c2..666dea9e1a 100644 --- a/packages/perspective-viewer/src/js/viewer/dragdrop.js +++ b/packages/perspective-viewer/src/js/viewer/dragdrop.js @@ -37,9 +37,15 @@ export function dragend(event) { } let idx = Array.prototype.slice.call(parent.children).indexOf(div.tagName === "PERSPECTIVE-ROW" ? div : event.target); let attr_name = parent.getAttribute("for"); - let attr_value = JSON.parse(this.getAttribute(attr_name)); - attr_value.splice(idx, 1); - this.setAttribute(attr_name, JSON.stringify(attr_value)); + if (this.hasAttribute(attr_name)) { + let attr_value = JSON.parse(this.getAttribute(attr_name)); + attr_value.splice(idx, 1); + if (attr_value.length === 0) { + this.removeAttribute(attr_name); + } else { + this.setAttribute(attr_name, JSON.stringify(attr_value)); + } + } } export function drop(ev) { @@ -118,7 +124,7 @@ export function column_dragover(event) { event.currentTarget.classList.add("dropping"); } if (!this._drop_target_hover.hasAttribute("drop-target")) { - this._drop_target_hover.setAttribute("drop-target", true); + this._drop_target_hover.toggleAttribute("drop-target", true); } let new_index = calc_index.call(this, event); let current_index = Array.prototype.slice.call(this._active_columns.children).indexOf(this._drop_target_hover); diff --git a/packages/perspective-viewer/src/js/viewer/perspective_element.js b/packages/perspective-viewer/src/js/viewer/perspective_element.js index f6ca18de96..2b82a94a53 100644 --- a/packages/perspective-viewer/src/js/viewer/perspective_element.js +++ b/packages/perspective-viewer/src/js/viewer/perspective_element.js @@ -179,7 +179,12 @@ export class PerspectiveElement extends StateElement { this._show_column_selectors(); - this.filters = this.getAttribute("filters"); + // Filters need type information to populate e.g. the operator dropdown, + // so reset them. + if (this.hasAttribute("filters")) { + this.filters = this.getAttribute("filters"); + } + await this._debounce_update({force_update: true}); resolve(); } diff --git a/packages/perspective-viewer/src/less/default.less b/packages/perspective-viewer/src/less/default.less index 789064c13a..0012c9643b 100644 --- a/packages/perspective-viewer/src/less/default.less +++ b/packages/perspective-viewer/src/less/default.less @@ -110,7 +110,7 @@ --row_draggable-height: var(--column--height, 21px); } -:host(:not([row-pivots="[]"])) { +:host([row-pivots]) { #active_columns perspective-row { height: var(--active-column--height, 41px); min-height: var(--active-column--height, 41px); From cb881c733ed39906685d02bce47d268b6748ad0c Mon Sep 17 00:00:00 2001 From: Andrew Stein Date: Mon, 2 Sep 2019 21:02:36 -0400 Subject: [PATCH 5/7] Make render warnings deterministic --- packages/perspective-viewer-d3fc/test/results/results.json | 6 +++--- .../perspective-viewer/src/js/viewer/perspective_element.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/perspective-viewer-d3fc/test/results/results.json b/packages/perspective-viewer-d3fc/test/results/results.json index 914e9c581a..7b2357e5f0 100644 --- a/packages/perspective-viewer-d3fc/test/results/results.json +++ b/packages/perspective-viewer-d3fc/test/results/results.json @@ -104,8 +104,8 @@ "heatmap.html/sorts by an alpha column.": "0baee7f2e20ce704d75d4fc59cc0a1b0", "heatmap.html/displays visible columns.": "382f28ea5e7e7f5af24ab6688884ac2b", "candlestick.html/filter to date range.": "5edaf5770c832422360b1ca8ddde01e8", - "candlestick.html/filter by a single instrument.": "f988ca6494d7a36bada09928cd1a544e", - "ohlc.html/filter by a single instrument.": "0110fac1f2befac1b97a9d33f0022acf", + "candlestick.html/filter by a single instrument.": "f15786ce7b2e831cdf8af64489a2f840", + "ohlc.html/filter by a single instrument.": "187bbbc9a9265807b9fd517c14082271", "ohlc.html/filter to date range.": "7252edbb00597bed894c89cc7b8301e7", "sunburst.html/shows a grid without any settings applied.": "e61a53b560304cfbe3a6464218910cb0", "sunburst.html/pivots by a row.": "099b036c83a9e2b50d1c59507f06ddc4", @@ -120,5 +120,5 @@ "sunburst.html/highlights invalid filter.": "e48566c24b1655a202d1d227424f71c5", "sunburst.html/sorts by an alpha column.": "dca6a09d9b0c4b4a3fd6cdb91fa1eb1a", "sunburst.html/displays visible columns.": "92769650f8ccaf823d070f3bd12aa73b", - "__GIT_COMMIT__": "0a60a1ac38467647c4a424ad0827605f680334e7" + "__GIT_COMMIT__": "7a828e3bd1081b553b02584ac18c6dc5c09e54d9" } \ No newline at end of file diff --git a/packages/perspective-viewer/src/js/viewer/perspective_element.js b/packages/perspective-viewer/src/js/viewer/perspective_element.js index 2b82a94a53..a32f308f05 100644 --- a/packages/perspective-viewer/src/js/viewer/perspective_element.js +++ b/packages/perspective-viewer/src/js/viewer/perspective_element.js @@ -261,7 +261,7 @@ export class PerspectiveElement extends StateElement { if (limit_points) { const {max_cols, max_rows} = await this.get_maxes(); if (!task.cancelled) { - this._warn_render_size_exceeded(max_cols, max_rows); + await this._warn_render_size_exceeded(max_cols, max_rows); await updater.call(this, this._datavis, this._view, task, max_cols, max_rows); } } else { @@ -366,7 +366,7 @@ export class PerspectiveElement extends StateElement { const {max_cols, max_rows} = await this.get_maxes(); if (!ignore_size_check) { - this._warn_render_size_exceeded(max_cols, max_rows); + await this._warn_render_size_exceeded(max_cols, max_rows); } const timer = this._render_time(); From ef27b1924f68224134ad7309fdc4951c83bf42be Mon Sep 17 00:00:00 2001 From: Andrew Stein Date: Mon, 2 Sep 2019 21:10:17 -0400 Subject: [PATCH 6/7] Double-click side panel resize reverts override --- packages/perspective-viewer/src/js/viewer/action_element.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/perspective-viewer/src/js/viewer/action_element.js b/packages/perspective-viewer/src/js/viewer/action_element.js index a37f53b7fc..7892217360 100644 --- a/packages/perspective-viewer/src/js/viewer/action_element.js +++ b/packages/perspective-viewer/src/js/viewer/action_element.js @@ -213,6 +213,10 @@ export class ActionElement extends DomElement { } } + _reset_sidepanel() { + this._side_panel.style.width = ""; + } + _resize_sidepanel(event) { const initial = document.body.style.cursor; document.body.style.cursor = "col-resize"; @@ -270,6 +274,7 @@ export class ActionElement extends DomElement { this._transpose_button.addEventListener("click", this._transpose.bind(this)); this._drop_target.addEventListener("dragover", dragover.bind(this)); this._resize_bar.addEventListener("mousedown", this._resize_sidepanel.bind(this)); + this._resize_bar.addEventListener("dblclick", this._reset_sidepanel.bind(this)); this._vis_selector.addEventListener("change", () => { this._plugin_information.classList.add("hidden"); From 03e48a5efdff55add94cea182183fd3c268ff2d7 Mon Sep 17 00:00:00 2001 From: Andrew Stein Date: Mon, 2 Sep 2019 21:26:40 -0400 Subject: [PATCH 7/7] Correctly deserialize `save()` token --- packages/perspective-viewer/src/js/viewer.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/perspective-viewer/src/js/viewer.js b/packages/perspective-viewer/src/js/viewer.js index 6890f14fc4..ac3dff7a6d 100755 --- a/packages/perspective-viewer/src/js/viewer.js +++ b/packages/perspective-viewer/src/js/viewer.js @@ -530,7 +530,11 @@ class PerspectiveViewer extends ActionElement { for (let key = 0; key < this.attributes.length; key++) { let attr = this.attributes[key]; if (cols.has(attr.name)) { - obj[attr.name] = attr.value; + if (attr.name !== "plugin" && attr.value !== undefined && attr.value !== null) { + obj[attr.name] = JSON.parse(attr.value); + } else { + obj[attr.name] = attr.value; + } cols.delete(attr.name); } }