@tasknotes/calendar 0.1.2 → 0.1.4
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 -0
- package/README.md +6 -1
- package/dist/calendar.css +267 -0
- package/dist/cjs/index.cjs +537 -62
- package/dist/cjs/react.cjs +537 -62
- package/dist/esm/{chunk-ZJQMMA6W.js → chunk-26DEUGQH.js} +537 -62
- package/dist/esm/index.js +1 -1
- package/dist/esm/react.js +1 -1
- package/dist/types/dom/calendar.d.cts +21 -0
- package/dist/types/dom/calendar.d.ts +21 -0
- package/dist/types/dom/context.d.cts +9 -0
- package/dist/types/dom/context.d.ts +9 -0
- package/dist/types/dom/options.d.cts +50 -0
- package/dist/types/dom/options.d.ts +50 -0
- package/dist/types/dom/views/list.d.cts +25 -2
- package/dist/types/dom/views/list.d.ts +25 -2
- package/dist/types/dom/views/strip.d.cts +63 -0
- package/dist/types/dom/views/strip.d.ts +63 -0
- package/package.json +1 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -948,6 +948,10 @@ function defaultOptions() {
|
|
|
948
948
|
dayCount: 3,
|
|
949
949
|
listDayCount: 7,
|
|
950
950
|
listStart: "date",
|
|
951
|
+
listAgenda: false,
|
|
952
|
+
dateStrip: false,
|
|
953
|
+
listLeadTitle: null,
|
|
954
|
+
listAddText: null,
|
|
951
955
|
monthDisplay: "full",
|
|
952
956
|
timeZone: "local",
|
|
953
957
|
locale: void 0,
|
|
@@ -997,6 +1001,10 @@ var LAYOUT_KEYS = /* @__PURE__ */ new Set([
|
|
|
997
1001
|
"dayCount",
|
|
998
1002
|
"listDayCount",
|
|
999
1003
|
"listStart",
|
|
1004
|
+
"listAgenda",
|
|
1005
|
+
"dateStrip",
|
|
1006
|
+
"listLeadTitle",
|
|
1007
|
+
"listAddText",
|
|
1000
1008
|
"monthDisplay",
|
|
1001
1009
|
"timeZone",
|
|
1002
1010
|
"locale",
|
|
@@ -1339,21 +1347,48 @@ var DayGridView = class {
|
|
|
1339
1347
|
};
|
|
1340
1348
|
|
|
1341
1349
|
// src/dom/views/list.ts
|
|
1350
|
+
var LOAD_AHEAD = "0px 0px 600px 0px";
|
|
1342
1351
|
var ListView = class {
|
|
1343
1352
|
constructor(ctx) {
|
|
1344
1353
|
this.ctx = ctx;
|
|
1345
|
-
|
|
1346
|
-
this.
|
|
1347
|
-
this.
|
|
1354
|
+
const { doc, win, opts } = ctx;
|
|
1355
|
+
this.el = h(doc, "div", "tnc-list");
|
|
1356
|
+
this.scroller = h(doc, "div", "tnc-list-scroll");
|
|
1357
|
+
this.body = h(doc, "div", "tnc-list-body", { role: "list", "aria-label": ctx.view.title });
|
|
1348
1358
|
this.scroller.append(this.body);
|
|
1349
1359
|
this.el.append(this.scroller);
|
|
1360
|
+
this.agenda = opts.listAgenda;
|
|
1361
|
+
if (!this.agenda) return;
|
|
1362
|
+
this.el.classList.add("tnc-list--agenda");
|
|
1363
|
+
this.sentinel = h(doc, "div", "tnc-list-more", { "aria-hidden": "true" });
|
|
1364
|
+
this.scroller.append(this.sentinel);
|
|
1365
|
+
const IO = win.IntersectionObserver;
|
|
1366
|
+
if (IO) {
|
|
1367
|
+
this.loader = new IO(
|
|
1368
|
+
(entries) => {
|
|
1369
|
+
if (entries.some((e) => e.isIntersecting)) ctx.extendList(ctx.view.range.endDay + Math.max(7, opts.listDayCount));
|
|
1370
|
+
},
|
|
1371
|
+
// Auto height scrolls the page, so watch the viewport; fixed height scrolls the list.
|
|
1372
|
+
{ root: opts.height === "auto" ? null : this.scroller, rootMargin: LOAD_AHEAD }
|
|
1373
|
+
);
|
|
1374
|
+
}
|
|
1375
|
+
win.addEventListener("scroll", this.onScroll, { capture: true, passive: true });
|
|
1376
|
+
this.scroller.addEventListener("wheel", this.fold, { passive: true });
|
|
1377
|
+
this.scroller.addEventListener("touchmove", this.fold, { passive: true });
|
|
1350
1378
|
}
|
|
1351
1379
|
ctx;
|
|
1352
1380
|
el;
|
|
1353
1381
|
scroller;
|
|
1354
1382
|
body;
|
|
1383
|
+
agenda;
|
|
1355
1384
|
pools = /* @__PURE__ */ new Map();
|
|
1356
1385
|
sections = /* @__PURE__ */ new Map();
|
|
1386
|
+
lead = null;
|
|
1387
|
+
sentinel = null;
|
|
1388
|
+
loader = null;
|
|
1389
|
+
frame = null;
|
|
1390
|
+
/** A picked day stays marked until the list is scrolled by hand. */
|
|
1391
|
+
pinned = false;
|
|
1357
1392
|
itemsOn(day) {
|
|
1358
1393
|
const { store, opts } = this.ctx;
|
|
1359
1394
|
const out = store.spanningIn(day, day + 1, true);
|
|
@@ -1362,46 +1397,45 @@ var ListView = class {
|
|
|
1362
1397
|
return out.filter((n) => n.item.display !== "background" && n.item.display !== "none" && (opts.allDaySlot || !n.allDay)).sort((a, b) => Number(b.allDay) - Number(a.allDay) || this.ctx.order(a, b));
|
|
1363
1398
|
}
|
|
1364
1399
|
render(_change) {
|
|
1365
|
-
const { ctx } = this;
|
|
1366
|
-
const { doc, days } = ctx;
|
|
1400
|
+
const { ctx, agenda } = this;
|
|
1401
|
+
const { doc, days, opts } = ctx;
|
|
1402
|
+
const includes = agenda && opts.listLeadTitle !== null ? opts.listLeadIncludes : void 0;
|
|
1403
|
+
const leadItems = [];
|
|
1404
|
+
const inLead = /* @__PURE__ */ new Set();
|
|
1367
1405
|
const keep = /* @__PURE__ */ new Set();
|
|
1368
1406
|
let prev = null;
|
|
1369
1407
|
for (const day of days) {
|
|
1370
|
-
|
|
1371
|
-
if (
|
|
1408
|
+
let items = this.itemsOn(day);
|
|
1409
|
+
if (includes) {
|
|
1410
|
+
items = items.filter((n) => {
|
|
1411
|
+
if (!includes(n.item)) return true;
|
|
1412
|
+
if (!inLead.has(n.id)) {
|
|
1413
|
+
inLead.add(n.id);
|
|
1414
|
+
leadItems.push(n);
|
|
1415
|
+
}
|
|
1416
|
+
return false;
|
|
1417
|
+
});
|
|
1418
|
+
}
|
|
1419
|
+
if (!items.length && !agenda) continue;
|
|
1372
1420
|
keep.add(day);
|
|
1373
1421
|
let section = this.sections.get(day);
|
|
1374
1422
|
let pool = this.pools.get(day);
|
|
1375
1423
|
if (!section || !pool) {
|
|
1376
1424
|
const arg = ctx.dayArg(day);
|
|
1377
1425
|
const heading = h(doc, "div", ["tnc-list-day", ...ctx.dayClasses(day)].join(" "), { "data-date": arg.dateStr });
|
|
1378
|
-
const custom =
|
|
1426
|
+
const custom = opts.dayHeaderContent?.(arg);
|
|
1379
1427
|
const label = custom ?? ctx.fmt.longDate(day);
|
|
1380
|
-
heading.append(
|
|
1381
|
-
const
|
|
1382
|
-
section = h(doc, "div", "tnc-list-section", { role: "listitem", "aria-label": ctx.fmt.longDate(day), "data-date": arg.dateStr }, heading,
|
|
1383
|
-
|
|
1384
|
-
|
|
1428
|
+
heading.append(opts.navLinks ? ctx.navLink(day, label, "tnc-list-day-link") : label);
|
|
1429
|
+
const rows = h(doc, "div", "tnc-list-rows", { role: "group" });
|
|
1430
|
+
section = h(doc, "div", "tnc-list-section", { role: "listitem", "aria-label": ctx.fmt.longDate(day), "data-date": arg.dateStr }, heading, rows);
|
|
1431
|
+
if (agenda && opts.listAddText !== null) section.append(this.addRow(day));
|
|
1432
|
+
opts.dayHeaderDidMount?.({ ...arg, el: heading });
|
|
1433
|
+
pool = new ItemPool(ctx, rows);
|
|
1385
1434
|
this.sections.set(day, section);
|
|
1386
1435
|
this.pools.set(day, pool);
|
|
1387
1436
|
}
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
pool.place(n.id, {
|
|
1391
|
-
n,
|
|
1392
|
-
day,
|
|
1393
|
-
isStart: n.startDay === day,
|
|
1394
|
-
isEnd: n.endDay === day + 1,
|
|
1395
|
-
variant: "list",
|
|
1396
|
-
mirror: false
|
|
1397
|
-
});
|
|
1398
|
-
}
|
|
1399
|
-
pool.end();
|
|
1400
|
-
const rows = pool.container;
|
|
1401
|
-
for (const n of items) {
|
|
1402
|
-
const el = pool.elementsFor(n.id)[0];
|
|
1403
|
-
if (el) rows.append(el);
|
|
1404
|
-
}
|
|
1437
|
+
section.classList.toggle("is-empty", !items.length);
|
|
1438
|
+
this.fill(pool, items, () => day);
|
|
1405
1439
|
if (prev ? prev.nextSibling !== section : this.body.firstChild !== section) {
|
|
1406
1440
|
if (prev) prev.after(section);
|
|
1407
1441
|
else this.body.prepend(section);
|
|
@@ -1415,18 +1449,111 @@ var ListView = class {
|
|
|
1415
1449
|
section.remove();
|
|
1416
1450
|
this.sections.delete(day);
|
|
1417
1451
|
}
|
|
1452
|
+
this.renderLead(leadItems);
|
|
1418
1453
|
let empty = this.body.querySelector(":scope > .tnc-list-empty");
|
|
1419
|
-
if (!keep.size) {
|
|
1454
|
+
if (!keep.size && !leadItems.length) {
|
|
1420
1455
|
if (!empty) {
|
|
1421
1456
|
empty = h(doc, "div", "tnc-list-empty", { role: "listitem" }, ctx.text.noItems);
|
|
1422
1457
|
this.body.append(empty);
|
|
1423
1458
|
}
|
|
1424
1459
|
} else empty?.remove();
|
|
1460
|
+
this.ctx.strip?.draw();
|
|
1461
|
+
if (this.loader && this.sentinel) {
|
|
1462
|
+
this.loader.unobserve(this.sentinel);
|
|
1463
|
+
this.loader.observe(this.sentinel);
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
/** Places items in a pool, keeping DOM order equal to item order. */
|
|
1467
|
+
fill(pool, items, dayFor) {
|
|
1468
|
+
pool.begin();
|
|
1469
|
+
for (const n of items) {
|
|
1470
|
+
const d = dayFor(n);
|
|
1471
|
+
pool.place(n.id, { n, day: d, isStart: n.startDay === d, isEnd: n.endDay === d + 1, variant: "list", mirror: false });
|
|
1472
|
+
}
|
|
1473
|
+
pool.end();
|
|
1474
|
+
for (const n of items) {
|
|
1475
|
+
const el = pool.elementsFor(n.id)[0];
|
|
1476
|
+
if (el) pool.container.append(el);
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
renderLead(items) {
|
|
1480
|
+
const { ctx } = this;
|
|
1481
|
+
const { doc, opts } = ctx;
|
|
1482
|
+
if (!items.length || opts.listLeadTitle === null) {
|
|
1483
|
+
if (!this.lead) return;
|
|
1484
|
+
opts.listLeadWillUnmount?.({ el: this.lead.slot });
|
|
1485
|
+
this.lead.pool.clear();
|
|
1486
|
+
this.lead.section.remove();
|
|
1487
|
+
this.lead = null;
|
|
1488
|
+
return;
|
|
1489
|
+
}
|
|
1490
|
+
if (!this.lead) {
|
|
1491
|
+
const title = opts.listLeadTitle;
|
|
1492
|
+
const slot = h(doc, "div", "tnc-list-lead-slot", { "data-tnc-no-drag": "" });
|
|
1493
|
+
const heading = h(doc, "div", "tnc-list-day tnc-list-lead-head", null, h(doc, "span", "tnc-list-lead-title", null, title), slot);
|
|
1494
|
+
const rows = h(doc, "div", "tnc-list-rows", { role: "group" });
|
|
1495
|
+
const section = h(doc, "div", "tnc-list-section tnc-list-lead", { role: "listitem", "aria-label": title }, heading, rows);
|
|
1496
|
+
this.lead = { section, slot, pool: new ItemPool(ctx, rows) };
|
|
1497
|
+
opts.listLeadDidMount?.({ el: slot });
|
|
1498
|
+
}
|
|
1499
|
+
if (this.body.firstChild !== this.lead.section) this.body.prepend(this.lead.section);
|
|
1500
|
+
const first = ctx.days[0] ?? ctx.view.range.startDay;
|
|
1501
|
+
this.fill(this.lead.pool, items, (n) => Math.max(n.startDay, first));
|
|
1502
|
+
}
|
|
1503
|
+
addRow(day) {
|
|
1504
|
+
const { ctx } = this;
|
|
1505
|
+
const label = ctx.opts.listAddText ?? "";
|
|
1506
|
+
const btn = h(ctx.doc, "button", "tnc-list-add", { type: "button", "aria-label": `${label}, ${ctx.fmt.longDate(day)}` }, label);
|
|
1507
|
+
btn.addEventListener("click", (ev) => ctx.opts.onListAdd?.({ ...ctx.dayArg(day), jsEvent: ev }));
|
|
1508
|
+
return btn;
|
|
1509
|
+
}
|
|
1510
|
+
/** Brings a day's section to the top, under the strip; the first day brings the lead section too. */
|
|
1511
|
+
scrollToDay(day) {
|
|
1512
|
+
const section = day === this.ctx.days[0] && this.lead ? this.lead.section : this.sections.get(day);
|
|
1513
|
+
if (!section) return;
|
|
1514
|
+
this.ctx.strip?.measure();
|
|
1515
|
+
section.scrollIntoView({ block: "start" });
|
|
1516
|
+
this.ctx.strip?.setFocus(day);
|
|
1517
|
+
this.pinned = true;
|
|
1518
|
+
}
|
|
1519
|
+
fold = () => {
|
|
1520
|
+
this.pinned = false;
|
|
1521
|
+
const strip = this.ctx.strip;
|
|
1522
|
+
if (strip?.expanded) strip.setExpanded(false);
|
|
1523
|
+
};
|
|
1524
|
+
onScroll = () => {
|
|
1525
|
+
if (this.frame !== null) return;
|
|
1526
|
+
this.frame = this.ctx.win.requestAnimationFrame(() => {
|
|
1527
|
+
this.frame = null;
|
|
1528
|
+
this.followTop();
|
|
1529
|
+
});
|
|
1530
|
+
};
|
|
1531
|
+
/** Marks the first day not hidden under the strip. */
|
|
1532
|
+
followTop() {
|
|
1533
|
+
const strip = this.ctx.strip;
|
|
1534
|
+
if (!strip || this.pinned) return;
|
|
1535
|
+
const edge = strip.el.getBoundingClientRect().bottom + 1;
|
|
1536
|
+
for (const day of this.ctx.days) {
|
|
1537
|
+
const section = this.sections.get(day);
|
|
1538
|
+
if (section && section.getBoundingClientRect().bottom > edge) {
|
|
1539
|
+
strip.setFocus(day);
|
|
1540
|
+
return;
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1425
1543
|
}
|
|
1426
1544
|
showSelection(_sel) {
|
|
1427
1545
|
}
|
|
1428
|
-
hit(
|
|
1429
|
-
|
|
1546
|
+
hit(x, y) {
|
|
1547
|
+
const el = this.ctx.doc.elementFromPoint(x, y);
|
|
1548
|
+
const section = el?.closest(".tnc-list-section");
|
|
1549
|
+
if (!section || !this.body.contains(section)) return null;
|
|
1550
|
+
if (section === this.lead?.section) {
|
|
1551
|
+
const item = el?.closest(".tnc-item");
|
|
1552
|
+
const meta = item ? this.ctx.meta.get(item) : void 0;
|
|
1553
|
+
return { kind: "day", day: meta?.day ?? this.ctx.days[0] ?? this.ctx.view.range.startDay };
|
|
1554
|
+
}
|
|
1555
|
+
const day = parseDay(section.dataset.date ?? "");
|
|
1556
|
+
return day === null ? null : { kind: "day", day };
|
|
1430
1557
|
}
|
|
1431
1558
|
getScroll() {
|
|
1432
1559
|
return { top: this.scroller.scrollTop, left: 0 };
|
|
@@ -1435,14 +1562,231 @@ var ListView = class {
|
|
|
1435
1562
|
this.scroller.scrollTop = s.top;
|
|
1436
1563
|
}
|
|
1437
1564
|
elementsFor(id) {
|
|
1438
|
-
|
|
1565
|
+
const pools = [...this.pools.values(), ...this.lead ? [this.lead.pool] : []];
|
|
1566
|
+
return pools.flatMap((p) => p.elementsFor(id));
|
|
1439
1567
|
}
|
|
1440
1568
|
destroy() {
|
|
1569
|
+
const { win, opts } = this.ctx;
|
|
1570
|
+
win.removeEventListener("scroll", this.onScroll, { capture: true });
|
|
1571
|
+
if (this.frame !== null) win.cancelAnimationFrame(this.frame);
|
|
1572
|
+
this.loader?.disconnect();
|
|
1573
|
+
if (this.lead) opts.listLeadWillUnmount?.({ el: this.lead.slot });
|
|
1574
|
+
this.lead?.pool.clear();
|
|
1441
1575
|
for (const p of this.pools.values()) p.clear();
|
|
1442
1576
|
this.el.remove();
|
|
1443
1577
|
}
|
|
1444
1578
|
};
|
|
1445
1579
|
|
|
1580
|
+
// src/dom/views/strip.ts
|
|
1581
|
+
var SWIPE_PX = 40;
|
|
1582
|
+
var DateStrip = class {
|
|
1583
|
+
constructor(ctx, pick, focus, onShown) {
|
|
1584
|
+
this.ctx = ctx;
|
|
1585
|
+
this.pick = pick;
|
|
1586
|
+
this.onShown = onShown;
|
|
1587
|
+
const { doc, text } = ctx;
|
|
1588
|
+
this.focus = focus;
|
|
1589
|
+
this.shown = startOfWeek(focus, ctx.firstDay);
|
|
1590
|
+
this.el = h(doc, "div", "tnc-strip");
|
|
1591
|
+
const head = h(doc, "div", "tnc-strip-head");
|
|
1592
|
+
this.title = h(doc, "button", "tnc-strip-title", { type: "button", "aria-expanded": "false" });
|
|
1593
|
+
this.title.addEventListener("click", () => this.setExpanded(!this.expanded));
|
|
1594
|
+
const prev = h(doc, "button", "tnc-strip-nav tnc-strip-prev", { type: "button", "aria-label": text.prev, title: text.prev }, "\u2039");
|
|
1595
|
+
const next = h(doc, "button", "tnc-strip-nav tnc-strip-next", { type: "button", "aria-label": text.next, title: text.next }, "\u203A");
|
|
1596
|
+
prev.addEventListener("click", () => this.step(-1));
|
|
1597
|
+
next.addEventListener("click", () => this.step(1));
|
|
1598
|
+
this.todayBtn = h(doc, "button", "tnc-strip-today", { type: "button" }, text.today);
|
|
1599
|
+
this.todayBtn.addEventListener("click", (ev) => this.pick(ctx.today, ev));
|
|
1600
|
+
this.slot = h(doc, "div", "tnc-strip-slot", { "data-tnc-no-drag": "" });
|
|
1601
|
+
head.append(this.title, this.todayBtn, prev, next, this.slot);
|
|
1602
|
+
this.dows = h(doc, "div", "tnc-strip-dows", { "aria-hidden": "true" });
|
|
1603
|
+
this.grid = h(doc, "div", "tnc-strip-grid", { role: "group" });
|
|
1604
|
+
const handle = h(doc, "button", "tnc-strip-handle", { type: "button", tabindex: "-1", "aria-hidden": "true" });
|
|
1605
|
+
handle.addEventListener("click", () => this.setExpanded(!this.expanded));
|
|
1606
|
+
this.el.append(head, this.dows, this.grid, handle);
|
|
1607
|
+
this.grid.addEventListener("click", this.onClick);
|
|
1608
|
+
this.grid.addEventListener("keydown", this.onKeyDown);
|
|
1609
|
+
this.el.addEventListener("pointerdown", this.onPointerDown);
|
|
1610
|
+
this.el.addEventListener("pointerup", this.onPointerUp);
|
|
1611
|
+
this.el.addEventListener("pointercancel", this.onPointerCancel);
|
|
1612
|
+
this.el.addEventListener("click", this.onClickCapture, true);
|
|
1613
|
+
this.draw();
|
|
1614
|
+
ctx.opts.stripSlotDidMount?.({ el: this.slot });
|
|
1615
|
+
}
|
|
1616
|
+
ctx;
|
|
1617
|
+
pick;
|
|
1618
|
+
onShown;
|
|
1619
|
+
el;
|
|
1620
|
+
title;
|
|
1621
|
+
dows;
|
|
1622
|
+
grid;
|
|
1623
|
+
slot;
|
|
1624
|
+
todayBtn;
|
|
1625
|
+
/** Day at the top of the list. */
|
|
1626
|
+
focus;
|
|
1627
|
+
/** First day of the week shown, or the month shown when expanded. */
|
|
1628
|
+
shown;
|
|
1629
|
+
target = null;
|
|
1630
|
+
/** Days tinted as on screen besides the focus (a time grid's range). */
|
|
1631
|
+
range = null;
|
|
1632
|
+
shownKey = "";
|
|
1633
|
+
swipe = null;
|
|
1634
|
+
swallowClick = false;
|
|
1635
|
+
expanded = false;
|
|
1636
|
+
/** The list scrolled or the grid moved: mark the day and keep it in view. */
|
|
1637
|
+
setFocus(day, range = this.range) {
|
|
1638
|
+
const sameRange = range?.start === this.range?.start && range?.end === this.range?.end;
|
|
1639
|
+
if (day === this.focus && sameRange) return;
|
|
1640
|
+
this.range = range;
|
|
1641
|
+
if (day !== this.focus) {
|
|
1642
|
+
this.focus = day;
|
|
1643
|
+
if (!this.expanded) this.shown = startOfWeek(day, this.ctx.firstDay);
|
|
1644
|
+
else if (startOfMonth(day) !== this.shown) this.shown = startOfMonth(day);
|
|
1645
|
+
}
|
|
1646
|
+
this.draw();
|
|
1647
|
+
}
|
|
1648
|
+
/** Publishes the strip's height as `--tnc-strip-h`, for what sticks below it. */
|
|
1649
|
+
measure() {
|
|
1650
|
+
this.el.parentElement?.style.setProperty("--tnc-strip-h", `${this.el.offsetHeight}px`);
|
|
1651
|
+
}
|
|
1652
|
+
/** First day and end (exclusive) of the days on show. */
|
|
1653
|
+
window() {
|
|
1654
|
+
const days = this.days();
|
|
1655
|
+
return { start: days[0], end: days[days.length - 1] + 1 };
|
|
1656
|
+
}
|
|
1657
|
+
setExpanded(on) {
|
|
1658
|
+
if (on === this.expanded) return;
|
|
1659
|
+
this.expanded = on;
|
|
1660
|
+
this.shown = on ? startOfMonth(this.focus) : startOfWeek(this.focus, this.ctx.firstDay);
|
|
1661
|
+
this.draw();
|
|
1662
|
+
}
|
|
1663
|
+
/** Highlights the day a dragged item would land on. */
|
|
1664
|
+
markTarget(day) {
|
|
1665
|
+
if (day === this.target) return;
|
|
1666
|
+
this.target = day;
|
|
1667
|
+
for (const cell of this.grid.querySelectorAll(".tnc-strip-day"))
|
|
1668
|
+
cell.classList.toggle("is-drop-target", day !== null && cell.dataset.date === this.ctx.dayArg(day).dateStr);
|
|
1669
|
+
}
|
|
1670
|
+
dayAt(el) {
|
|
1671
|
+
const cell = el.closest(".tnc-strip-day");
|
|
1672
|
+
return cell && this.grid.contains(cell) ? parseDay(cell.dataset.date ?? "") : null;
|
|
1673
|
+
}
|
|
1674
|
+
step(dir) {
|
|
1675
|
+
this.shown = this.expanded ? addMonths(this.shown, dir) : this.shown + 7 * dir;
|
|
1676
|
+
this.draw();
|
|
1677
|
+
}
|
|
1678
|
+
days() {
|
|
1679
|
+
if (!this.expanded) return Array.from({ length: 7 }, (_, i) => this.shown + i);
|
|
1680
|
+
const first = startOfWeek(this.shown, this.ctx.firstDay);
|
|
1681
|
+
const end = addMonths(this.shown, 1);
|
|
1682
|
+
const weeks = Math.ceil((end - first) / 7);
|
|
1683
|
+
return Array.from({ length: weeks * 7 }, (_, i) => first + i);
|
|
1684
|
+
}
|
|
1685
|
+
/** Redraws the cells; also called on every list render to refresh the dots. */
|
|
1686
|
+
draw() {
|
|
1687
|
+
const { ctx } = this;
|
|
1688
|
+
const { doc, fmt, store } = ctx;
|
|
1689
|
+
const days = this.days();
|
|
1690
|
+
const titleDay = this.expanded ? this.shown : days.includes(this.focus) ? this.focus : this.shown + 3;
|
|
1691
|
+
this.title.replaceChildren(fmt.monthYear(titleDay), h(doc, "span", "tnc-strip-caret", { "aria-hidden": "true" }));
|
|
1692
|
+
this.title.setAttribute("aria-expanded", String(this.expanded));
|
|
1693
|
+
this.el.classList.toggle("is-expanded", this.expanded);
|
|
1694
|
+
this.todayBtn.hidden = this.focus === ctx.today;
|
|
1695
|
+
this.dows.replaceChildren(...days.slice(0, 7).map((d) => h(doc, "span", null, null, fmt.weekdayNarrow(d))));
|
|
1696
|
+
const focused = this.grid.contains(doc.activeElement) ? doc.activeElement.dataset.date : void 0;
|
|
1697
|
+
const month = ymdOfDay(this.shown).month;
|
|
1698
|
+
const tabDay = days.includes(this.focus) ? this.focus : days[0];
|
|
1699
|
+
const cells = days.map((d) => {
|
|
1700
|
+
const arg = ctx.dayArg(d);
|
|
1701
|
+
const count = store.inRange(d, d + 1).filter((n) => n.item.display !== "background" && n.item.display !== "none").length;
|
|
1702
|
+
const first = ymdOfDay(d).day === 1 && d !== days[0] && !this.expanded;
|
|
1703
|
+
const label = fmt.longDate(d);
|
|
1704
|
+
const cell = h(
|
|
1705
|
+
doc,
|
|
1706
|
+
"button",
|
|
1707
|
+
[
|
|
1708
|
+
"tnc-strip-day",
|
|
1709
|
+
...ctx.dayClasses(d),
|
|
1710
|
+
d === this.focus ? "is-focus" : "",
|
|
1711
|
+
d !== this.focus && this.range && d >= this.range.start && d < this.range.end ? "is-in-range" : "",
|
|
1712
|
+
count ? "has-items" : "",
|
|
1713
|
+
this.expanded && ymdOfDay(d).month !== month ? "is-other-month" : "",
|
|
1714
|
+
d === this.target ? "is-drop-target" : ""
|
|
1715
|
+
].filter(Boolean).join(" "),
|
|
1716
|
+
{
|
|
1717
|
+
type: "button",
|
|
1718
|
+
"data-date": arg.dateStr,
|
|
1719
|
+
"aria-label": count ? `${label}, ${ctx.text.itemCount(count)}` : label,
|
|
1720
|
+
"aria-pressed": String(d === this.focus),
|
|
1721
|
+
tabindex: d === tabDay ? "0" : "-1"
|
|
1722
|
+
}
|
|
1723
|
+
);
|
|
1724
|
+
if (first) cell.append(h(doc, "span", "tnc-strip-month", { "aria-hidden": "true" }, fmt.monthShort(d)));
|
|
1725
|
+
cell.append(h(doc, "span", "tnc-strip-num", null, fmt.dayNumber(d)), h(doc, "span", "tnc-strip-dot", { "aria-hidden": "true" }));
|
|
1726
|
+
return cell;
|
|
1727
|
+
});
|
|
1728
|
+
this.grid.replaceChildren(...cells);
|
|
1729
|
+
this.grid.setAttribute("aria-label", fmt.monthYear(titleDay));
|
|
1730
|
+
if (focused) this.grid.querySelector(`[data-date="${focused}"]`)?.focus({ preventScroll: true });
|
|
1731
|
+
const key = `${days[0]}:${days.length}`;
|
|
1732
|
+
if (key !== this.shownKey) {
|
|
1733
|
+
this.shownKey = key;
|
|
1734
|
+
this.onShown();
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
onClick = (ev) => {
|
|
1738
|
+
const day = this.dayAt(ev.target);
|
|
1739
|
+
if (day === null) return;
|
|
1740
|
+
if (this.expanded) this.setExpanded(false);
|
|
1741
|
+
this.pick(day, ev);
|
|
1742
|
+
};
|
|
1743
|
+
onKeyDown = (ev) => {
|
|
1744
|
+
const day = this.dayAt(ev.target);
|
|
1745
|
+
if (day === null) return;
|
|
1746
|
+
const move = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -7, ArrowDown: 7 }[ev.key];
|
|
1747
|
+
if (move === void 0) return;
|
|
1748
|
+
ev.preventDefault();
|
|
1749
|
+
const next = day + move;
|
|
1750
|
+
const days = this.days();
|
|
1751
|
+
if (next < days[0] || next > days[days.length - 1]) {
|
|
1752
|
+
this.shown = this.expanded ? startOfMonth(next) : startOfWeek(next, this.ctx.firstDay);
|
|
1753
|
+
this.draw();
|
|
1754
|
+
}
|
|
1755
|
+
const cell = this.grid.querySelector(`[data-date="${this.ctx.dayArg(next).dateStr}"]`);
|
|
1756
|
+
if (!cell) return;
|
|
1757
|
+
for (const c of this.grid.querySelectorAll("[tabindex='0']")) c.setAttribute("tabindex", "-1");
|
|
1758
|
+
cell.setAttribute("tabindex", "0");
|
|
1759
|
+
cell.focus();
|
|
1760
|
+
};
|
|
1761
|
+
onPointerDown = (ev) => {
|
|
1762
|
+
if (ev.button !== 0 || this.ctx.draggingId) return;
|
|
1763
|
+
this.swipe = { id: ev.pointerId, x: ev.clientX, y: ev.clientY };
|
|
1764
|
+
};
|
|
1765
|
+
onPointerUp = (ev) => {
|
|
1766
|
+
const s = this.swipe;
|
|
1767
|
+
this.swipe = null;
|
|
1768
|
+
if (!s || s.id !== ev.pointerId) return;
|
|
1769
|
+
const dx = ev.clientX - s.x;
|
|
1770
|
+
if (Math.abs(dx) < SWIPE_PX || Math.abs(dx) < Math.abs(ev.clientY - s.y)) return;
|
|
1771
|
+
this.swallowClick = true;
|
|
1772
|
+
this.step(dx < 0 ? 1 : -1);
|
|
1773
|
+
};
|
|
1774
|
+
onPointerCancel = () => {
|
|
1775
|
+
this.swipe = null;
|
|
1776
|
+
};
|
|
1777
|
+
/** A swipe that ends on a day is not a pick. */
|
|
1778
|
+
onClickCapture = (ev) => {
|
|
1779
|
+
if (!this.swallowClick) return;
|
|
1780
|
+
this.swallowClick = false;
|
|
1781
|
+
ev.stopPropagation();
|
|
1782
|
+
ev.preventDefault();
|
|
1783
|
+
};
|
|
1784
|
+
destroy() {
|
|
1785
|
+
this.ctx.opts.stripSlotWillUnmount?.({ el: this.slot });
|
|
1786
|
+
this.el.remove();
|
|
1787
|
+
}
|
|
1788
|
+
};
|
|
1789
|
+
|
|
1446
1790
|
// src/dom/views/timegrid.ts
|
|
1447
1791
|
var BAR_GAP2 = 2;
|
|
1448
1792
|
var TimeGridView = class {
|
|
@@ -2579,6 +2923,7 @@ ${this.ctx.text.more(items.length - 8)}` : ""}` : label;
|
|
|
2579
2923
|
|
|
2580
2924
|
// src/dom/calendar.ts
|
|
2581
2925
|
var EXTERNAL_ID = "__tnc_external__";
|
|
2926
|
+
var MAX_AGENDA_DAYS = 366;
|
|
2582
2927
|
function sameOption(a, b, depth = 0) {
|
|
2583
2928
|
if (a === b) return true;
|
|
2584
2929
|
if (typeof a === "function" && typeof b === "function") return true;
|
|
@@ -2647,10 +2992,15 @@ var Calendar = class {
|
|
|
2647
2992
|
slot = { min: 0, max: MINUTES_PER_DAY, duration: 30, snap: 30, scroll: 480 };
|
|
2648
2993
|
draggingId = null;
|
|
2649
2994
|
dragRow = null;
|
|
2995
|
+
strip = null;
|
|
2996
|
+
stripSize = null;
|
|
2997
|
+
rebuilding = false;
|
|
2650
2998
|
toolbarEl;
|
|
2651
2999
|
viewHost;
|
|
2652
3000
|
current = null;
|
|
2653
3001
|
anchor = 0;
|
|
3002
|
+
/** End of an agenda list grown past its `listDayCount`. */
|
|
3003
|
+
listEnd = null;
|
|
2654
3004
|
session = null;
|
|
2655
3005
|
selection = null;
|
|
2656
3006
|
suppressClick = false;
|
|
@@ -2843,6 +3193,10 @@ var Calendar = class {
|
|
|
2843
3193
|
if (!p) return;
|
|
2844
3194
|
const day = p.kind === "day" ? p.day : dayOf(p.ms, this.zone);
|
|
2845
3195
|
const ms = p.kind === "day" ? toInstant(p.day, 0, this.zone) : p.ms;
|
|
3196
|
+
if (this.agendaList()) {
|
|
3197
|
+
this.showListDay(day);
|
|
3198
|
+
return;
|
|
3199
|
+
}
|
|
2846
3200
|
const { startDay, endDay } = this.view.range;
|
|
2847
3201
|
if (day < startDay || day >= endDay) {
|
|
2848
3202
|
this.anchor = day;
|
|
@@ -2854,7 +3208,7 @@ var Calendar = class {
|
|
|
2854
3208
|
}
|
|
2855
3209
|
/** What is under a client point: a time slot, a day, or a timeline row and date. Null outside the view. */
|
|
2856
3210
|
hitTest(clientX, clientY) {
|
|
2857
|
-
return this.
|
|
3211
|
+
return this.hitAt(clientX, clientY);
|
|
2858
3212
|
}
|
|
2859
3213
|
/** Item elements currently rendered for an id. */
|
|
2860
3214
|
elementsFor(id) {
|
|
@@ -2896,6 +3250,9 @@ var Calendar = class {
|
|
|
2896
3250
|
this.unbindEvents();
|
|
2897
3251
|
this.current?.destroy();
|
|
2898
3252
|
this.current = null;
|
|
3253
|
+
this.stripSize?.disconnect();
|
|
3254
|
+
this.strip?.destroy();
|
|
3255
|
+
this.strip = null;
|
|
2899
3256
|
this.root.remove();
|
|
2900
3257
|
}
|
|
2901
3258
|
/* ========================================================== view context */
|
|
@@ -2926,7 +3283,8 @@ var Calendar = class {
|
|
|
2926
3283
|
return out;
|
|
2927
3284
|
}
|
|
2928
3285
|
isEditable(n, what) {
|
|
2929
|
-
if (n.id === EXTERNAL_ID || this.opts.view === "
|
|
3286
|
+
if (n.id === EXTERNAL_ID || this.opts.view === "year") return false;
|
|
3287
|
+
if (this.opts.view === "list" && (!this.opts.listAgenda || what === "duration")) return false;
|
|
2930
3288
|
const it = n.item;
|
|
2931
3289
|
const base = it.editable ?? this.opts.editable;
|
|
2932
3290
|
const specific = what === "start" ? it.startEditable : it.durationEditable;
|
|
@@ -3123,7 +3481,8 @@ var Calendar = class {
|
|
|
3123
3481
|
}
|
|
3124
3482
|
case "list": {
|
|
3125
3483
|
const s = this.opts.listStart === "week" ? startOfWeek(a, this.firstDay) : a;
|
|
3126
|
-
|
|
3484
|
+
const end = s + Math.max(1, listDayCount);
|
|
3485
|
+
return { startDay: s, endDay: this.opts.listAgenda && this.listEnd !== null ? Math.max(end, this.listEnd) : end };
|
|
3127
3486
|
}
|
|
3128
3487
|
case "month": {
|
|
3129
3488
|
const s = startOfWeek(startOfMonth(a), this.firstDay);
|
|
@@ -3161,6 +3520,33 @@ var Calendar = class {
|
|
|
3161
3520
|
const duration = Math.max(1, parseDurationMinutes(this.opts.slotDuration, 30));
|
|
3162
3521
|
const snap = Math.max(1, parseDurationMinutes(this.opts.snapDuration ?? null, duration));
|
|
3163
3522
|
this.slot = { min, max, duration, snap, scroll: parseDurationMinutes(this.opts.scrollTime, 480) };
|
|
3523
|
+
if (navigated) this.listEnd = null;
|
|
3524
|
+
const prev = this.view;
|
|
3525
|
+
this.applyRange();
|
|
3526
|
+
this.conflicts = dependencyConflicts(this.store);
|
|
3527
|
+
this.root.className = "tnc";
|
|
3528
|
+
this.root.classList.add(`tnc--${this.opts.view}`, `tnc--height-${this.opts.height}`);
|
|
3529
|
+
if (!this.opts.timeGrid) this.root.classList.add("tnc--no-time-grid");
|
|
3530
|
+
this.renderToolbar();
|
|
3531
|
+
this.rebuilding = true;
|
|
3532
|
+
this.syncStrip(navigated);
|
|
3533
|
+
this.rebuilding = false;
|
|
3534
|
+
this.view.dataRange = this.dataRangeFor(this.view.range);
|
|
3535
|
+
const rangeChanged = rangeMoved(prev, this.view);
|
|
3536
|
+
this.current?.destroy();
|
|
3537
|
+
this.current = this.createView();
|
|
3538
|
+
this.viewHost.replaceChildren(this.current.el);
|
|
3539
|
+
this.current.render(null);
|
|
3540
|
+
if (keep && this.current instanceof TimelineView) {
|
|
3541
|
+
this.current.setScroll({ top: keep.top, left: 0 });
|
|
3542
|
+
this.current.placeInstant(keep.ms, keep.vx);
|
|
3543
|
+
} else if (scroll) this.current.setScroll(scroll);
|
|
3544
|
+
else if (this.current instanceof TimeGridView) this.current.scrollToTime(this.slot.scroll);
|
|
3545
|
+
else if (this.current instanceof TimelineView) this.current.scrollToDay(this.anchor);
|
|
3546
|
+
if (rangeChanged) this.opts.onRangeChange?.(this.view);
|
|
3547
|
+
}
|
|
3548
|
+
/** Sets the visible days and view info from the anchor. */
|
|
3549
|
+
applyRange() {
|
|
3164
3550
|
const { startDay, endDay } = this.computeRange();
|
|
3165
3551
|
const hidden = new Set(this.opts.hiddenDays);
|
|
3166
3552
|
if (!this.opts.weekends) {
|
|
@@ -3177,26 +3563,100 @@ var Calendar = class {
|
|
|
3177
3563
|
start: new Date(toInstant(startDay, 0, this.zone)),
|
|
3178
3564
|
end: new Date(toInstant(endDay, 0, this.zone))
|
|
3179
3565
|
};
|
|
3180
|
-
|
|
3181
|
-
this.view = { type: this.opts.view, range, anchor: this.anchor, title: "" };
|
|
3566
|
+
this.view = { type: this.opts.view, range, anchor: this.anchor, title: "", dataRange: this.dataRangeFor(range) };
|
|
3182
3567
|
this.view.title = this.computeTitle(startDay, endDay);
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
this.
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3568
|
+
}
|
|
3569
|
+
/** The range, run on to the end of the strip's days so their dots are right. */
|
|
3570
|
+
dataRangeFor(range) {
|
|
3571
|
+
const end = this.strip ? Math.max(range.endDay, this.strip.window().end) : range.endDay;
|
|
3572
|
+
if (end === range.endDay) return range;
|
|
3573
|
+
return { ...range, endDay: end, end: new Date(toInstant(end, 0, this.zone)) };
|
|
3574
|
+
}
|
|
3575
|
+
/** Adds, updates or removes the date strip for the current view. */
|
|
3576
|
+
syncStrip(navigated) {
|
|
3577
|
+
const { view, listAgenda, dateStrip } = this.opts;
|
|
3578
|
+
const list = view === "list";
|
|
3579
|
+
const want = list ? listAgenda : dateStrip && (view === "day" || view === "days" || view === "week");
|
|
3580
|
+
if (!want) {
|
|
3581
|
+
if (!this.strip) return;
|
|
3582
|
+
this.stripSize?.disconnect();
|
|
3583
|
+
this.stripSize = null;
|
|
3584
|
+
this.strip.destroy();
|
|
3585
|
+
this.strip = null;
|
|
3586
|
+
this.root.style.removeProperty("--tnc-strip-h");
|
|
3587
|
+
return;
|
|
3588
|
+
}
|
|
3589
|
+
this.root.classList.add("tnc--has-strip");
|
|
3590
|
+
const { startDay, endDay } = this.view.range;
|
|
3591
|
+
if (!this.strip) {
|
|
3592
|
+
const strip = new DateStrip(this, (day, ev) => this.pickStripDay(day, ev), startDay, () => this.stripShown());
|
|
3593
|
+
this.strip = strip;
|
|
3594
|
+
this.root.insertBefore(strip.el, this.viewHost);
|
|
3595
|
+
const RO = this.win.ResizeObserver;
|
|
3596
|
+
if (RO) {
|
|
3597
|
+
this.stripSize = new RO(() => strip.measure());
|
|
3598
|
+
this.stripSize.observe(strip.el);
|
|
3599
|
+
}
|
|
3600
|
+
}
|
|
3601
|
+
if (!list) this.strip.setFocus(startDay, { start: startDay, end: endDay });
|
|
3602
|
+
else if (navigated) this.strip.setFocus(startDay, null);
|
|
3603
|
+
this.strip.draw();
|
|
3604
|
+
}
|
|
3605
|
+
pickStripDay(day, ev) {
|
|
3606
|
+
this.dateClick(day, ev);
|
|
3607
|
+
if (this.agendaList()) {
|
|
3608
|
+
this.showListDay(day);
|
|
3609
|
+
return;
|
|
3610
|
+
}
|
|
3611
|
+
const { startDay, endDay } = this.view.range;
|
|
3612
|
+
if (this.opts.view === "week" && day >= startDay && day < endDay) return;
|
|
3613
|
+
this.anchor = day;
|
|
3614
|
+
this.rebuild(true);
|
|
3615
|
+
}
|
|
3616
|
+
/** The strip moved to other days: load theirs too. */
|
|
3617
|
+
stripShown() {
|
|
3618
|
+
if (!this.strip || this.rebuilding || !this.view) return;
|
|
3619
|
+
const next = this.dataRangeFor(this.view.range);
|
|
3620
|
+
if (next.endDay === this.view.dataRange.endDay) return;
|
|
3621
|
+
this.view = { ...this.view, dataRange: next };
|
|
3622
|
+
this.opts.onRangeChange?.(this.view);
|
|
3623
|
+
}
|
|
3624
|
+
/** What is under a point, the date strip included. */
|
|
3625
|
+
hitAt(x, y) {
|
|
3626
|
+
const strip = this.strip;
|
|
3627
|
+
if (strip) {
|
|
3628
|
+
const el = this.doc.elementFromPoint(x, y);
|
|
3629
|
+
const day = el ? strip.dayAt(el) : null;
|
|
3630
|
+
strip.markTarget(day !== null && this.draggingId ? day : null);
|
|
3631
|
+
if (day !== null) return { kind: "day", day };
|
|
3632
|
+
}
|
|
3633
|
+
return this.current?.hit(x, y) ?? null;
|
|
3634
|
+
}
|
|
3635
|
+
agendaList() {
|
|
3636
|
+
return this.opts.view === "list" && this.opts.listAgenda && this.current instanceof ListView ? this.current : null;
|
|
3637
|
+
}
|
|
3638
|
+
extendList(endDay) {
|
|
3639
|
+
if (!this.agendaList()) return;
|
|
3640
|
+
const { startDay, endDay: current } = this.view.range;
|
|
3641
|
+
const end = Math.min(endDay, startDay + MAX_AGENDA_DAYS);
|
|
3642
|
+
if (end <= current) return;
|
|
3643
|
+
this.listEnd = end;
|
|
3644
|
+
this.applyRange();
|
|
3645
|
+
this.current?.render(null);
|
|
3646
|
+
this.strip?.draw();
|
|
3647
|
+
this.opts.onRangeChange?.(this.view);
|
|
3648
|
+
}
|
|
3649
|
+
showListDay(day) {
|
|
3650
|
+
if (!this.agendaList()) return;
|
|
3651
|
+
if (day < this.view.range.startDay || day - this.view.range.startDay >= MAX_AGENDA_DAYS) {
|
|
3652
|
+
this.anchor = day;
|
|
3653
|
+
this.rebuild(true);
|
|
3654
|
+
}
|
|
3655
|
+
this.extendList(day + 2 * Math.max(7, this.opts.listDayCount));
|
|
3656
|
+
this.agendaList()?.scrollToDay(day);
|
|
3657
|
+
}
|
|
3658
|
+
dateClick(day, jsEvent) {
|
|
3659
|
+
this.clickSlot({ kind: "day", day }, jsEvent, true);
|
|
3200
3660
|
}
|
|
3201
3661
|
createView() {
|
|
3202
3662
|
switch (this.opts.view) {
|
|
@@ -3499,8 +3959,9 @@ var Calendar = class {
|
|
|
3499
3959
|
};
|
|
3500
3960
|
this.session = s;
|
|
3501
3961
|
if (touch && itemEl?.classList.contains("is-draggable")) {
|
|
3502
|
-
const
|
|
3503
|
-
|
|
3962
|
+
const x = scrollerOf(itemEl, "x");
|
|
3963
|
+
const y = scrollerOf(itemEl, "y");
|
|
3964
|
+
s.pan = { x, y, left: x?.scrollLeft ?? 0, top: y?.scrollTop ?? 0, active: false };
|
|
3504
3965
|
}
|
|
3505
3966
|
if (touch) {
|
|
3506
3967
|
s.timer = this.win.setTimeout(() => {
|
|
@@ -3541,8 +4002,8 @@ var Calendar = class {
|
|
|
3541
4002
|
if (!pan.active && s.timer !== null) this.win.clearTimeout(s.timer);
|
|
3542
4003
|
s.timer = null;
|
|
3543
4004
|
pan.active = true;
|
|
3544
|
-
pan.
|
|
3545
|
-
pan.
|
|
4005
|
+
if (pan.x) pan.x.scrollLeft = pan.left - (ev.clientX - s.x0);
|
|
4006
|
+
if (pan.y) pan.y.scrollTop = pan.top - (ev.clientY - s.y0);
|
|
3546
4007
|
} else if (dist > 8) this.endSession(null, true);
|
|
3547
4008
|
return;
|
|
3548
4009
|
}
|
|
@@ -3580,7 +4041,7 @@ var Calendar = class {
|
|
|
3580
4041
|
};
|
|
3581
4042
|
/** Proposal for the current pointer position. */
|
|
3582
4043
|
track(s, ev) {
|
|
3583
|
-
const hit = this.
|
|
4044
|
+
const hit = this.hitAt(ev.clientX, ev.clientY);
|
|
3584
4045
|
if (s.mode === "external") {
|
|
3585
4046
|
this.trackExternal(s, hit);
|
|
3586
4047
|
return;
|
|
@@ -3706,7 +4167,7 @@ var Calendar = class {
|
|
|
3706
4167
|
return this.dayProposal(hit.day, hit.day + this.opts.defaultAllDayDays);
|
|
3707
4168
|
}
|
|
3708
4169
|
if (hit.kind === "day") {
|
|
3709
|
-
const dd = hit.day - (grab.kind === "
|
|
4170
|
+
const dd = hit.day - (grab.kind === "timeline" ? hit.day : grab.day);
|
|
3710
4171
|
if (o.allDay) return this.dayProposal(o.startDay + dd, o.startDay + dd + days);
|
|
3711
4172
|
const startMs2 = this.civilToMs(this.civil(o.startMs) + dd * MINUTES_PER_DAY);
|
|
3712
4173
|
const endMs = this.civilToMs(this.civil(o.endMs) + dd * MINUTES_PER_DAY);
|
|
@@ -3841,6 +4302,7 @@ var Calendar = class {
|
|
|
3841
4302
|
this.draggingId = null;
|
|
3842
4303
|
this.dragRow = null;
|
|
3843
4304
|
if (draggedId) for (const el of this.elementsFor(draggedId)) el.classList.remove("is-dragging");
|
|
4305
|
+
this.strip?.markTarget(null);
|
|
3844
4306
|
if (!s.active) return;
|
|
3845
4307
|
if (s.mode === "select") {
|
|
3846
4308
|
if (cancel) this.unselectSilently();
|
|
@@ -4127,6 +4589,19 @@ var Calendar = class {
|
|
|
4127
4589
|
});
|
|
4128
4590
|
};
|
|
4129
4591
|
};
|
|
4592
|
+
function scrollerOf(el, axis) {
|
|
4593
|
+
const doc = el.ownerDocument;
|
|
4594
|
+
const win = doc.defaultView;
|
|
4595
|
+
for (let p = el.parentElement; p && p !== doc.body && p !== doc.documentElement; p = p.parentElement) {
|
|
4596
|
+
const overflow = win?.getComputedStyle(p)[axis === "x" ? "overflowX" : "overflowY"] ?? "";
|
|
4597
|
+
const room = axis === "x" ? p.scrollWidth > p.clientWidth : p.scrollHeight > p.clientHeight;
|
|
4598
|
+
if (room && /auto|scroll|overlay/.test(overflow)) return p;
|
|
4599
|
+
}
|
|
4600
|
+
return doc.scrollingElement;
|
|
4601
|
+
}
|
|
4602
|
+
function rangeMoved(prev, next) {
|
|
4603
|
+
return !prev || prev.type !== next.type || prev.range.startDay !== next.range.startDay || prev.range.endDay !== next.range.endDay || prev.dataRange.endDay !== next.dataRange.endDay;
|
|
4604
|
+
}
|
|
4130
4605
|
|
|
4131
4606
|
// src/tasknotes/bases.ts
|
|
4132
4607
|
var FROM_BASES = {
|