commit 04c6aa6f2b8297b62ae6c7cb450b8b15a1eba2dd
parent 21adfe40d8ff9bdcf3abcbf545e0a6feec142e10
Author: Lorenz A <me@lorenzackermann.xyz>
Date: Mon, 15 Dec 2025 14:00:40 +0000
Bug 2004256 - [devtools] Turn devtools/client/shared/widgets/TableWidget.js into an ES class. r=devtools-reviewers,nchevobbe
Differential Revision: https://phabricator.services.mozilla.com/D276390
Diffstat:
1 file changed, 326 insertions(+), 322 deletions(-)
diff --git a/devtools/client/shared/widgets/TableWidget.js b/devtools/client/shared/widgets/TableWidget.js
@@ -53,106 +53,109 @@ Object.defineProperty(this, "EVENTS", {
* A table widget with various features like resizble/toggleable columns,
* sorting, keyboard navigation etc.
*
- * @param {Node} node
- * The container element for the table widget.
- * @param {object} options
- * - initialColumns: map of key vs display name for initial columns of
- * the table. See @setupColumns for more info.
- * - uniqueId: the column which will be the unique identifier of each
- * entry in the table. Default: name.
- * - wrapTextInElements: Don't ever use 'value' attribute on labels.
- * Default: false.
- * - emptyText: Localization ID for the text to display when there are
- * no entries in the table to display.
- * - highlightUpdated: true to highlight the changed/added row.
- * - removableColumns: Whether columns are removeable. If set to false,
- * the context menu in the headers will not appear.
- * - firstColumn: key of the first column that should appear.
- * - cellContextMenuId: ID of a <menupopup> element to be set as a
- * context menu of every cell.
*/
-function TableWidget(node, options = {}) {
- EventEmitter.decorate(this);
-
- this.document = node.ownerDocument;
- this.window = this.document.defaultView;
- this._parent = node;
-
- const {
- initialColumns,
- emptyText,
- uniqueId,
- highlightUpdated,
- removableColumns,
- firstColumn,
- wrapTextInElements,
- cellContextMenuId,
- l10n,
- } = options;
- this.emptyText = emptyText || "";
- this.uniqueId = uniqueId || "name";
- this.wrapTextInElements = wrapTextInElements || false;
- this.firstColumn = firstColumn || "";
- this.highlightUpdated = highlightUpdated || false;
- this.removableColumns = removableColumns !== false;
- this.cellContextMenuId = cellContextMenuId;
- this.l10n = l10n;
-
- this.tbody = this.document.createXULElement("hbox");
- this.tbody.className = "table-widget-body theme-body";
- this.tbody.setAttribute("flex", "1");
- this.tbody.setAttribute("tabindex", "0");
- this._parent.appendChild(this.tbody);
- this.afterScroll = this.afterScroll.bind(this);
- this.tbody.addEventListener("scroll", this.onScroll.bind(this));
-
- // Prepare placeholder
- this.placeholder = this.document.createElement("div");
- this.placeholder.className = "table-widget-empty-text";
- this._parent.appendChild(this.placeholder);
- this.setPlaceholder(this.emptyText);
-
- this.items = new Map();
- this.columns = new Map();
-
- // Setup the column headers context menu to allow users to hide columns at
- // will.
- if (this.removableColumns) {
- this.onPopupCommand = this.onPopupCommand.bind(this);
- this.setupHeadersContextMenu();
- }
-
- if (initialColumns) {
- this.setColumns(initialColumns, uniqueId);
- }
-
- this.bindSelectedRow = id => {
- this.selectedRow = id;
- };
- this.on(EVENTS.ROW_SELECTED, this.bindSelectedRow);
-
- this.onChange = this.onChange.bind(this);
- this.onEditorDestroyed = this.onEditorDestroyed.bind(this);
- this.onEditorTab = this.onEditorTab.bind(this);
- this.onKeydown = this.onKeydown.bind(this);
- this.onMousedown = this.onMousedown.bind(this);
- this.onRowRemoved = this.onRowRemoved.bind(this);
-
- this.document.addEventListener("keydown", this.onKeydown);
- this.document.addEventListener("mousedown", this.onMousedown);
-}
+class TableWidget extends EventEmitter {
+ static EVENTS = EVENTS;
+
+ /**
+ * @param {Node} node
+ * The container element for the table widget.
+ * @param {object} options
+ * - initialColumns: map of key vs display name for initial columns of
+ * the table. See @setupColumns for more info.
+ * - uniqueId: the column which will be the unique identifier of each
+ * entry in the table. Default: name.
+ * - wrapTextInElements: Don't ever use 'value' attribute on labels.
+ * Default: false.
+ * - emptyText: Localization ID for the text to display when there are
+ * no entries in the table to display.
+ * - highlightUpdated: true to highlight the changed/added row.
+ * - removableColumns: Whether columns are removeable. If set to false,
+ * the context menu in the headers will not appear.
+ * - firstColumn: key of the first column that should appear.
+ * - cellContextMenuId: ID of a <menupopup> element to be set as a
+ * context menu of every cell.
+ */
+ constructor(node, options = {}) {
+ super();
+
+ this.document = node.ownerDocument;
+ this.window = this.document.defaultView;
+ this._parent = node;
+
+ const {
+ initialColumns,
+ emptyText,
+ uniqueId,
+ highlightUpdated,
+ removableColumns,
+ firstColumn,
+ wrapTextInElements,
+ cellContextMenuId,
+ l10n,
+ } = options;
+ this.emptyText = emptyText || "";
+ this.uniqueId = uniqueId || "name";
+ this.wrapTextInElements = wrapTextInElements || false;
+ this.firstColumn = firstColumn || "";
+ this.highlightUpdated = highlightUpdated || false;
+ this.removableColumns = removableColumns !== false;
+ this.cellContextMenuId = cellContextMenuId;
+ this.l10n = l10n;
+
+ this.tbody = this.document.createXULElement("hbox");
+ this.tbody.className = "table-widget-body theme-body";
+ this.tbody.setAttribute("flex", "1");
+ this.tbody.setAttribute("tabindex", "0");
+ this._parent.appendChild(this.tbody);
+ this.afterScroll = this.afterScroll.bind(this);
+ this.tbody.addEventListener("scroll", this.onScroll.bind(this));
+
+ // Prepare placeholder
+ this.placeholder = this.document.createElement("div");
+ this.placeholder.className = "table-widget-empty-text";
+ this._parent.appendChild(this.placeholder);
+ this.setPlaceholder(this.emptyText);
+
+ this.items = new Map();
+ this.columns = new Map();
+
+ // Setup the column headers context menu to allow users to hide columns at
+ // will.
+ if (this.removableColumns) {
+ this.onPopupCommand = this.onPopupCommand.bind(this);
+ this.setupHeadersContextMenu();
+ }
+
+ if (initialColumns) {
+ this.setColumns(initialColumns, uniqueId);
+ }
+
+ this.bindSelectedRow = id => {
+ this.selectedRow = id;
+ };
+ this.on(EVENTS.ROW_SELECTED, this.bindSelectedRow);
-TableWidget.prototype = {
- items: null,
- editBookmark: null,
- scrollIntoViewOnUpdate: null,
+ this.onChange = this.onChange.bind(this);
+ this.onEditorDestroyed = this.onEditorDestroyed.bind(this);
+ this.onEditorTab = this.onEditorTab.bind(this);
+ this.onKeydown = this.onKeydown.bind(this);
+ this.onMousedown = this.onMousedown.bind(this);
+ this.onRowRemoved = this.onRowRemoved.bind(this);
+ this.document.addEventListener("keydown", this.onKeydown);
+ this.document.addEventListener("mousedown", this.onMousedown);
+ }
+
+ items = null;
+ editBookmark = null;
+ scrollIntoViewOnUpdate = null;
/**
* Return true if the table body has a scrollbar.
*/
get hasScrollbar() {
return this.tbody.scrollHeight > this.tbody.clientHeight;
- },
+ }
/**
* Getter for the headers context menu popup id.
@@ -162,7 +165,7 @@ TableWidget.prototype = {
return this.menupopup.id;
}
return null;
- },
+ }
/**
* Select the row corresponding to the json object `id`
@@ -176,7 +179,7 @@ TableWidget.prototype = {
column.selectRow(null);
}
}
- },
+ }
/**
* Is a row currently selected?
@@ -189,14 +192,14 @@ TableWidget.prototype = {
this.columns.get(this.uniqueId) &&
this.columns.get(this.uniqueId).selectedRow
);
- },
+ }
/**
* Returns the json object corresponding to the selected row.
*/
get selectedRow() {
return this.items.get(this.columns.get(this.uniqueId).selectedRow);
- },
+ }
/**
* Selects the row at index `index`.
@@ -205,14 +208,14 @@ TableWidget.prototype = {
for (const column of this.columns.values()) {
column.selectRowAt(index);
}
- },
+ }
/**
* Returns the index of the selected row.
*/
get selectedIndex() {
return this.columns.get(this.uniqueId).selectedIndex;
- },
+ }
/**
* Returns the index of the selected row disregarding hidden rows.
@@ -228,7 +231,7 @@ TableWidget.prototype = {
}
return -1;
- },
+ }
/**
* Returns the first visible column.
@@ -245,7 +248,7 @@ TableWidget.prototype = {
}
return null;
- },
+ }
/**
* returns all editable columns.
@@ -273,7 +276,7 @@ TableWidget.prototype = {
const columns = this._parent.querySelectorAll(".table-widget-column");
return filter(columns);
- },
+ }
/**
* Ensures all cells in a specific row have consistent heights by synchronizing their styles.
@@ -310,7 +313,7 @@ TableWidget.prototype = {
cell.style.height = `${maxHeight}px`;
}
}
- },
+ }
/**
* Emit all cell edit events.
@@ -353,11 +356,11 @@ TableWidget.prototype = {
}
this.syncRowHeight(change.items.uniqueKey);
- },
+ }
onEditorDestroyed() {
this._editableFieldsEngine = null;
- },
+ }
/**
* Called by the inplace editor when Tab / Shift-Tab is pressed in edit-mode.
@@ -457,7 +460,7 @@ TableWidget.prototype = {
// Prevent default input tabbing behaviour
event.preventDefault();
- },
+ }
/**
* Get the cell that will be edited next on tab / shift tab and highlight the
@@ -519,7 +522,7 @@ TableWidget.prototype = {
}
return cell;
- },
+ }
/**
* Reset the editable fields engine if the currently edited row is removed.
@@ -544,7 +547,7 @@ TableWidget.prototype = {
// The target is lost so we need to hide the remove the textbox from the DOM
// and reset the target nodes.
this.onEditorTargetLost();
- },
+ }
/**
* Cancel an edit because the edit target has been lost.
@@ -557,7 +560,7 @@ TableWidget.prototype = {
}
editor.cancelEdit();
- },
+ }
/**
* Keydown event handler for the table. Used for keyboard navigation amongst
@@ -626,7 +629,7 @@ TableWidget.prototype = {
this.emit(EVENTS.ROW_SELECTED, cell.getAttribute("data-id"));
break;
}
- },
+ }
/**
* Close any editors if the area "outside the table" is clicked. In reality,
@@ -643,7 +646,7 @@ TableWidget.prototype = {
// Force any editor fields to hide due to XUL focus quirks.
this._editableFieldsEngine.blur();
- },
+ }
/**
* Make table fields editable.
@@ -688,7 +691,7 @@ TableWidget.prototype = {
this.emit(EVENTS.FIELDS_EDITABLE, this._editableFieldsEngine);
}
- },
+ }
destroy() {
this.off(EVENTS.ROW_SELECTED, this.bindSelectedRow);
@@ -709,7 +712,7 @@ TableWidget.prototype = {
this.menupopup.removeEventListener("command", this.onPopupCommand);
this.menupopup.remove();
}
- },
+ }
/**
* Sets the localization ID of the description to be shown when the table is empty.
@@ -736,7 +739,7 @@ TableWidget.prototype = {
}
this.l10n.setAttributes(this.placeholder, l10nID);
- },
+ }
/**
* Prepares the context menu for the headers of the table columns. This
@@ -755,7 +758,7 @@ TableWidget.prototype = {
this.menupopup.addEventListener("command", this.onPopupCommand);
popupset.appendChild(this.menupopup);
this.populateMenuPopup();
- },
+ }
/**
* Populates the header context menu with the names of the columns along with
@@ -792,7 +795,7 @@ TableWidget.prototype = {
if (checked.length == 2) {
checked[checked.length - 1].setAttribute("disabled", "true");
}
- },
+ }
/**
* Event handler for the `command` event on the column headers context menu
@@ -809,7 +812,7 @@ TableWidget.prototype = {
} else if (disabled.length > 1) {
disabled[disabled.length - 1].removeAttribute("disabled");
}
- },
+ }
/**
* Creates the columns in the table. Without calling this method, data cannot
@@ -877,7 +880,7 @@ TableWidget.prototype = {
this.sortedOn = sortOn;
this.sortBy(this.sortedOn);
this.populateMenuPopup(privateColumns);
- },
+ }
/**
* Returns true if the passed string or the row json object corresponds to the
@@ -889,14 +892,14 @@ TableWidget.prototype = {
}
return this.selectedRow && item == this.selectedRow[this.uniqueId];
- },
+ }
/**
* Selects the row corresponding to the `id` json.
*/
selectRow(id) {
this.selectedRow = id;
- },
+ }
/**
* Selects the next row. Cycles over to the first row if last row is selected
@@ -905,7 +908,7 @@ TableWidget.prototype = {
for (const column of this.columns.values()) {
column.selectNextRow();
}
- },
+ }
/**
* Selects the previous row. Cycles over to the last row if first row is
@@ -915,14 +918,14 @@ TableWidget.prototype = {
for (const column of this.columns.values()) {
column.selectPreviousRow();
}
- },
+ }
/**
* Clears any selected row.
*/
clearSelection() {
this.selectedIndex = -1;
- },
+ }
/**
* Adds a row into the table.
@@ -967,7 +970,7 @@ TableWidget.prototype = {
this.emit(EVENTS.ROW_EDIT, item[this.uniqueId]);
this.syncRowHeight(item[this.uniqueId]);
- },
+ }
/**
* Removes the row associated with the `item` object.
@@ -994,7 +997,7 @@ TableWidget.prototype = {
}
this.emit(EVENTS.ROW_REMOVED, item);
- },
+ }
/**
* Updates the items in the row corresponding to the `item` object previously
@@ -1019,7 +1022,7 @@ TableWidget.prototype = {
this.emit(EVENTS.ROW_UPDATED, item[this.uniqueId]);
this.emit(EVENTS.ROW_EDIT, item[this.uniqueId]);
}
- },
+ }
/**
* Removes all of the rows from the table.
@@ -1035,7 +1038,7 @@ TableWidget.prototype = {
this.selectedRow = null;
this.emit(EVENTS.TABLE_CLEARED, this);
- },
+ }
/**
* Sorts the table by a given column.
@@ -1064,7 +1067,7 @@ TableWidget.prototype = {
col.sort(sortedItems);
}
}
- },
+ }
/**
* Filters the table based on a specific value
@@ -1105,7 +1108,7 @@ TableWidget.prototype = {
}
}
this.emit(EVENTS.TABLE_FILTERED, itemsToHide);
- },
+ }
/**
* Calls the afterScroll function when the user has stopped scrolling
@@ -1113,7 +1116,7 @@ TableWidget.prototype = {
onScroll() {
clearNamedTimeout("table-scroll");
setNamedTimeout("table-scroll", AFTER_SCROLL_DELAY, this.afterScroll);
- },
+ }
/**
* Emits the "scroll-end" event when the whole table is scrolled
@@ -1124,89 +1127,88 @@ TableWidget.prototype = {
if (this.tbody.scrollTop >= 0.9 * maxScrollTop) {
this.emit("scroll-end");
}
- },
-};
-
-TableWidget.EVENTS = EVENTS;
+ }
+}
module.exports.TableWidget = TableWidget;
/**
* A single column object in the table.
- *
- * @param {TableWidget} table
- * The table object to which the column belongs.
- * @param {string} id
- * Id of the column.
- * @param {string} header
- * 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;
- this.id = id;
- this.uniqueId = table.uniqueId;
- this.wrapTextInElements = table.wrapTextInElements;
- this.table = table;
- this.cells = [];
- this.items = {};
-
- this.highlightUpdated = table.highlightUpdated;
-
- this.column = this.document.createElementNS(HTML_NS, "div");
- this.column.id = id;
- this.column.className = "table-widget-column";
- this.tbody.appendChild(this.column);
-
- this.splitter = this.document.createXULElement("splitter");
- this.splitter.className = "devtools-side-splitter";
- this.tbody.appendChild(this.splitter);
-
- this.header = this.document.createXULElement("label");
- this.header.className = "devtools-toolbar table-widget-column-header";
- this.header.setAttribute("value", header);
- this.column.appendChild(this.header);
- if (table.headersContextMenu) {
- this.header.setAttribute("context", table.headersContextMenu);
- }
- this.toggleColumn = this.toggleColumn.bind(this);
- this.table.on(EVENTS.HEADER_CONTEXT_MENU, this.toggleColumn);
-
- this.onColumnSorted = this.onColumnSorted.bind(this);
- this.table.on(EVENTS.COLUMN_SORTED, this.onColumnSorted);
-
- this.onRowUpdated = this.onRowUpdated.bind(this);
- this.table.on(EVENTS.ROW_UPDATED, this.onRowUpdated);
-
- this.onTableFiltered = this.onTableFiltered.bind(this);
- this.table.on(EVENTS.TABLE_FILTERED, this.onTableFiltered);
-
- this.onClick = this.onClick.bind(this);
- this.onMousedown = this.onMousedown.bind(this);
- this.column.addEventListener("click", this.onClick);
- this.column.addEventListener("mousedown", this.onMousedown);
-}
+class Column {
+ /**
+ * @param {TableWidget} table
+ * The table object to which the column belongs.
+ * @param {string} id
+ * Id of the column.
+ * @param {string} header
+ * The displayed string on the column's header.
+ */
+ constructor(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;
+ this.id = id;
+ this.uniqueId = table.uniqueId;
+ this.wrapTextInElements = table.wrapTextInElements;
+ this.table = table;
+ this.cells = [];
+ this.items = {};
+
+ this.highlightUpdated = table.highlightUpdated;
+
+ this.column = this.document.createElementNS(HTML_NS, "div");
+ this.column.id = id;
+ this.column.className = "table-widget-column";
+ this.tbody.appendChild(this.column);
+
+ this.splitter = this.document.createXULElement("splitter");
+ this.splitter.className = "devtools-side-splitter";
+ this.tbody.appendChild(this.splitter);
+
+ this.header = this.document.createXULElement("label");
+ this.header.className = "devtools-toolbar table-widget-column-header";
+ this.header.setAttribute("value", header);
+ this.column.appendChild(this.header);
+ if (table.headersContextMenu) {
+ this.header.setAttribute("context", table.headersContextMenu);
+ }
+ this.toggleColumn = this.toggleColumn.bind(this);
+ this.table.on(EVENTS.HEADER_CONTEXT_MENU, this.toggleColumn);
+
+ this.onColumnSorted = this.onColumnSorted.bind(this);
+ this.table.on(EVENTS.COLUMN_SORTED, this.onColumnSorted);
+
+ this.onRowUpdated = this.onRowUpdated.bind(this);
+ this.table.on(EVENTS.ROW_UPDATED, this.onRowUpdated);
+
+ this.onTableFiltered = this.onTableFiltered.bind(this);
+ this.table.on(EVENTS.TABLE_FILTERED, this.onTableFiltered);
+
+ this.onClick = this.onClick.bind(this);
+ this.onMousedown = this.onMousedown.bind(this);
+ this.column.addEventListener("click", this.onClick);
+ this.column.addEventListener("mousedown", this.onMousedown);
+ }
-Column.prototype = {
// items is a cell-id to cell-index map. It is basically a reverse map of the
// this.cells object and is used to quickly reverse lookup a cell by its id
// instead of looping through the cells array. This reverse map is not kept
// upto date in sync with the cells array as updating it is in itself a loop
// through all the cells of the columns. Thus update it on demand when it goes
// out of sync with this.cells.
- items: null,
+ items = null;
// _itemsDirty is a flag which becomes true when this.items goes out of sync
// with this.cells
- _itemsDirty: null,
+ _itemsDirty = null;
- selectedRow: null,
+ selectedRow = null;
- cells: null,
+ cells = null;
/**
* Gets whether the table is sorted on this column or not.
@@ -1216,21 +1218,21 @@ Column.prototype = {
*/
get sorted() {
return this._sortState || 0;
- },
+ }
/**
* Returns a boolean indicating whether the column is hidden.
*/
get hidden() {
return this.column.hidden;
- },
+ }
/**
* 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).
@@ -1240,7 +1242,7 @@ Column.prototype = {
*/
set private(state) {
this._private = state;
- },
+ }
/**
* Sets the sorted value
@@ -1255,7 +1257,7 @@ Column.prototype = {
);
}
this._sortState = value;
- },
+ }
/**
* Gets the selected row in the column.
@@ -1265,11 +1267,11 @@ Column.prototype = {
return -1;
}
return this.items[this.selectedRow];
- },
+ }
get cellNodes() {
return [...this.column.querySelectorAll(".table-widget-cell")];
- },
+ }
get visibleCellNodes() {
const editor = this.table._editableFieldsEngine;
@@ -1282,7 +1284,7 @@ Column.prototype = {
});
return nodes;
- },
+ }
/**
* Called when the column is sorted by.
@@ -1300,7 +1302,7 @@ Column.prototype = {
this.sorted = 2;
}
this.updateZebra();
- },
+ }
onTableFiltered(itemsToHide) {
this._updateItems();
@@ -1314,7 +1316,7 @@ Column.prototype = {
this.cells[this.items[id]].hidden = true;
}
this.updateZebra();
- },
+ }
/**
* Called when a row is updated e.g. a cell is changed. This means that
@@ -1358,7 +1360,7 @@ Column.prototype = {
}
this.updateZebra();
- },
+ }
destroy() {
this.table.off(EVENTS.COLUMN_SORTED, this.onColumnSorted);
@@ -1374,7 +1376,7 @@ Column.prototype = {
this.cells = null;
this.items = null;
this.selectedRow = null;
- },
+ }
/**
* Selects the row at the `index` index
@@ -1393,7 +1395,7 @@ Column.prototype = {
} else {
this.selectedRow = null;
}
- },
+ }
/**
* Selects the row with the object having the `uniqueId` value as `id`
@@ -1401,7 +1403,7 @@ Column.prototype = {
selectRow(id) {
this._updateItems();
this.selectRowAt(this.items[id]);
- },
+ }
/**
* Selects the next row. Cycles to first if last row is selected.
@@ -1413,7 +1415,7 @@ Column.prototype = {
index = 0;
}
this.selectRowAt(index);
- },
+ }
/**
* Selects the previous row. Cycles to last if first row is selected.
@@ -1425,7 +1427,7 @@ Column.prototype = {
index = this.cells.length - 1;
}
this.selectRowAt(index);
- },
+ }
/**
* Pushes the `item` object into the column. If this column is sorted on,
@@ -1472,7 +1474,7 @@ Column.prototype = {
this.items[item[this.uniqueId]] = this.cells.length;
return this.cells.push(new Cell(this, item)) - 1;
- },
+ }
/**
* Inserts the `item` object at the given `index` index in the table.
@@ -1484,7 +1486,7 @@ Column.prototype = {
this.items[item[this.uniqueId]] = index;
this.cells.splice(index, 0, new Cell(this, item, this.cells[index]));
this.updateZebra();
- },
+ }
/**
* Event handler for the command event coming from the header context menu.
@@ -1515,7 +1517,7 @@ Column.prototype = {
this.column.hidden = true;
this.splitter.remove();
}
- },
+ }
/**
* Removes the corresponding item from the column and hide the last visible
@@ -1534,7 +1536,7 @@ Column.prototype = {
this.cells[index].destroy();
this.cells.splice(index, 1);
delete this.items[item[this.uniqueId]];
- },
+ }
/**
* Updates the corresponding item from the column.
@@ -1548,7 +1550,7 @@ Column.prototype = {
}
this.cells[index].value = item[this.id];
- },
+ }
/**
* Updates the `this.items` cell-id vs cell-index map to be in sync with
@@ -1562,7 +1564,7 @@ Column.prototype = {
this.items[this.cells[i].id] = i;
}
this._itemsDirty = false;
- },
+ }
/**
* Clears the current column
@@ -1574,7 +1576,7 @@ Column.prototype = {
while (this.header.nextSibling) {
this.header.nextSibling.remove();
}
- },
+ }
/**
* Sorts the given items and returns the sorted list if the table was sorted
@@ -1629,7 +1631,7 @@ Column.prototype = {
this._itemsDirty = false;
this.updateZebra();
return items;
- },
+ }
updateZebra() {
this._updateItems();
@@ -1642,7 +1644,7 @@ Column.prototype = {
const even = !(i % 2);
cell.classList.toggle("even", even);
}
- },
+ }
/**
* Click event handler for the column. Used to detect click on header for
@@ -1679,7 +1681,7 @@ Column.prototype = {
}
}
}
- },
+ }
/**
* Mousedown event handler for the column. Used to select rows.
@@ -1703,66 +1705,67 @@ Column.prototype = {
const dataid = closest.getAttribute("data-id");
this.table.emit(EVENTS.ROW_SELECTED, dataid);
}
- },
-};
+ }
+}
/**
* A single cell in a column
- *
- * @param {Column} column
- * The column object to which the cell belongs.
- * @param {object} item
- * The object representing the row. It contains a key value pair
- * representing the column id and its associated value. The value
- * can be a DOMNode that is appended or a string value.
- * @param {Cell} nextCell
- * The cell object which is next to this cell. null if this cell is last
- * cell of the column
*/
-function Cell(column, item, nextCell) {
- const document = column.document;
-
- this.wrapTextInElements = column.wrapTextInElements;
- this.label = document.createXULElement("label");
- this.label.setAttribute("crop", "end");
- this.label.className = "table-widget-cell";
-
- if (nextCell) {
- column.column.insertBefore(this.label, nextCell.label);
- } else {
- column.column.appendChild(this.label);
- }
-
- if (column.table.cellContextMenuId) {
- this.label.setAttribute("context", column.table.cellContextMenuId);
- this.label.addEventListener("contextmenu", () => {
- // Make the ID of the clicked cell available as a property on the table.
- // It's then available for the popupshowing or command handler.
- column.table.contextMenuRowId = this.id;
- });
- }
+class Cell {
+ /**
+ * @param {Column} column
+ * The column object to which the cell belongs.
+ * @param {object} item
+ * The object representing the row. It contains a key value pair
+ * representing the column id and its associated value. The value
+ * can be a DOMNode that is appended or a string value.
+ * @param {Cell} nextCell
+ * The cell object which is next to this cell. null if this cell is last
+ * cell of the column
+ */
+ constructor(column, item, nextCell) {
+ const document = column.document;
+
+ this.wrapTextInElements = column.wrapTextInElements;
+ this.label = document.createXULElement("label");
+ this.label.setAttribute("crop", "end");
+ this.label.className = "table-widget-cell";
+
+ if (nextCell) {
+ column.column.insertBefore(this.label, nextCell.label);
+ } else {
+ column.column.appendChild(this.label);
+ }
- this.value = item[column.id];
- this.id = item[column.uniqueId];
-}
+ if (column.table.cellContextMenuId) {
+ this.label.setAttribute("context", column.table.cellContextMenuId);
+ this.label.addEventListener("contextmenu", () => {
+ // Make the ID of the clicked cell available as a property on the table.
+ // It's then available for the popupshowing or command handler.
+ column.table.contextMenuRowId = this.id;
+ });
+ }
+
+ this.value = item[column.id];
+ this.id = item[column.uniqueId];
+ }
-Cell.prototype = {
set id(value) {
this._id = value;
this.label.setAttribute("data-id", value);
- },
+ }
get id() {
return this._id;
- },
+ }
get hidden() {
return this.label.hidden;
- },
+ }
set hidden(value) {
this.label.hidden = value;
- },
+ }
set value(value) {
this._value = value;
@@ -1783,15 +1786,15 @@ Cell.prototype = {
} else {
this.label.setAttribute("value", value + "");
}
- },
+ }
get value() {
return this._value;
- },
+ }
get classList() {
return this.label.classList;
- },
+ }
/**
* Flashes the cell for a brief time. This when done for with cells in all
@@ -1810,78 +1813,79 @@ Cell.prototype = {
};
this.label.addEventListener("animationend", onAnimEnd);
this.label.classList.add("flash-out");
- },
+ }
focus() {
this.label.focus();
- },
+ }
scrollIntoView() {
this.label.scrollIntoView(false);
- },
+ }
destroy() {
this.label.remove();
this.label = null;
- },
-};
+ }
+}
/**
* Simple widget to make nodes matching a CSS selector editable.
- *
- * @param {object} options
- * An object with the following format:
- * {
- * // The node that will act as a container for the editor e.g. a
- * // div or table.
- * root: someNode,
- *
- * // The onTab event to be handled by the caller.
- * onTab: function(event) { ... }
- *
- * // Optional event used to trigger the editor. By default this is
- * // dblclick.
- * onTriggerEvent: "dblclick",
- *
- * // Array or comma separated string of CSS Selectors matching
- * // elements that are to be made editable.
- * selectors: [
- * "#name .table-widget-cell",
- * "#value .table-widget-cell"
- * ]
- * }
*/
-function EditableFieldsEngine(options) {
- EventEmitter.decorate(this);
+class EditableFieldsEngine extends EventEmitter {
+ /**
+ * @param {object} options
+ * An object with the following format:
+ * {
+ * // The node that will act as a container for the editor e.g. a
+ * // div or table.
+ * root: someNode,
+ *
+ * // The onTab event to be handled by the caller.
+ * onTab: function(event) { ... }
+ *
+ * // Optional event used to trigger the editor. By default this is
+ * // dblclick.
+ * onTriggerEvent: "dblclick",
+ *
+ * // Array or comma separated string of CSS Selectors matching
+ * // elements that are to be made editable.
+ * selectors: [
+ * "#name .table-widget-cell",
+ * "#value .table-widget-cell"
+ * ]
+ * }
+ */
+ constructor(options) {
+ super();
- if (!Array.isArray(options.selectors)) {
- options.selectors = [options.selectors];
- }
+ if (!Array.isArray(options.selectors)) {
+ options.selectors = [options.selectors];
+ }
- this.root = options.root;
- this.selectors = options.selectors;
- this.onTab = options.onTab;
- this.onTriggerEvent = options.onTriggerEvent || "dblclick";
- this.items = options.items;
+ this.root = options.root;
+ this.selectors = options.selectors;
+ this.onTab = options.onTab;
+ this.onTriggerEvent = options.onTriggerEvent || "dblclick";
+ this.items = options.items;
- this.edit = this.edit.bind(this);
- this.cancelEdit = this.cancelEdit.bind(this);
- this.destroy = this.destroy.bind(this);
+ this.edit = this.edit.bind(this);
+ this.cancelEdit = this.cancelEdit.bind(this);
+ this.destroy = this.destroy.bind(this);
- this.onTrigger = this.onTrigger.bind(this);
- this.root.addEventListener(this.onTriggerEvent, this.onTrigger);
-}
+ this.onTrigger = this.onTrigger.bind(this);
+ this.root.addEventListener(this.onTriggerEvent, this.onTrigger);
+ }
-EditableFieldsEngine.prototype = {
- INPUT_ID: "inlineEditor",
+ INPUT_ID = "inlineEditor";
get changePending() {
return this.isEditing && this.textbox.value !== this.currentValue;
- },
+ }
get isEditing() {
return this.root && !this.textbox.hidden;
- },
+ }
get textbox() {
if (!this._textbox) {
@@ -1897,7 +1901,7 @@ EditableFieldsEngine.prototype = {
}
return this._textbox;
- },
+ }
/**
* Called when a trigger event is detected (default is dblclick).
@@ -1907,7 +1911,7 @@ EditableFieldsEngine.prototype = {
*/
onTrigger({ target }) {
this.edit(target);
- },
+ }
/**
* Handle keydowns when in edit mode:
@@ -1938,7 +1942,7 @@ EditableFieldsEngine.prototype = {
}
break;
}
- },
+ }
/**
* Overlay the target node with an edit field.
@@ -1981,7 +1985,7 @@ EditableFieldsEngine.prototype = {
this.textbox.focus();
this.textbox.select();
- },
+ }
completeEdit() {
if (!this.isEditing) {
@@ -2012,7 +2016,7 @@ EditableFieldsEngine.prototype = {
this.emit("change", data);
}
- },
+ }
/**
* Cancel an edit.
@@ -2026,7 +2030,7 @@ EditableFieldsEngine.prototype = {
}
this.textbox.hidden = true;
- },
+ }
/**
* Stop edit mode and apply changes.
@@ -2035,7 +2039,7 @@ EditableFieldsEngine.prototype = {
if (this.isEditing) {
this.completeEdit();
}
- },
+ }
/**
* Copies various styles from one node to another.
@@ -2070,7 +2074,7 @@ EditableFieldsEngine.prototype = {
// We need to set the label width to 100% to work around a XUL flex bug.
destination.style.width = "100%";
- },
+ }
/**
* Destroys all editors in the current document.
@@ -2090,5 +2094,5 @@ EditableFieldsEngine.prototype = {
this.currentTarget = this.currentValue = null;
this.emit("destroyed");
- },
-};
+ }
+}