Planner: one compact toolbar (title, Calendar | Tasks, view switch, agenda range, Add event)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b8ab84a009
commit
4173d2c058
3 files changed
+22
-13
No files matched your search
+6
-6
@@ -54,11 +54,13 @@ const calState = { y: now0.getFullYear(), m: now0.getMonth(), sel: ymd(now0) };
|
||||
async function viewCalendar() {
|
||||
const mode = store.get('cal_view', 'month');
|
||||
const v = $('#view');
|
||||
v.innerHTML = `<div class="wrap">${plannerHead('calendar', `<button class="btn" id="ne">${icon('cal')}Add event</button>`)}
|
||||
<div class="seg">${[['month', 'Month'], ['week', 'Week'], ['day', 'Day'], ['agenda', 'Agenda']].map(([k, l]) => `<a href="#" data-v="${k}" class="${k === mode ? 'on' : ''}">${l}</a>`).join('')}</div>
|
||||
const views = `<div class="sgrp" role="tablist" aria-label="Calendar view">${[['month', 'Month'], ['week', 'Week'], ['day', 'Day'], ['agenda', 'Agenda']].map(([k, l]) => `<a href="#" role="tab" data-v="${k}" aria-selected="${k === mode}" class="${k === mode ? 'on' : ''}">${l}</a>`).join('')}</div>`
|
||||
+ (mode === 'agenda' ? `<select id="crange" class="psel" aria-label="How far ahead">${[[1, 'Today'], [7, '7 days'], [30, '30 days']].map(([n, l]) => `<option value="${n}" ${n === store.get('cal_range', 7) ? 'selected' : ''}>${l}</option>`).join('')}</select>` : '');
|
||||
v.innerHTML = `<div class="wrap">${plannerHead('calendar', `<button class="btn" id="ne">${icon('cal')}Add event</button>`, views)}
|
||||
<div id="cbody"><p class="empty">Loading…</p></div><div class="bar"><button class="btn ghost" id="mc">Manage calendars</button></div></div>`;
|
||||
$('#ne').onclick = () => newEvent(mode === 'agenda' ? ymd(new Date()) : calState.sel);
|
||||
v.querySelectorAll('.seg a[data-v]').forEach(a => a.onclick = e => { e.preventDefault(); store.set('cal_view', a.dataset.v); viewCalendar(); });
|
||||
if ($('#crange')) $('#crange').onchange = e => { store.set('cal_range', +e.target.value); viewCalendar(); };
|
||||
v.querySelectorAll('.sgrp a[data-v]').forEach(a => a.onclick = e => { e.preventDefault(); store.set('cal_view', a.dataset.v); viewCalendar(); });
|
||||
$('#mc').onclick = manageCalendars;
|
||||
try {
|
||||
const cals = await jfetch('/api/cal/list');
|
||||
@@ -72,10 +74,8 @@ async function renderAgendaList() {
|
||||
const { byDay, errors } = await loadAgenda(range);
|
||||
if (!location.hash.startsWith('#/calendar') || !$('#cbody')) return;
|
||||
const keys = [...byDay.keys()].sort();
|
||||
$('#cbody').innerHTML = `<div class="seg">${[[1, 'Today'], [7, '7 days'], [30, '30 days']].map(([n, l]) => `<a href="#" data-n="${n}" class="${n === range ? 'on' : ''}">${l}</a>`).join('')}</div>`
|
||||
+ (keys.map(k => `<h2>${esc(dayLabel(k))}</h2><div class="list">${byDay.get(k).map(evRow).join('')}</div>`).join('') || '<p class="empty">Nothing coming up</p>')
|
||||
$('#cbody').innerHTML = (keys.map(k => `<h2>${esc(dayLabel(k))}</h2><div class="list">${byDay.get(k).map(evRow).join('')}</div>`).join('') || '<p class="empty">Nothing coming up</p>')
|
||||
+ errors.map(e => `<p class="err">${esc(e)}</p>`).join('');
|
||||
$('#cbody').querySelectorAll('.seg a[data-n]').forEach(a => a.onclick = e => { e.preventDefault(); store.set('cal_range', +a.dataset.n); viewCalendar(); });
|
||||
}
|
||||
|
||||
async function renderMonth() {
|
||||
|
||||
+6
-5
@@ -6,11 +6,12 @@ function viewPlanner(args = []) {
|
||||
const want = args[0] && ok.includes(args[0]) ? args[0] : store.get('planner_pane', ok[0]);
|
||||
location.replace('#/' + (ok.includes(want) ? want : ok[0] || 'home'));
|
||||
}
|
||||
/** The page heading, with a Calendar | Tasks switch when this person may use both. `extra` (a button, say) sits on the same row, at the right. */
|
||||
function plannerHead(active, extra = '') {
|
||||
/** One compact toolbar instead of stacked rows: title, Calendar | Tasks switch, then `mid` (the view switch) and `extra` (a button) pushed to the right. */
|
||||
function plannerHead(active, extra = '', mid = '') {
|
||||
try { localStorage.setItem(nk('planner_pane'), JSON.stringify(active)); } catch {}
|
||||
const panes = PLANNER_PANES.filter(p => allowed(p[0]));
|
||||
const seg = panes.length < 2 ? '' : `<div class="seg" role="tablist" aria-label="Planner">${panes.map(([k, l]) => `<a href="#/${k}" role="tab" aria-selected="${k === active}" class="${k === active ? 'on' : ''}">${l}</a>`).join('')}</div>`;
|
||||
const title = panes.length < 2 ? (PLANNER_PANES.find(p => p[0] === active) || [0, 'Planner'])[1] : 'Planner';
|
||||
return `<h1>${esc(title)}</h1>${seg || extra ? `<div class="prow">${seg}${extra}</div>` : ''}`;
|
||||
const two = panes.length > 1;
|
||||
const group = two ? `<div class="sgrp" role="tablist" aria-label="Planner">${panes.map(([k, l]) => `<a href="#/${k}" role="tab" aria-selected="${k === active}" class="${k === active ? 'on' : ''}">${l}</a>`).join('')}</div>` : '';
|
||||
const title = two ? 'Planner' : (PLANNER_PANES.find(p => p[0] === active) || [0, 'Planner'])[1];
|
||||
return `<div class="ptop"><h1>${esc(title)}</h1>${group}${mid}<span class="pspace"></span>${extra}</div>`;
|
||||
}
|
||||
+10
-2
@@ -560,5 +560,13 @@ body.lyropen { overflow: hidden; }
|
||||
body.nav-mini .ngrp { margin: 6px 14px; padding-top: 0; }
|
||||
body.nav-mini .ngrp span { display: none; }
|
||||
|
||||
.prow { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; margin: 6px 0 10px; }
|
||||
.prow .seg { margin: 0; }
|
||||
|
||||
/* Planner toolbar: one row (wraps on phones) */
|
||||
.ptop { display: flex; align-items: center; gap: 10px 12px; flex-wrap: wrap; margin: 0 0 12px; }
|
||||
.ptop h1 { margin: 0 4px 0 0; font-size: 24px; }
|
||||
.pspace { flex: 1; }
|
||||
.sgrp { display: inline-flex; background: var(--panel2); border-radius: 999px; padding: 3px; gap: 2px; max-width: 100%; overflow-x: auto; }
|
||||
.sgrp a { padding: 6px 14px; border-radius: 999px; color: var(--muted); font-size: 14px; white-space: nowrap; min-height: 32px; display: flex; align-items: center; }
|
||||
.sgrp a.on { background: var(--accent); color: var(--accent-t); font-weight: 600; }
|
||||
.psel { width: auto; min-height: 38px; padding: 0 12px; border-radius: 999px; font-size: 14px; }
|
||||
@media (max-width: 560px) { .ptop h1 { width: 100%; } .pspace { display: none; } .ptop .btn { margin-left: auto; } }
|
||||
Reference in new issue
Block a user