// Copyright Epic Games, Inc. All Rights Reserved. "use strict"; import { Component } from "./component.js" import { Friendly } from "../util/friendly.js" //////////////////////////////////////////////////////////////////////////////// class Widget extends Component { } //////////////////////////////////////////////////////////////////////////////// class TableCell extends Widget { constructor(element, row) { super(element); this._row = row; } get_table() { return this.get_row().get_table(); } get_row() { return this._row; } } //////////////////////////////////////////////////////////////////////////////// class TableRow extends Widget { constructor(element, table, index, cells) { super(element); this._table = table; this._index = index; this._cells = cells; } *[Symbol.iterator]() { for (var cell of this._cells) yield cell; } get_table() { return this._table; } get_index() { return this._index; } get_cell(index) { return this._cells.at(index); } } //////////////////////////////////////////////////////////////////////////////// export class Table extends Widget { static Flag_EvenSpacing = 1 << 0; static Flag_PackRight = 1 << 1; static Flag_BiasLeft = 1 << 2; static Flag_FitLeft = 1 << 3; constructor(parent, column_names, flags=Table.Flag_EvenSpacing, index_base=0) { var root = parent.tag().classify("zen_table"); super(root); const column_width = 0 | (100 / column_names.length); var column_style; if (flags & Table.Flag_FitLeft) column_style = "max-content"; else if (column_names.length == 1) column_style = "1fr"; else if (flags & Table.Flag_BiasLeft) column_style = `minmax(${column_width * 2}%, 1fr)`; else column_style = `minmax(${column_width}%, 1fr)`; for (var i = 1; i < column_names.length; ++i) { const style = (flags & Table.Flag_PackRight) ? " auto" : " 1fr"; column_style += style; } if (index_base >= 0) { column_names = ["#", ...column_names]; column_style = "max-content " + column_style; } root.style("gridTemplateColumns", column_style); this._add_row(column_names, false); this._index = index_base; this._num_columns = column_names.length; this._rows = []; } *[Symbol.iterator]() { for (var row of this._rows) yield row; } get_row(index) { return this._rows.at(index); } _add_row(cells, indexed=true) { var index = -1; if (indexed && this._index >= 0) { index = this._index++; cells = [index, ...cells]; } cells = cells.slice(0, this._num_columns); while (cells.length < this._num_columns) cells.push(""); var ret = []; var row = this.tag(); row = new TableRow(row, this, index, ret); for (const cell of cells) { var leaf = row.tag().text(cell); ret.push(new TableCell(leaf, row)); } if (this._index >= 0) ret.shift(); return row; } add_row(...args) { var row = this._add_row(args); this._rows.push(row); return row; } clear(index=0) { const elem = this._element; elem.replaceChildren(elem.firstElementChild); this._index = (this._index >= 0) ? index : -1; this._rows = []; } } //////////////////////////////////////////////////////////////////////////////// export class PropTable extends Table { constructor(parent) { super(parent, ["prop", "value"], Table.Flag_FitLeft, -1); this.classify("zen_proptable"); } add_property(key, value) { return this.add_row(key, value); } add_object(object, friendly=false, prec=2) { const impl = (node, prefix="") => { for (const key in node) { var value = node[key]; if (value instanceof Object && (value.constructor.name == "Object" || value.constructor.name == "Array")) { impl(value, prefix + key + "."); continue; } if (friendly && ((typeof value == "number") || (typeof value == "bigint"))) { if (key.indexOf("memory") >= 0) value = Friendly.kib(value); else if (key.indexOf("disk") >= 0) value = Friendly.kib(value); else if (value > 100000) value = Friendly.k(value); else if (value % 1) value = Friendly.sep(value, 3); else value = Friendly.sep(value, 0); } this.add_property(prefix + key, value); } }; return impl(object); } filter(...needles) { for (var row of this) row.retag("div"); if (needles.length == 0) return; for (var row of this) { var hide = false; var cell = row.get_cell(0); for (var needle of needles) hide = hide || (cell.inner().innerHTML.indexOf(needle) < 0); if (hide) row.retag("hidden"); } } } //////////////////////////////////////////////////////////////////////////////// export class Toolbar extends Widget { static Side = class extends Widget { add(name, tag="div") { return this.tag(tag).text(name); } sep() { return this.tag().text("|").classify("zen_toolbar_sep"); } } constructor(parent, inline=false) { var root = parent.tag().classify("zen_toolbar"); super(root); if (inline) root.classify("zen_toolbar_inline"); this._left = new Toolbar.Side(root.tag()); this._right = new Toolbar.Side(root.tag()); } left() { return this._left; } right() { return this._right; } } //////////////////////////////////////////////////////////////////////////////// export class ProgressBar extends Widget { constructor(parent) { const root = parent.tag().classify("zen_progressbar"); super(root); this._label = root.tag(); root.tag(); // bg this._bar = root.tag(); } set_progress(what, count=0, end=1) { const percent = (((count * 100) / end) | 0).toString() + "%"; this._bar.style("width", percent); this._label.text(`${what}... ${count}/${end} (${percent})`); } } //////////////////////////////////////////////////////////////////////////////// export class WidgetHost { constructor(parent, depth=1) { this._parent = parent; this._depth = depth; } add_section(name) { var node = this._parent.tag(); if (this._depth == 1) node.classify("zen_sector"); node.tag("h" + this._depth).text(name); return new WidgetHost(node, this._depth + 1); } add_widget(type, ...args) { if (!(type.prototype instanceof Widget)) throw Error("Incorrect widget type"); return new type(this._parent, ...args); } tag(...args) { return this._parent.tag(...args); } }