(function (root, factory) { if (typeof module === 'object' && module.exports) module.exports = factory; else root.createMobileTaskDock = factory; })(typeof self !== 'undefined' ? self : this, function createMobileTaskDock(options) { const nav = options.nav; const buttons = options.buttons; const overlays = options.overlays || []; let launcher = null; let wasHidden = false; function select(name) { if (!buttons[name]) return false; Object.entries(buttons).forEach(([key, button]) => { if (key === name) button.setAttribute('aria-current', 'page'); else button.removeAttribute('aria-current'); }); return true; } function refreshVisibility() { const hidden = overlays.some(overlay => overlay.open || overlay.classList.contains('open')); nav.hidden = hidden; if (options.sessionHud) { if (hidden) options.sessionHud.setAttribute('data-overlay-hidden', 'true'); else options.sessionHud.removeAttribute('data-overlay-hidden'); } if (wasHidden && !hidden && launcher) launcher.focus(); wasHidden = hidden; } function openQueues() { if (!options.queueSheet || options.queueSheet.open) return; options.queueSheet.showModal(); } function closeQueues() { options.queueSheet?.open && options.queueSheet.close(); } function start() { Object.entries(buttons).forEach(([name, button]) => { button.addEventListener('click', event => { launcher = event.currentTarget; select(name); name === 'queues' && options.queueSheet ? openQueues() : options.actions[name](); }); }); if (options.queueSheet) { options.queueClose?.addEventListener('click', closeQueues); options.queueSheet.addEventListener('cancel', event => { event.preventDefault(); closeQueues(); }); options.queueSheet.addEventListener('close', () => buttons.queues?.focus()); Object.entries(options.queueRows || {}).forEach(([name, row]) => { row.addEventListener('click', () => { closeQueues(); options.onSelectQueue?.(name, row); }); }); } options.observe(refreshVisibility, overlays); refreshVisibility(); } function updateWork(mode) { const labels = { continue: ['Continue', 'Continue Today'], resume: ['Resume', 'Resume Today'], start: ['Start', 'Start Today'], plan: ['Plan', 'Plan Today'], find: ['Find', 'Find work'], work: ['Work', 'Work'], }; const workMode = labels[mode] ? mode : 'work'; if (options.workLabel) options.workLabel.textContent = labels[workMode][0]; if (buttons.work) { buttons.work.setAttribute('aria-label', labels[workMode][1]); } } function updateAttention(count) { const total = Math.max(0, Number(count) || 0); const visible = total > 0; if (options.attentionBadge) { options.attentionBadge.textContent = String(total); options.attentionBadge.hidden = !visible; } if (buttons.attention) { buttons.attention.hidden = !visible; buttons.attention.setAttribute('aria-label', 'Attention, ' + total + ' items'); } if (visible) nav.setAttribute('data-attention', 'true'); else nav.removeAttribute('data-attention'); } function updateQueues(counts) { const names = 'today agenda attention update later draft'.split(' '); const normalized = Object.fromEntries(names.map(name => [name, Math.max(0, Number(counts?.[name]) || 0)])); const total = ['today', 'attention', 'later', 'draft'].reduce((sum, name) => sum + normalized[name], 0); Object.entries(options.queueCounts || {}).forEach(([name, element]) => { element.textContent = String(normalized[name] || 0); }); options.queueRows?.update?.setAttribute( 'aria-label', 'Updates, ' + normalized.update + ' unread conversations' ); if (options.queueBadge) { options.queueBadge.textContent = String(total); options.queueBadge.hidden = total === 0; } if (options.deadlineBadge) { options.deadlineBadge.textContent = normalized.agenda + ' due'; options.deadlineBadge.hidden = normalized.agenda === 0; } if (options.queueRows?.agenda) { if (normalized.agenda > 0) options.queueRows.agenda.setAttribute('data-deadlines', 'true'); else options.queueRows.agenda.removeAttribute('data-deadlines'); } const deadlineLabel = normalized.agenda > 0 ? ', ' + normalized.agenda + ' upcoming deadlines' : ''; buttons.queues?.setAttribute('aria-label', 'Queues, ' + total + ' items' + deadlineLabel); } return {start, select, refreshVisibility, updateAttention, updateQueues, updateWork}; });