94 lines
2.8 KiB
JavaScript
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');
|
|
}
|
|
}
|
|
}
|