33356ca978
state.js's currentTeam() was hard-coded to teams[0] and never really meant "the team currently selected" — team.js's settings page and queue.js's filter chips each kept their own separate, unsynchronized notion of "which team" instead, so picking one on one page had no effect on the other. Replaces both with a single state.selectedTeamID, set only through the new setSelectedTeam (persisted in localStorage, unlike the queue's old per-tab sessionStorage filter) and broadcast to listeners via onTeamChange. A new teamselector.js control — a coloured dot plus the team's name, or "All teams" — sits at the top of both the desktop sidebar and the mobile topbar, opening the existing bottom-sheet menu to switch. Shown only once someone is in more than one team, matching every other team-aware control in this app. Colours come from a new teamColorClass() in format.js, hashing a team's id into the six-colour rc1..rc6 palette already used for the rota's per-person chips, so no schema or API change is needed. The queue's team filter chips pick up the same colours.
64 lines
2.6 KiB
JavaScript
64 lines
2.6 KiB
JavaScript
// The global team selector: a small control, once per layout (the desktop
|
|
// sidebar and the mobile topbar each have their own button in index.html),
|
|
// showing the current team's colour and name — or "All teams" — and opening a
|
|
// sheet to switch. Shown only once there is more than one team to choose
|
|
// between, the same rule every other team-aware control in this app follows;
|
|
// see state.js's currentTeam() for why nobody with just one ever has to.
|
|
|
|
import { h, openSheet, closeSheet } from './ui.js';
|
|
import { state, currentTeam, setSelectedTeam, onTeamChange } from './state.js';
|
|
import { teamColorClass } from './format.js';
|
|
|
|
// Not a real team id (ids are positive), so it can never collide with one —
|
|
// the value closeSheet resolves with for "All teams", distinct from the null
|
|
// a dismissed sheet resolves with.
|
|
const ALL_TEAMS = '__all__';
|
|
|
|
const buttons = () => [
|
|
document.getElementById('team-selector'),
|
|
document.getElementById('team-selector-mobile'),
|
|
].filter(Boolean);
|
|
|
|
// init wires the buttons once, at boot. render (below) is what actually fills
|
|
// them in and is called again by state.js whenever the selection changes.
|
|
export function init() {
|
|
for (const btn of buttons()) btn.addEventListener('click', open);
|
|
onTeamChange(render);
|
|
}
|
|
|
|
export function render() {
|
|
const multiTeam = (state.teams || []).length > 1;
|
|
const team = currentTeam();
|
|
const label = team ? team.name : 'All teams';
|
|
const dotClass = team ? `team-dot ${teamColorClass(team.id)}` : 'team-dot';
|
|
for (const btn of buttons()) {
|
|
btn.hidden = !multiTeam;
|
|
btn.replaceChildren(
|
|
h('span', { class: dotClass }),
|
|
h('span', { class: 'team-selector-label', text: label }),
|
|
);
|
|
}
|
|
}
|
|
|
|
function open() {
|
|
const teams = state.teams || [];
|
|
openSheet(() => [
|
|
h('h2', { class: 'sheet-title', text: 'Switch team' }),
|
|
h('ul', { class: 'menu', role: 'menu' },
|
|
h('li', {}, h('button', {
|
|
class: 'menu-item', type: 'button', role: 'menuitemradio',
|
|
'aria-checked': String(state.selectedTeamID == null),
|
|
onclick: () => closeSheet(ALL_TEAMS),
|
|
}, h('span', { class: 'team-dot' }), ' All teams')),
|
|
teams.map((t) => h('li', {}, h('button', {
|
|
class: 'menu-item', type: 'button', role: 'menuitemradio',
|
|
'aria-checked': String(t.id === state.selectedTeamID),
|
|
onclick: () => closeSheet(t.id),
|
|
}, h('span', { class: `team-dot ${teamColorClass(t.id)}` }), ' ' + t.name))),
|
|
),
|
|
]).then((choice) => {
|
|
if (choice == null) return; // dismissed: backdrop, escape, or cancel
|
|
setSelectedTeam(choice === ALL_TEAMS ? null : choice);
|
|
});
|
|
}
|