@tasknotes/calendar 0.1.8 → 0.1.11
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/CHANGELOG.md +23 -1
- package/README.md +1 -0
- package/dist/calendar.css +62 -2
- package/dist/cjs/core.cjs +20 -26
- package/dist/cjs/index.cjs +225 -53
- package/dist/cjs/react.cjs +221 -53
- package/dist/esm/{chunk-66A6EMCU.js → chunk-JWORVABV.js} +20 -26
- package/dist/esm/{chunk-EXKFHPW2.js → chunk-TTM45UAN.js} +202 -28
- package/dist/esm/core.js +1 -1
- package/dist/esm/index.js +6 -2
- package/dist/esm/react.js +2 -2
- package/dist/types/core/layout.d.cts +5 -0
- package/dist/types/core/layout.d.ts +5 -0
- package/dist/types/dom/calendar.d.cts +8 -0
- package/dist/types/dom/calendar.d.ts +8 -0
- package/dist/types/dom/format.d.cts +2 -1
- package/dist/types/dom/format.d.ts +2 -1
- package/dist/types/dom/options.d.cts +6 -0
- package/dist/types/dom/options.d.ts +6 -0
- package/dist/types/dom/views/daygrid.d.cts +2 -0
- package/dist/types/dom/views/daygrid.d.ts +2 -0
- package/dist/types/dom/views/list.d.cts +1 -0
- package/dist/types/dom/views/list.d.ts +1 -0
- package/dist/types/dom/views/strip.d.cts +3 -1
- package/dist/types/dom/views/strip.d.ts +3 -1
- package/dist/types/dom/views/timegrid.d.cts +4 -1
- package/dist/types/dom/views/timegrid.d.ts +4 -1
- package/dist/types/tasknotes/bases.d.cts +2 -0
- package/dist/types/tasknotes/bases.d.ts +2 -0
- package/package.json +1 -1
|
@@ -425,43 +425,44 @@ function segmentInWindow(n, day, minMinute, maxMinute, zone) {
|
|
|
425
425
|
};
|
|
426
426
|
}
|
|
427
427
|
var defaultOrder = (a, b) => a.startMs - b.startMs || b.endMs - b.startMs - (a.endMs - a.startMs) || Number(b.allDay) - Number(a.allDay) || a.item.title.localeCompare(b.item.title);
|
|
428
|
+
function collides(a, b, minMinutes) {
|
|
429
|
+
if (b.startMin < a.endMin) return true;
|
|
430
|
+
if (b.startMin === a.endMin && a.endMin > a.startMin) return false;
|
|
431
|
+
return b.startMin < a.startMin + minMinutes;
|
|
432
|
+
}
|
|
433
|
+
function overlaps(a, b, minMinutes) {
|
|
434
|
+
return a.startMin <= b.startMin ? collides(a, b, minMinutes) : collides(b, a, minMinutes);
|
|
435
|
+
}
|
|
428
436
|
function packTimed(segs, minMinutes, order = defaultOrder) {
|
|
429
|
-
const sorted = segs.
|
|
437
|
+
const sorted = [...segs].sort((a, b) => a.startMin - b.startMin || Math.max(b.endMin, b.startMin + minMinutes) - Math.max(a.endMin, a.startMin + minMinutes) || order(a.item, b.item));
|
|
430
438
|
const out = [];
|
|
431
439
|
let cluster = [];
|
|
432
|
-
let
|
|
433
|
-
let clusterEnd = -Infinity;
|
|
440
|
+
let colLast = [];
|
|
434
441
|
let clusterIndex = 0;
|
|
435
442
|
const flush = () => {
|
|
436
443
|
if (cluster.length) clusterIndex++;
|
|
437
|
-
const cols =
|
|
444
|
+
const cols = colLast.length;
|
|
438
445
|
for (const p of cluster) {
|
|
439
446
|
p.cols = cols;
|
|
440
447
|
let span = 1;
|
|
441
|
-
while (p.col + span < cols && !cluster.some((o) => o.col === p.col + span && o
|
|
448
|
+
while (p.col + span < cols && !cluster.some((o) => o.col === p.col + span && overlaps(o, p, minMinutes))) span++;
|
|
442
449
|
p.span = span;
|
|
443
450
|
}
|
|
444
451
|
cluster = [];
|
|
445
|
-
|
|
452
|
+
colLast = [];
|
|
446
453
|
};
|
|
447
454
|
for (const s of sorted) {
|
|
448
|
-
if (
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
}
|
|
452
|
-
let col = colEnds.findIndex((end) => end <= s.startMin);
|
|
453
|
-
if (col < 0) {
|
|
454
|
-
col = colEnds.length;
|
|
455
|
-
colEnds.push(0);
|
|
456
|
-
}
|
|
457
|
-
colEnds[col] = s.end;
|
|
455
|
+
if (!cluster.some((o) => collides(o, s, minMinutes))) flush();
|
|
456
|
+
let col = colLast.findIndex((last) => !collides(last, s, minMinutes));
|
|
457
|
+
if (col < 0) col = colLast.length;
|
|
458
458
|
const p = { ...s, col, cols: 1, span: 1, cluster: clusterIndex, split: false };
|
|
459
|
+
colLast[col] = p;
|
|
459
460
|
cluster.push(p);
|
|
460
461
|
out.push(p);
|
|
461
|
-
clusterEnd = Math.max(clusterEnd, s.end);
|
|
462
462
|
}
|
|
463
463
|
flush();
|
|
464
|
-
|
|
464
|
+
const starts = new Set(out.map((p) => p.startMin));
|
|
465
|
+
for (const p of out) if (p.endMin > p.startMin && p.startMin + minMinutes > p.endMin && starts.has(p.endMin)) p.abutted = true;
|
|
465
466
|
return out;
|
|
466
467
|
}
|
|
467
468
|
function layoutTimedDay(items, day, zone, minMinutes, order, window = [0, MINUTES_PER_DAY]) {
|
|
@@ -483,15 +484,8 @@ function layoutTimedDay(items, day, zone, minMinutes, order, window = [0, MINUTE
|
|
|
483
484
|
}
|
|
484
485
|
function markSplit(lane, other, minMinutes) {
|
|
485
486
|
if (!lane.length || !other.length) return;
|
|
486
|
-
const end = (p) => Math.max(p.endMin, p.startMin + minMinutes);
|
|
487
|
-
const ranges = /* @__PURE__ */ new Map();
|
|
488
|
-
for (const p of lane) {
|
|
489
|
-
const r = ranges.get(p.cluster);
|
|
490
|
-
if (r) r[1] = Math.max(r[1], end(p));
|
|
491
|
-
else ranges.set(p.cluster, [p.startMin, end(p)]);
|
|
492
|
-
}
|
|
493
487
|
const split = /* @__PURE__ */ new Set();
|
|
494
|
-
for (const
|
|
488
|
+
for (const p of lane) if (!split.has(p.cluster) && other.some((o) => overlaps(p, o, minMinutes))) split.add(p.cluster);
|
|
495
489
|
for (const p of lane) p.split = split.has(p.cluster);
|
|
496
490
|
}
|
|
497
491
|
function layoutRow(items, days, order = defaultOrder) {
|
|
@@ -27,18 +27,20 @@ import {
|
|
|
27
27
|
wallTime,
|
|
28
28
|
weekdayOf,
|
|
29
29
|
ymdOfDay
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-JWORVABV.js";
|
|
31
31
|
|
|
32
32
|
// src/dom/format.ts
|
|
33
33
|
var Formatter = class {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
hour12;
|
|
37
|
-
constructor(locale, hour12) {
|
|
34
|
+
constructor(locale, hour12, formatTime) {
|
|
35
|
+
this.formatTime = formatTime;
|
|
38
36
|
this.locale = locale;
|
|
39
37
|
const probe = new Intl.DateTimeFormat(locale, { hour: "numeric" }).resolvedOptions();
|
|
40
38
|
this.hour12 = hour12 ?? (probe.hour12 === true || probe.hourCycle === "h12" || probe.hourCycle === "h11");
|
|
41
39
|
}
|
|
40
|
+
formatTime;
|
|
41
|
+
cache = /* @__PURE__ */ new Map();
|
|
42
|
+
locale;
|
|
43
|
+
hour12;
|
|
42
44
|
fmt(key, opts) {
|
|
43
45
|
let f = this.cache.get(key);
|
|
44
46
|
if (!f) {
|
|
@@ -96,6 +98,7 @@ var Formatter = class {
|
|
|
96
98
|
/** Wall-clock minutes to a label. `compact` drops ":00" in 12-hour formats ("9am"). */
|
|
97
99
|
time(minute, compact = false) {
|
|
98
100
|
const m = (Math.round(minute) % 1440 + 1440) % 1440;
|
|
101
|
+
if (this.formatTime) return this.formatTime(m);
|
|
99
102
|
const date = new Date(Date.UTC(2e3, 0, 1, Math.floor(m / 60), m % 60));
|
|
100
103
|
if (this.hour12) {
|
|
101
104
|
const parts = this.fmt("t12", { hour: "numeric", minute: "2-digit", hour12: true }).formatToParts(date);
|
|
@@ -187,6 +190,7 @@ function defaultOptions() {
|
|
|
187
190
|
snapDuration: void 0,
|
|
188
191
|
scrollTime: "08:00",
|
|
189
192
|
slotHeight: 24,
|
|
193
|
+
pinchZoom: true,
|
|
190
194
|
expandRows: false,
|
|
191
195
|
nowIndicator: true,
|
|
192
196
|
selectMirror: true,
|
|
@@ -228,6 +232,7 @@ var LAYOUT_KEYS = /* @__PURE__ */ new Set([
|
|
|
228
232
|
"locale",
|
|
229
233
|
"firstDay",
|
|
230
234
|
"hour12",
|
|
235
|
+
"formatTime",
|
|
231
236
|
"weekNumbers",
|
|
232
237
|
"weekends",
|
|
233
238
|
"hiddenDays",
|
|
@@ -240,9 +245,11 @@ var LAYOUT_KEYS = /* @__PURE__ */ new Set([
|
|
|
240
245
|
"slotDuration",
|
|
241
246
|
"snapDuration",
|
|
242
247
|
"slotHeight",
|
|
248
|
+
"pinchZoom",
|
|
243
249
|
"expandRows",
|
|
244
250
|
"nowIndicator",
|
|
245
251
|
"slotEventOverlap",
|
|
252
|
+
"overlapStackThreshold",
|
|
246
253
|
"eventMinHeight",
|
|
247
254
|
"eventMaxStack",
|
|
248
255
|
"dayMaxEvents",
|
|
@@ -443,6 +450,7 @@ var DayGridView = class {
|
|
|
443
450
|
topPx = 24;
|
|
444
451
|
lastHeights = "";
|
|
445
452
|
dotsMode;
|
|
453
|
+
dragPreviews = [];
|
|
446
454
|
daysPerWeek() {
|
|
447
455
|
const { days } = this.ctx;
|
|
448
456
|
const first = days[0];
|
|
@@ -456,6 +464,36 @@ var DayGridView = class {
|
|
|
456
464
|
}
|
|
457
465
|
render(_change) {
|
|
458
466
|
const { ctx } = this;
|
|
467
|
+
this.clearDragPreviews();
|
|
468
|
+
const dragged = ctx.draggingId ? ctx.store.get(ctx.draggingId) : void 0;
|
|
469
|
+
const source = dragged ? this.elementsFor(dragged.id)[0] : void 0;
|
|
470
|
+
if (dragged && source && this.el.closest(".tnc-dragging-move")) {
|
|
471
|
+
const proposals = [dragged, ...[...ctx.store.all()].filter((item) => item.id !== dragged.id && item.item.preview)];
|
|
472
|
+
for (const proposal of proposals) {
|
|
473
|
+
const original = this.elementsFor(proposal.id)[0] ?? source;
|
|
474
|
+
const bounds = original.getBoundingClientRect();
|
|
475
|
+
const sourceWeek = original.closest(".tnc-dg-week");
|
|
476
|
+
const offset = bounds.top - sourceWeek.getBoundingClientRect().top;
|
|
477
|
+
for (const week of this.weeks) {
|
|
478
|
+
const start = week.days.findIndex((day) => day >= proposal.startDay && day < proposal.endDay);
|
|
479
|
+
if (start < 0) continue;
|
|
480
|
+
let end = start + 1;
|
|
481
|
+
while (!this.dotsMode && end < week.days.length && week.days[end] < proposal.endDay) end++;
|
|
482
|
+
const preview = ctx.createItemEl({ n: proposal, day: week.days[start], isStart: proposal.startDay >= week.days[start], isEnd: proposal.endDay <= week.days[end - 1] + 1, variant: ctx.meta.get(original).variant, mirror: true });
|
|
483
|
+
preview.classList.add("tnc-month-preview");
|
|
484
|
+
preview.inert = true;
|
|
485
|
+
setStyle(preview, {
|
|
486
|
+
left: `calc(${pct(start / week.days.length)} + 2px)`,
|
|
487
|
+
width: `calc(${pct((end - start) / week.days.length)} - 4px)`,
|
|
488
|
+
top: `${Math.max(this.topPx, Math.min(offset, week.row.clientHeight - bounds.height - 4))}px`,
|
|
489
|
+
height: `${bounds.height}px`
|
|
490
|
+
});
|
|
491
|
+
week.layer.append(preview);
|
|
492
|
+
this.dragPreviews.push(preview);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
459
497
|
this.dotsMode = ctx.opts.monthDisplay !== "full" || this.grid.clientWidth / this.daysPerWeek() < 72;
|
|
460
498
|
this.el.classList.toggle("tnc-dg--dots", this.dotsMode);
|
|
461
499
|
this.el.classList.toggle("tnc-dg--compact", this.dotsMode && ctx.opts.monthDisplay === "full");
|
|
@@ -487,6 +525,10 @@ var DayGridView = class {
|
|
|
487
525
|
}
|
|
488
526
|
}
|
|
489
527
|
}
|
|
528
|
+
clearDragPreviews() {
|
|
529
|
+
for (const preview of this.dragPreviews) this.ctx.destroyItemEl(preview);
|
|
530
|
+
this.dragPreviews = [];
|
|
531
|
+
}
|
|
490
532
|
/** Height of a "+N more" link as the host styles it (`--tnc-more-h`, or a taller min-height). */
|
|
491
533
|
measureMore(cs) {
|
|
492
534
|
const fallback = px(cs, "--tnc-more-h", this.barPx);
|
|
@@ -678,6 +720,7 @@ var DayGridView = class {
|
|
|
678
720
|
return this.weeks.flatMap((w) => [...w.pool.elementsFor(id), ...(w.dots ?? []).flatMap((c) => c.pool.elementsFor(id))]);
|
|
679
721
|
}
|
|
680
722
|
destroy() {
|
|
723
|
+
this.clearDragPreviews();
|
|
681
724
|
this.resizeObs?.disconnect();
|
|
682
725
|
for (const w of this.weeks) {
|
|
683
726
|
w.pool.clear();
|
|
@@ -730,6 +773,7 @@ var ListView = class {
|
|
|
730
773
|
frame = null;
|
|
731
774
|
/** A picked day stays marked until the list is scrolled by hand. */
|
|
732
775
|
pinned = false;
|
|
776
|
+
pinnedTop = 0;
|
|
733
777
|
itemsOn(day) {
|
|
734
778
|
const { store, opts } = this.ctx;
|
|
735
779
|
const out = store.spanningIn(day, day + 1, true);
|
|
@@ -861,6 +905,7 @@ var ListView = class {
|
|
|
861
905
|
this.scroller.scrollTop += section.getBoundingClientRect().top - this.scroller.getBoundingClientRect().top;
|
|
862
906
|
this.ctx.strip?.setFocus(day);
|
|
863
907
|
this.pinned = true;
|
|
908
|
+
this.pinnedTop = this.scroller.scrollTop;
|
|
864
909
|
}
|
|
865
910
|
fold = () => {
|
|
866
911
|
this.pinned = false;
|
|
@@ -868,6 +913,7 @@ var ListView = class {
|
|
|
868
913
|
if (strip?.expanded) strip.setExpanded(false);
|
|
869
914
|
};
|
|
870
915
|
onScroll = () => {
|
|
916
|
+
if (this.pinned && Math.abs(this.scroller.scrollTop - this.pinnedTop) > 1) this.pinned = false;
|
|
871
917
|
if (this.frame !== null) return;
|
|
872
918
|
this.frame = this.ctx.win.requestAnimationFrame(() => {
|
|
873
919
|
this.frame = null;
|
|
@@ -878,7 +924,7 @@ var ListView = class {
|
|
|
878
924
|
followTop() {
|
|
879
925
|
const strip = this.ctx.strip;
|
|
880
926
|
if (!strip || this.pinned) return;
|
|
881
|
-
const edge = strip.el.getBoundingClientRect().bottom + 1;
|
|
927
|
+
const edge = Math.max(strip.el.getBoundingClientRect().bottom, this.scroller.getBoundingClientRect().top) + 1;
|
|
882
928
|
for (const day of this.ctx.days) {
|
|
883
929
|
const section = this.sections.get(day);
|
|
884
930
|
if (section && section.getBoundingClientRect().bottom > edge) {
|
|
@@ -926,10 +972,11 @@ var ListView = class {
|
|
|
926
972
|
// src/dom/views/strip.ts
|
|
927
973
|
var SWIPE_PX = 40;
|
|
928
974
|
var DateStrip = class {
|
|
929
|
-
constructor(ctx, pick, focus, onShown) {
|
|
975
|
+
constructor(ctx, pick, focus, onShown, onPage) {
|
|
930
976
|
this.ctx = ctx;
|
|
931
977
|
this.pick = pick;
|
|
932
978
|
this.onShown = onShown;
|
|
979
|
+
this.onPage = onPage;
|
|
933
980
|
const { doc, text } = ctx;
|
|
934
981
|
this.focus = focus;
|
|
935
982
|
this.shown = startOfWeek(focus, ctx.firstDay);
|
|
@@ -953,6 +1000,10 @@ var DateStrip = class {
|
|
|
953
1000
|
this.grid.addEventListener("keydown", this.onKeyDown);
|
|
954
1001
|
this.el.addEventListener("pointerdown", this.onPointerDown);
|
|
955
1002
|
this.el.addEventListener("pointerup", this.onPointerUp);
|
|
1003
|
+
this.el.addEventListener("touchstart", this.ownTouch, { passive: true });
|
|
1004
|
+
this.el.addEventListener("touchmove", this.ownTouch, { passive: true });
|
|
1005
|
+
this.el.addEventListener("touchend", this.ownTouch, { passive: true });
|
|
1006
|
+
this.el.addEventListener("touchcancel", this.ownTouch, { passive: true });
|
|
956
1007
|
this.el.addEventListener("pointercancel", this.onPointerCancel);
|
|
957
1008
|
this.el.addEventListener("click", this.onClickCapture, true);
|
|
958
1009
|
this.draw();
|
|
@@ -961,6 +1012,7 @@ var DateStrip = class {
|
|
|
961
1012
|
ctx;
|
|
962
1013
|
pick;
|
|
963
1014
|
onShown;
|
|
1015
|
+
onPage;
|
|
964
1016
|
el;
|
|
965
1017
|
title;
|
|
966
1018
|
dows;
|
|
@@ -1019,6 +1071,7 @@ var DateStrip = class {
|
|
|
1019
1071
|
step(dir) {
|
|
1020
1072
|
this.shown = this.expanded ? addMonths(this.shown, dir) : this.shown + 7 * dir;
|
|
1021
1073
|
this.draw();
|
|
1074
|
+
this.onPage?.(this.expanded ? this.shown : this.focus + 7 * dir);
|
|
1022
1075
|
}
|
|
1023
1076
|
days() {
|
|
1024
1077
|
if (!this.expanded) return Array.from({ length: 7 }, (_, i) => this.shown + i);
|
|
@@ -1043,7 +1096,8 @@ var DateStrip = class {
|
|
|
1043
1096
|
const tabDay = days.includes(this.focus) ? this.focus : days[0];
|
|
1044
1097
|
const cells = days.map((d) => {
|
|
1045
1098
|
const arg = ctx.dayArg(d);
|
|
1046
|
-
const
|
|
1099
|
+
const items = store.inRange(d, d + 1).filter((n) => n.item.display !== "background" && n.item.display !== "none");
|
|
1100
|
+
const count = items.length;
|
|
1047
1101
|
const first = ymdOfDay(d).day === 1 && d !== days[0] && !this.expanded;
|
|
1048
1102
|
const label = fmt.longDate(d);
|
|
1049
1103
|
const cell = h(
|
|
@@ -1067,7 +1121,15 @@ var DateStrip = class {
|
|
|
1067
1121
|
}
|
|
1068
1122
|
);
|
|
1069
1123
|
if (first) cell.append(h(doc, "span", "tnc-strip-month", { "aria-hidden": "true" }, fmt.monthShort(d)));
|
|
1070
|
-
cell.append(h(doc, "span", "tnc-strip-num", null, fmt.dayNumber(d))
|
|
1124
|
+
cell.append(h(doc, "span", "tnc-strip-num", null, fmt.dayNumber(d)));
|
|
1125
|
+
const dots = h(doc, "span", "tnc-strip-dots", { "aria-hidden": "true" });
|
|
1126
|
+
for (const item of items.slice(0, 3)) {
|
|
1127
|
+
const dot = h(doc, "span", "tnc-strip-dot");
|
|
1128
|
+
const color = item.item.color || item.item.borderColor || item.item.backgroundColor;
|
|
1129
|
+
if (color) dot.style.backgroundColor = color;
|
|
1130
|
+
dots.append(dot);
|
|
1131
|
+
}
|
|
1132
|
+
cell.append(dots);
|
|
1071
1133
|
return cell;
|
|
1072
1134
|
});
|
|
1073
1135
|
this.grid.replaceChildren(...cells);
|
|
@@ -1107,6 +1169,9 @@ var DateStrip = class {
|
|
|
1107
1169
|
if (ev.button !== 0 || this.ctx.draggingId) return;
|
|
1108
1170
|
this.swipe = { id: ev.pointerId, x: ev.clientX, y: ev.clientY };
|
|
1109
1171
|
};
|
|
1172
|
+
ownTouch = (event) => {
|
|
1173
|
+
event.stopPropagation();
|
|
1174
|
+
};
|
|
1110
1175
|
onPointerUp = (ev) => {
|
|
1111
1176
|
const s = this.swipe;
|
|
1112
1177
|
this.swipe = null;
|
|
@@ -1146,6 +1211,7 @@ var TimeGridView = class {
|
|
|
1146
1211
|
const showGrid = opts.timeGrid;
|
|
1147
1212
|
this.el = h(doc, "div", "tnc-tg", { role: "table", "aria-label": ctx.view.title });
|
|
1148
1213
|
this.el.classList.toggle("tnc-tg--no-grid", !showGrid);
|
|
1214
|
+
this.el.classList.toggle("tnc-tg--pinch", showGrid && opts.pinchZoom);
|
|
1149
1215
|
this.scroller = h(doc, "div", "tnc-tg-scroll");
|
|
1150
1216
|
this.dayNavigation = h(doc, "div", "tnc-tg-day-navigation", { role: "group", "aria-label": ctx.view.title });
|
|
1151
1217
|
this.scroller.addEventListener("scroll", () => this.updateDayNavigation());
|
|
@@ -1295,6 +1361,11 @@ var TimeGridView = class {
|
|
|
1295
1361
|
}
|
|
1296
1362
|
if (this.nowGutter) gutter.append(this.nowGutter);
|
|
1297
1363
|
}
|
|
1364
|
+
/** Slot height as drawn, which `expandRows` can stretch past the option. */
|
|
1365
|
+
get drawnSlotHeight() {
|
|
1366
|
+
return this.ctx.slot.duration * this.ppm;
|
|
1367
|
+
}
|
|
1368
|
+
/** Applies `slotHeight`. Items are placed in percentages, so they follow without a render. */
|
|
1298
1369
|
applyScale() {
|
|
1299
1370
|
const { opts, slot } = this.ctx;
|
|
1300
1371
|
this.ppm = opts.slotHeight / slot.duration;
|
|
@@ -1475,15 +1546,17 @@ var TimeGridView = class {
|
|
|
1475
1546
|
const { opts } = ctx;
|
|
1476
1547
|
const readableWidth = Number.parseFloat(ctx.win.getComputedStyle(this.el).getPropertyValue("--tnc-event-min-width")) || 128;
|
|
1477
1548
|
const laneWidth = c.col.clientWidth * (lane === "actual" && placements.some((placement) => placement.split) ? 0.22 : 1);
|
|
1478
|
-
const
|
|
1549
|
+
const overlapThreshold = opts.overlapStackThreshold ?? 0;
|
|
1550
|
+
const collapsedClusters = new Set(placements.filter((placement) => {
|
|
1479
1551
|
const columns = opts.eventMaxStack > 0 ? Math.min(placement.cols, opts.eventMaxStack) : placement.cols;
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
const maxStack = collapse ? 1 : opts.eventMaxStack;
|
|
1552
|
+
const denseOverlap = overlapThreshold > 1 && placement.cols >= overlapThreshold;
|
|
1553
|
+
return columns > 1 && (columns * readableWidth > laneWidth || denseOverlap);
|
|
1554
|
+
}).map((placement) => placement.cluster));
|
|
1484
1555
|
const hiddenByCluster = /* @__PURE__ */ new Map();
|
|
1485
1556
|
pool.begin();
|
|
1486
1557
|
for (const p of placements) {
|
|
1558
|
+
const withMore = lane === "plan" || collapsedClusters.has(p.cluster);
|
|
1559
|
+
const maxStack = collapsedClusters.has(p.cluster) ? 1 : opts.eventMaxStack;
|
|
1487
1560
|
const preview = p.item.item.preview || p.item.id === ctx.draggingId;
|
|
1488
1561
|
if (!preview && withMore && maxStack > 0 && p.cols > maxStack && p.col >= maxStack - 1) {
|
|
1489
1562
|
const rec = hiddenByCluster.get(p.cluster) ?? { count: 0, start: p.startMin, end: p.endMin, split: p.split, items: [] };
|
|
@@ -1496,7 +1569,7 @@ var TimeGridView = class {
|
|
|
1496
1569
|
}
|
|
1497
1570
|
const cols = !preview && withMore && maxStack > 0 ? Math.min(p.cols, maxStack) : p.cols;
|
|
1498
1571
|
const span = Math.min(p.span, cols - p.col);
|
|
1499
|
-
const left = p.col / cols;
|
|
1572
|
+
const left = p.item.id === ctx.draggingId ? 0 : p.col / cols;
|
|
1500
1573
|
let width = span / cols;
|
|
1501
1574
|
if (opts.slotEventOverlap && cols > 1 && p.col + span < cols) width = Math.min(1 - left, width * 1.7);
|
|
1502
1575
|
const el = pool.place(p.item.id, this.meta(p, false));
|
|
@@ -1504,15 +1577,16 @@ var TimeGridView = class {
|
|
|
1504
1577
|
el.classList.toggle("is-short", short);
|
|
1505
1578
|
setStyle(el, {
|
|
1506
1579
|
top: top(p.startMin),
|
|
1507
|
-
height: `max(${height(p)}, ${opts.eventMinHeight}px)`,
|
|
1580
|
+
height: p.abutted ? height(p) : `max(${height(p)}, ${opts.eventMinHeight}px)`,
|
|
1508
1581
|
...inLane(lane, p.split, left, width),
|
|
1509
1582
|
"--tnc-event-readable-width": pct(span / cols / width),
|
|
1510
1583
|
"z-index": String(p.col + 1)
|
|
1511
1584
|
});
|
|
1512
1585
|
}
|
|
1513
1586
|
pool.end();
|
|
1514
|
-
if (!withMore) return;
|
|
1515
1587
|
for (const [cluster, rec] of hiddenByCluster) {
|
|
1588
|
+
const collapse = collapsedClusters.has(cluster);
|
|
1589
|
+
const maxStack = collapse ? 1 : opts.eventMaxStack;
|
|
1516
1590
|
const key = `${lane}-${cluster}`;
|
|
1517
1591
|
let link = c.more.querySelector(`[data-cluster="${key}"]`);
|
|
1518
1592
|
if (!link) {
|
|
@@ -1678,7 +1752,9 @@ var TimeGridView = class {
|
|
|
1678
1752
|
scrollToTime(minute) {
|
|
1679
1753
|
if (!this.body) return;
|
|
1680
1754
|
const y = (minute - this.ctx.slot.min) * this.ppm;
|
|
1681
|
-
|
|
1755
|
+
const label = this.gutter?.querySelector(".tnc-slot-label");
|
|
1756
|
+
const inset = label ? Math.ceil(label.getBoundingClientRect().height / 2) + 2 : 0;
|
|
1757
|
+
this.scroller.scrollTop = Math.max(0, y - inset);
|
|
1682
1758
|
}
|
|
1683
1759
|
getScroll() {
|
|
1684
1760
|
return { top: this.scroller.scrollTop, left: this.scroller.scrollLeft };
|
|
@@ -2342,7 +2418,7 @@ ${this.ctx.text.more(items.length - 8)}` : ""}` : label;
|
|
|
2342
2418
|
const bounds = month.getBoundingClientRect();
|
|
2343
2419
|
const viewport = this.scroller.getBoundingClientRect();
|
|
2344
2420
|
if (bounds.top < viewport.top) this.scroller.scrollTop += bounds.top - viewport.top;
|
|
2345
|
-
else if (bounds.bottom > viewport.bottom) this.scroller.scrollTop += bounds.
|
|
2421
|
+
else if (bounds.bottom > viewport.bottom) this.scroller.scrollTop += bounds.top - viewport.top;
|
|
2346
2422
|
}
|
|
2347
2423
|
elementsFor(_id) {
|
|
2348
2424
|
return [];
|
|
@@ -2356,6 +2432,8 @@ ${this.ctx.text.more(items.length - 8)}` : ""}` : label;
|
|
|
2356
2432
|
var EXTERNAL_ID = "__tnc_external__";
|
|
2357
2433
|
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
2358
2434
|
var MAX_AGENDA_DAYS = 366;
|
|
2435
|
+
var PINCH_MIN_SLOT = 16;
|
|
2436
|
+
var PINCH_MAX_SLOT = 96;
|
|
2359
2437
|
function sameOption(a, b, depth = 0) {
|
|
2360
2438
|
if (a === b) return true;
|
|
2361
2439
|
if (typeof a === "function" && typeof b === "function") return true;
|
|
@@ -2383,7 +2461,7 @@ var Calendar = class {
|
|
|
2383
2461
|
this.win = this.doc.defaultView ?? globalThis;
|
|
2384
2462
|
this.opts = { ...defaultOptions(), ...options };
|
|
2385
2463
|
this.zone = getTimeZone(this.opts.timeZone);
|
|
2386
|
-
this.fmt = new Formatter(this.opts.locale, this.opts.hour12);
|
|
2464
|
+
this.fmt = new Formatter(this.opts.locale, this.opts.hour12, this.opts.formatTime);
|
|
2387
2465
|
this.text = this.resolveText();
|
|
2388
2466
|
this.order = parseOrder(this.opts.eventOrder);
|
|
2389
2467
|
this.store = new ItemStore(this.zone, {
|
|
@@ -2425,13 +2503,16 @@ var Calendar = class {
|
|
|
2425
2503
|
draggingId = null;
|
|
2426
2504
|
listDragPreview = null;
|
|
2427
2505
|
dragSources = [];
|
|
2506
|
+
dragWidth = null;
|
|
2428
2507
|
dragScrollFrame = null;
|
|
2429
2508
|
dragScrollOrigins = /* @__PURE__ */ new Map();
|
|
2509
|
+
dragScrollEdges = /* @__PURE__ */ new Map();
|
|
2430
2510
|
itemWriteStates = /* @__PURE__ */ new Map();
|
|
2431
2511
|
dragRow = null;
|
|
2432
2512
|
strip = null;
|
|
2433
2513
|
stripSize = null;
|
|
2434
2514
|
rebuilding = false;
|
|
2515
|
+
pinch = null;
|
|
2435
2516
|
toolbarEl;
|
|
2436
2517
|
viewHost;
|
|
2437
2518
|
current = null;
|
|
@@ -2540,7 +2621,7 @@ var Calendar = class {
|
|
|
2540
2621
|
defaultAllDayDays: this.opts.defaultAllDayDays
|
|
2541
2622
|
});
|
|
2542
2623
|
}
|
|
2543
|
-
if (has("locale") || has("hour12")) this.fmt = new Formatter(this.opts.locale, this.opts.hour12);
|
|
2624
|
+
if (has("locale") || has("hour12") || has("formatTime")) this.fmt = new Formatter(this.opts.locale, this.opts.hour12, this.opts.formatTime);
|
|
2544
2625
|
if (has("text") || has("locale")) this.text = this.resolveText();
|
|
2545
2626
|
if (has("eventOrder")) this.order = parseOrder(this.opts.eventOrder);
|
|
2546
2627
|
if (has("date")) this.anchor = this.toDay(this.opts.date) ?? this.anchor;
|
|
@@ -2779,7 +2860,10 @@ var Calendar = class {
|
|
|
2779
2860
|
if (this.conflicts.has(n.id)) el.classList.add("is-conflict");
|
|
2780
2861
|
const indicator = variant === "indicator";
|
|
2781
2862
|
if (!preview && !indicator && (this.isEditable(n, "start", true) || this.isEditable(n, "duration", true))) el.classList.add("is-draggable");
|
|
2782
|
-
if (this.draggingId === n.id)
|
|
2863
|
+
if (this.draggingId === n.id) {
|
|
2864
|
+
el.classList.add("is-dragging");
|
|
2865
|
+
if (variant === "timed" && this.dragWidth !== null) el.style.setProperty("--tnc-drag-width", `${this.dragWidth}px`);
|
|
2866
|
+
}
|
|
2783
2867
|
if (this.selectedIds.has(n.id) && !preview) el.classList.add("is-selected");
|
|
2784
2868
|
el.classList.add(...classList(it.className));
|
|
2785
2869
|
const arg = this.itemArg(el, meta);
|
|
@@ -3047,7 +3131,9 @@ var Calendar = class {
|
|
|
3047
3131
|
this.root.classList.add("tnc--has-strip");
|
|
3048
3132
|
const { startDay, endDay } = this.view.range;
|
|
3049
3133
|
if (!this.strip) {
|
|
3050
|
-
const strip = new DateStrip(this, (day, ev) => this.pickStripDay(day, ev), startDay, () => this.stripShown())
|
|
3134
|
+
const strip = new DateStrip(this, (day, ev) => this.pickStripDay(day, ev), startDay, () => this.stripShown(), (day) => {
|
|
3135
|
+
if (this.agendaList()) this.showListDay(day);
|
|
3136
|
+
});
|
|
3051
3137
|
this.strip = strip;
|
|
3052
3138
|
this.root.insertBefore(strip.el, this.viewHost);
|
|
3053
3139
|
const RO = this.win.ResizeObserver;
|
|
@@ -3411,7 +3497,10 @@ var Calendar = class {
|
|
|
3411
3497
|
r.addEventListener("dragover", this.onNativeDragOver);
|
|
3412
3498
|
r.addEventListener("dragleave", this.onNativeDragLeave);
|
|
3413
3499
|
r.addEventListener("drop", this.onNativeDrop);
|
|
3500
|
+
r.addEventListener("touchstart", this.onTouchStart, { passive: true });
|
|
3414
3501
|
r.addEventListener("touchmove", this.onTouchMove, { passive: false });
|
|
3502
|
+
r.addEventListener("touchend", this.onTouchEnd);
|
|
3503
|
+
r.addEventListener("touchcancel", this.onTouchEnd);
|
|
3415
3504
|
r.addEventListener("pointerover", this.onAdoptCheck);
|
|
3416
3505
|
r.addEventListener("focusin", this.onAdoptCheck);
|
|
3417
3506
|
this.doc.addEventListener("pointerdown", this.onDocPointerDown, true);
|
|
@@ -3430,7 +3519,10 @@ var Calendar = class {
|
|
|
3430
3519
|
r.removeEventListener("dragover", this.onNativeDragOver);
|
|
3431
3520
|
r.removeEventListener("dragleave", this.onNativeDragLeave);
|
|
3432
3521
|
r.removeEventListener("drop", this.onNativeDrop);
|
|
3522
|
+
r.removeEventListener("touchstart", this.onTouchStart);
|
|
3433
3523
|
r.removeEventListener("touchmove", this.onTouchMove);
|
|
3524
|
+
r.removeEventListener("touchend", this.onTouchEnd);
|
|
3525
|
+
r.removeEventListener("touchcancel", this.onTouchEnd);
|
|
3434
3526
|
r.removeEventListener("pointerover", this.onAdoptCheck);
|
|
3435
3527
|
r.removeEventListener("focusin", this.onAdoptCheck);
|
|
3436
3528
|
this.doc.removeEventListener("pointerdown", this.onDocPointerDown, true);
|
|
@@ -3525,7 +3617,11 @@ var Calendar = class {
|
|
|
3525
3617
|
if (s.origin) {
|
|
3526
3618
|
this.draggingId = s.origin.id;
|
|
3527
3619
|
for (const el of this.elementsFor(s.origin.id)) {
|
|
3528
|
-
if (this.
|
|
3620
|
+
if (this.meta.get(el)?.variant === "timed" && s.mode === "move") {
|
|
3621
|
+
this.dragWidth = el.getBoundingClientRect().width;
|
|
3622
|
+
el.style.setProperty("--tnc-drag-width", `${this.dragWidth}px`);
|
|
3623
|
+
}
|
|
3624
|
+
if (this.opts.view !== "list" && !(this.current instanceof DayGridView) && s.mode === "move") {
|
|
3529
3625
|
const source = el.cloneNode(true);
|
|
3530
3626
|
source.classList.remove("is-selected");
|
|
3531
3627
|
source.classList.add("tnc-drag-source");
|
|
@@ -3578,24 +3674,96 @@ var Calendar = class {
|
|
|
3578
3674
|
const coordinate = axis === "x" ? pointer.clientX : pointer.clientY;
|
|
3579
3675
|
const lower = Math.max(0, axis === "x" ? viewport.left : viewport.top);
|
|
3580
3676
|
const upper = Math.min(axis === "x" ? this.win.innerWidth : this.win.innerHeight, axis === "x" ? viewport.right : viewport.bottom);
|
|
3581
|
-
if (coordinate < lower || coordinate > upper)
|
|
3677
|
+
if (coordinate < lower || coordinate > upper) {
|
|
3678
|
+
this.dragScrollEdges.delete(axis);
|
|
3679
|
+
continue;
|
|
3680
|
+
}
|
|
3582
3681
|
const delta = coordinate < lower + 32 ? -Math.min(12, (lower + 32 - coordinate) / 2) : coordinate > upper - 32 ? Math.min(12, (coordinate - upper + 32) / 2) : 0;
|
|
3583
|
-
if (!delta)
|
|
3682
|
+
if (!delta) {
|
|
3683
|
+
this.dragScrollEdges.delete(axis);
|
|
3684
|
+
continue;
|
|
3685
|
+
}
|
|
3686
|
+
const edge = this.dragScrollEdges.get(axis);
|
|
3687
|
+
const direction = Math.sign(delta);
|
|
3688
|
+
const now = this.win.performance.now();
|
|
3689
|
+
if (!edge || edge.scroller !== scroller || edge.direction !== direction) {
|
|
3690
|
+
this.dragScrollEdges.set(axis, { scroller, direction, since: now });
|
|
3691
|
+
continue;
|
|
3692
|
+
}
|
|
3693
|
+
if (now - edge.since < 180) continue;
|
|
3584
3694
|
if (!this.dragScrollOrigins.has(scroller)) this.dragScrollOrigins.set(scroller, { top: scroller.scrollTop, left: scroller.scrollLeft });
|
|
3585
3695
|
const before = axis === "x" ? scroller.scrollLeft : scroller.scrollTop;
|
|
3586
3696
|
if (axis === "x") scroller.scrollLeft += delta;
|
|
3587
3697
|
else scroller.scrollTop += delta;
|
|
3588
3698
|
moved ||= before !== (axis === "x" ? scroller.scrollLeft : scroller.scrollTop);
|
|
3589
3699
|
}
|
|
3590
|
-
}
|
|
3700
|
+
} else this.dragScrollEdges.clear();
|
|
3591
3701
|
if (moved) this.track(session, pointer);
|
|
3592
3702
|
this.dragScrollFrame = this.win.requestAnimationFrame(tick);
|
|
3593
3703
|
};
|
|
3594
3704
|
this.dragScrollFrame = this.win.requestAnimationFrame(tick);
|
|
3595
3705
|
}
|
|
3706
|
+
onTouchStart = (ev) => {
|
|
3707
|
+
const first = ev.touches[0];
|
|
3708
|
+
const second = ev.touches[1];
|
|
3709
|
+
if (!first || !second) {
|
|
3710
|
+
this.endPinch();
|
|
3711
|
+
return;
|
|
3712
|
+
}
|
|
3713
|
+
const view = this.current;
|
|
3714
|
+
if (this.session?.active || !this.opts.pinchZoom || !(view instanceof TimeGridView)) return;
|
|
3715
|
+
const body = first.target.closest(".tnc-tg-body");
|
|
3716
|
+
if (!body?.contains(second.target)) return;
|
|
3717
|
+
this.endSession(null, true);
|
|
3718
|
+
if (this.pinch) return;
|
|
3719
|
+
const { top, height } = body.getBoundingClientRect();
|
|
3720
|
+
const y = (first.clientY + second.clientY) / 2;
|
|
3721
|
+
this.pinch = {
|
|
3722
|
+
body,
|
|
3723
|
+
distance: Math.max(1, Math.hypot(first.clientX - second.clientX, first.clientY - second.clientY)),
|
|
3724
|
+
y,
|
|
3725
|
+
anchor: (y - top) / height,
|
|
3726
|
+
drawn: view.drawnSlotHeight,
|
|
3727
|
+
option: this.opts.slotHeight
|
|
3728
|
+
};
|
|
3729
|
+
};
|
|
3596
3730
|
onTouchMove = (ev) => {
|
|
3597
3731
|
if (this.session?.active) ev.preventDefault();
|
|
3732
|
+
const p = this.pinch;
|
|
3733
|
+
const first = ev.touches[0];
|
|
3734
|
+
const second = ev.touches[1];
|
|
3735
|
+
if (!p || !first || !second) return;
|
|
3736
|
+
if (!ev.cancelable) {
|
|
3737
|
+
this.endPinch();
|
|
3738
|
+
return;
|
|
3739
|
+
}
|
|
3740
|
+
ev.preventDefault();
|
|
3741
|
+
const height = p.drawn * Math.hypot(first.clientX - second.clientX, first.clientY - second.clientY) / p.distance;
|
|
3742
|
+
p.y = (first.clientY + second.clientY) / 2;
|
|
3743
|
+
this.pinchTo(p, Math.min(Math.max(p.drawn, PINCH_MAX_SLOT), Math.max(Math.min(p.drawn, PINCH_MIN_SLOT), height)));
|
|
3598
3744
|
};
|
|
3745
|
+
onTouchEnd = (ev) => {
|
|
3746
|
+
if (ev.touches.length < 2) this.endPinch();
|
|
3747
|
+
};
|
|
3748
|
+
/** Rescales the grid, keeping the anchored time under the fingers. */
|
|
3749
|
+
pinchTo(p, slotHeight) {
|
|
3750
|
+
this.opts = { ...this.opts, slotHeight };
|
|
3751
|
+
if (!(this.current instanceof TimeGridView)) return;
|
|
3752
|
+
this.current.applyScale();
|
|
3753
|
+
const { top, height } = p.body.getBoundingClientRect();
|
|
3754
|
+
const scroller = scrollerOf(p.body, "y");
|
|
3755
|
+
if (scroller) scroller.scrollTop += top + height * p.anchor - p.y;
|
|
3756
|
+
}
|
|
3757
|
+
endPinch() {
|
|
3758
|
+
const p = this.pinch;
|
|
3759
|
+
this.pinch = null;
|
|
3760
|
+
if (!p?.body.isConnected) return;
|
|
3761
|
+
const settled = Math.round(this.opts.slotHeight);
|
|
3762
|
+
const slotHeight = settled === Math.round(p.drawn) ? p.option : settled;
|
|
3763
|
+
this.pinchTo(p, slotHeight);
|
|
3764
|
+
this.current?.render(null);
|
|
3765
|
+
if (slotHeight !== p.option) this.opts.onSlotHeightChange?.(slotHeight);
|
|
3766
|
+
}
|
|
3599
3767
|
onPointerMove = (ev) => {
|
|
3600
3768
|
const s = this.session;
|
|
3601
3769
|
if (!s || ev.pointerId !== s.pointerId) return;
|
|
@@ -3945,6 +4113,7 @@ var Calendar = class {
|
|
|
3945
4113
|
scroller.scrollLeft = position.left;
|
|
3946
4114
|
}
|
|
3947
4115
|
this.dragScrollOrigins.clear();
|
|
4116
|
+
this.dragScrollEdges.clear();
|
|
3948
4117
|
this.session = null;
|
|
3949
4118
|
this.listDragPreview?.element.remove();
|
|
3950
4119
|
this.listDragPreview = null;
|
|
@@ -3963,10 +4132,15 @@ var Calendar = class {
|
|
|
3963
4132
|
);
|
|
3964
4133
|
const draggedId = this.draggingId;
|
|
3965
4134
|
this.draggingId = null;
|
|
4135
|
+
this.dragWidth = null;
|
|
3966
4136
|
this.dragRow = null;
|
|
3967
|
-
if (draggedId) for (const el of this.elementsFor(draggedId))
|
|
4137
|
+
if (draggedId) for (const el of this.elementsFor(draggedId)) {
|
|
4138
|
+
el.classList.remove("is-dragging");
|
|
4139
|
+
el.style.removeProperty("--tnc-drag-width");
|
|
4140
|
+
}
|
|
3968
4141
|
this.strip?.markTarget(null);
|
|
3969
4142
|
if (!s.active) return;
|
|
4143
|
+
if (this.current instanceof DayGridView) this.current.render(null);
|
|
3970
4144
|
if (s.mode === "select") {
|
|
3971
4145
|
if (cancel) this.unselectSilently();
|
|
3972
4146
|
else if (this.selection) this.fireSelect(this.selection, ev);
|
package/dist/esm/core.js
CHANGED
package/dist/esm/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
TIMELINE_UNIT_DAYS,
|
|
8
8
|
defaultOptions,
|
|
9
9
|
localeFirstDay
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-TTM45UAN.js";
|
|
11
11
|
import {
|
|
12
12
|
DAY_MS,
|
|
13
13
|
ItemStore,
|
|
@@ -49,7 +49,7 @@ import {
|
|
|
49
49
|
wallTime,
|
|
50
50
|
weekdayOf,
|
|
51
51
|
ymdOfDay
|
|
52
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-JWORVABV.js";
|
|
53
53
|
|
|
54
54
|
// src/tasknotes/bases.ts
|
|
55
55
|
var FROM_BASES = {
|
|
@@ -91,6 +91,8 @@ var BASES_CALENDAR_DEFAULTS = {
|
|
|
91
91
|
slotDuration: "00:30:00",
|
|
92
92
|
snapDuration: "00:30:00",
|
|
93
93
|
scrollTime: "08:00:00",
|
|
94
|
+
slotHeight: 24,
|
|
95
|
+
pinchZoom: true,
|
|
94
96
|
nowIndicator: true,
|
|
95
97
|
showTimeGrid: true,
|
|
96
98
|
selectMirror: true,
|
|
@@ -155,6 +157,8 @@ function basesLayoutOptions(s, context = {}) {
|
|
|
155
157
|
slotDuration,
|
|
156
158
|
snapDuration: time("snapDuration") ?? slotDuration,
|
|
157
159
|
scrollTime: time("scrollTime", 48) ?? d.scrollTime,
|
|
160
|
+
slotHeight: clamp(Math.round(num(s.slotHeight, d.slotHeight)), 5, 96),
|
|
161
|
+
pinchZoom: pick("pinchZoom") !== false,
|
|
158
162
|
nowIndicator: pick("nowIndicator") !== false,
|
|
159
163
|
timeGrid: showTimeGrid,
|
|
160
164
|
selectMirror: pick("selectMirror") !== false,
|
package/dist/esm/react.js
CHANGED