diff options
Diffstat (limited to 'devtools/client/shared/widgets')
-rw-r--r-- | devtools/client/shared/widgets/Chart.jsm | 25 | ||||
-rw-r--r-- | devtools/client/shared/widgets/TableWidget.js | 142 | ||||
-rw-r--r-- | devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js | 11 |
3 files changed, 145 insertions, 33 deletions
diff --git a/devtools/client/shared/widgets/Chart.jsm b/devtools/client/shared/widgets/Chart.jsm index 0894a62ca..0b7cb71fb 100644 --- a/devtools/client/shared/widgets/Chart.jsm +++ b/devtools/client/shared/widgets/Chart.jsm @@ -105,7 +105,7 @@ function PieTableChart(node, pie, table) { * - "mouseout", when the mouse leaves a slice or a row * - "click", when the mouse enters a slice or a row */ -function createPieTableChart(document, { title, diameter, data, strings, totals, sorted }) { +function createPieTableChart(document, { title, diameter, data, strings, totals, sorted, header }) { if (data && sorted) { data = data.slice().sort((a, b) => +(a.size < b.size)); } @@ -119,7 +119,8 @@ function createPieTableChart(document, { title, diameter, data, strings, totals, title: title, data: data, strings: strings, - totals: totals + totals: totals, + header: header, }); let container = document.createElement("hbox"); @@ -338,7 +339,7 @@ function createPieChart(document, { data, width, height, centerX, centerY, radiu * - "mouseout", when the mouse leaves a row * - "click", when the mouse clicks a row */ -function createTableChart(document, { title, data, strings, totals }) { +function createTableChart(document, { title, data, strings, totals, header }) { strings = strings || {}; totals = totals || {}; let isPlaceholder = false; @@ -371,6 +372,24 @@ function createTableChart(document, { title, data, strings, totals }) { tableNode.className = "plain table-chart-grid"; container.appendChild(tableNode); + const headerNode = document.createElement("div"); + headerNode.className = "table-chart-row"; + + const headerBoxNode = document.createElement("div"); + headerBoxNode.className = "table-chart-row-box"; + headerNode.appendChild(headerBoxNode); + + for (let [key, value] of Object.entries(header)) { + let headerLabelNode = document.createElement("span"); + headerLabelNode.className = "plain table-chart-row-label"; + headerLabelNode.setAttribute("name", key); + headerLabelNode.textContent = value; + + headerNode.appendChild(headerLabelNode); + } + + tableNode.appendChild(headerNode); + for (let rowInfo of data) { let rowNode = document.createElement("hbox"); rowNode.className = "table-chart-row"; diff --git a/devtools/client/shared/widgets/TableWidget.js b/devtools/client/shared/widgets/TableWidget.js index 5dacd1b67..57d2914d5 100644 --- a/devtools/client/shared/widgets/TableWidget.js +++ b/devtools/client/shared/widgets/TableWidget.js @@ -8,6 +8,8 @@ loader.lazyRequireGetter(this, "setNamedTimeout", "devtools/client/shared/widgets/view-helpers", true); loader.lazyRequireGetter(this, "clearNamedTimeout", "devtools/client/shared/widgets/view-helpers", true); +loader.lazyRequireGetter(this, "naturalSortCaseInsensitive", + "devtools/client/shared/natural-sort", true); const {KeyCodes} = require("devtools/client/shared/keycodes"); const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; @@ -123,6 +125,8 @@ function TableWidget(node, options = {}) { TableWidget.prototype = { items: null, + editBookmark: null, + scrollIntoViewOnUpdate: null, /** * Getter for the headers context menu popup id. @@ -139,7 +143,12 @@ TableWidget.prototype = { */ set selectedRow(id) { for (let column of this.columns.values()) { - column.selectRow(id[this.uniqueId] || id); + if (id) { + column.selectRow(id[this.uniqueId] || id); + } else { + column.selectedRow = null; + column.selectRow(null); + } } }, @@ -454,7 +463,14 @@ TableWidget.prototype = { return; } - let selectedCell = this.tbody.querySelector(".theme-selected"); + // We need to get the first *visible* selected cell. Some columns are hidden + // e.g. because they contain a unique compound key for cookies that is never + // displayed in the UI. To do this we get all selected cells and filter out + // any that are hidden. + let selectedCells = [...this.tbody.querySelectorAll(".theme-selected")] + .filter(cell => cell.clientWidth > 0); + // Select the first visible selected cell. + let selectedCell = selectedCells[0]; if (!selectedCell) { return; } @@ -615,8 +631,13 @@ TableWidget.prototype = { /** * Populates the header context menu with the names of the columns along with * displaying which columns are hidden or visible. + * + * @param {Array} privateColumns=[] + * An array of column names that should never appear in the table. This + * allows us to e.g. have an invisible compound primary key for a + * table's rows. */ - populateMenuPopup: function () { + populateMenuPopup: function (privateColumns = []) { if (!this.menupopup) { return; } @@ -626,6 +647,10 @@ TableWidget.prototype = { } for (let column of this.columns.values()) { + if (privateColumns.includes(column.id)) { + continue; + } + let menuitem = this.document.createElementNS(XUL_NS, "menuitem"); menuitem.setAttribute("label", column.header.getAttribute("value")); menuitem.setAttribute("data-id", column.id); @@ -663,16 +688,21 @@ TableWidget.prototype = { * Creates the columns in the table. Without calling this method, data cannot * be inserted into the table unless `initialColumns` was supplied. * - * @param {object} columns + * @param {Object} columns * A key value pair representing the columns of the table. Where the * key represents the id of the column and the value is the displayed * label in the header of the column. - * @param {string} sortOn + * @param {String} sortOn * The id of the column on which the table will be initially sorted on. - * @param {array} hiddenColumns + * @param {Array} hiddenColumns * Ids of all the columns that are hidden by default. + * @param {Array} privateColumns=[] + * An array of column names that should never appear in the table. This + * allows us to e.g. have an invisible compound primary key for a + * table's rows. */ - setColumns: function (columns, sortOn = this.sortedOn, hiddenColumns = []) { + setColumns: function (columns, sortOn = this.sortedOn, hiddenColumns = [], + privateColumns = []) { for (let column of this.columns.values()) { column.destroy(); } @@ -702,13 +732,18 @@ TableWidget.prototype = { } this.columns.set(id, new Column(this, id, columns[id])); - if (hiddenColumns.indexOf(id) > -1) { + if (hiddenColumns.includes(id) || privateColumns.includes(id)) { + // Hide the column. this.columns.get(id).toggleColumn(); + + if (privateColumns.includes(id)) { + this.columns.get(id).private = true; + } } } this.sortedOn = sortOn; this.sortBy(this.sortedOn); - this.populateMenuPopup(); + this.populateMenuPopup(privateColumns); }, /** @@ -778,6 +813,11 @@ TableWidget.prototype = { return; } + if (this.editBookmark && !this.items.has(this.editBookmark)) { + // Key has been updated... update bookmark. + this.editBookmark = item[this.uniqueId]; + } + let index = this.columns.get(this.sortedOn).push(item); for (let [key, column] of this.columns) { if (key != this.sortedOn) { @@ -814,7 +854,8 @@ TableWidget.prototype = { column.remove(item); column.updateZebra(); } - if (this.items.size == 0) { + if (this.items.size === 0) { + this.selectedRow = null; this.tbody.setAttribute("empty", "empty"); } @@ -857,6 +898,8 @@ TableWidget.prototype = { this.tbody.setAttribute("empty", "empty"); this.setPlaceholderText(this.emptyText); + this.selectedRow = null; + this.emit(EVENTS.TABLE_CLEARED, this); }, @@ -958,6 +1001,9 @@ module.exports.TableWidget = TableWidget; * The displayed string on the column's header. */ function Column(table, id, header) { + // By default cells are visible in the UI. + this._private = false; + this.tbody = table.tbody; this.document = table.document; this.window = table.window; @@ -1041,6 +1087,23 @@ Column.prototype = { }, /** + * Get the private state of the column (visibility in the UI). + */ + get private() { + return this._private; + }, + + /** + * Set the private state of the column (visibility in the UI). + * + * @param {Boolean} state + * Private (true or false) + */ + set private(state) { + this._private = state; + }, + + /** * Sets the sorted value */ set sorted(value) { @@ -1115,7 +1178,9 @@ Column.prototype = { }, /** - * Called when a row is updated. + * Called when a row is updated e.g. a cell is changed. This means that + * for a new row this method will be called once for each column. If a single + * cell is changed this method will be called just once. * * @param {string} event * The event name of the event. i.e. EVENTS.ROW_UPDATED @@ -1124,7 +1189,23 @@ Column.prototype = { */ onRowUpdated: function (event, id) { this._updateItems(); + if (this.highlightUpdated && this.items[id] != null) { + if (this.table.scrollIntoViewOnUpdate) { + let cell = this.cells[this.items[id]]; + + // When a new row is created this method is called once for each column + // as each cell is updated. We can only scroll to cells if they are + // visible. We check for visibility and once we find the first visible + // cell in a row we scroll it into view and reset the + // scrollIntoViewOnUpdate flag. + if (cell.label.clientHeight > 0) { + cell.scrollIntoView(); + + this.table.scrollIntoViewOnUpdate = null; + } + } + if (this.table.editBookmark) { // A rows position in the table can change as the result of an edit. In // order to ensure that the correct row is highlighted after an edit we @@ -1136,6 +1217,7 @@ Column.prototype = { this.cells[this.items[id]].flash(); } + this.updateZebra(); }, @@ -1160,15 +1242,16 @@ Column.prototype = { */ selectRowAt: function (index) { if (this.selectedRow != null) { - this.cells[this.items[this.selectedRow]].toggleClass("theme-selected"); + this.cells[this.items[this.selectedRow]].classList.remove("theme-selected"); } - if (index < 0) { + + let cell = this.cells[index]; + if (cell) { + cell.classList.add("theme-selected"); + this.selectedRow = cell.id; + } else { this.selectedRow = null; - return; } - let cell = this.cells[index]; - cell.toggleClass("theme-selected"); - this.selectedRow = cell.id; }, /** @@ -1218,11 +1301,11 @@ Column.prototype = { let index; if (this.sorted == 1) { index = this.cells.findIndex(element => { - return value < element.value; + return naturalSortCaseInsensitive(value, element.value) === -1; }); } else { index = this.cells.findIndex(element => { - return value > element.value; + return naturalSortCaseInsensitive(value, element.value) === 1; }); } index = index >= 0 ? index : this.cells.length; @@ -1332,7 +1415,6 @@ Column.prototype = { this.cells = []; this.items = {}; this._itemsDirty = false; - this.selectedRow = null; while (this.header.nextSibling) { this.header.nextSibling.remove(); } @@ -1350,7 +1432,7 @@ Column.prototype = { a[this.id].textContent : a[this.id]; let val2 = (b[this.id] instanceof Node) ? b[this.id].textContent : b[this.id]; - return val1 > val2; + return naturalSortCaseInsensitive(val1, val2); }); } else if (this.sorted > 1) { items.sort((a, b) => { @@ -1358,12 +1440,12 @@ Column.prototype = { a[this.id].textContent : a[this.id]; let val2 = (b[this.id] instanceof Node) ? b[this.id].textContent : b[this.id]; - return val2 > val1; + return naturalSortCaseInsensitive(val2, val1); }); } if (this.selectedRow) { - this.cells[this.items[this.selectedRow]].toggleClass("theme-selected"); + this.cells[this.items[this.selectedRow]].classList.remove("theme-selected"); } this.items = {}; // Otherwise, just use the sorted array passed to update the cells value. @@ -1373,7 +1455,7 @@ Column.prototype = { this.cells[i].id = item[this.uniqueId]; }); if (this.selectedRow) { - this.cells[this.items[this.selectedRow]].toggleClass("theme-selected"); + this.cells[this.items[this.selectedRow]].classList.add("theme-selected"); } this._itemsDirty = false; this.updateZebra(); @@ -1387,7 +1469,9 @@ Column.prototype = { if (!cell.hidden) { i++; } - cell.toggleClass("even", !(i % 2)); + + let even = !(i % 2); + cell.classList.toggle("even", even); } }, @@ -1523,8 +1607,8 @@ Cell.prototype = { return this._value; }, - toggleClass: function (className, condition) { - this.label.classList.toggle(className, condition); + get classList() { + return this.label.classList; }, /** @@ -1550,6 +1634,10 @@ Cell.prototype = { this.label.focus(); }, + scrollIntoView: function () { + this.label.scrollIntoView(false); + }, + destroy: function () { this.label.remove(); this.label = null; diff --git a/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js b/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js index bf211b8b9..6a18ec12c 100644 --- a/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js +++ b/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js @@ -28,8 +28,12 @@ const XHTML_NS = "http://www.w3.org/1999/xhtml"; * inline editor. * @param {InspectorPanel} inspector * The inspector panel, needed for the eyedropper. + * @param {Function} supportsCssColor4ColorFunction + * A function for checking the supporting of css-color-4 color function. */ -function SwatchColorPickerTooltip(document, inspector) { +function SwatchColorPickerTooltip(document, + inspector, + {supportsCssColor4ColorFunction}) { let stylesheet = "chrome://devtools/content/shared/widgets/spectrum.css"; SwatchBasedEditorTooltip.call(this, document, stylesheet); @@ -40,6 +44,7 @@ function SwatchColorPickerTooltip(document, inspector) { this.spectrum = this.setColorPickerContent([0, 0, 0, 1]); this._onSpectrumColorChange = this._onSpectrumColorChange.bind(this); this._openEyeDropper = this._openEyeDropper.bind(this); + this.cssColor4 = supportsCssColor4ColorFunction(); } SwatchColorPickerTooltip.prototype = Heritage.extend(SwatchBasedEditorTooltip.prototype, { @@ -159,14 +164,14 @@ SwatchColorPickerTooltip.prototype = Heritage.extend(SwatchBasedEditorTooltip.pr }, _colorToRgba: function (color) { - color = new colorUtils.CssColor(color); + color = new colorUtils.CssColor(color, this.cssColor4); let rgba = color._getRGBATuple(); return [rgba.r, rgba.g, rgba.b, rgba.a]; }, _toDefaultType: function (color) { let colorObj = new colorUtils.CssColor(color); - colorObj.setAuthoredUnitFromColor(this._originalColor); + colorObj.setAuthoredUnitFromColor(this._originalColor, this.cssColor4); return colorObj.toString(); }, |