@muten/shadcn 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/registry/agenda.js +7 -2
- package/registry/kit/core.js +22 -12
package/package.json
CHANGED
package/registry/agenda.js
CHANGED
|
@@ -40,6 +40,11 @@
|
|
|
40
40
|
// lang, currency, locale and cents change live too.
|
|
41
41
|
export function mount(el, inputs, on) {
|
|
42
42
|
let api = null, map = null, pending = null, lastCmd = String(inputs.command || ''); // the command it was born with never runs
|
|
43
|
+
// an app sends its inputs again on every change of its own, often the same data: repaint only when something drawn
|
|
44
|
+
// changed (`start` only places the first paint, `command` runs apart)
|
|
45
|
+
const SKIP = new Set(['command', 'start']);
|
|
46
|
+
const sigOf = (i) => JSON.stringify(Object.keys(i).filter((k) => !SKIP.has(k)).sort().map((k) => [k, i[k]]));
|
|
47
|
+
let lastSig = sigOf(inputs);
|
|
43
48
|
import('@muten/shadcn/registry/kit/core.js').then(({ kit, today, addDays }) => {
|
|
44
49
|
const iso = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
|
|
45
50
|
const list = (v) => (Array.isArray(v) ? v : []);
|
|
@@ -82,8 +87,8 @@ export function mount(el, inputs, on) {
|
|
|
82
87
|
onRange: (r) => send('range', { from: iso(r.from), to: iso(r.to), view: r.view }),
|
|
83
88
|
onChange: (evs) => send('change', evs.map((e) => ({ id: e.id, resource: e.resource, day: iso(e.day), start: e.start, dur: e.dur, status: e.status || '' }))),
|
|
84
89
|
});
|
|
85
|
-
if (pending) { api.update(data(pending)); pending = null; }
|
|
90
|
+
if (pending) { const sig = sigOf(pending); if (sig !== lastSig) { lastSig = sig; api.update(data(pending)); } pending = null; }
|
|
86
91
|
});
|
|
87
92
|
const command = (next) => { const c = String(next.command || ''); if (c === lastCmd) return; lastCmd = c; if (c) api.run(c.split('#')[0]); };
|
|
88
|
-
return (next) => { if (api
|
|
93
|
+
return (next) => { if (!api || !map) { pending = next; return; } const sig = sigOf(next); if (sig !== lastSig) { lastSig = sig; api.update(map(next)); } command(next); };
|
|
89
94
|
}
|
package/registry/kit/core.js
CHANGED
|
@@ -152,7 +152,8 @@ watchReveal();
|
|
|
152
152
|
};
|
|
153
153
|
|
|
154
154
|
// ── tabs with a sliding indicator (for panels a Custom builds) ──
|
|
155
|
-
|
|
155
|
+
// redraws: the owner paints the tabs again on a click (the agenda), so a click does not animate tabs about to go
|
|
156
|
+
const initTabs = (list, { redraws = false } = {}) => {
|
|
156
157
|
const tabs = $$('.cx-tab', list);
|
|
157
158
|
const ind = document.createElement('span'); ind.className = 'cx-tab-ind'; list.prepend(ind);
|
|
158
159
|
const place = (animate) => {
|
|
@@ -168,14 +169,15 @@ watchReveal();
|
|
|
168
169
|
place(true);
|
|
169
170
|
list.dispatchEvent(new CustomEvent('pick', { detail: tabs.indexOf(tab) }));
|
|
170
171
|
};
|
|
171
|
-
tabs.forEach((t) => { t.tabIndex = t.getAttribute('aria-selected') === 'true' ? 0 : -1; t.onclick = () => pick(t); });
|
|
172
|
+
tabs.forEach((t) => { t.tabIndex = t.getAttribute('aria-selected') === 'true' ? 0 : -1; if (!redraws) t.onclick = () => pick(t); });
|
|
172
173
|
list.addEventListener('keydown', (e) => {
|
|
173
174
|
const i = tabs.indexOf(document.activeElement); if (i < 0) return;
|
|
174
175
|
const to = { ArrowRight: i + 1, ArrowLeft: i - 1, Home: 0, End: tabs.length - 1 }[e.key];
|
|
175
176
|
if (to === undefined) return; e.preventDefault(); pick(tabs[(to + tabs.length) % tabs.length], true);
|
|
176
177
|
});
|
|
178
|
+
// the first placing comes from the observer too: it runs once the browser has laid the tabs out, so it measures
|
|
179
|
+
// without forcing a layout of the whole page (a list painted again and again paid two each time)
|
|
177
180
|
new ResizeObserver(() => place(false)).observe(list);
|
|
178
|
-
place(false);
|
|
179
181
|
return pick;
|
|
180
182
|
};
|
|
181
183
|
|
|
@@ -717,7 +719,7 @@ let wheelsNow = [];
|
|
|
717
719
|
const weekHead = (d, rid) => { const n = evOn(d, rid).length, q = queue.filter((x) => sameDay(x.day, d)).length, shut = isClosed(d); const sub = [shut && !n ? w.closed : n ? w.countOf(n) : '', q ? w.waiting(q) : ''].filter(Boolean).join(' · '); return `<span class="cx-ag-colday">${w.dayS[d.getDay()]} ${d.getDate()}</span>${sub ? `<small class="cx-ag-colsub"${q ? ' data-queue' : ''}>${sub}</small>` : ''}`; };
|
|
718
720
|
const tellRange = () => { const v = eff(); const a = v === 'range' ? rangeSel[0] : v === 'week' ? weekDays()[0] : day, b = v === 'range' ? rangeSel[1] : v === 'week' ? weekDays()[6] : day; const key = `${v}|${+a}|${+b}`; if (key === lastRange) return; lastRange = key; onRange({ from: a, to: b, view: v }); };
|
|
719
721
|
const paint = () => {
|
|
720
|
-
|
|
722
|
+
if (fit) { const k = `${eff()}|${from}|${to}|${narrow}`; if (k !== fitKey && fitSeen.has(k)) { hourPx = fitSeen.get(k); fitKey = k; } }
|
|
721
723
|
if (typeof drag !== 'undefined' && drag) { const d = drag; drag = null; clearTimeout(d.timer); cancelAnimationFrame(scrollRaf); d.ghost?.remove(); }
|
|
722
724
|
const v = eff(); // the phone keeps the calendar: day grid or list, the week needs a wide screen
|
|
723
725
|
const cols = v === 'week' ? weekDays().map((d) => ({ day: d, rid: who === 'all' || who.startsWith('kind:') ? 'all' : who, head: weekHead(d, who === 'all' || who.startsWith('kind:') ? null : who), label: `${w.dayL[d.getDay()]} ${d.getDate()}`, today: sameDay(d, today), closed: isClosed(d) })) : dayCols().map((r) => ({ day, rid: r.id, head: `${r.photo ? `<span class="cn-avatar cx-ag-face" style="background-image:url('${r.photo}')"></span>` : ui.avatar(r.name, { size: 'sm' })}<span class="cx-ag-colname"><b>${r.name}</b>${roleOf(r) ? `<small>${roleOf(r)}</small>` : ''}</span>`, label: r.name }));
|
|
@@ -728,8 +730,9 @@ let wheelsNow = [];
|
|
|
728
730
|
if (!loading) { hadNow = top.includes('cx-ag-nowcard'); hadQueue = top.includes('cx-ag-queue'); }
|
|
729
731
|
root.innerHTML = toolbar() + `<div class="cx-ag-frame"${loading ? ` aria-busy="true" aria-label="${w.loading}"` : ''}><div class="cx-ag-top">${people()}${v === 'week' ? '' : strip()}${top}${alert}</div>${v === 'range' ? `<div class="cx-ag-listwrap">${loading ? skelList() : rangeList()}</div>` : v === 'list' ? `<div class="cx-ag-listwrap">${loading ? skelList() : list()}</div>` : grid(cols)}</div>`;
|
|
730
732
|
root.toggleAttribute('data-loading', !!loading);
|
|
731
|
-
$$('.cx-tabs', root).forEach(initTabs);
|
|
733
|
+
$$('.cx-tabs', root).forEach((tl) => initTabs(tl, { redraws: true }));
|
|
732
734
|
const now = $('.cx-ag-now', root); if (now) $('.cx-ag-scroll', root).scrollTop = Math.max(0, parseFloat(now.style.top) - 120);
|
|
735
|
+
if (refit()) { paint(); return; } // the hours' height changed: once more at the right size
|
|
733
736
|
tellRange();
|
|
734
737
|
};
|
|
735
738
|
// drag to move · drag the bottom edge to resize (15 min). A ghost shows where it lands and turns red where the
|
|
@@ -848,7 +851,7 @@ let wheelsNow = [];
|
|
|
848
851
|
$$('.cx-ag-now', root).forEach((x) => { x.style.top = y(m) + 'px'; }); const t = $('.cx-ag-nowtime', root); if (t) { t.style.top = y(m) + 'px'; t.textContent = clk(toHM(m)); }
|
|
849
852
|
$$('.cx-ag-col', root).forEach((c) => { if (!sameDay(new Date(+c.dataset.day), today)) return; const g = $('.cx-ag-gone', c); if (g) g.style.height = y(m) + 'px'; });
|
|
850
853
|
}, 30000);
|
|
851
|
-
new ResizeObserver(() => requestAnimationFrame(() => { const n = root.clientWidth < 700; const h = fit ? root.querySelector('.cx-ag-scroll')?.clientHeight || 0 : 0; if (n !== narrow) { narrow = n; paint(); } else if (fit && h && h !== fitH) paint(); })).observe(root);
|
|
854
|
+
new ResizeObserver(() => requestAnimationFrame(() => { const n = root.clientWidth < 700; const h = fit ? root.querySelector('.cx-ag-scroll')?.clientHeight || 0 : 0; if (n !== narrow) { narrow = n; paint(); } else if (fit && h && h !== fitH) { fitKey = ''; fitSeen.clear(); paint(); } })).observe(root);
|
|
852
855
|
|
|
853
856
|
// ── the event sheet (showcase): detail · lifecycle · payment · move (second step) · cancel (second step).
|
|
854
857
|
// In app mode a tapped cita goes to onOpen and the app opens its own sheet. ──
|
|
@@ -971,16 +974,23 @@ let wheelsNow = [];
|
|
|
971
974
|
body.innerHTML = '<div class="cx-ag-datesheet"></div>';
|
|
972
975
|
dateUI($('.cx-ag-datesheet', body), (r) => { closeModal(); applyDate(r); });
|
|
973
976
|
};
|
|
974
|
-
// fit: the hours share the height the app gives the agenda (never under baseHour), as a day view on a monitor should
|
|
975
|
-
|
|
977
|
+
// fit: the hours share the height the app gives the agenda (never under baseHour), as a day view on a monitor should.
|
|
978
|
+
// Measured after a paint and only when what decides it changed (view, hours, width, the agenda's height): reading
|
|
979
|
+
// the layout on every paint forced the whole page to lay out each time
|
|
980
|
+
let hourPx = baseHour, fitH = 0, fitKey = '';
|
|
981
|
+
const fitSeen = new Map(); // each view's measured hour, so going back to a view paints once at its size
|
|
976
982
|
const refit = () => {
|
|
977
|
-
if (!fit) return;
|
|
983
|
+
if (!fit) return false;
|
|
984
|
+
const key = `${eff()}|${from}|${to}|${narrow}`; if (key === fitKey) return false;
|
|
978
985
|
const sc = root.querySelector('.cx-ag-scroll'), ag = sc ? sc.querySelector('.cx-ag') : null, hours = sc ? sc.querySelector('.cx-ag-body') : null;
|
|
979
|
-
if (!sc || !sc.clientHeight || !ag || !hours) return;
|
|
980
|
-
fitH = sc.clientHeight;
|
|
986
|
+
if (!sc || !sc.clientHeight || !ag || !hours) return false;
|
|
987
|
+
fitKey = key; fitH = sc.clientHeight;
|
|
981
988
|
// everything in the scroll that is not the hours (the day heads, gaps, padding) keeps its height
|
|
982
989
|
const around = ag.offsetHeight - hours.offsetHeight;
|
|
983
|
-
|
|
990
|
+
const next = Math.max(baseHour, Math.floor((sc.clientHeight - around) / Math.max(1, to - from)));
|
|
991
|
+
fitSeen.set(key, next);
|
|
992
|
+
if (next === hourPx) return false;
|
|
993
|
+
hourPx = next; return true;
|
|
984
994
|
};
|
|
985
995
|
let blockDay = null; // the one open day of the week, when the form had nothing to choose from
|
|
986
996
|
const openBlock = () => { blockOpen = true; sheetOf('agBlock'); };
|