@tasknotes/calendar 0.1.8 → 0.1.10
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 +18 -1
- package/README.md +1 -0
- package/dist/calendar.css +26 -0
- package/dist/cjs/index.cjs +204 -26
- package/dist/cjs/react.cjs +200 -26
- package/dist/esm/{chunk-EXKFHPW2.js → chunk-5PHSJK36.js} +200 -26
- package/dist/esm/index.js +5 -1
- package/dist/esm/react.js +1 -1
- 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
package/dist/cjs/react.cjs
CHANGED
|
@@ -575,14 +575,16 @@ function pct(n) {
|
|
|
575
575
|
|
|
576
576
|
// src/dom/format.ts
|
|
577
577
|
var Formatter = class {
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
hour12;
|
|
581
|
-
constructor(locale, hour12) {
|
|
578
|
+
constructor(locale, hour12, formatTime) {
|
|
579
|
+
this.formatTime = formatTime;
|
|
582
580
|
this.locale = locale;
|
|
583
581
|
const probe = new Intl.DateTimeFormat(locale, { hour: "numeric" }).resolvedOptions();
|
|
584
582
|
this.hour12 = hour12 ?? (probe.hour12 === true || probe.hourCycle === "h12" || probe.hourCycle === "h11");
|
|
585
583
|
}
|
|
584
|
+
formatTime;
|
|
585
|
+
cache = /* @__PURE__ */ new Map();
|
|
586
|
+
locale;
|
|
587
|
+
hour12;
|
|
586
588
|
fmt(key, opts) {
|
|
587
589
|
let f = this.cache.get(key);
|
|
588
590
|
if (!f) {
|
|
@@ -640,6 +642,7 @@ var Formatter = class {
|
|
|
640
642
|
/** Wall-clock minutes to a label. `compact` drops ":00" in 12-hour formats ("9am"). */
|
|
641
643
|
time(minute, compact = false) {
|
|
642
644
|
const m = (Math.round(minute) % 1440 + 1440) % 1440;
|
|
645
|
+
if (this.formatTime) return this.formatTime(m);
|
|
643
646
|
const date = new Date(Date.UTC(2e3, 0, 1, Math.floor(m / 60), m % 60));
|
|
644
647
|
if (this.hour12) {
|
|
645
648
|
const parts = this.fmt("t12", { hour: "numeric", minute: "2-digit", hour12: true }).formatToParts(date);
|
|
@@ -731,6 +734,7 @@ function defaultOptions() {
|
|
|
731
734
|
snapDuration: void 0,
|
|
732
735
|
scrollTime: "08:00",
|
|
733
736
|
slotHeight: 24,
|
|
737
|
+
pinchZoom: true,
|
|
734
738
|
expandRows: false,
|
|
735
739
|
nowIndicator: true,
|
|
736
740
|
selectMirror: true,
|
|
@@ -772,6 +776,7 @@ var LAYOUT_KEYS = /* @__PURE__ */ new Set([
|
|
|
772
776
|
"locale",
|
|
773
777
|
"firstDay",
|
|
774
778
|
"hour12",
|
|
779
|
+
"formatTime",
|
|
775
780
|
"weekNumbers",
|
|
776
781
|
"weekends",
|
|
777
782
|
"hiddenDays",
|
|
@@ -784,9 +789,11 @@ var LAYOUT_KEYS = /* @__PURE__ */ new Set([
|
|
|
784
789
|
"slotDuration",
|
|
785
790
|
"snapDuration",
|
|
786
791
|
"slotHeight",
|
|
792
|
+
"pinchZoom",
|
|
787
793
|
"expandRows",
|
|
788
794
|
"nowIndicator",
|
|
789
795
|
"slotEventOverlap",
|
|
796
|
+
"overlapStackThreshold",
|
|
790
797
|
"eventMinHeight",
|
|
791
798
|
"eventMaxStack",
|
|
792
799
|
"dayMaxEvents",
|
|
@@ -1066,6 +1073,7 @@ var DayGridView = class {
|
|
|
1066
1073
|
topPx = 24;
|
|
1067
1074
|
lastHeights = "";
|
|
1068
1075
|
dotsMode;
|
|
1076
|
+
dragPreviews = [];
|
|
1069
1077
|
daysPerWeek() {
|
|
1070
1078
|
const { days } = this.ctx;
|
|
1071
1079
|
const first = days[0];
|
|
@@ -1079,6 +1087,36 @@ var DayGridView = class {
|
|
|
1079
1087
|
}
|
|
1080
1088
|
render(_change) {
|
|
1081
1089
|
const { ctx } = this;
|
|
1090
|
+
this.clearDragPreviews();
|
|
1091
|
+
const dragged = ctx.draggingId ? ctx.store.get(ctx.draggingId) : void 0;
|
|
1092
|
+
const source = dragged ? this.elementsFor(dragged.id)[0] : void 0;
|
|
1093
|
+
if (dragged && source && this.el.closest(".tnc-dragging-move")) {
|
|
1094
|
+
const proposals = [dragged, ...[...ctx.store.all()].filter((item) => item.id !== dragged.id && item.item.preview)];
|
|
1095
|
+
for (const proposal of proposals) {
|
|
1096
|
+
const original = this.elementsFor(proposal.id)[0] ?? source;
|
|
1097
|
+
const bounds = original.getBoundingClientRect();
|
|
1098
|
+
const sourceWeek = original.closest(".tnc-dg-week");
|
|
1099
|
+
const offset = bounds.top - sourceWeek.getBoundingClientRect().top;
|
|
1100
|
+
for (const week of this.weeks) {
|
|
1101
|
+
const start = week.days.findIndex((day) => day >= proposal.startDay && day < proposal.endDay);
|
|
1102
|
+
if (start < 0) continue;
|
|
1103
|
+
let end = start + 1;
|
|
1104
|
+
while (!this.dotsMode && end < week.days.length && week.days[end] < proposal.endDay) end++;
|
|
1105
|
+
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 });
|
|
1106
|
+
preview.classList.add("tnc-month-preview");
|
|
1107
|
+
preview.inert = true;
|
|
1108
|
+
setStyle(preview, {
|
|
1109
|
+
left: `calc(${pct(start / week.days.length)} + 2px)`,
|
|
1110
|
+
width: `calc(${pct((end - start) / week.days.length)} - 4px)`,
|
|
1111
|
+
top: `${Math.max(this.topPx, Math.min(offset, week.row.clientHeight - bounds.height - 4))}px`,
|
|
1112
|
+
height: `${bounds.height}px`
|
|
1113
|
+
});
|
|
1114
|
+
week.layer.append(preview);
|
|
1115
|
+
this.dragPreviews.push(preview);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1082
1120
|
this.dotsMode = ctx.opts.monthDisplay !== "full" || this.grid.clientWidth / this.daysPerWeek() < 72;
|
|
1083
1121
|
this.el.classList.toggle("tnc-dg--dots", this.dotsMode);
|
|
1084
1122
|
this.el.classList.toggle("tnc-dg--compact", this.dotsMode && ctx.opts.monthDisplay === "full");
|
|
@@ -1110,6 +1148,10 @@ var DayGridView = class {
|
|
|
1110
1148
|
}
|
|
1111
1149
|
}
|
|
1112
1150
|
}
|
|
1151
|
+
clearDragPreviews() {
|
|
1152
|
+
for (const preview of this.dragPreviews) this.ctx.destroyItemEl(preview);
|
|
1153
|
+
this.dragPreviews = [];
|
|
1154
|
+
}
|
|
1113
1155
|
/** Height of a "+N more" link as the host styles it (`--tnc-more-h`, or a taller min-height). */
|
|
1114
1156
|
measureMore(cs) {
|
|
1115
1157
|
const fallback = px(cs, "--tnc-more-h", this.barPx);
|
|
@@ -1301,6 +1343,7 @@ var DayGridView = class {
|
|
|
1301
1343
|
return this.weeks.flatMap((w) => [...w.pool.elementsFor(id), ...(w.dots ?? []).flatMap((c) => c.pool.elementsFor(id))]);
|
|
1302
1344
|
}
|
|
1303
1345
|
destroy() {
|
|
1346
|
+
this.clearDragPreviews();
|
|
1304
1347
|
this.resizeObs?.disconnect();
|
|
1305
1348
|
for (const w of this.weeks) {
|
|
1306
1349
|
w.pool.clear();
|
|
@@ -1353,6 +1396,7 @@ var ListView = class {
|
|
|
1353
1396
|
frame = null;
|
|
1354
1397
|
/** A picked day stays marked until the list is scrolled by hand. */
|
|
1355
1398
|
pinned = false;
|
|
1399
|
+
pinnedTop = 0;
|
|
1356
1400
|
itemsOn(day) {
|
|
1357
1401
|
const { store, opts } = this.ctx;
|
|
1358
1402
|
const out = store.spanningIn(day, day + 1, true);
|
|
@@ -1484,6 +1528,7 @@ var ListView = class {
|
|
|
1484
1528
|
this.scroller.scrollTop += section.getBoundingClientRect().top - this.scroller.getBoundingClientRect().top;
|
|
1485
1529
|
this.ctx.strip?.setFocus(day);
|
|
1486
1530
|
this.pinned = true;
|
|
1531
|
+
this.pinnedTop = this.scroller.scrollTop;
|
|
1487
1532
|
}
|
|
1488
1533
|
fold = () => {
|
|
1489
1534
|
this.pinned = false;
|
|
@@ -1491,6 +1536,7 @@ var ListView = class {
|
|
|
1491
1536
|
if (strip?.expanded) strip.setExpanded(false);
|
|
1492
1537
|
};
|
|
1493
1538
|
onScroll = () => {
|
|
1539
|
+
if (this.pinned && Math.abs(this.scroller.scrollTop - this.pinnedTop) > 1) this.pinned = false;
|
|
1494
1540
|
if (this.frame !== null) return;
|
|
1495
1541
|
this.frame = this.ctx.win.requestAnimationFrame(() => {
|
|
1496
1542
|
this.frame = null;
|
|
@@ -1501,7 +1547,7 @@ var ListView = class {
|
|
|
1501
1547
|
followTop() {
|
|
1502
1548
|
const strip = this.ctx.strip;
|
|
1503
1549
|
if (!strip || this.pinned) return;
|
|
1504
|
-
const edge = strip.el.getBoundingClientRect().bottom + 1;
|
|
1550
|
+
const edge = Math.max(strip.el.getBoundingClientRect().bottom, this.scroller.getBoundingClientRect().top) + 1;
|
|
1505
1551
|
for (const day of this.ctx.days) {
|
|
1506
1552
|
const section = this.sections.get(day);
|
|
1507
1553
|
if (section && section.getBoundingClientRect().bottom > edge) {
|
|
@@ -1549,10 +1595,11 @@ var ListView = class {
|
|
|
1549
1595
|
// src/dom/views/strip.ts
|
|
1550
1596
|
var SWIPE_PX = 40;
|
|
1551
1597
|
var DateStrip = class {
|
|
1552
|
-
constructor(ctx, pick, focus, onShown) {
|
|
1598
|
+
constructor(ctx, pick, focus, onShown, onPage) {
|
|
1553
1599
|
this.ctx = ctx;
|
|
1554
1600
|
this.pick = pick;
|
|
1555
1601
|
this.onShown = onShown;
|
|
1602
|
+
this.onPage = onPage;
|
|
1556
1603
|
const { doc, text } = ctx;
|
|
1557
1604
|
this.focus = focus;
|
|
1558
1605
|
this.shown = startOfWeek(focus, ctx.firstDay);
|
|
@@ -1576,6 +1623,10 @@ var DateStrip = class {
|
|
|
1576
1623
|
this.grid.addEventListener("keydown", this.onKeyDown);
|
|
1577
1624
|
this.el.addEventListener("pointerdown", this.onPointerDown);
|
|
1578
1625
|
this.el.addEventListener("pointerup", this.onPointerUp);
|
|
1626
|
+
this.el.addEventListener("touchstart", this.ownTouch, { passive: true });
|
|
1627
|
+
this.el.addEventListener("touchmove", this.ownTouch, { passive: true });
|
|
1628
|
+
this.el.addEventListener("touchend", this.ownTouch, { passive: true });
|
|
1629
|
+
this.el.addEventListener("touchcancel", this.ownTouch, { passive: true });
|
|
1579
1630
|
this.el.addEventListener("pointercancel", this.onPointerCancel);
|
|
1580
1631
|
this.el.addEventListener("click", this.onClickCapture, true);
|
|
1581
1632
|
this.draw();
|
|
@@ -1584,6 +1635,7 @@ var DateStrip = class {
|
|
|
1584
1635
|
ctx;
|
|
1585
1636
|
pick;
|
|
1586
1637
|
onShown;
|
|
1638
|
+
onPage;
|
|
1587
1639
|
el;
|
|
1588
1640
|
title;
|
|
1589
1641
|
dows;
|
|
@@ -1642,6 +1694,7 @@ var DateStrip = class {
|
|
|
1642
1694
|
step(dir) {
|
|
1643
1695
|
this.shown = this.expanded ? addMonths(this.shown, dir) : this.shown + 7 * dir;
|
|
1644
1696
|
this.draw();
|
|
1697
|
+
this.onPage?.(this.expanded ? this.shown : this.focus + 7 * dir);
|
|
1645
1698
|
}
|
|
1646
1699
|
days() {
|
|
1647
1700
|
if (!this.expanded) return Array.from({ length: 7 }, (_, i) => this.shown + i);
|
|
@@ -1666,7 +1719,8 @@ var DateStrip = class {
|
|
|
1666
1719
|
const tabDay = days.includes(this.focus) ? this.focus : days[0];
|
|
1667
1720
|
const cells = days.map((d) => {
|
|
1668
1721
|
const arg = ctx.dayArg(d);
|
|
1669
|
-
const
|
|
1722
|
+
const items = store.inRange(d, d + 1).filter((n) => n.item.display !== "background" && n.item.display !== "none");
|
|
1723
|
+
const count = items.length;
|
|
1670
1724
|
const first = ymdOfDay(d).day === 1 && d !== days[0] && !this.expanded;
|
|
1671
1725
|
const label = fmt.longDate(d);
|
|
1672
1726
|
const cell = h(
|
|
@@ -1690,7 +1744,15 @@ var DateStrip = class {
|
|
|
1690
1744
|
}
|
|
1691
1745
|
);
|
|
1692
1746
|
if (first) cell.append(h(doc, "span", "tnc-strip-month", { "aria-hidden": "true" }, fmt.monthShort(d)));
|
|
1693
|
-
cell.append(h(doc, "span", "tnc-strip-num", null, fmt.dayNumber(d))
|
|
1747
|
+
cell.append(h(doc, "span", "tnc-strip-num", null, fmt.dayNumber(d)));
|
|
1748
|
+
const dots = h(doc, "span", "tnc-strip-dots", { "aria-hidden": "true" });
|
|
1749
|
+
for (const item of items.slice(0, 3)) {
|
|
1750
|
+
const dot = h(doc, "span", "tnc-strip-dot");
|
|
1751
|
+
const color = item.item.color || item.item.borderColor || item.item.backgroundColor;
|
|
1752
|
+
if (color) dot.style.backgroundColor = color;
|
|
1753
|
+
dots.append(dot);
|
|
1754
|
+
}
|
|
1755
|
+
cell.append(dots);
|
|
1694
1756
|
return cell;
|
|
1695
1757
|
});
|
|
1696
1758
|
this.grid.replaceChildren(...cells);
|
|
@@ -1730,6 +1792,9 @@ var DateStrip = class {
|
|
|
1730
1792
|
if (ev.button !== 0 || this.ctx.draggingId) return;
|
|
1731
1793
|
this.swipe = { id: ev.pointerId, x: ev.clientX, y: ev.clientY };
|
|
1732
1794
|
};
|
|
1795
|
+
ownTouch = (event) => {
|
|
1796
|
+
event.stopPropagation();
|
|
1797
|
+
};
|
|
1733
1798
|
onPointerUp = (ev) => {
|
|
1734
1799
|
const s = this.swipe;
|
|
1735
1800
|
this.swipe = null;
|
|
@@ -1769,6 +1834,7 @@ var TimeGridView = class {
|
|
|
1769
1834
|
const showGrid = opts.timeGrid;
|
|
1770
1835
|
this.el = h(doc, "div", "tnc-tg", { role: "table", "aria-label": ctx.view.title });
|
|
1771
1836
|
this.el.classList.toggle("tnc-tg--no-grid", !showGrid);
|
|
1837
|
+
this.el.classList.toggle("tnc-tg--pinch", showGrid && opts.pinchZoom);
|
|
1772
1838
|
this.scroller = h(doc, "div", "tnc-tg-scroll");
|
|
1773
1839
|
this.dayNavigation = h(doc, "div", "tnc-tg-day-navigation", { role: "group", "aria-label": ctx.view.title });
|
|
1774
1840
|
this.scroller.addEventListener("scroll", () => this.updateDayNavigation());
|
|
@@ -1918,6 +1984,11 @@ var TimeGridView = class {
|
|
|
1918
1984
|
}
|
|
1919
1985
|
if (this.nowGutter) gutter.append(this.nowGutter);
|
|
1920
1986
|
}
|
|
1987
|
+
/** Slot height as drawn, which `expandRows` can stretch past the option. */
|
|
1988
|
+
get drawnSlotHeight() {
|
|
1989
|
+
return this.ctx.slot.duration * this.ppm;
|
|
1990
|
+
}
|
|
1991
|
+
/** Applies `slotHeight`. Items are placed in percentages, so they follow without a render. */
|
|
1921
1992
|
applyScale() {
|
|
1922
1993
|
const { opts, slot } = this.ctx;
|
|
1923
1994
|
this.ppm = opts.slotHeight / slot.duration;
|
|
@@ -2098,15 +2169,17 @@ var TimeGridView = class {
|
|
|
2098
2169
|
const { opts } = ctx;
|
|
2099
2170
|
const readableWidth = Number.parseFloat(ctx.win.getComputedStyle(this.el).getPropertyValue("--tnc-event-min-width")) || 128;
|
|
2100
2171
|
const laneWidth = c.col.clientWidth * (lane === "actual" && placements.some((placement) => placement.split) ? 0.22 : 1);
|
|
2101
|
-
const
|
|
2172
|
+
const overlapThreshold = opts.overlapStackThreshold ?? 0;
|
|
2173
|
+
const collapsedClusters = new Set(placements.filter((placement) => {
|
|
2102
2174
|
const columns = opts.eventMaxStack > 0 ? Math.min(placement.cols, opts.eventMaxStack) : placement.cols;
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
const maxStack = collapse ? 1 : opts.eventMaxStack;
|
|
2175
|
+
const denseOverlap = overlapThreshold > 1 && placement.cols >= overlapThreshold;
|
|
2176
|
+
return columns > 1 && (columns * readableWidth > laneWidth || denseOverlap);
|
|
2177
|
+
}).map((placement) => placement.cluster));
|
|
2107
2178
|
const hiddenByCluster = /* @__PURE__ */ new Map();
|
|
2108
2179
|
pool.begin();
|
|
2109
2180
|
for (const p of placements) {
|
|
2181
|
+
const withMore = lane === "plan" || collapsedClusters.has(p.cluster);
|
|
2182
|
+
const maxStack = collapsedClusters.has(p.cluster) ? 1 : opts.eventMaxStack;
|
|
2110
2183
|
const preview = p.item.item.preview || p.item.id === ctx.draggingId;
|
|
2111
2184
|
if (!preview && withMore && maxStack > 0 && p.cols > maxStack && p.col >= maxStack - 1) {
|
|
2112
2185
|
const rec = hiddenByCluster.get(p.cluster) ?? { count: 0, start: p.startMin, end: p.endMin, split: p.split, items: [] };
|
|
@@ -2119,7 +2192,7 @@ var TimeGridView = class {
|
|
|
2119
2192
|
}
|
|
2120
2193
|
const cols = !preview && withMore && maxStack > 0 ? Math.min(p.cols, maxStack) : p.cols;
|
|
2121
2194
|
const span = Math.min(p.span, cols - p.col);
|
|
2122
|
-
const left = p.col / cols;
|
|
2195
|
+
const left = p.item.id === ctx.draggingId ? 0 : p.col / cols;
|
|
2123
2196
|
let width = span / cols;
|
|
2124
2197
|
if (opts.slotEventOverlap && cols > 1 && p.col + span < cols) width = Math.min(1 - left, width * 1.7);
|
|
2125
2198
|
const el = pool.place(p.item.id, this.meta(p, false));
|
|
@@ -2134,8 +2207,9 @@ var TimeGridView = class {
|
|
|
2134
2207
|
});
|
|
2135
2208
|
}
|
|
2136
2209
|
pool.end();
|
|
2137
|
-
if (!withMore) return;
|
|
2138
2210
|
for (const [cluster, rec] of hiddenByCluster) {
|
|
2211
|
+
const collapse = collapsedClusters.has(cluster);
|
|
2212
|
+
const maxStack = collapse ? 1 : opts.eventMaxStack;
|
|
2139
2213
|
const key = `${lane}-${cluster}`;
|
|
2140
2214
|
let link = c.more.querySelector(`[data-cluster="${key}"]`);
|
|
2141
2215
|
if (!link) {
|
|
@@ -2301,7 +2375,9 @@ var TimeGridView = class {
|
|
|
2301
2375
|
scrollToTime(minute) {
|
|
2302
2376
|
if (!this.body) return;
|
|
2303
2377
|
const y = (minute - this.ctx.slot.min) * this.ppm;
|
|
2304
|
-
|
|
2378
|
+
const label = this.gutter?.querySelector(".tnc-slot-label");
|
|
2379
|
+
const inset = label ? Math.ceil(label.getBoundingClientRect().height / 2) + 2 : 0;
|
|
2380
|
+
this.scroller.scrollTop = Math.max(0, y - inset);
|
|
2305
2381
|
}
|
|
2306
2382
|
getScroll() {
|
|
2307
2383
|
return { top: this.scroller.scrollTop, left: this.scroller.scrollLeft };
|
|
@@ -2965,7 +3041,7 @@ ${this.ctx.text.more(items.length - 8)}` : ""}` : label;
|
|
|
2965
3041
|
const bounds = month.getBoundingClientRect();
|
|
2966
3042
|
const viewport = this.scroller.getBoundingClientRect();
|
|
2967
3043
|
if (bounds.top < viewport.top) this.scroller.scrollTop += bounds.top - viewport.top;
|
|
2968
|
-
else if (bounds.bottom > viewport.bottom) this.scroller.scrollTop += bounds.
|
|
3044
|
+
else if (bounds.bottom > viewport.bottom) this.scroller.scrollTop += bounds.top - viewport.top;
|
|
2969
3045
|
}
|
|
2970
3046
|
elementsFor(_id) {
|
|
2971
3047
|
return [];
|
|
@@ -2979,6 +3055,8 @@ ${this.ctx.text.more(items.length - 8)}` : ""}` : label;
|
|
|
2979
3055
|
var EXTERNAL_ID = "__tnc_external__";
|
|
2980
3056
|
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
2981
3057
|
var MAX_AGENDA_DAYS = 366;
|
|
3058
|
+
var PINCH_MIN_SLOT = 16;
|
|
3059
|
+
var PINCH_MAX_SLOT = 96;
|
|
2982
3060
|
function sameOption(a, b, depth = 0) {
|
|
2983
3061
|
if (a === b) return true;
|
|
2984
3062
|
if (typeof a === "function" && typeof b === "function") return true;
|
|
@@ -3006,7 +3084,7 @@ var Calendar = class {
|
|
|
3006
3084
|
this.win = this.doc.defaultView ?? globalThis;
|
|
3007
3085
|
this.opts = { ...defaultOptions(), ...options };
|
|
3008
3086
|
this.zone = getTimeZone(this.opts.timeZone);
|
|
3009
|
-
this.fmt = new Formatter(this.opts.locale, this.opts.hour12);
|
|
3087
|
+
this.fmt = new Formatter(this.opts.locale, this.opts.hour12, this.opts.formatTime);
|
|
3010
3088
|
this.text = this.resolveText();
|
|
3011
3089
|
this.order = parseOrder(this.opts.eventOrder);
|
|
3012
3090
|
this.store = new ItemStore(this.zone, {
|
|
@@ -3048,13 +3126,16 @@ var Calendar = class {
|
|
|
3048
3126
|
draggingId = null;
|
|
3049
3127
|
listDragPreview = null;
|
|
3050
3128
|
dragSources = [];
|
|
3129
|
+
dragWidth = null;
|
|
3051
3130
|
dragScrollFrame = null;
|
|
3052
3131
|
dragScrollOrigins = /* @__PURE__ */ new Map();
|
|
3132
|
+
dragScrollEdges = /* @__PURE__ */ new Map();
|
|
3053
3133
|
itemWriteStates = /* @__PURE__ */ new Map();
|
|
3054
3134
|
dragRow = null;
|
|
3055
3135
|
strip = null;
|
|
3056
3136
|
stripSize = null;
|
|
3057
3137
|
rebuilding = false;
|
|
3138
|
+
pinch = null;
|
|
3058
3139
|
toolbarEl;
|
|
3059
3140
|
viewHost;
|
|
3060
3141
|
current = null;
|
|
@@ -3163,7 +3244,7 @@ var Calendar = class {
|
|
|
3163
3244
|
defaultAllDayDays: this.opts.defaultAllDayDays
|
|
3164
3245
|
});
|
|
3165
3246
|
}
|
|
3166
|
-
if (has("locale") || has("hour12")) this.fmt = new Formatter(this.opts.locale, this.opts.hour12);
|
|
3247
|
+
if (has("locale") || has("hour12") || has("formatTime")) this.fmt = new Formatter(this.opts.locale, this.opts.hour12, this.opts.formatTime);
|
|
3167
3248
|
if (has("text") || has("locale")) this.text = this.resolveText();
|
|
3168
3249
|
if (has("eventOrder")) this.order = parseOrder(this.opts.eventOrder);
|
|
3169
3250
|
if (has("date")) this.anchor = this.toDay(this.opts.date) ?? this.anchor;
|
|
@@ -3402,7 +3483,10 @@ var Calendar = class {
|
|
|
3402
3483
|
if (this.conflicts.has(n.id)) el.classList.add("is-conflict");
|
|
3403
3484
|
const indicator = variant === "indicator";
|
|
3404
3485
|
if (!preview && !indicator && (this.isEditable(n, "start", true) || this.isEditable(n, "duration", true))) el.classList.add("is-draggable");
|
|
3405
|
-
if (this.draggingId === n.id)
|
|
3486
|
+
if (this.draggingId === n.id) {
|
|
3487
|
+
el.classList.add("is-dragging");
|
|
3488
|
+
if (variant === "timed" && this.dragWidth !== null) el.style.setProperty("--tnc-drag-width", `${this.dragWidth}px`);
|
|
3489
|
+
}
|
|
3406
3490
|
if (this.selectedIds.has(n.id) && !preview) el.classList.add("is-selected");
|
|
3407
3491
|
el.classList.add(...classList(it.className));
|
|
3408
3492
|
const arg = this.itemArg(el, meta);
|
|
@@ -3670,7 +3754,9 @@ var Calendar = class {
|
|
|
3670
3754
|
this.root.classList.add("tnc--has-strip");
|
|
3671
3755
|
const { startDay, endDay } = this.view.range;
|
|
3672
3756
|
if (!this.strip) {
|
|
3673
|
-
const strip = new DateStrip(this, (day, ev) => this.pickStripDay(day, ev), startDay, () => this.stripShown())
|
|
3757
|
+
const strip = new DateStrip(this, (day, ev) => this.pickStripDay(day, ev), startDay, () => this.stripShown(), (day) => {
|
|
3758
|
+
if (this.agendaList()) this.showListDay(day);
|
|
3759
|
+
});
|
|
3674
3760
|
this.strip = strip;
|
|
3675
3761
|
this.root.insertBefore(strip.el, this.viewHost);
|
|
3676
3762
|
const RO = this.win.ResizeObserver;
|
|
@@ -4034,7 +4120,10 @@ var Calendar = class {
|
|
|
4034
4120
|
r.addEventListener("dragover", this.onNativeDragOver);
|
|
4035
4121
|
r.addEventListener("dragleave", this.onNativeDragLeave);
|
|
4036
4122
|
r.addEventListener("drop", this.onNativeDrop);
|
|
4123
|
+
r.addEventListener("touchstart", this.onTouchStart, { passive: true });
|
|
4037
4124
|
r.addEventListener("touchmove", this.onTouchMove, { passive: false });
|
|
4125
|
+
r.addEventListener("touchend", this.onTouchEnd);
|
|
4126
|
+
r.addEventListener("touchcancel", this.onTouchEnd);
|
|
4038
4127
|
r.addEventListener("pointerover", this.onAdoptCheck);
|
|
4039
4128
|
r.addEventListener("focusin", this.onAdoptCheck);
|
|
4040
4129
|
this.doc.addEventListener("pointerdown", this.onDocPointerDown, true);
|
|
@@ -4053,7 +4142,10 @@ var Calendar = class {
|
|
|
4053
4142
|
r.removeEventListener("dragover", this.onNativeDragOver);
|
|
4054
4143
|
r.removeEventListener("dragleave", this.onNativeDragLeave);
|
|
4055
4144
|
r.removeEventListener("drop", this.onNativeDrop);
|
|
4145
|
+
r.removeEventListener("touchstart", this.onTouchStart);
|
|
4056
4146
|
r.removeEventListener("touchmove", this.onTouchMove);
|
|
4147
|
+
r.removeEventListener("touchend", this.onTouchEnd);
|
|
4148
|
+
r.removeEventListener("touchcancel", this.onTouchEnd);
|
|
4057
4149
|
r.removeEventListener("pointerover", this.onAdoptCheck);
|
|
4058
4150
|
r.removeEventListener("focusin", this.onAdoptCheck);
|
|
4059
4151
|
this.doc.removeEventListener("pointerdown", this.onDocPointerDown, true);
|
|
@@ -4148,7 +4240,11 @@ var Calendar = class {
|
|
|
4148
4240
|
if (s.origin) {
|
|
4149
4241
|
this.draggingId = s.origin.id;
|
|
4150
4242
|
for (const el of this.elementsFor(s.origin.id)) {
|
|
4151
|
-
if (this.
|
|
4243
|
+
if (this.meta.get(el)?.variant === "timed" && s.mode === "move") {
|
|
4244
|
+
this.dragWidth = el.getBoundingClientRect().width;
|
|
4245
|
+
el.style.setProperty("--tnc-drag-width", `${this.dragWidth}px`);
|
|
4246
|
+
}
|
|
4247
|
+
if (this.opts.view !== "list" && !(this.current instanceof DayGridView) && s.mode === "move") {
|
|
4152
4248
|
const source = el.cloneNode(true);
|
|
4153
4249
|
source.classList.remove("is-selected");
|
|
4154
4250
|
source.classList.add("tnc-drag-source");
|
|
@@ -4201,24 +4297,96 @@ var Calendar = class {
|
|
|
4201
4297
|
const coordinate = axis === "x" ? pointer.clientX : pointer.clientY;
|
|
4202
4298
|
const lower = Math.max(0, axis === "x" ? viewport.left : viewport.top);
|
|
4203
4299
|
const upper = Math.min(axis === "x" ? this.win.innerWidth : this.win.innerHeight, axis === "x" ? viewport.right : viewport.bottom);
|
|
4204
|
-
if (coordinate < lower || coordinate > upper)
|
|
4300
|
+
if (coordinate < lower || coordinate > upper) {
|
|
4301
|
+
this.dragScrollEdges.delete(axis);
|
|
4302
|
+
continue;
|
|
4303
|
+
}
|
|
4205
4304
|
const delta = coordinate < lower + 32 ? -Math.min(12, (lower + 32 - coordinate) / 2) : coordinate > upper - 32 ? Math.min(12, (coordinate - upper + 32) / 2) : 0;
|
|
4206
|
-
if (!delta)
|
|
4305
|
+
if (!delta) {
|
|
4306
|
+
this.dragScrollEdges.delete(axis);
|
|
4307
|
+
continue;
|
|
4308
|
+
}
|
|
4309
|
+
const edge = this.dragScrollEdges.get(axis);
|
|
4310
|
+
const direction = Math.sign(delta);
|
|
4311
|
+
const now = this.win.performance.now();
|
|
4312
|
+
if (!edge || edge.scroller !== scroller || edge.direction !== direction) {
|
|
4313
|
+
this.dragScrollEdges.set(axis, { scroller, direction, since: now });
|
|
4314
|
+
continue;
|
|
4315
|
+
}
|
|
4316
|
+
if (now - edge.since < 180) continue;
|
|
4207
4317
|
if (!this.dragScrollOrigins.has(scroller)) this.dragScrollOrigins.set(scroller, { top: scroller.scrollTop, left: scroller.scrollLeft });
|
|
4208
4318
|
const before = axis === "x" ? scroller.scrollLeft : scroller.scrollTop;
|
|
4209
4319
|
if (axis === "x") scroller.scrollLeft += delta;
|
|
4210
4320
|
else scroller.scrollTop += delta;
|
|
4211
4321
|
moved ||= before !== (axis === "x" ? scroller.scrollLeft : scroller.scrollTop);
|
|
4212
4322
|
}
|
|
4213
|
-
}
|
|
4323
|
+
} else this.dragScrollEdges.clear();
|
|
4214
4324
|
if (moved) this.track(session, pointer);
|
|
4215
4325
|
this.dragScrollFrame = this.win.requestAnimationFrame(tick);
|
|
4216
4326
|
};
|
|
4217
4327
|
this.dragScrollFrame = this.win.requestAnimationFrame(tick);
|
|
4218
4328
|
}
|
|
4329
|
+
onTouchStart = (ev) => {
|
|
4330
|
+
const first = ev.touches[0];
|
|
4331
|
+
const second = ev.touches[1];
|
|
4332
|
+
if (!first || !second) {
|
|
4333
|
+
this.endPinch();
|
|
4334
|
+
return;
|
|
4335
|
+
}
|
|
4336
|
+
const view = this.current;
|
|
4337
|
+
if (this.session?.active || !this.opts.pinchZoom || !(view instanceof TimeGridView)) return;
|
|
4338
|
+
const body = first.target.closest(".tnc-tg-body");
|
|
4339
|
+
if (!body?.contains(second.target)) return;
|
|
4340
|
+
this.endSession(null, true);
|
|
4341
|
+
if (this.pinch) return;
|
|
4342
|
+
const { top, height } = body.getBoundingClientRect();
|
|
4343
|
+
const y = (first.clientY + second.clientY) / 2;
|
|
4344
|
+
this.pinch = {
|
|
4345
|
+
body,
|
|
4346
|
+
distance: Math.max(1, Math.hypot(first.clientX - second.clientX, first.clientY - second.clientY)),
|
|
4347
|
+
y,
|
|
4348
|
+
anchor: (y - top) / height,
|
|
4349
|
+
drawn: view.drawnSlotHeight,
|
|
4350
|
+
option: this.opts.slotHeight
|
|
4351
|
+
};
|
|
4352
|
+
};
|
|
4219
4353
|
onTouchMove = (ev) => {
|
|
4220
4354
|
if (this.session?.active) ev.preventDefault();
|
|
4355
|
+
const p = this.pinch;
|
|
4356
|
+
const first = ev.touches[0];
|
|
4357
|
+
const second = ev.touches[1];
|
|
4358
|
+
if (!p || !first || !second) return;
|
|
4359
|
+
if (!ev.cancelable) {
|
|
4360
|
+
this.endPinch();
|
|
4361
|
+
return;
|
|
4362
|
+
}
|
|
4363
|
+
ev.preventDefault();
|
|
4364
|
+
const height = p.drawn * Math.hypot(first.clientX - second.clientX, first.clientY - second.clientY) / p.distance;
|
|
4365
|
+
p.y = (first.clientY + second.clientY) / 2;
|
|
4366
|
+
this.pinchTo(p, Math.min(Math.max(p.drawn, PINCH_MAX_SLOT), Math.max(Math.min(p.drawn, PINCH_MIN_SLOT), height)));
|
|
4221
4367
|
};
|
|
4368
|
+
onTouchEnd = (ev) => {
|
|
4369
|
+
if (ev.touches.length < 2) this.endPinch();
|
|
4370
|
+
};
|
|
4371
|
+
/** Rescales the grid, keeping the anchored time under the fingers. */
|
|
4372
|
+
pinchTo(p, slotHeight) {
|
|
4373
|
+
this.opts = { ...this.opts, slotHeight };
|
|
4374
|
+
if (!(this.current instanceof TimeGridView)) return;
|
|
4375
|
+
this.current.applyScale();
|
|
4376
|
+
const { top, height } = p.body.getBoundingClientRect();
|
|
4377
|
+
const scroller = scrollerOf(p.body, "y");
|
|
4378
|
+
if (scroller) scroller.scrollTop += top + height * p.anchor - p.y;
|
|
4379
|
+
}
|
|
4380
|
+
endPinch() {
|
|
4381
|
+
const p = this.pinch;
|
|
4382
|
+
this.pinch = null;
|
|
4383
|
+
if (!p?.body.isConnected) return;
|
|
4384
|
+
const settled = Math.round(this.opts.slotHeight);
|
|
4385
|
+
const slotHeight = settled === Math.round(p.drawn) ? p.option : settled;
|
|
4386
|
+
this.pinchTo(p, slotHeight);
|
|
4387
|
+
this.current?.render(null);
|
|
4388
|
+
if (slotHeight !== p.option) this.opts.onSlotHeightChange?.(slotHeight);
|
|
4389
|
+
}
|
|
4222
4390
|
onPointerMove = (ev) => {
|
|
4223
4391
|
const s = this.session;
|
|
4224
4392
|
if (!s || ev.pointerId !== s.pointerId) return;
|
|
@@ -4568,6 +4736,7 @@ var Calendar = class {
|
|
|
4568
4736
|
scroller.scrollLeft = position.left;
|
|
4569
4737
|
}
|
|
4570
4738
|
this.dragScrollOrigins.clear();
|
|
4739
|
+
this.dragScrollEdges.clear();
|
|
4571
4740
|
this.session = null;
|
|
4572
4741
|
this.listDragPreview?.element.remove();
|
|
4573
4742
|
this.listDragPreview = null;
|
|
@@ -4586,10 +4755,15 @@ var Calendar = class {
|
|
|
4586
4755
|
);
|
|
4587
4756
|
const draggedId = this.draggingId;
|
|
4588
4757
|
this.draggingId = null;
|
|
4758
|
+
this.dragWidth = null;
|
|
4589
4759
|
this.dragRow = null;
|
|
4590
|
-
if (draggedId) for (const el of this.elementsFor(draggedId))
|
|
4760
|
+
if (draggedId) for (const el of this.elementsFor(draggedId)) {
|
|
4761
|
+
el.classList.remove("is-dragging");
|
|
4762
|
+
el.style.removeProperty("--tnc-drag-width");
|
|
4763
|
+
}
|
|
4591
4764
|
this.strip?.markTarget(null);
|
|
4592
4765
|
if (!s.active) return;
|
|
4766
|
+
if (this.current instanceof DayGridView) this.current.render(null);
|
|
4593
4767
|
if (s.mode === "select") {
|
|
4594
4768
|
if (cancel) this.unselectSilently();
|
|
4595
4769
|
else if (this.selection) this.fireSelect(this.selection, ev);
|