134 lines
5.6 KiB
JavaScript
134 lines
5.6 KiB
JavaScript
// ==========================================
|
|
// Chat Switchboard — UserMenu Component
|
|
// ==========================================
|
|
// v0.25.0: Extracted from chat.html + ui-core.js.
|
|
// Reusable across all authenticated surfaces.
|
|
// Pattern: Go template partial (user-menu.html) + JS factory (this file).
|
|
//
|
|
// Usage:
|
|
// const menu = UserMenu.create({ id: 'global' });
|
|
// menu.setUser({ display_name: 'Jeff', username: 'jeff', avatar: null });
|
|
// menu.showAdmin(true);
|
|
// menu.destroy();
|
|
|
|
const UserMenu = {
|
|
...createComponentRegistry('UserMenu'),
|
|
|
|
create(opts) {
|
|
const pfx = opts.id || '';
|
|
const instance = componentMixin({
|
|
id: pfx,
|
|
btnEl: document.getElementById(pfx + 'userMenuBtn'),
|
|
avatarEl: document.getElementById(pfx + 'userAvatar'),
|
|
letterEl: document.getElementById(pfx + 'avatarLetter'),
|
|
nameEl: document.getElementById(pfx + 'userName'),
|
|
flyoutEl: document.getElementById(pfx + 'userFlyout'),
|
|
settingsEl: document.getElementById(pfx + 'menuSettings'),
|
|
adminEl: document.getElementById(pfx + 'menuAdmin'),
|
|
teamAdminEl: document.getElementById(pfx + 'menuTeamAdmin'),
|
|
debugEl: document.getElementById(pfx + 'menuDebug'),
|
|
signoutEl: document.getElementById(pfx + 'menuSignout'),
|
|
_open: false,
|
|
|
|
// ── User display ────────────────────
|
|
|
|
setUser(user) {
|
|
if (!user) return;
|
|
const name = user.display_name || user.username || 'User';
|
|
const letter = (name[0] || '?').toUpperCase();
|
|
|
|
// Avatar image or initial letter
|
|
if (this.avatarEl) {
|
|
let img = this.avatarEl.querySelector('.user-avatar-img');
|
|
if (user.avatar) {
|
|
if (this.letterEl) this.letterEl.style.display = 'none';
|
|
if (img) {
|
|
img.src = user.avatar;
|
|
} else {
|
|
img = document.createElement('img');
|
|
img.src = user.avatar;
|
|
img.alt = '';
|
|
img.className = 'user-avatar-img';
|
|
img.onerror = function() {
|
|
this.remove();
|
|
if (instance.letterEl) {
|
|
instance.letterEl.style.display = '';
|
|
instance.letterEl.textContent = letter;
|
|
}
|
|
};
|
|
this.avatarEl.insertBefore(img, this.letterEl);
|
|
}
|
|
} else {
|
|
if (img) img.remove();
|
|
if (this.letterEl) {
|
|
this.letterEl.style.display = '';
|
|
this.letterEl.textContent = letter;
|
|
}
|
|
}
|
|
}
|
|
if (this.nameEl) this.nameEl.textContent = name;
|
|
},
|
|
|
|
// ── Visibility controls ─────────────
|
|
|
|
showAdmin(show) {
|
|
if (this.adminEl) this.adminEl.style.display = show ? '' : 'none';
|
|
if (this.debugEl) this.debugEl.style.display = show ? '' : 'none';
|
|
},
|
|
|
|
showTeamAdmin(show) {
|
|
if (this.teamAdminEl) this.teamAdminEl.style.display = show ? '' : 'none';
|
|
},
|
|
|
|
// ── Flyout toggle ───────────────────
|
|
|
|
open() {
|
|
if (this.flyoutEl) this.flyoutEl.classList.add('open');
|
|
this._open = true;
|
|
},
|
|
|
|
close() {
|
|
if (this.flyoutEl) this.flyoutEl.classList.remove('open');
|
|
this._open = false;
|
|
},
|
|
|
|
toggle() {
|
|
if (this._open) this.close(); else this.open();
|
|
},
|
|
|
|
// ── Event wiring ────────────────────
|
|
|
|
/**
|
|
* Wire event handlers. Accepts callbacks for menu actions.
|
|
* Call once after create().
|
|
*/
|
|
bind(handlers) {
|
|
handlers = handlers || {};
|
|
|
|
// Toggle flyout on avatar click
|
|
this._on(this.btnEl, 'click', (e) => {
|
|
e.stopPropagation();
|
|
this.toggle();
|
|
});
|
|
|
|
// Close on outside click
|
|
this._on(document, 'click', (e) => {
|
|
if (this._open && !e.target.closest('#' + pfx + 'userMenuWrap')) {
|
|
this.close();
|
|
}
|
|
});
|
|
|
|
// Menu items
|
|
this._on(this.settingsEl, 'click', () => { this.close(); if (handlers.onSettings) handlers.onSettings(); });
|
|
this._on(this.adminEl, 'click', () => { this.close(); if (handlers.onAdmin) handlers.onAdmin(); });
|
|
this._on(this.teamAdminEl, 'click', () => { this.close(); if (handlers.onTeamAdmin) handlers.onTeamAdmin(); });
|
|
this._on(this.debugEl, 'click', () => { this.close(); if (handlers.onDebug) handlers.onDebug(); });
|
|
this._on(this.signoutEl, 'click', () => { this.close(); if (handlers.onSignout) handlers.onSignout(); });
|
|
},
|
|
}, UserMenu);
|
|
|
|
UserMenu._register(pfx, instance);
|
|
return instance;
|
|
},
|
|
};
|