@tasknotes/calendar 0.1.3 → 0.1.5
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 +24 -0
- package/README.md +7 -2
- package/dist/calendar.css +78 -6
- package/dist/cjs/index.cjs +390 -232
- package/dist/cjs/react.cjs +390 -232
- package/dist/esm/{chunk-XHPZ7K4W.js → chunk-25TY2UVR.js} +390 -232
- package/dist/esm/index.js +1 -1
- package/dist/esm/react.js +1 -1
- package/dist/types/core/types.d.cts +2 -0
- package/dist/types/core/types.d.ts +2 -0
- package/dist/types/dom/calendar.d.cts +14 -1
- package/dist/types/dom/calendar.d.ts +14 -1
- package/dist/types/dom/context.d.cts +4 -3
- package/dist/types/dom/context.d.ts +4 -3
- package/dist/types/dom/options.d.cts +33 -2
- package/dist/types/dom/options.d.ts +33 -2
- package/dist/types/dom/views/list.d.cts +10 -8
- package/dist/types/dom/views/list.d.ts +10 -8
- package/dist/types/dom/views/strip.d.cts +26 -6
- package/dist/types/dom/views/strip.d.ts +26 -6
- package/package.json +1 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -949,6 +949,9 @@ function defaultOptions() {
|
|
|
949
949
|
listDayCount: 7,
|
|
950
950
|
listStart: "date",
|
|
951
951
|
listAgenda: false,
|
|
952
|
+
dateStrip: false,
|
|
953
|
+
listLeadTitle: null,
|
|
954
|
+
listAddText: null,
|
|
952
955
|
monthDisplay: "full",
|
|
953
956
|
timeZone: "local",
|
|
954
957
|
locale: void 0,
|
|
@@ -999,6 +1002,9 @@ var LAYOUT_KEYS = /* @__PURE__ */ new Set([
|
|
|
999
1002
|
"listDayCount",
|
|
1000
1003
|
"listStart",
|
|
1001
1004
|
"listAgenda",
|
|
1005
|
+
"dateStrip",
|
|
1006
|
+
"listLeadTitle",
|
|
1007
|
+
"listAddText",
|
|
1002
1008
|
"monthDisplay",
|
|
1003
1009
|
"timeZone",
|
|
1004
1010
|
"locale",
|
|
@@ -1204,17 +1210,22 @@ var DayGridView = class {
|
|
|
1204
1210
|
const n = w.days.length;
|
|
1205
1211
|
const hidden = new Array(n).fill([]).map(() => []);
|
|
1206
1212
|
w.pool.begin();
|
|
1213
|
+
const previewLevels = new Array(n).fill(0);
|
|
1207
1214
|
let maxLevel = -1;
|
|
1208
1215
|
for (const p of row.placements) {
|
|
1209
1216
|
let level = p.level;
|
|
1210
1217
|
let room = Infinity;
|
|
1211
1218
|
for (let c = p.startCol; c < p.endCol; c++) room = Math.min(room, visible(c));
|
|
1212
1219
|
if (level >= room) {
|
|
1213
|
-
if (p.item.id !== ctx.draggingId) {
|
|
1220
|
+
if (!p.item.item.preview && p.item.id !== ctx.draggingId) {
|
|
1214
1221
|
for (let c = p.startCol; c < p.endCol; c++) hidden[c].push(p.item);
|
|
1215
1222
|
continue;
|
|
1216
1223
|
}
|
|
1217
|
-
|
|
1224
|
+
if (p.item.item.preview) {
|
|
1225
|
+
level = 0;
|
|
1226
|
+
for (let c = p.startCol; c < p.endCol; c++) level = Math.max(level, visible(c) + 1, previewLevels[c]);
|
|
1227
|
+
for (let c = p.startCol; c < p.endCol; c++) previewLevels[c] = level + 1;
|
|
1228
|
+
} else level = Math.max(0, room - 1);
|
|
1218
1229
|
}
|
|
1219
1230
|
maxLevel = Math.max(maxLevel, level);
|
|
1220
1231
|
const single = !p.item.allDay && p.item.endDay - p.item.startDay <= 1;
|
|
@@ -1340,12 +1351,244 @@ var DayGridView = class {
|
|
|
1340
1351
|
}
|
|
1341
1352
|
};
|
|
1342
1353
|
|
|
1354
|
+
// src/dom/views/list.ts
|
|
1355
|
+
var LOAD_AHEAD = "0px 0px 600px 0px";
|
|
1356
|
+
var ListView = class {
|
|
1357
|
+
constructor(ctx) {
|
|
1358
|
+
this.ctx = ctx;
|
|
1359
|
+
const { doc, win, opts } = ctx;
|
|
1360
|
+
this.el = h(doc, "div", "tnc-list");
|
|
1361
|
+
this.scroller = h(doc, "div", "tnc-list-scroll");
|
|
1362
|
+
this.body = h(doc, "div", "tnc-list-body", { role: "list", "aria-label": ctx.view.title });
|
|
1363
|
+
this.scroller.append(this.body);
|
|
1364
|
+
this.el.append(this.scroller);
|
|
1365
|
+
this.agenda = opts.listAgenda;
|
|
1366
|
+
if (!this.agenda) return;
|
|
1367
|
+
this.el.classList.add("tnc-list--agenda");
|
|
1368
|
+
this.sentinel = h(doc, "div", "tnc-list-more", { "aria-hidden": "true" });
|
|
1369
|
+
this.scroller.append(this.sentinel);
|
|
1370
|
+
const IO = win.IntersectionObserver;
|
|
1371
|
+
if (IO) {
|
|
1372
|
+
this.loader = new IO(
|
|
1373
|
+
(entries) => {
|
|
1374
|
+
if (entries.some((e) => e.isIntersecting)) ctx.extendList(ctx.view.range.endDay + Math.max(7, opts.listDayCount));
|
|
1375
|
+
},
|
|
1376
|
+
// Auto height scrolls the page, so watch the viewport; fixed height scrolls the list.
|
|
1377
|
+
{ root: opts.height === "auto" ? null : this.scroller, rootMargin: LOAD_AHEAD }
|
|
1378
|
+
);
|
|
1379
|
+
}
|
|
1380
|
+
win.addEventListener("scroll", this.onScroll, { capture: true, passive: true });
|
|
1381
|
+
this.scroller.addEventListener("wheel", this.fold, { passive: true });
|
|
1382
|
+
this.scroller.addEventListener("touchmove", this.fold, { passive: true });
|
|
1383
|
+
}
|
|
1384
|
+
ctx;
|
|
1385
|
+
el;
|
|
1386
|
+
scroller;
|
|
1387
|
+
body;
|
|
1388
|
+
agenda;
|
|
1389
|
+
pools = /* @__PURE__ */ new Map();
|
|
1390
|
+
sections = /* @__PURE__ */ new Map();
|
|
1391
|
+
lead = null;
|
|
1392
|
+
sentinel = null;
|
|
1393
|
+
loader = null;
|
|
1394
|
+
frame = null;
|
|
1395
|
+
/** A picked day stays marked until the list is scrolled by hand. */
|
|
1396
|
+
pinned = false;
|
|
1397
|
+
itemsOn(day) {
|
|
1398
|
+
const { store, opts } = this.ctx;
|
|
1399
|
+
const out = store.spanningIn(day, day + 1, true);
|
|
1400
|
+
const seen = new Set(out.map((n) => n.id));
|
|
1401
|
+
for (const n of store.timedOn(day)) if (!seen.has(n.id)) out.push(n);
|
|
1402
|
+
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));
|
|
1403
|
+
}
|
|
1404
|
+
render(_change) {
|
|
1405
|
+
const { ctx, agenda } = this;
|
|
1406
|
+
const { doc, days, opts } = ctx;
|
|
1407
|
+
const includes = agenda && opts.listLeadTitle !== null ? opts.listLeadIncludes : void 0;
|
|
1408
|
+
const leadItems = [];
|
|
1409
|
+
const inLead = /* @__PURE__ */ new Set();
|
|
1410
|
+
const keep = /* @__PURE__ */ new Set();
|
|
1411
|
+
let prev = null;
|
|
1412
|
+
for (const day of days) {
|
|
1413
|
+
let items = this.itemsOn(day);
|
|
1414
|
+
if (includes) {
|
|
1415
|
+
items = items.filter((n) => {
|
|
1416
|
+
if (!includes(n.item)) return true;
|
|
1417
|
+
if (!inLead.has(n.id)) {
|
|
1418
|
+
inLead.add(n.id);
|
|
1419
|
+
leadItems.push(n);
|
|
1420
|
+
}
|
|
1421
|
+
return false;
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
1424
|
+
if (!items.length && !agenda) continue;
|
|
1425
|
+
keep.add(day);
|
|
1426
|
+
let section = this.sections.get(day);
|
|
1427
|
+
let pool = this.pools.get(day);
|
|
1428
|
+
if (!section || !pool) {
|
|
1429
|
+
const arg = ctx.dayArg(day);
|
|
1430
|
+
const heading = h(doc, "div", ["tnc-list-day", ...ctx.dayClasses(day)].join(" "), { "data-date": arg.dateStr });
|
|
1431
|
+
const custom = opts.dayHeaderContent?.(arg);
|
|
1432
|
+
const label = custom ?? ctx.fmt.longDate(day);
|
|
1433
|
+
heading.append(opts.navLinks ? ctx.navLink(day, label, "tnc-list-day-link") : label);
|
|
1434
|
+
const rows = h(doc, "div", "tnc-list-rows", { role: "group" });
|
|
1435
|
+
section = h(doc, "div", "tnc-list-section", { role: "listitem", "aria-label": ctx.fmt.longDate(day), "data-date": arg.dateStr }, heading, rows);
|
|
1436
|
+
if (agenda && opts.listAddText !== null) section.append(this.addRow(day));
|
|
1437
|
+
opts.dayHeaderDidMount?.({ ...arg, el: heading });
|
|
1438
|
+
pool = new ItemPool(ctx, rows);
|
|
1439
|
+
this.sections.set(day, section);
|
|
1440
|
+
this.pools.set(day, pool);
|
|
1441
|
+
}
|
|
1442
|
+
section.classList.toggle("is-empty", !items.length);
|
|
1443
|
+
this.fill(pool, items, () => day);
|
|
1444
|
+
if (prev ? prev.nextSibling !== section : this.body.firstChild !== section) {
|
|
1445
|
+
if (prev) prev.after(section);
|
|
1446
|
+
else this.body.prepend(section);
|
|
1447
|
+
}
|
|
1448
|
+
prev = section;
|
|
1449
|
+
}
|
|
1450
|
+
for (const [day, section] of this.sections) {
|
|
1451
|
+
if (keep.has(day)) continue;
|
|
1452
|
+
this.pools.get(day)?.clear();
|
|
1453
|
+
this.pools.delete(day);
|
|
1454
|
+
section.remove();
|
|
1455
|
+
this.sections.delete(day);
|
|
1456
|
+
}
|
|
1457
|
+
this.renderLead(leadItems);
|
|
1458
|
+
let empty = this.body.querySelector(":scope > .tnc-list-empty");
|
|
1459
|
+
if (!keep.size && !leadItems.length) {
|
|
1460
|
+
if (!empty) {
|
|
1461
|
+
empty = h(doc, "div", "tnc-list-empty", { role: "listitem" }, ctx.text.noItems);
|
|
1462
|
+
this.body.append(empty);
|
|
1463
|
+
}
|
|
1464
|
+
} else empty?.remove();
|
|
1465
|
+
this.ctx.strip?.draw();
|
|
1466
|
+
if (this.loader && this.sentinel) {
|
|
1467
|
+
this.loader.unobserve(this.sentinel);
|
|
1468
|
+
this.loader.observe(this.sentinel);
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
/** Places items in a pool, keeping DOM order equal to item order. */
|
|
1472
|
+
fill(pool, items, dayFor) {
|
|
1473
|
+
pool.begin();
|
|
1474
|
+
for (const n of items) {
|
|
1475
|
+
const d = dayFor(n);
|
|
1476
|
+
pool.place(n.id, { n, day: d, isStart: n.startDay === d, isEnd: n.endDay === d + 1, variant: "list", mirror: false });
|
|
1477
|
+
}
|
|
1478
|
+
pool.end();
|
|
1479
|
+
for (const n of items) {
|
|
1480
|
+
const el = pool.elementsFor(n.id)[0];
|
|
1481
|
+
if (el) pool.container.append(el);
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
renderLead(items) {
|
|
1485
|
+
const { ctx } = this;
|
|
1486
|
+
const { doc, opts } = ctx;
|
|
1487
|
+
if (!items.length || opts.listLeadTitle === null) {
|
|
1488
|
+
if (!this.lead) return;
|
|
1489
|
+
opts.listLeadWillUnmount?.({ el: this.lead.slot });
|
|
1490
|
+
this.lead.pool.clear();
|
|
1491
|
+
this.lead.section.remove();
|
|
1492
|
+
this.lead = null;
|
|
1493
|
+
return;
|
|
1494
|
+
}
|
|
1495
|
+
if (!this.lead) {
|
|
1496
|
+
const title = opts.listLeadTitle;
|
|
1497
|
+
const slot = h(doc, "div", "tnc-list-lead-slot", { "data-tnc-no-drag": "" });
|
|
1498
|
+
const heading = h(doc, "div", "tnc-list-day tnc-list-lead-head", null, h(doc, "span", "tnc-list-lead-title", null, title), slot);
|
|
1499
|
+
const rows = h(doc, "div", "tnc-list-rows", { role: "group" });
|
|
1500
|
+
const section = h(doc, "div", "tnc-list-section tnc-list-lead", { role: "listitem", "aria-label": title }, heading, rows);
|
|
1501
|
+
this.lead = { section, slot, pool: new ItemPool(ctx, rows) };
|
|
1502
|
+
opts.listLeadDidMount?.({ el: slot });
|
|
1503
|
+
}
|
|
1504
|
+
if (this.body.firstChild !== this.lead.section) this.body.prepend(this.lead.section);
|
|
1505
|
+
const first = ctx.days[0] ?? ctx.view.range.startDay;
|
|
1506
|
+
this.fill(this.lead.pool, items, (n) => Math.max(n.startDay, first));
|
|
1507
|
+
}
|
|
1508
|
+
addRow(day) {
|
|
1509
|
+
const { ctx } = this;
|
|
1510
|
+
const label = ctx.opts.listAddText ?? "";
|
|
1511
|
+
const btn = h(ctx.doc, "button", "tnc-list-add", { type: "button", "aria-label": `${label}, ${ctx.fmt.longDate(day)}` }, label);
|
|
1512
|
+
btn.addEventListener("click", (ev) => ctx.opts.onListAdd?.({ ...ctx.dayArg(day), jsEvent: ev }));
|
|
1513
|
+
return btn;
|
|
1514
|
+
}
|
|
1515
|
+
/** Brings a day's section to the top, under the strip; the first day brings the lead section too. */
|
|
1516
|
+
scrollToDay(day) {
|
|
1517
|
+
const section = day === this.ctx.days[0] && this.lead ? this.lead.section : this.sections.get(day);
|
|
1518
|
+
if (!section) return;
|
|
1519
|
+
this.ctx.strip?.measure();
|
|
1520
|
+
section.scrollIntoView({ block: "start" });
|
|
1521
|
+
this.ctx.strip?.setFocus(day);
|
|
1522
|
+
this.pinned = true;
|
|
1523
|
+
}
|
|
1524
|
+
fold = () => {
|
|
1525
|
+
this.pinned = false;
|
|
1526
|
+
const strip = this.ctx.strip;
|
|
1527
|
+
if (strip?.expanded) strip.setExpanded(false);
|
|
1528
|
+
};
|
|
1529
|
+
onScroll = () => {
|
|
1530
|
+
if (this.frame !== null) return;
|
|
1531
|
+
this.frame = this.ctx.win.requestAnimationFrame(() => {
|
|
1532
|
+
this.frame = null;
|
|
1533
|
+
this.followTop();
|
|
1534
|
+
});
|
|
1535
|
+
};
|
|
1536
|
+
/** Marks the first day not hidden under the strip. */
|
|
1537
|
+
followTop() {
|
|
1538
|
+
const strip = this.ctx.strip;
|
|
1539
|
+
if (!strip || this.pinned) return;
|
|
1540
|
+
const edge = strip.el.getBoundingClientRect().bottom + 1;
|
|
1541
|
+
for (const day of this.ctx.days) {
|
|
1542
|
+
const section = this.sections.get(day);
|
|
1543
|
+
if (section && section.getBoundingClientRect().bottom > edge) {
|
|
1544
|
+
strip.setFocus(day);
|
|
1545
|
+
return;
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
showSelection(_sel) {
|
|
1550
|
+
}
|
|
1551
|
+
hit(x, y) {
|
|
1552
|
+
const el = this.ctx.doc.elementFromPoint(x, y);
|
|
1553
|
+
const section = el?.closest(".tnc-list-section");
|
|
1554
|
+
if (!section || !this.body.contains(section)) return null;
|
|
1555
|
+
if (section === this.lead?.section) {
|
|
1556
|
+
const item = el?.closest(".tnc-item");
|
|
1557
|
+
const meta = item ? this.ctx.meta.get(item) : void 0;
|
|
1558
|
+
return { kind: "day", day: meta?.day ?? this.ctx.days[0] ?? this.ctx.view.range.startDay };
|
|
1559
|
+
}
|
|
1560
|
+
const day = parseDay(section.dataset.date ?? "");
|
|
1561
|
+
return day === null ? null : { kind: "day", day };
|
|
1562
|
+
}
|
|
1563
|
+
getScroll() {
|
|
1564
|
+
return { top: this.scroller.scrollTop, left: 0 };
|
|
1565
|
+
}
|
|
1566
|
+
setScroll(s) {
|
|
1567
|
+
this.scroller.scrollTop = s.top;
|
|
1568
|
+
}
|
|
1569
|
+
elementsFor(id) {
|
|
1570
|
+
const pools = [...this.pools.values(), ...this.lead ? [this.lead.pool] : []];
|
|
1571
|
+
return pools.flatMap((p) => p.elementsFor(id));
|
|
1572
|
+
}
|
|
1573
|
+
destroy() {
|
|
1574
|
+
const { win, opts } = this.ctx;
|
|
1575
|
+
win.removeEventListener("scroll", this.onScroll, { capture: true });
|
|
1576
|
+
if (this.frame !== null) win.cancelAnimationFrame(this.frame);
|
|
1577
|
+
this.loader?.disconnect();
|
|
1578
|
+
if (this.lead) opts.listLeadWillUnmount?.({ el: this.lead.slot });
|
|
1579
|
+
this.lead?.pool.clear();
|
|
1580
|
+
for (const p of this.pools.values()) p.clear();
|
|
1581
|
+
this.el.remove();
|
|
1582
|
+
}
|
|
1583
|
+
};
|
|
1584
|
+
|
|
1343
1585
|
// src/dom/views/strip.ts
|
|
1344
1586
|
var SWIPE_PX = 40;
|
|
1345
1587
|
var DateStrip = class {
|
|
1346
|
-
constructor(ctx, pick, focus) {
|
|
1588
|
+
constructor(ctx, pick, focus, onShown) {
|
|
1347
1589
|
this.ctx = ctx;
|
|
1348
1590
|
this.pick = pick;
|
|
1591
|
+
this.onShown = onShown;
|
|
1349
1592
|
const { doc, text } = ctx;
|
|
1350
1593
|
this.focus = focus;
|
|
1351
1594
|
this.shown = startOfWeek(focus, ctx.firstDay);
|
|
@@ -1357,8 +1600,10 @@ var DateStrip = class {
|
|
|
1357
1600
|
const next = h(doc, "button", "tnc-strip-nav tnc-strip-next", { type: "button", "aria-label": text.next, title: text.next }, "\u203A");
|
|
1358
1601
|
prev.addEventListener("click", () => this.step(-1));
|
|
1359
1602
|
next.addEventListener("click", () => this.step(1));
|
|
1603
|
+
this.todayBtn = h(doc, "button", "tnc-strip-today", { type: "button" }, text.today);
|
|
1604
|
+
this.todayBtn.addEventListener("click", (ev) => this.pick(ctx.today, ev));
|
|
1360
1605
|
this.slot = h(doc, "div", "tnc-strip-slot", { "data-tnc-no-drag": "" });
|
|
1361
|
-
head.append(this.title, prev, next, this.slot);
|
|
1606
|
+
head.append(this.title, this.todayBtn, prev, next, this.slot);
|
|
1362
1607
|
this.dows = h(doc, "div", "tnc-strip-dows", { "aria-hidden": "true" });
|
|
1363
1608
|
this.grid = h(doc, "div", "tnc-strip-grid", { role: "group" });
|
|
1364
1609
|
const handle = h(doc, "button", "tnc-strip-handle", { type: "button", tabindex: "-1", "aria-hidden": "true" });
|
|
@@ -1375,27 +1620,45 @@ var DateStrip = class {
|
|
|
1375
1620
|
}
|
|
1376
1621
|
ctx;
|
|
1377
1622
|
pick;
|
|
1623
|
+
onShown;
|
|
1378
1624
|
el;
|
|
1379
1625
|
title;
|
|
1380
1626
|
dows;
|
|
1381
1627
|
grid;
|
|
1382
1628
|
slot;
|
|
1629
|
+
todayBtn;
|
|
1383
1630
|
/** Day at the top of the list. */
|
|
1384
1631
|
focus;
|
|
1385
1632
|
/** First day of the week shown, or the month shown when expanded. */
|
|
1386
1633
|
shown;
|
|
1387
1634
|
target = null;
|
|
1635
|
+
/** Days tinted as on screen besides the focus (a time grid's range). */
|
|
1636
|
+
range = null;
|
|
1637
|
+
shownKey = "";
|
|
1388
1638
|
swipe = null;
|
|
1389
1639
|
swallowClick = false;
|
|
1390
1640
|
expanded = false;
|
|
1391
|
-
/** The list scrolled: mark
|
|
1392
|
-
setFocus(day) {
|
|
1393
|
-
|
|
1394
|
-
this.focus
|
|
1395
|
-
|
|
1396
|
-
|
|
1641
|
+
/** The list scrolled or the grid moved: mark the day and keep it in view. */
|
|
1642
|
+
setFocus(day, range = this.range) {
|
|
1643
|
+
const sameRange = range?.start === this.range?.start && range?.end === this.range?.end;
|
|
1644
|
+
if (day === this.focus && sameRange) return;
|
|
1645
|
+
this.range = range;
|
|
1646
|
+
if (day !== this.focus) {
|
|
1647
|
+
this.focus = day;
|
|
1648
|
+
if (!this.expanded) this.shown = startOfWeek(day, this.ctx.firstDay);
|
|
1649
|
+
else if (startOfMonth(day) !== this.shown) this.shown = startOfMonth(day);
|
|
1650
|
+
}
|
|
1397
1651
|
this.draw();
|
|
1398
1652
|
}
|
|
1653
|
+
/** Publishes the strip's height as `--tnc-strip-h`, for what sticks below it. */
|
|
1654
|
+
measure() {
|
|
1655
|
+
this.el.parentElement?.style.setProperty("--tnc-strip-h", `${this.el.offsetHeight}px`);
|
|
1656
|
+
}
|
|
1657
|
+
/** First day and end (exclusive) of the days on show. */
|
|
1658
|
+
window() {
|
|
1659
|
+
const days = this.days();
|
|
1660
|
+
return { start: days[0], end: days[days.length - 1] + 1 };
|
|
1661
|
+
}
|
|
1399
1662
|
setExpanded(on) {
|
|
1400
1663
|
if (on === this.expanded) return;
|
|
1401
1664
|
this.expanded = on;
|
|
@@ -1433,6 +1696,7 @@ var DateStrip = class {
|
|
|
1433
1696
|
this.title.replaceChildren(fmt.monthYear(titleDay), h(doc, "span", "tnc-strip-caret", { "aria-hidden": "true" }));
|
|
1434
1697
|
this.title.setAttribute("aria-expanded", String(this.expanded));
|
|
1435
1698
|
this.el.classList.toggle("is-expanded", this.expanded);
|
|
1699
|
+
this.todayBtn.hidden = this.focus === ctx.today;
|
|
1436
1700
|
this.dows.replaceChildren(...days.slice(0, 7).map((d) => h(doc, "span", null, null, fmt.weekdayNarrow(d))));
|
|
1437
1701
|
const focused = this.grid.contains(doc.activeElement) ? doc.activeElement.dataset.date : void 0;
|
|
1438
1702
|
const month = ymdOfDay(this.shown).month;
|
|
@@ -1449,6 +1713,7 @@ var DateStrip = class {
|
|
|
1449
1713
|
"tnc-strip-day",
|
|
1450
1714
|
...ctx.dayClasses(d),
|
|
1451
1715
|
d === this.focus ? "is-focus" : "",
|
|
1716
|
+
d !== this.focus && this.range && d >= this.range.start && d < this.range.end ? "is-in-range" : "",
|
|
1452
1717
|
count ? "has-items" : "",
|
|
1453
1718
|
this.expanded && ymdOfDay(d).month !== month ? "is-other-month" : "",
|
|
1454
1719
|
d === this.target ? "is-drop-target" : ""
|
|
@@ -1468,6 +1733,11 @@ var DateStrip = class {
|
|
|
1468
1733
|
this.grid.replaceChildren(...cells);
|
|
1469
1734
|
this.grid.setAttribute("aria-label", fmt.monthYear(titleDay));
|
|
1470
1735
|
if (focused) this.grid.querySelector(`[data-date="${focused}"]`)?.focus({ preventScroll: true });
|
|
1736
|
+
const key = `${days[0]}:${days.length}`;
|
|
1737
|
+
if (key !== this.shownKey) {
|
|
1738
|
+
this.shownKey = key;
|
|
1739
|
+
this.onShown();
|
|
1740
|
+
}
|
|
1471
1741
|
}
|
|
1472
1742
|
onClick = (ev) => {
|
|
1473
1743
|
const day = this.dayAt(ev.target);
|
|
@@ -1522,205 +1792,6 @@ var DateStrip = class {
|
|
|
1522
1792
|
}
|
|
1523
1793
|
};
|
|
1524
1794
|
|
|
1525
|
-
// src/dom/views/list.ts
|
|
1526
|
-
var LOAD_AHEAD = "0px 0px 600px 0px";
|
|
1527
|
-
var ListView = class {
|
|
1528
|
-
constructor(ctx) {
|
|
1529
|
-
this.ctx = ctx;
|
|
1530
|
-
const { doc, win, opts } = ctx;
|
|
1531
|
-
this.el = h(doc, "div", "tnc-list");
|
|
1532
|
-
this.scroller = h(doc, "div", "tnc-list-scroll");
|
|
1533
|
-
this.body = h(doc, "div", "tnc-list-body", { role: "list", "aria-label": ctx.view.title });
|
|
1534
|
-
this.scroller.append(this.body);
|
|
1535
|
-
this.el.append(this.scroller);
|
|
1536
|
-
if (!opts.listAgenda) return;
|
|
1537
|
-
this.el.classList.add("tnc-list--agenda");
|
|
1538
|
-
this.strip = new DateStrip(ctx, (day, ev) => this.pick(day, ev), ctx.view.range.startDay);
|
|
1539
|
-
this.el.prepend(this.strip.el);
|
|
1540
|
-
this.sentinel = h(doc, "div", "tnc-list-more", { "aria-hidden": "true" });
|
|
1541
|
-
this.scroller.append(this.sentinel);
|
|
1542
|
-
const globals = win;
|
|
1543
|
-
const IO = globals.IntersectionObserver;
|
|
1544
|
-
if (IO) {
|
|
1545
|
-
this.loader = new IO(
|
|
1546
|
-
(entries) => {
|
|
1547
|
-
if (entries.some((e) => e.isIntersecting)) ctx.extendList(ctx.view.range.endDay + Math.max(7, opts.listDayCount));
|
|
1548
|
-
},
|
|
1549
|
-
// Auto height scrolls the page, so watch the viewport; fixed height scrolls the list.
|
|
1550
|
-
{ root: opts.height === "auto" ? null : this.scroller, rootMargin: LOAD_AHEAD }
|
|
1551
|
-
);
|
|
1552
|
-
}
|
|
1553
|
-
const RO = globals.ResizeObserver;
|
|
1554
|
-
if (RO) {
|
|
1555
|
-
const strip = this.strip.el;
|
|
1556
|
-
this.stripSize = new RO(() => this.el.style.setProperty("--tnc-strip-h", `${strip.offsetHeight}px`));
|
|
1557
|
-
this.stripSize.observe(strip);
|
|
1558
|
-
}
|
|
1559
|
-
win.addEventListener("scroll", this.onScroll, { capture: true, passive: true });
|
|
1560
|
-
this.scroller.addEventListener("wheel", this.fold, { passive: true });
|
|
1561
|
-
this.scroller.addEventListener("touchmove", this.fold, { passive: true });
|
|
1562
|
-
}
|
|
1563
|
-
ctx;
|
|
1564
|
-
el;
|
|
1565
|
-
scroller;
|
|
1566
|
-
body;
|
|
1567
|
-
pools = /* @__PURE__ */ new Map();
|
|
1568
|
-
sections = /* @__PURE__ */ new Map();
|
|
1569
|
-
strip = null;
|
|
1570
|
-
sentinel = null;
|
|
1571
|
-
loader = null;
|
|
1572
|
-
stripSize = null;
|
|
1573
|
-
frame = null;
|
|
1574
|
-
/** A picked day stays marked until the list is scrolled by hand. */
|
|
1575
|
-
pinned = false;
|
|
1576
|
-
itemsOn(day) {
|
|
1577
|
-
const { store, opts } = this.ctx;
|
|
1578
|
-
const out = store.spanningIn(day, day + 1, true);
|
|
1579
|
-
const seen = new Set(out.map((n) => n.id));
|
|
1580
|
-
for (const n of store.timedOn(day)) if (!seen.has(n.id)) out.push(n);
|
|
1581
|
-
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));
|
|
1582
|
-
}
|
|
1583
|
-
render(_change) {
|
|
1584
|
-
const { ctx } = this;
|
|
1585
|
-
const { doc, days } = ctx;
|
|
1586
|
-
const agenda = !!this.strip;
|
|
1587
|
-
const keep = /* @__PURE__ */ new Set();
|
|
1588
|
-
let prev = null;
|
|
1589
|
-
for (const day of days) {
|
|
1590
|
-
const items = this.itemsOn(day);
|
|
1591
|
-
if (!items.length && !agenda) continue;
|
|
1592
|
-
keep.add(day);
|
|
1593
|
-
let section = this.sections.get(day);
|
|
1594
|
-
let pool = this.pools.get(day);
|
|
1595
|
-
if (!section || !pool) {
|
|
1596
|
-
const arg = ctx.dayArg(day);
|
|
1597
|
-
const heading = h(doc, "div", ["tnc-list-day", ...ctx.dayClasses(day)].join(" "), { "data-date": arg.dateStr });
|
|
1598
|
-
const custom = ctx.opts.dayHeaderContent?.(arg);
|
|
1599
|
-
const label = custom ?? ctx.fmt.longDate(day);
|
|
1600
|
-
heading.append(ctx.opts.navLinks ? ctx.navLink(day, label, "tnc-list-day-link") : label);
|
|
1601
|
-
const rows2 = h(doc, "div", "tnc-list-rows", { role: "group" });
|
|
1602
|
-
section = h(doc, "div", "tnc-list-section", { role: "listitem", "aria-label": ctx.fmt.longDate(day), "data-date": arg.dateStr }, heading, rows2);
|
|
1603
|
-
ctx.opts.dayHeaderDidMount?.({ ...arg, el: heading });
|
|
1604
|
-
pool = new ItemPool(ctx, rows2);
|
|
1605
|
-
this.sections.set(day, section);
|
|
1606
|
-
this.pools.set(day, pool);
|
|
1607
|
-
}
|
|
1608
|
-
section.classList.toggle("is-empty", !items.length);
|
|
1609
|
-
pool.begin();
|
|
1610
|
-
for (const n of items) {
|
|
1611
|
-
pool.place(n.id, {
|
|
1612
|
-
n,
|
|
1613
|
-
day,
|
|
1614
|
-
isStart: n.startDay === day,
|
|
1615
|
-
isEnd: n.endDay === day + 1,
|
|
1616
|
-
variant: "list",
|
|
1617
|
-
mirror: false
|
|
1618
|
-
});
|
|
1619
|
-
}
|
|
1620
|
-
pool.end();
|
|
1621
|
-
const rows = pool.container;
|
|
1622
|
-
for (const n of items) {
|
|
1623
|
-
const el = pool.elementsFor(n.id)[0];
|
|
1624
|
-
if (el) rows.append(el);
|
|
1625
|
-
}
|
|
1626
|
-
if (prev ? prev.nextSibling !== section : this.body.firstChild !== section) {
|
|
1627
|
-
if (prev) prev.after(section);
|
|
1628
|
-
else this.body.prepend(section);
|
|
1629
|
-
}
|
|
1630
|
-
prev = section;
|
|
1631
|
-
}
|
|
1632
|
-
for (const [day, section] of this.sections) {
|
|
1633
|
-
if (keep.has(day)) continue;
|
|
1634
|
-
this.pools.get(day)?.clear();
|
|
1635
|
-
this.pools.delete(day);
|
|
1636
|
-
section.remove();
|
|
1637
|
-
this.sections.delete(day);
|
|
1638
|
-
}
|
|
1639
|
-
let empty = this.body.querySelector(":scope > .tnc-list-empty");
|
|
1640
|
-
if (!keep.size) {
|
|
1641
|
-
if (!empty) {
|
|
1642
|
-
empty = h(doc, "div", "tnc-list-empty", { role: "listitem" }, ctx.text.noItems);
|
|
1643
|
-
this.body.append(empty);
|
|
1644
|
-
}
|
|
1645
|
-
} else empty?.remove();
|
|
1646
|
-
this.strip?.draw();
|
|
1647
|
-
if (this.loader && this.sentinel) {
|
|
1648
|
-
this.loader.unobserve(this.sentinel);
|
|
1649
|
-
this.loader.observe(this.sentinel);
|
|
1650
|
-
}
|
|
1651
|
-
}
|
|
1652
|
-
/** A strip day: the host hears of it, and the list brings it to the top. */
|
|
1653
|
-
pick(day, ev) {
|
|
1654
|
-
this.ctx.dateClick(day, ev);
|
|
1655
|
-
this.ctx.showListDay(day);
|
|
1656
|
-
}
|
|
1657
|
-
/** Brings a day's section to the top, under the strip. */
|
|
1658
|
-
scrollToDay(day) {
|
|
1659
|
-
const section = this.sections.get(day);
|
|
1660
|
-
if (!section) return;
|
|
1661
|
-
if (this.strip) this.el.style.setProperty("--tnc-strip-h", `${this.strip.el.offsetHeight}px`);
|
|
1662
|
-
section.scrollIntoView({ block: "start" });
|
|
1663
|
-
this.strip?.setFocus(day);
|
|
1664
|
-
this.pinned = true;
|
|
1665
|
-
}
|
|
1666
|
-
fold = () => {
|
|
1667
|
-
this.pinned = false;
|
|
1668
|
-
if (this.strip?.expanded) this.strip.setExpanded(false);
|
|
1669
|
-
};
|
|
1670
|
-
onScroll = () => {
|
|
1671
|
-
if (this.frame !== null) return;
|
|
1672
|
-
this.frame = this.ctx.win.requestAnimationFrame(() => {
|
|
1673
|
-
this.frame = null;
|
|
1674
|
-
this.followTop();
|
|
1675
|
-
});
|
|
1676
|
-
};
|
|
1677
|
-
/** Marks the first day not hidden under the strip. */
|
|
1678
|
-
followTop() {
|
|
1679
|
-
if (!this.strip || this.pinned) return;
|
|
1680
|
-
const edge = this.strip.el.getBoundingClientRect().bottom + 1;
|
|
1681
|
-
for (const day of this.ctx.days) {
|
|
1682
|
-
const section = this.sections.get(day);
|
|
1683
|
-
if (section && section.getBoundingClientRect().bottom > edge) {
|
|
1684
|
-
this.strip.setFocus(day);
|
|
1685
|
-
return;
|
|
1686
|
-
}
|
|
1687
|
-
}
|
|
1688
|
-
}
|
|
1689
|
-
showSelection(_sel) {
|
|
1690
|
-
}
|
|
1691
|
-
hit(x, y) {
|
|
1692
|
-
const el = this.ctx.doc.elementFromPoint(x, y);
|
|
1693
|
-
const stripDay = el && this.strip ? this.strip.dayAt(el) : null;
|
|
1694
|
-
this.strip?.markTarget(stripDay !== null && this.ctx.draggingId ? stripDay : null);
|
|
1695
|
-
if (stripDay !== null) return { kind: "day", day: stripDay };
|
|
1696
|
-
const section = el?.closest(".tnc-list-section");
|
|
1697
|
-
const day = section && this.body.contains(section) ? parseDay(section.dataset.date ?? "") : null;
|
|
1698
|
-
return day === null ? null : { kind: "day", day };
|
|
1699
|
-
}
|
|
1700
|
-
dragEnded() {
|
|
1701
|
-
this.strip?.markTarget(null);
|
|
1702
|
-
}
|
|
1703
|
-
getScroll() {
|
|
1704
|
-
return { top: this.scroller.scrollTop, left: 0 };
|
|
1705
|
-
}
|
|
1706
|
-
setScroll(s) {
|
|
1707
|
-
this.scroller.scrollTop = s.top;
|
|
1708
|
-
}
|
|
1709
|
-
elementsFor(id) {
|
|
1710
|
-
return [...this.pools.values()].flatMap((p) => p.elementsFor(id));
|
|
1711
|
-
}
|
|
1712
|
-
destroy() {
|
|
1713
|
-
const { win } = this.ctx;
|
|
1714
|
-
win.removeEventListener("scroll", this.onScroll, { capture: true });
|
|
1715
|
-
if (this.frame !== null) win.cancelAnimationFrame(this.frame);
|
|
1716
|
-
this.loader?.disconnect();
|
|
1717
|
-
this.stripSize?.disconnect();
|
|
1718
|
-
this.strip?.destroy();
|
|
1719
|
-
for (const p of this.pools.values()) p.clear();
|
|
1720
|
-
this.el.remove();
|
|
1721
|
-
}
|
|
1722
|
-
};
|
|
1723
|
-
|
|
1724
1795
|
// src/dom/views/timegrid.ts
|
|
1725
1796
|
var BAR_GAP2 = 2;
|
|
1726
1797
|
var TimeGridView = class {
|
|
@@ -1939,17 +2010,22 @@ var TimeGridView = class {
|
|
|
1939
2010
|
const pool = this.allDayPool;
|
|
1940
2011
|
pool.begin();
|
|
1941
2012
|
const hidden = new Array(days.length).fill(0);
|
|
2013
|
+
const previewLevels = new Array(days.length).fill(0);
|
|
1942
2014
|
let maxLevel = -1;
|
|
1943
2015
|
for (const p of row.placements) {
|
|
1944
2016
|
let level = p.level;
|
|
1945
2017
|
let room = Infinity;
|
|
1946
2018
|
for (let c = p.startCol; c < p.endCol; c++) room = Math.min(room, visibleLevels(c));
|
|
1947
2019
|
if (level >= room) {
|
|
1948
|
-
if (p.item.id !== ctx.draggingId) {
|
|
2020
|
+
if (!p.item.item.preview && p.item.id !== ctx.draggingId) {
|
|
1949
2021
|
for (let c = p.startCol; c < p.endCol; c++) hidden[c]++;
|
|
1950
2022
|
continue;
|
|
1951
2023
|
}
|
|
1952
|
-
|
|
2024
|
+
if (p.item.item.preview) {
|
|
2025
|
+
level = 0;
|
|
2026
|
+
for (let c = p.startCol; c < p.endCol; c++) level = Math.max(level, visibleLevels(c) + 1, previewLevels[c]);
|
|
2027
|
+
for (let c = p.startCol; c < p.endCol; c++) previewLevels[c] = level + 1;
|
|
2028
|
+
} else level = Math.max(0, room - 1);
|
|
1953
2029
|
}
|
|
1954
2030
|
maxLevel = Math.max(maxLevel, level);
|
|
1955
2031
|
const el = pool.place(p.item.id, {
|
|
@@ -2037,7 +2113,8 @@ var TimeGridView = class {
|
|
|
2037
2113
|
const hiddenByCluster = /* @__PURE__ */ new Map();
|
|
2038
2114
|
pool.begin();
|
|
2039
2115
|
for (const p of placements) {
|
|
2040
|
-
|
|
2116
|
+
const preview = p.item.item.preview || p.item.id === ctx.draggingId;
|
|
2117
|
+
if (!preview && withMore && maxStack > 0 && p.cols > maxStack && p.col >= maxStack - 1) {
|
|
2041
2118
|
const rec = hiddenByCluster.get(p.cluster) ?? { count: 0, start: p.startMin, end: p.endMin, items: [] };
|
|
2042
2119
|
rec.count++;
|
|
2043
2120
|
rec.start = Math.min(rec.start, p.startMin);
|
|
@@ -2046,7 +2123,7 @@ var TimeGridView = class {
|
|
|
2046
2123
|
hiddenByCluster.set(p.cluster, rec);
|
|
2047
2124
|
continue;
|
|
2048
2125
|
}
|
|
2049
|
-
const cols = withMore && maxStack > 0 ? Math.min(p.cols, maxStack) : p.cols;
|
|
2126
|
+
const cols = !preview && withMore && maxStack > 0 ? Math.min(p.cols, maxStack) : p.cols;
|
|
2050
2127
|
const span = Math.min(p.span, cols - p.col);
|
|
2051
2128
|
const left = p.col / cols;
|
|
2052
2129
|
let width = span / cols;
|
|
@@ -2926,6 +3003,9 @@ var Calendar = class {
|
|
|
2926
3003
|
slot = { min: 0, max: MINUTES_PER_DAY, duration: 30, snap: 30, scroll: 480 };
|
|
2927
3004
|
draggingId = null;
|
|
2928
3005
|
dragRow = null;
|
|
3006
|
+
strip = null;
|
|
3007
|
+
stripSize = null;
|
|
3008
|
+
rebuilding = false;
|
|
2929
3009
|
toolbarEl;
|
|
2930
3010
|
viewHost;
|
|
2931
3011
|
current = null;
|
|
@@ -3139,7 +3219,7 @@ var Calendar = class {
|
|
|
3139
3219
|
}
|
|
3140
3220
|
/** What is under a client point: a time slot, a day, or a timeline row and date. Null outside the view. */
|
|
3141
3221
|
hitTest(clientX, clientY) {
|
|
3142
|
-
return this.
|
|
3222
|
+
return this.hitAt(clientX, clientY);
|
|
3143
3223
|
}
|
|
3144
3224
|
/** Item elements currently rendered for an id. */
|
|
3145
3225
|
elementsFor(id) {
|
|
@@ -3181,6 +3261,9 @@ var Calendar = class {
|
|
|
3181
3261
|
this.unbindEvents();
|
|
3182
3262
|
this.current?.destroy();
|
|
3183
3263
|
this.current = null;
|
|
3264
|
+
this.stripSize?.disconnect();
|
|
3265
|
+
this.strip?.destroy();
|
|
3266
|
+
this.strip = null;
|
|
3184
3267
|
this.root.remove();
|
|
3185
3268
|
}
|
|
3186
3269
|
/* ========================================================== view context */
|
|
@@ -3211,7 +3294,7 @@ var Calendar = class {
|
|
|
3211
3294
|
return out;
|
|
3212
3295
|
}
|
|
3213
3296
|
isEditable(n, what) {
|
|
3214
|
-
if (n.id === EXTERNAL_ID || this.opts.view === "year") return false;
|
|
3297
|
+
if (n.id === EXTERNAL_ID || n.item.preview || this.opts.view === "year") return false;
|
|
3215
3298
|
if (this.opts.view === "list" && (!this.opts.listAgenda || what === "duration")) return false;
|
|
3216
3299
|
const it = n.item;
|
|
3217
3300
|
const base = it.editable ?? this.opts.editable;
|
|
@@ -3237,6 +3320,7 @@ var Calendar = class {
|
|
|
3237
3320
|
createItemEl(meta) {
|
|
3238
3321
|
const { n, variant } = meta;
|
|
3239
3322
|
const it = n.item;
|
|
3323
|
+
const preview = meta.mirror || it.preview === true;
|
|
3240
3324
|
const el = h(this.doc, "div", "tnc-item");
|
|
3241
3325
|
el.classList.add(`tnc-item--${variant}`);
|
|
3242
3326
|
if (it.kind) el.classList.add(`tnc-k-${it.kind.replace(/[^\w-]/g, "-")}`);
|
|
@@ -3246,12 +3330,12 @@ var Calendar = class {
|
|
|
3246
3330
|
if (!meta.isStart) el.classList.add("is-cont-start");
|
|
3247
3331
|
if (!meta.isEnd) el.classList.add("is-cont-end");
|
|
3248
3332
|
if (n.allDay) el.classList.add("is-all-day");
|
|
3249
|
-
if (
|
|
3333
|
+
if (preview || n.id === EXTERNAL_ID) el.classList.add("is-mirror");
|
|
3250
3334
|
if (this.conflicts.has(n.id)) el.classList.add("is-conflict");
|
|
3251
3335
|
const indicator = variant === "indicator";
|
|
3252
|
-
if (!
|
|
3336
|
+
if (!preview && !indicator && (this.isEditable(n, "start") || this.isEditable(n, "duration"))) el.classList.add("is-draggable");
|
|
3253
3337
|
if (this.draggingId === n.id) el.classList.add("is-dragging");
|
|
3254
|
-
if (this.selectedIds.has(n.id) && !
|
|
3338
|
+
if (this.selectedIds.has(n.id) && !preview) el.classList.add("is-selected");
|
|
3255
3339
|
el.classList.add(...classList(it.className));
|
|
3256
3340
|
const arg = this.itemArg(el, meta);
|
|
3257
3341
|
el.classList.add(...classList(this.opts.itemClassNames?.(arg)));
|
|
@@ -3262,7 +3346,7 @@ var Calendar = class {
|
|
|
3262
3346
|
if (it.backgroundColor) el.style.setProperty("--tnc-bg", it.backgroundColor);
|
|
3263
3347
|
if (it.borderColor) el.style.setProperty("--tnc-border", it.borderColor);
|
|
3264
3348
|
if (it.textColor) el.style.setProperty("--tnc-fg", it.textColor);
|
|
3265
|
-
if (indicator) el.setAttribute("aria-hidden", "true");
|
|
3349
|
+
if (indicator || preview) el.setAttribute("aria-hidden", "true");
|
|
3266
3350
|
else if (it.display !== "background") {
|
|
3267
3351
|
el.tabIndex = 0;
|
|
3268
3352
|
el.setAttribute("role", "button");
|
|
@@ -3279,7 +3363,7 @@ var Calendar = class {
|
|
|
3279
3363
|
} else {
|
|
3280
3364
|
el.append(this.defaultContent(meta));
|
|
3281
3365
|
}
|
|
3282
|
-
if (variant !== "list" && !indicator && it.display !== "background") this.addHandles(el, meta);
|
|
3366
|
+
if (!preview && variant !== "list" && !indicator && it.display !== "background") this.addHandles(el, meta);
|
|
3283
3367
|
this.opts.itemDidMount?.(arg);
|
|
3284
3368
|
return el;
|
|
3285
3369
|
}
|
|
@@ -3288,7 +3372,7 @@ var Calendar = class {
|
|
|
3288
3372
|
if (time) this.fillTime(time, meta.n, meta.variant, meta.day);
|
|
3289
3373
|
el.classList.toggle("is-dragging", this.draggingId === meta.n.id);
|
|
3290
3374
|
el.classList.toggle("is-conflict", this.conflicts.has(meta.n.id));
|
|
3291
|
-
el.classList.toggle("is-selected", this.selectedIds.has(meta.n.id) && !meta.mirror);
|
|
3375
|
+
el.classList.toggle("is-selected", this.selectedIds.has(meta.n.id) && !meta.mirror && !meta.n.item.preview);
|
|
3292
3376
|
}
|
|
3293
3377
|
selectedSet() {
|
|
3294
3378
|
const s = this.opts.selected;
|
|
@@ -3298,7 +3382,7 @@ var Calendar = class {
|
|
|
3298
3382
|
this.selectedIds = this.selectedSet();
|
|
3299
3383
|
for (const el of this.viewHost.querySelectorAll(".tnc-item")) {
|
|
3300
3384
|
const meta = this.meta.get(el);
|
|
3301
|
-
if (meta) el.classList.toggle("is-selected", this.selectedIds.has(meta.n.id) && !meta.mirror);
|
|
3385
|
+
if (meta) el.classList.toggle("is-selected", this.selectedIds.has(meta.n.id) && !meta.mirror && !meta.n.item.preview);
|
|
3302
3386
|
}
|
|
3303
3387
|
}
|
|
3304
3388
|
destroyItemEl(el) {
|
|
@@ -3449,12 +3533,18 @@ var Calendar = class {
|
|
|
3449
3533
|
const snap = Math.max(1, parseDurationMinutes(this.opts.snapDuration ?? null, duration));
|
|
3450
3534
|
this.slot = { min, max, duration, snap, scroll: parseDurationMinutes(this.opts.scrollTime, 480) };
|
|
3451
3535
|
if (navigated) this.listEnd = null;
|
|
3452
|
-
const
|
|
3536
|
+
const prev = this.view;
|
|
3537
|
+
this.applyRange();
|
|
3453
3538
|
this.conflicts = dependencyConflicts(this.store);
|
|
3454
3539
|
this.root.className = "tnc";
|
|
3455
3540
|
this.root.classList.add(`tnc--${this.opts.view}`, `tnc--height-${this.opts.height}`);
|
|
3456
3541
|
if (!this.opts.timeGrid) this.root.classList.add("tnc--no-time-grid");
|
|
3457
3542
|
this.renderToolbar();
|
|
3543
|
+
this.rebuilding = true;
|
|
3544
|
+
this.syncStrip(navigated);
|
|
3545
|
+
this.rebuilding = false;
|
|
3546
|
+
this.view.dataRange = this.dataRangeFor(this.view.range);
|
|
3547
|
+
const rangeChanged = rangeMoved(prev, this.view);
|
|
3458
3548
|
this.current?.destroy();
|
|
3459
3549
|
this.current = this.createView();
|
|
3460
3550
|
this.viewHost.replaceChildren(this.current.el);
|
|
@@ -3467,7 +3557,7 @@ var Calendar = class {
|
|
|
3467
3557
|
else if (this.current instanceof TimelineView) this.current.scrollToDay(this.anchor);
|
|
3468
3558
|
if (rangeChanged) this.opts.onRangeChange?.(this.view);
|
|
3469
3559
|
}
|
|
3470
|
-
/** Sets the visible days and view info from the anchor
|
|
3560
|
+
/** Sets the visible days and view info from the anchor. */
|
|
3471
3561
|
applyRange() {
|
|
3472
3562
|
const { startDay, endDay } = this.computeRange();
|
|
3473
3563
|
const hidden = new Set(this.opts.hiddenDays);
|
|
@@ -3485,10 +3575,74 @@ var Calendar = class {
|
|
|
3485
3575
|
start: new Date(toInstant(startDay, 0, this.zone)),
|
|
3486
3576
|
end: new Date(toInstant(endDay, 0, this.zone))
|
|
3487
3577
|
};
|
|
3488
|
-
|
|
3489
|
-
this.view = { type: this.opts.view, range, anchor: this.anchor, title: "" };
|
|
3578
|
+
this.view = { type: this.opts.view, range, anchor: this.anchor, title: "", dataRange: this.dataRangeFor(range) };
|
|
3490
3579
|
this.view.title = this.computeTitle(startDay, endDay);
|
|
3491
|
-
|
|
3580
|
+
}
|
|
3581
|
+
/** The range, run on to the end of the strip's days so their dots are right. */
|
|
3582
|
+
dataRangeFor(range) {
|
|
3583
|
+
const end = this.strip ? Math.max(range.endDay, this.strip.window().end) : range.endDay;
|
|
3584
|
+
if (end === range.endDay) return range;
|
|
3585
|
+
return { ...range, endDay: end, end: new Date(toInstant(end, 0, this.zone)) };
|
|
3586
|
+
}
|
|
3587
|
+
/** Adds, updates or removes the date strip for the current view. */
|
|
3588
|
+
syncStrip(navigated) {
|
|
3589
|
+
const { view, listAgenda, dateStrip } = this.opts;
|
|
3590
|
+
const list = view === "list";
|
|
3591
|
+
const want = list ? listAgenda : dateStrip && (view === "day" || view === "days" || view === "week");
|
|
3592
|
+
if (!want) {
|
|
3593
|
+
if (!this.strip) return;
|
|
3594
|
+
this.stripSize?.disconnect();
|
|
3595
|
+
this.stripSize = null;
|
|
3596
|
+
this.strip.destroy();
|
|
3597
|
+
this.strip = null;
|
|
3598
|
+
this.root.style.removeProperty("--tnc-strip-h");
|
|
3599
|
+
return;
|
|
3600
|
+
}
|
|
3601
|
+
this.root.classList.add("tnc--has-strip");
|
|
3602
|
+
const { startDay, endDay } = this.view.range;
|
|
3603
|
+
if (!this.strip) {
|
|
3604
|
+
const strip = new DateStrip(this, (day, ev) => this.pickStripDay(day, ev), startDay, () => this.stripShown());
|
|
3605
|
+
this.strip = strip;
|
|
3606
|
+
this.root.insertBefore(strip.el, this.viewHost);
|
|
3607
|
+
const RO = this.win.ResizeObserver;
|
|
3608
|
+
if (RO) {
|
|
3609
|
+
this.stripSize = new RO(() => strip.measure());
|
|
3610
|
+
this.stripSize.observe(strip.el);
|
|
3611
|
+
}
|
|
3612
|
+
}
|
|
3613
|
+
if (!list) this.strip.setFocus(startDay, { start: startDay, end: endDay });
|
|
3614
|
+
else if (navigated) this.strip.setFocus(startDay, null);
|
|
3615
|
+
this.strip.draw();
|
|
3616
|
+
}
|
|
3617
|
+
pickStripDay(day, ev) {
|
|
3618
|
+
this.dateClick(day, ev);
|
|
3619
|
+
if (this.agendaList()) {
|
|
3620
|
+
this.showListDay(day);
|
|
3621
|
+
return;
|
|
3622
|
+
}
|
|
3623
|
+
const { startDay, endDay } = this.view.range;
|
|
3624
|
+
if (this.opts.view === "week" && day >= startDay && day < endDay) return;
|
|
3625
|
+
this.anchor = day;
|
|
3626
|
+
this.rebuild(true);
|
|
3627
|
+
}
|
|
3628
|
+
/** The strip moved to other days: load theirs too. */
|
|
3629
|
+
stripShown() {
|
|
3630
|
+
if (!this.strip || this.rebuilding || !this.view) return;
|
|
3631
|
+
const next = this.dataRangeFor(this.view.range);
|
|
3632
|
+
if (next.endDay === this.view.dataRange.endDay) return;
|
|
3633
|
+
this.view = { ...this.view, dataRange: next };
|
|
3634
|
+
this.opts.onRangeChange?.(this.view);
|
|
3635
|
+
}
|
|
3636
|
+
/** What is under a point, the date strip included. */
|
|
3637
|
+
hitAt(x, y) {
|
|
3638
|
+
const strip = this.strip;
|
|
3639
|
+
if (strip) {
|
|
3640
|
+
const el = this.doc.elementFromPoint(x, y);
|
|
3641
|
+
const day = el ? strip.dayAt(el) : null;
|
|
3642
|
+
strip.markTarget(day !== null && this.draggingId ? day : null);
|
|
3643
|
+
if (day !== null) return { kind: "day", day };
|
|
3644
|
+
}
|
|
3645
|
+
return this.current?.hit(x, y) ?? null;
|
|
3492
3646
|
}
|
|
3493
3647
|
agendaList() {
|
|
3494
3648
|
return this.opts.view === "list" && this.opts.listAgenda && this.current instanceof ListView ? this.current : null;
|
|
@@ -3501,6 +3655,7 @@ var Calendar = class {
|
|
|
3501
3655
|
this.listEnd = end;
|
|
3502
3656
|
this.applyRange();
|
|
3503
3657
|
this.current?.render(null);
|
|
3658
|
+
this.strip?.draw();
|
|
3504
3659
|
this.opts.onRangeChange?.(this.view);
|
|
3505
3660
|
}
|
|
3506
3661
|
showListDay(day) {
|
|
@@ -3622,7 +3777,7 @@ var Calendar = class {
|
|
|
3622
3777
|
day: meta.day,
|
|
3623
3778
|
isStart: meta.isStart,
|
|
3624
3779
|
isEnd: meta.isEnd,
|
|
3625
|
-
isMirror: meta.mirror,
|
|
3780
|
+
isMirror: meta.mirror || meta.n.item.preview === true,
|
|
3626
3781
|
variant: meta.variant,
|
|
3627
3782
|
timeText: meta.n.allDay ? "" : end ? `${start} \u2013 ${end}` : start
|
|
3628
3783
|
};
|
|
@@ -3767,7 +3922,7 @@ var Calendar = class {
|
|
|
3767
3922
|
}
|
|
3768
3923
|
itemElFrom(target) {
|
|
3769
3924
|
const el = target?.closest?.(".tnc-item");
|
|
3770
|
-
return el && this.root.contains(el) && this.meta.has(el) ? el : null;
|
|
3925
|
+
return el && this.root.contains(el) && this.meta.has(el) && !this.meta.get(el)?.n.item.preview ? el : null;
|
|
3771
3926
|
}
|
|
3772
3927
|
onDocPointerDown = (ev) => {
|
|
3773
3928
|
const t = ev.target;
|
|
@@ -3898,7 +4053,7 @@ var Calendar = class {
|
|
|
3898
4053
|
};
|
|
3899
4054
|
/** Proposal for the current pointer position. */
|
|
3900
4055
|
track(s, ev) {
|
|
3901
|
-
const hit = this.
|
|
4056
|
+
const hit = this.hitAt(ev.clientX, ev.clientY);
|
|
3902
4057
|
if (s.mode === "external") {
|
|
3903
4058
|
this.trackExternal(s, hit);
|
|
3904
4059
|
return;
|
|
@@ -4024,7 +4179,7 @@ var Calendar = class {
|
|
|
4024
4179
|
return this.dayProposal(hit.day, hit.day + this.opts.defaultAllDayDays);
|
|
4025
4180
|
}
|
|
4026
4181
|
if (hit.kind === "day") {
|
|
4027
|
-
const dd = hit.day - (grab.kind === "
|
|
4182
|
+
const dd = hit.day - (grab.kind === "timeline" ? hit.day : grab.day);
|
|
4028
4183
|
if (o.allDay) return this.dayProposal(o.startDay + dd, o.startDay + dd + days);
|
|
4029
4184
|
const startMs2 = this.civilToMs(this.civil(o.startMs) + dd * MINUTES_PER_DAY);
|
|
4030
4185
|
const endMs = this.civilToMs(this.civil(o.endMs) + dd * MINUTES_PER_DAY);
|
|
@@ -4159,7 +4314,7 @@ var Calendar = class {
|
|
|
4159
4314
|
this.draggingId = null;
|
|
4160
4315
|
this.dragRow = null;
|
|
4161
4316
|
if (draggedId) for (const el of this.elementsFor(draggedId)) el.classList.remove("is-dragging");
|
|
4162
|
-
this.
|
|
4317
|
+
this.strip?.markTarget(null);
|
|
4163
4318
|
if (!s.active) return;
|
|
4164
4319
|
if (s.mode === "select") {
|
|
4165
4320
|
if (cancel) this.unselectSilently();
|
|
@@ -4456,6 +4611,9 @@ function scrollerOf(el, axis) {
|
|
|
4456
4611
|
}
|
|
4457
4612
|
return doc.scrollingElement;
|
|
4458
4613
|
}
|
|
4614
|
+
function rangeMoved(prev, next) {
|
|
4615
|
+
return !prev || prev.type !== next.type || prev.range.startDay !== next.range.startDay || prev.range.endDay !== next.range.endDay || prev.dataRange.endDay !== next.dataRange.endDay;
|
|
4616
|
+
}
|
|
4459
4617
|
|
|
4460
4618
|
// src/tasknotes/bases.ts
|
|
4461
4619
|
var FROM_BASES = {
|