MWSE/public/studio/ColumnView.js

94 lines
2.8 KiB
JavaScript

// Miller-column view: manages a horizontal row of up to 5 columns.
// Selecting an item in column N removes columns N+1…end and adds the
// new column opened by that item's onSelect callback.
import Column from '/studio/Column.js';
export default class ColumnView {
constructor(container) {
this._container = container;
this._columns = []; // Column instances in order
this._els = []; // corresponding DOM elements
this._root = null;
}
mount() {
const root = document.createElement('div');
root.className = 'mwse-studio__columns';
this._root = root;
this._container.appendChild(root);
return this;
}
// Push a new column to the right. Returns the Column.
pushColumn(title, items, opts = {}) {
const col = new Column({ title, items, ...opts });
const el = col.mount();
this._columns.push(col);
this._els.push(el);
this._root.appendChild(el);
// Mark the new column as active (rightmost).
this._els.forEach((e, i) => {
e.classList.toggle('mwse-col--active', i === this._els.length - 1);
});
// Scroll so the new column is fully visible.
requestAnimationFrame(() => {
this._root.scrollLeft = this._root.scrollWidth;
});
return col;
}
// Remove all columns from index `depth` onwards, then push `title` + `items`.
replaceFrom(depth, title, items, opts) {
this._truncateTo(depth);
return this.pushColumn(title, items, opts);
}
// Remove columns deeper than `depth` (0-based).
// Useful when the user navigates back by clicking an earlier column item.
truncateTo(depth) {
this._truncateTo(depth + 1);
}
// Pop the rightmost column.
popColumn() {
if (this._columns.length === 0) return;
const el = this._els.pop();
this._columns.pop();
el.remove();
if (this._els.length) {
this._els[this._els.length - 1].classList.add('mwse-col--active');
}
}
// Remove all columns from index `toDepth` (exclusive) onwards.
popTo(toDepth) {
while (this._columns.length > toDepth) this.popColumn();
}
// Force all columns to re-render their meta text.
refresh() {
for (const col of this._columns) col.refresh();
}
get depth() { return this._columns.length; }
col(i) { return this._columns[i] ?? null; }
// ---- Internal -------------------------------------------------------
_truncateTo(count) {
while (this._columns.length > count) {
const el = this._els.pop();
this._columns.pop();
el.remove();
}
if (this._els.length) {
this._els[this._els.length - 1].classList.add('mwse-col--active');
}
}
}