Rejected Whatcom Staff Page Sort by Team Sort Alphabetical Feature

For demonstration purposes since it is going completely away, there is this video.
<div class="hidden-team-links" style="display:none;">
<a href="https://whatcomcd.specialdistrict.org/all-staff-teams?mode=team&team=Administration%20%26%20Finance">
Administration & Finance
</a><br>
<a href="https://whatcomcd.specialdistrict.org/all-staff-teams?mode=team&team=Climate%20Resilience%20and%20Preparedness">
Climate Resilience and Preparedness
</a><br>
<a href="https://whatcomcd.specialdistrict.org/all-staff-teams?mode=team&team=Conservation%20Planning">
Conservation Planning
</a><br>
<a href="https://whatcomcd.specialdistrict.org/all-staff-teams?mode=team&team=Engineering">
Engineering
</a><br>
<a href="https://whatcomcd.specialdistrict.org/all-staff-teams?mode=team&team=Habitat%20Restoration">
Habitat Restoration
</a><br>
<a href="https://whatcomcd.specialdistrict.org/all-staff-teams?mode=team&team=Outreach%20Education">
Outreach Education
</a>
</div>
<!-- Staff Directory: Expand All | By Team | A–Z -->
<style>
/* ---------- Controls bar (mode buttons) ---------- */
.items .staff-controls {
display: flex;
flex-wrap: wrap;
gap: .5rem;
padding: 0 1rem;
margin: 1rem 0 .25rem;
}
.items .staff-controls button {
background: #f5f5f5;
border: 1px solid #ddd;
border-radius: 6px;
padding: .5rem .9rem;
cursor: pointer;
}
.items .staff-controls button[aria-pressed="true"] {
background: #fff;
border-color: #bbb;
font-weight: 600;
}
/* ---------- Tab bar ---------- */
.items .tablist {
display: flex;
flex-wrap: wrap;
gap: .5rem;
margin: .5rem 0 0;
padding: 0 1rem;
}
.items .tablist [role="tab"] {
background: #f5f5f5;
border: 1px solid transparent;
border-bottom: none;
border-radius: 4px 4px 0 0;
padding: .45rem .8rem;
cursor: pointer;
white-space: nowrap;
}
.items .tablist [role="tab"]:hover { background: #e9e9e9; }
.items .tablist [role="tab"][aria-selected="true"] {
background: #fff;
border-color: #ccc;
font-weight: 600;
}
/* ---------- Panels + cards layout ---------- */
.items .tabpanels { width: 100%; }
.items .tabpanels [role="tabpanel"] {
display: flex;
flex-wrap: wrap;
gap: 1rem;
padding: 1rem;
width: 100%;
box-sizing: border-box;
}
.items .tabpanels [role="tabpanel"][hidden] { display: none !important; }
.items .tabpanels .poc-instance {
flex: 0 0 calc((100% - 2rem)/3);
max-width: calc((100% - 2rem)/3);
box-sizing: border-box;
}
@media (max-width: 800px) {
.items .tabpanels .poc-instance {
flex: 0 0 calc((100% - 1rem)/2);
max-width: calc((100% - 1rem)/2);
}
}
@media (max-width: 480px) {
.items .tabpanels .poc-instance {
flex: 0 0 100%;
max-width: 100%;
}
}
/* ---------- Team headers for Expand All ---------- */
.items .staff-heading {
display: block;
width: 100%;
margin: 32px 0 12px 6px;
font-size: 28px;
line-height: 1.35;
font-weight: 700;
clear: both;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', () => {
const container = document.querySelector('.items');
if (!container) return;
// ---------- Helpers ----------
const slugify = s => (s || '')
.toString()
.normalize('NFKD').replace(/[\u0300-\u036f]/g, '') // strip accents
.toLowerCase()
.replace(/&/g, ' and ')
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '');
const getParams = () => {
const search = new URLSearchParams(location.search);
// Accept hash as either "#team=Foo", "#mode=alpha&letter=A", or "#foo-bar" (team slug)
const rawHash = (location.hash || '').replace(/^#/, '');
const hashAsParams = new URLSearchParams(
rawHash.includes('=') ? rawHash : ''
);
const hashAsTeamSlug = rawHash && !rawHash.includes('=') ? rawHash : '';
// Priority: query params > hash params > bare hash team slug
return {
mode: search.get('mode') || hashAsParams.get('mode') || null,
team: search.get('team') || hashAsParams.get('team') || (hashAsTeamSlug || null),
letter: search.get('letter') || hashAsParams.get('letter') || null
};
};
const setURL = ({ mode, team, letter }, replace = true) => {
const url = new URL(location.href);
url.searchParams.delete('mode');
url.searchParams.delete('team');
url.searchParams.delete('letter');
if (mode) url.searchParams.set('mode', mode);
if (team) url.searchParams.set('team', team);
if (letter) url.searchParams.set('letter', letter);
// For expand+team jump, keep a clean hash to the team header
if (mode === 'expand' && team) {
url.hash = slugify(team);
} else {
url.hash = '';
}
(replace ? history.replaceState : history.pushState).call(history, null, '', url.toString());
};
// ---------- Grab & parse cards ----------
const originals = Array.from(container.querySelectorAll('article.poc-instance'));
if (!originals.length) return;
function parseTitle(card) {
const titleEl = card.querySelector('h3 span, h3');
const raw = titleEl ? titleEl.textContent.trim() : '';
const m = raw.match(/^(.*?)\s*\(([^)]+)\)\s*$/);
const name = m ? m[1].trim() : raw;
const team = m ? m[2].trim() : 'Other';
return { name, team, raw, titleElSelector: titleEl ? (titleEl.matches('h3 span') ? 'h3 span' : 'h3') : 'h3' };
}
const data = originals.map((node, i) => {
const parsed = parseTitle(node);
return { i, node, ...parsed, teamSlug: slugify(parsed.team), firstLetter: (parsed.name.charAt(0) || '#').toUpperCase() };
});
const teams = Array.from(new Set(data.map(d => d.team))).sort((a,b)=>a.localeCompare(b));
const letters = Array.from(new Set(data.map(d => d.firstLetter))).sort((a,b)=>a.localeCompare(b));
// ---------- Build UI skeleton ----------
container.innerHTML = '';
const controls = document.createElement('div');
controls.className = 'staff-controls';
container.appendChild(controls);
const modes = [
{ key: 'expand', label: 'Expand all' },
{ key: 'team', label: 'By team' },
{ key: 'alpha', label: 'A–Z' }
];
let activeMode = 'expand';
const tablist = document.createElement('div');
tablist.className = 'tablist';
tablist.setAttribute('role', 'tablist');
container.appendChild(tablist);
const panels = document.createElement('div');
panels.className = 'tabpanels';
container.appendChild(panels);
function cloneCard(d, { showTeamInTitle }) {
const clone = d.node.cloneNode(true);
const t = clone.querySelector(d.titleElSelector) || clone.querySelector('h3');
if (t) t.textContent = showTeamInTitle ? d.raw : d.name;
return clone;
}
function resetTabs() {
tablist.innerHTML = '';
panels.innerHTML = '';
}
function wireKeyboard(keys) {
keys.forEach((k, idx) => {
const tab = document.getElementById(`tab-${k}`);
tab.addEventListener('keydown', e => {
let next;
if (e.key === 'ArrowRight') next = (idx + 1) % keys.length;
if (e.key === 'ArrowLeft') next = (idx - 1 + keys.length) % keys.length;
if (e.key === 'Home') next = 0;
if (e.key === 'End') next = keys.length - 1;
if (next !== undefined) {
e.preventDefault();
document.getElementById(`tab-${keys[next]}`).focus();
activate(keys[next], keys);
}
});
});
}
function activate(activeKey, keys) {
keys.forEach(k => {
const tab = document.getElementById(`tab-${k}`);
const panel = document.getElementById(`panel-${k}`);
const isActive = k === activeKey;
tab?.setAttribute('aria-selected', isActive);
if (isActive) tab?.removeAttribute('tabindex'); else tab?.setAttribute('tabindex', '-1');
if (panel) panel.hidden = !isActive;
});
}
// ---------- Renderers ----------
function renderExpandAll(scrollToTeamSlug = null) {
resetTabs();
const key = 'All';
const tab = document.createElement('button');
tab.setAttribute('role', 'tab');
tab.id = `tab-${key}`;
tab.textContent = 'All';
tab.setAttribute('aria-selected', 'true');
tablist.appendChild(tab);
const panel = document.createElement('div');
panel.setAttribute('role', 'tabpanel');
panel.id = `panel-${key}`;
panels.appendChild(panel);
const grouped = [...data].sort((a,b) => {
const t = a.team.localeCompare(b.team);
return t !== 0 ? t : a.name.localeCompare(b.name);
});
let lastTeam = null;
let jumpTarget = null;
grouped.forEach(d => {
if (d.team !== lastTeam) {
const h2 = document.createElement('h2');
h2.className = 'staff-heading';
h2.textContent = d.team;
h2.id = d.teamSlug; // <— anchor target for Expand All
panel.appendChild(h2);
lastTeam = d.team;
if (scrollToTeamSlug && d.teamSlug === scrollToTeamSlug) {
jumpTarget = h2;
}
}
panel.appendChild(cloneCard(d, { showTeamInTitle: false }));
});
if (jumpTarget) {
// Small delay to ensure layout is ready
requestAnimationFrame(() => jumpTarget.scrollIntoView({ behavior: 'smooth', block: 'start' }));
}
}
function renderByTeam(activeTeamKey = 'All Teams') {
resetTabs();
const keys = ['All Teams', ...teams.map(t => `team-${slugify(t)}`)];
const labelFromKey = k => k === 'All Teams'
? 'All Teams'
: teams.find(t => `team-${slugify(t)}` === k);
// Build tabs/panels
keys.forEach((k, idx) => {
const human = labelFromKey(k);
const tab = document.createElement('button');
tab.setAttribute('role', 'tab');
tab.id = `tab-${k}`;
const count = k === 'All Teams'
? data.length
: data.filter(d => `team-${d.teamSlug}` === k).length;
tab.textContent = `${human} (${count})`;
tab.setAttribute('aria-selected', idx === 0 ? 'true' : 'false');
if (idx !== 0) tab.setAttribute('tabindex', '-1');
tab.addEventListener('click', () => {
activate(k, keys);
// reflect in URL for shareable link
const teamName = k === 'All Teams' ? null : human;
setURL({ mode: 'team', team: teamName }, false);
});
tablist.appendChild(tab);
const panel = document.createElement('div');
panel.setAttribute('role', 'tabpanel');
panel.id = `panel-${k}`;
if (idx !== 0) panel.hidden = true;
panels.appendChild(panel);
const subset = k === 'All Teams'
? data
: data.filter(d => `team-${d.teamSlug}` === k);
subset
.slice()
.sort((a,b)=>a.name.localeCompare(b.name))
.forEach(d => panel.appendChild(cloneCard(d, { showTeamInTitle: false })));
});
wireKeyboard(keys);
// Activate requested team
if (keys.includes(activeTeamKey)) {
activate(activeTeamKey, keys);
// Scroll the active panel into view
const panelEl = document.getElementById(`panel-${activeTeamKey}`);
if (panelEl) {
const headerOffset = 120; // adjust to match your sticky header height
const elementPosition = panelEl.getBoundingClientRect().top + window.scrollY;
const offsetPosition = elementPosition - headerOffset;
window.scrollTo({
top: offsetPosition,
behavior: 'smooth'
});
}
} else {
activate('All Teams', keys);
}
}
function renderAlpha(activeLetter = 'All') {
resetTabs();
const keys = ['All', ...letters];
keys.forEach((letter, idx) => {
const tab = document.createElement('button');
tab.setAttribute('role', 'tab');
tab.id = `tab-${letter}`;
const count = letter === 'All'
? data.length
: data.filter(d => d.firstLetter === letter).length;
tab.textContent = `${letter} (${count})`;
tab.setAttribute('aria-selected', idx === 0 ? 'true' : 'false');
if (idx !== 0) tab.setAttribute('tabindex', '-1');
tab.addEventListener('click', () => {
activate(letter, keys);
setURL({ mode: 'alpha', letter }, false);
});
tablist.appendChild(tab);
const panel = document.createElement('div');
panel.setAttribute('role', 'tabpanel');
panel.id = `panel-${letter}`;
if (idx !== 0) panel.hidden = true;
panels.appendChild(panel);
const subset = letter === 'All'
? data
: data.filter(d => d.firstLetter === letter);
subset
.slice()
.sort((a,b)=>a.name.localeCompare(b.name))
.forEach(d => panel.appendChild(cloneCard(d, { showTeamInTitle: true })));
});
wireKeyboard(keys);
if (keys.includes(activeLetter)) activate(activeLetter, keys); else activate('All', keys);
}
// ---------- Controls ----------
let activeModeBtn = null;
modes.forEach(m => {
const btn = document.createElement('button');
btn.type = 'button';
btn.textContent = m.label;
btn.setAttribute('aria-pressed', m.key === 'expand' ? 'true' : 'false');
btn.addEventListener('click', () => {
activeModeBtn?.setAttribute('aria-pressed', 'false');
btn.setAttribute('aria-pressed', 'true');
activeModeBtn = btn;
if (m.key === 'expand') { renderExpandAll(); setURL({ mode: 'expand' }, false); }
if (m.key === 'team') { renderByTeam('All Teams'); setURL({ mode: 'team' }, false); }
if (m.key === 'alpha') { renderAlpha('All'); setURL({ mode: 'alpha' }, false); }
});
controls.appendChild(btn);
if (m.key === 'expand') activeModeBtn = btn; // default pressed
});
// ---------- Initialization with deep links ----------
function bootFromURL() {
const { mode, team, letter } = getParams();
// Default view
if (!mode) {
renderExpandAll();
setURL({ mode: 'expand' }, true);
return;
}
if (mode === 'expand') {
renderExpandAll(team ? slugify(team) : null);
} else if (mode === 'team') {
const targetKey = team ? `team-${slugify(team)}` : 'All Teams';
renderByTeam(targetKey);
} else if (mode === 'alpha') {
const L = (letter || 'All').toUpperCase();
renderAlpha(L);
} else {
renderExpandAll();
}
// Reflect mode button state
controls.querySelectorAll('button').forEach(b => b.setAttribute('aria-pressed', 'false'));
const btn = Array.from(controls.querySelectorAll('button')).find(b => b.textContent.startsWith(
mode === 'expand' ? 'Expand' : mode === 'team' ? 'By team' : 'A–Z'
));
if (btn) { btn.setAttribute('aria-pressed', 'true'); activeModeBtn = btn; }
// Normalize URL (encode exact params + hash)
setURL({ mode, team, letter }, true);
}
// Rehydrate on back/forward and hash changes
window.addEventListener('popstate', bootFromURL);
window.addEventListener('hashchange', bootFromURL);
bootFromURL();
});
</script>
We could have had it all. - Adele