@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/react.cjs
CHANGED
|
@@ -708,6 +708,10 @@ function defaultOptions() {
|
|
|
708
708
|
dayCount: 3,
|
|
709
709
|
listDayCount: 7,
|
|
710
710
|
listStart: "date",
|
|
711
|
+
listAgenda: false,
|
|
712
|
+
dateStrip: false,
|
|
713
|
+
listLeadTitle: null,
|
|
714
|
+
listAddText: null,
|
|
711
715
|
monthDisplay: "full",
|
|
712
716
|
timeZone: "local",
|
|
713
717
|
locale: void 0,
|
|
@@ -757,6 +761,10 @@ var LAYOUT_KEYS = /* @__PURE__ */ new Set([
|
|
|
757
761
|
"dayCount",
|
|
758
762
|
"listDayCount",
|
|
759
763
|
"listStart",
|
|
764
|
+
"listAgenda",
|
|
765
|
+
"dateStrip",
|
|
766
|
+
"listLeadTitle",
|
|
767
|
+
"listAddText",
|
|
760
768
|
"monthDisplay",
|
|
761
769
|
"timeZone",
|
|
762
770
|
"locale",
|
|
@@ -1247,21 +1255,48 @@ var DayGridView = class {
|
|
|
1247
1255
|
};
|
|
1248
1256
|
|
|
1249
1257
|
// src/dom/views/list.ts
|
|
1258
|
+
var LOAD_AHEAD = "0px 0px 600px 0px";
|
|
1250
1259
|
var ListView = class {
|
|
1251
1260
|
constructor(ctx) {
|
|
1252
1261
|
this.ctx = ctx;
|
|
1253
|
-
|
|
1254
|
-
this.
|
|
1255
|
-
this.
|
|
1262
|
+
const { doc, win, opts } = ctx;
|
|
1263
|
+
this.el = h(doc, "div", "tnc-list");
|
|
1264
|
+
this.scroller = h(doc, "div", "tnc-list-scroll");
|
|
1265
|
+
this.body = h(doc, "div", "tnc-list-body", { role: "list", "aria-label": ctx.view.title });
|
|
1256
1266
|
this.scroller.append(this.body);
|
|
1257
1267
|
this.el.append(this.scroller);
|
|
1268
|
+
this.agenda = opts.listAgenda;
|
|
1269
|
+
if (!this.agenda) return;
|
|
1270
|
+
this.el.classList.add("tnc-list--agenda");
|
|
1271
|
+
this.sentinel = h(doc, "div", "tnc-list-more", { "aria-hidden": "true" });
|
|
1272
|
+
this.scroller.append(this.sentinel);
|
|
1273
|
+
const IO = win.IntersectionObserver;
|
|
1274
|
+
if (IO) {
|
|
1275
|
+
this.loader = new IO(
|
|
1276
|
+
(entries) => {
|
|
1277
|
+
if (entries.some((e) => e.isIntersecting)) ctx.extendList(ctx.view.range.endDay + Math.max(7, opts.listDayCount));
|
|
1278
|
+
},
|
|
1279
|
+
// Auto height scrolls the page, so watch the viewport; fixed height scrolls the list.
|
|
1280
|
+
{ root: opts.height === "auto" ? null : this.scroller, rootMargin: LOAD_AHEAD }
|
|
1281
|
+
);
|
|
1282
|
+
}
|
|
1283
|
+
win.addEventListener("scroll", this.onScroll, { capture: true, passive: true });
|
|
1284
|
+
this.scroller.addEventListener("wheel", this.fold, { passive: true });
|
|
1285
|
+
this.scroller.addEventListener("touchmove", this.fold, { passive: true });
|
|
1258
1286
|
}
|
|
1259
1287
|
ctx;
|
|
1260
1288
|
el;
|
|
1261
1289
|
scroller;
|
|
1262
1290
|
body;
|
|
1291
|
+
agenda;
|
|
1263
1292
|
pools = /* @__PURE__ */ new Map();
|
|
1264
1293
|
sections = /* @__PURE__ */ new Map();
|
|
1294
|
+
lead = null;
|
|
1295
|
+
sentinel = null;
|
|
1296
|
+
loader = null;
|
|
1297
|
+
frame = null;
|
|
1298
|
+
/** A picked day stays marked until the list is scrolled by hand. */
|
|
1299
|
+
pinned = false;
|
|
1265
1300
|
itemsOn(day) {
|
|
1266
1301
|
const { store, opts } = this.ctx;
|
|
1267
1302
|
const out = store.spanningIn(day, day + 1, true);
|
|
@@ -1270,46 +1305,45 @@ var ListView = class {
|
|
|
1270
1305
|
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));
|
|
1271
1306
|
}
|
|
1272
1307
|
render(_change) {
|
|
1273
|
-
const { ctx } = this;
|
|
1274
|
-
const { doc, days } = ctx;
|
|
1308
|
+
const { ctx, agenda } = this;
|
|
1309
|
+
const { doc, days, opts } = ctx;
|
|
1310
|
+
const includes = agenda && opts.listLeadTitle !== null ? opts.listLeadIncludes : void 0;
|
|
1311
|
+
const leadItems = [];
|
|
1312
|
+
const inLead = /* @__PURE__ */ new Set();
|
|
1275
1313
|
const keep = /* @__PURE__ */ new Set();
|
|
1276
1314
|
let prev = null;
|
|
1277
1315
|
for (const day of days) {
|
|
1278
|
-
|
|
1279
|
-
if (
|
|
1316
|
+
let items = this.itemsOn(day);
|
|
1317
|
+
if (includes) {
|
|
1318
|
+
items = items.filter((n) => {
|
|
1319
|
+
if (!includes(n.item)) return true;
|
|
1320
|
+
if (!inLead.has(n.id)) {
|
|
1321
|
+
inLead.add(n.id);
|
|
1322
|
+
leadItems.push(n);
|
|
1323
|
+
}
|
|
1324
|
+
return false;
|
|
1325
|
+
});
|
|
1326
|
+
}
|
|
1327
|
+
if (!items.length && !agenda) continue;
|
|
1280
1328
|
keep.add(day);
|
|
1281
1329
|
let section = this.sections.get(day);
|
|
1282
1330
|
let pool = this.pools.get(day);
|
|
1283
1331
|
if (!section || !pool) {
|
|
1284
1332
|
const arg = ctx.dayArg(day);
|
|
1285
1333
|
const heading = h(doc, "div", ["tnc-list-day", ...ctx.dayClasses(day)].join(" "), { "data-date": arg.dateStr });
|
|
1286
|
-
const custom =
|
|
1334
|
+
const custom = opts.dayHeaderContent?.(arg);
|
|
1287
1335
|
const label = custom ?? ctx.fmt.longDate(day);
|
|
1288
|
-
heading.append(
|
|
1289
|
-
const
|
|
1290
|
-
section = h(doc, "div", "tnc-list-section", { role: "listitem", "aria-label": ctx.fmt.longDate(day), "data-date": arg.dateStr }, heading,
|
|
1291
|
-
|
|
1292
|
-
|
|
1336
|
+
heading.append(opts.navLinks ? ctx.navLink(day, label, "tnc-list-day-link") : label);
|
|
1337
|
+
const rows = h(doc, "div", "tnc-list-rows", { role: "group" });
|
|
1338
|
+
section = h(doc, "div", "tnc-list-section", { role: "listitem", "aria-label": ctx.fmt.longDate(day), "data-date": arg.dateStr }, heading, rows);
|
|
1339
|
+
if (agenda && opts.listAddText !== null) section.append(this.addRow(day));
|
|
1340
|
+
opts.dayHeaderDidMount?.({ ...arg, el: heading });
|
|
1341
|
+
pool = new ItemPool(ctx, rows);
|
|
1293
1342
|
this.sections.set(day, section);
|
|
1294
1343
|
this.pools.set(day, pool);
|
|
1295
1344
|
}
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
pool.place(n.id, {
|
|
1299
|
-
n,
|
|
1300
|
-
day,
|
|
1301
|
-
isStart: n.startDay === day,
|
|
1302
|
-
isEnd: n.endDay === day + 1,
|
|
1303
|
-
variant: "list",
|
|
1304
|
-
mirror: false
|
|
1305
|
-
});
|
|
1306
|
-
}
|
|
1307
|
-
pool.end();
|
|
1308
|
-
const rows = pool.container;
|
|
1309
|
-
for (const n of items) {
|
|
1310
|
-
const el = pool.elementsFor(n.id)[0];
|
|
1311
|
-
if (el) rows.append(el);
|
|
1312
|
-
}
|
|
1345
|
+
section.classList.toggle("is-empty", !items.length);
|
|
1346
|
+
this.fill(pool, items, () => day);
|
|
1313
1347
|
if (prev ? prev.nextSibling !== section : this.body.firstChild !== section) {
|
|
1314
1348
|
if (prev) prev.after(section);
|
|
1315
1349
|
else this.body.prepend(section);
|
|
@@ -1323,18 +1357,111 @@ var ListView = class {
|
|
|
1323
1357
|
section.remove();
|
|
1324
1358
|
this.sections.delete(day);
|
|
1325
1359
|
}
|
|
1360
|
+
this.renderLead(leadItems);
|
|
1326
1361
|
let empty = this.body.querySelector(":scope > .tnc-list-empty");
|
|
1327
|
-
if (!keep.size) {
|
|
1362
|
+
if (!keep.size && !leadItems.length) {
|
|
1328
1363
|
if (!empty) {
|
|
1329
1364
|
empty = h(doc, "div", "tnc-list-empty", { role: "listitem" }, ctx.text.noItems);
|
|
1330
1365
|
this.body.append(empty);
|
|
1331
1366
|
}
|
|
1332
1367
|
} else empty?.remove();
|
|
1368
|
+
this.ctx.strip?.draw();
|
|
1369
|
+
if (this.loader && this.sentinel) {
|
|
1370
|
+
this.loader.unobserve(this.sentinel);
|
|
1371
|
+
this.loader.observe(this.sentinel);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
/** Places items in a pool, keeping DOM order equal to item order. */
|
|
1375
|
+
fill(pool, items, dayFor) {
|
|
1376
|
+
pool.begin();
|
|
1377
|
+
for (const n of items) {
|
|
1378
|
+
const d = dayFor(n);
|
|
1379
|
+
pool.place(n.id, { n, day: d, isStart: n.startDay === d, isEnd: n.endDay === d + 1, variant: "list", mirror: false });
|
|
1380
|
+
}
|
|
1381
|
+
pool.end();
|
|
1382
|
+
for (const n of items) {
|
|
1383
|
+
const el = pool.elementsFor(n.id)[0];
|
|
1384
|
+
if (el) pool.container.append(el);
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
renderLead(items) {
|
|
1388
|
+
const { ctx } = this;
|
|
1389
|
+
const { doc, opts } = ctx;
|
|
1390
|
+
if (!items.length || opts.listLeadTitle === null) {
|
|
1391
|
+
if (!this.lead) return;
|
|
1392
|
+
opts.listLeadWillUnmount?.({ el: this.lead.slot });
|
|
1393
|
+
this.lead.pool.clear();
|
|
1394
|
+
this.lead.section.remove();
|
|
1395
|
+
this.lead = null;
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1398
|
+
if (!this.lead) {
|
|
1399
|
+
const title = opts.listLeadTitle;
|
|
1400
|
+
const slot = h(doc, "div", "tnc-list-lead-slot", { "data-tnc-no-drag": "" });
|
|
1401
|
+
const heading = h(doc, "div", "tnc-list-day tnc-list-lead-head", null, h(doc, "span", "tnc-list-lead-title", null, title), slot);
|
|
1402
|
+
const rows = h(doc, "div", "tnc-list-rows", { role: "group" });
|
|
1403
|
+
const section = h(doc, "div", "tnc-list-section tnc-list-lead", { role: "listitem", "aria-label": title }, heading, rows);
|
|
1404
|
+
this.lead = { section, slot, pool: new ItemPool(ctx, rows) };
|
|
1405
|
+
opts.listLeadDidMount?.({ el: slot });
|
|
1406
|
+
}
|
|
1407
|
+
if (this.body.firstChild !== this.lead.section) this.body.prepend(this.lead.section);
|
|
1408
|
+
const first = ctx.days[0] ?? ctx.view.range.startDay;
|
|
1409
|
+
this.fill(this.lead.pool, items, (n) => Math.max(n.startDay, first));
|
|
1410
|
+
}
|
|
1411
|
+
addRow(day) {
|
|
1412
|
+
const { ctx } = this;
|
|
1413
|
+
const label = ctx.opts.listAddText ?? "";
|
|
1414
|
+
const btn = h(ctx.doc, "button", "tnc-list-add", { type: "button", "aria-label": `${label}, ${ctx.fmt.longDate(day)}` }, label);
|
|
1415
|
+
btn.addEventListener("click", (ev) => ctx.opts.onListAdd?.({ ...ctx.dayArg(day), jsEvent: ev }));
|
|
1416
|
+
return btn;
|
|
1417
|
+
}
|
|
1418
|
+
/** Brings a day's section to the top, under the strip; the first day brings the lead section too. */
|
|
1419
|
+
scrollToDay(day) {
|
|
1420
|
+
const section = day === this.ctx.days[0] && this.lead ? this.lead.section : this.sections.get(day);
|
|
1421
|
+
if (!section) return;
|
|
1422
|
+
this.ctx.strip?.measure();
|
|
1423
|
+
section.scrollIntoView({ block: "start" });
|
|
1424
|
+
this.ctx.strip?.setFocus(day);
|
|
1425
|
+
this.pinned = true;
|
|
1426
|
+
}
|
|
1427
|
+
fold = () => {
|
|
1428
|
+
this.pinned = false;
|
|
1429
|
+
const strip = this.ctx.strip;
|
|
1430
|
+
if (strip?.expanded) strip.setExpanded(false);
|
|
1431
|
+
};
|
|
1432
|
+
onScroll = () => {
|
|
1433
|
+
if (this.frame !== null) return;
|
|
1434
|
+
this.frame = this.ctx.win.requestAnimationFrame(() => {
|
|
1435
|
+
this.frame = null;
|
|
1436
|
+
this.followTop();
|
|
1437
|
+
});
|
|
1438
|
+
};
|
|
1439
|
+
/** Marks the first day not hidden under the strip. */
|
|
1440
|
+
followTop() {
|
|
1441
|
+
const strip = this.ctx.strip;
|
|
1442
|
+
if (!strip || this.pinned) return;
|
|
1443
|
+
const edge = strip.el.getBoundingClientRect().bottom + 1;
|
|
1444
|
+
for (const day of this.ctx.days) {
|
|
1445
|
+
const section = this.sections.get(day);
|
|
1446
|
+
if (section && section.getBoundingClientRect().bottom > edge) {
|
|
1447
|
+
strip.setFocus(day);
|
|
1448
|
+
return;
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1333
1451
|
}
|
|
1334
1452
|
showSelection(_sel) {
|
|
1335
1453
|
}
|
|
1336
|
-
hit(
|
|
1337
|
-
|
|
1454
|
+
hit(x, y) {
|
|
1455
|
+
const el = this.ctx.doc.elementFromPoint(x, y);
|
|
1456
|
+
const section = el?.closest(".tnc-list-section");
|
|
1457
|
+
if (!section || !this.body.contains(section)) return null;
|
|
1458
|
+
if (section === this.lead?.section) {
|
|
1459
|
+
const item = el?.closest(".tnc-item");
|
|
1460
|
+
const meta = item ? this.ctx.meta.get(item) : void 0;
|
|
1461
|
+
return { kind: "day", day: meta?.day ?? this.ctx.days[0] ?? this.ctx.view.range.startDay };
|
|
1462
|
+
}
|
|
1463
|
+
const day = parseDay(section.dataset.date ?? "");
|
|
1464
|
+
return day === null ? null : { kind: "day", day };
|
|
1338
1465
|
}
|
|
1339
1466
|
getScroll() {
|
|
1340
1467
|
return { top: this.scroller.scrollTop, left: 0 };
|
|
@@ -1343,14 +1470,231 @@ var ListView = class {
|
|
|
1343
1470
|
this.scroller.scrollTop = s.top;
|
|
1344
1471
|
}
|
|
1345
1472
|
elementsFor(id) {
|
|
1346
|
-
|
|
1473
|
+
const pools = [...this.pools.values(), ...this.lead ? [this.lead.pool] : []];
|
|
1474
|
+
return pools.flatMap((p) => p.elementsFor(id));
|
|
1347
1475
|
}
|
|
1348
1476
|
destroy() {
|
|
1477
|
+
const { win, opts } = this.ctx;
|
|
1478
|
+
win.removeEventListener("scroll", this.onScroll, { capture: true });
|
|
1479
|
+
if (this.frame !== null) win.cancelAnimationFrame(this.frame);
|
|
1480
|
+
this.loader?.disconnect();
|
|
1481
|
+
if (this.lead) opts.listLeadWillUnmount?.({ el: this.lead.slot });
|
|
1482
|
+
this.lead?.pool.clear();
|
|
1349
1483
|
for (const p of this.pools.values()) p.clear();
|
|
1350
1484
|
this.el.remove();
|
|
1351
1485
|
}
|
|
1352
1486
|
};
|
|
1353
1487
|
|
|
1488
|
+
// src/dom/views/strip.ts
|
|
1489
|
+
var SWIPE_PX = 40;
|
|
1490
|
+
var DateStrip = class {
|
|
1491
|
+
constructor(ctx, pick, focus, onShown) {
|
|
1492
|
+
this.ctx = ctx;
|
|
1493
|
+
this.pick = pick;
|
|
1494
|
+
this.onShown = onShown;
|
|
1495
|
+
const { doc, text } = ctx;
|
|
1496
|
+
this.focus = focus;
|
|
1497
|
+
this.shown = startOfWeek(focus, ctx.firstDay);
|
|
1498
|
+
this.el = h(doc, "div", "tnc-strip");
|
|
1499
|
+
const head = h(doc, "div", "tnc-strip-head");
|
|
1500
|
+
this.title = h(doc, "button", "tnc-strip-title", { type: "button", "aria-expanded": "false" });
|
|
1501
|
+
this.title.addEventListener("click", () => this.setExpanded(!this.expanded));
|
|
1502
|
+
const prev = h(doc, "button", "tnc-strip-nav tnc-strip-prev", { type: "button", "aria-label": text.prev, title: text.prev }, "\u2039");
|
|
1503
|
+
const next = h(doc, "button", "tnc-strip-nav tnc-strip-next", { type: "button", "aria-label": text.next, title: text.next }, "\u203A");
|
|
1504
|
+
prev.addEventListener("click", () => this.step(-1));
|
|
1505
|
+
next.addEventListener("click", () => this.step(1));
|
|
1506
|
+
this.todayBtn = h(doc, "button", "tnc-strip-today", { type: "button" }, text.today);
|
|
1507
|
+
this.todayBtn.addEventListener("click", (ev) => this.pick(ctx.today, ev));
|
|
1508
|
+
this.slot = h(doc, "div", "tnc-strip-slot", { "data-tnc-no-drag": "" });
|
|
1509
|
+
head.append(this.title, this.todayBtn, prev, next, this.slot);
|
|
1510
|
+
this.dows = h(doc, "div", "tnc-strip-dows", { "aria-hidden": "true" });
|
|
1511
|
+
this.grid = h(doc, "div", "tnc-strip-grid", { role: "group" });
|
|
1512
|
+
const handle = h(doc, "button", "tnc-strip-handle", { type: "button", tabindex: "-1", "aria-hidden": "true" });
|
|
1513
|
+
handle.addEventListener("click", () => this.setExpanded(!this.expanded));
|
|
1514
|
+
this.el.append(head, this.dows, this.grid, handle);
|
|
1515
|
+
this.grid.addEventListener("click", this.onClick);
|
|
1516
|
+
this.grid.addEventListener("keydown", this.onKeyDown);
|
|
1517
|
+
this.el.addEventListener("pointerdown", this.onPointerDown);
|
|
1518
|
+
this.el.addEventListener("pointerup", this.onPointerUp);
|
|
1519
|
+
this.el.addEventListener("pointercancel", this.onPointerCancel);
|
|
1520
|
+
this.el.addEventListener("click", this.onClickCapture, true);
|
|
1521
|
+
this.draw();
|
|
1522
|
+
ctx.opts.stripSlotDidMount?.({ el: this.slot });
|
|
1523
|
+
}
|
|
1524
|
+
ctx;
|
|
1525
|
+
pick;
|
|
1526
|
+
onShown;
|
|
1527
|
+
el;
|
|
1528
|
+
title;
|
|
1529
|
+
dows;
|
|
1530
|
+
grid;
|
|
1531
|
+
slot;
|
|
1532
|
+
todayBtn;
|
|
1533
|
+
/** Day at the top of the list. */
|
|
1534
|
+
focus;
|
|
1535
|
+
/** First day of the week shown, or the month shown when expanded. */
|
|
1536
|
+
shown;
|
|
1537
|
+
target = null;
|
|
1538
|
+
/** Days tinted as on screen besides the focus (a time grid's range). */
|
|
1539
|
+
range = null;
|
|
1540
|
+
shownKey = "";
|
|
1541
|
+
swipe = null;
|
|
1542
|
+
swallowClick = false;
|
|
1543
|
+
expanded = false;
|
|
1544
|
+
/** The list scrolled or the grid moved: mark the day and keep it in view. */
|
|
1545
|
+
setFocus(day, range = this.range) {
|
|
1546
|
+
const sameRange = range?.start === this.range?.start && range?.end === this.range?.end;
|
|
1547
|
+
if (day === this.focus && sameRange) return;
|
|
1548
|
+
this.range = range;
|
|
1549
|
+
if (day !== this.focus) {
|
|
1550
|
+
this.focus = day;
|
|
1551
|
+
if (!this.expanded) this.shown = startOfWeek(day, this.ctx.firstDay);
|
|
1552
|
+
else if (startOfMonth(day) !== this.shown) this.shown = startOfMonth(day);
|
|
1553
|
+
}
|
|
1554
|
+
this.draw();
|
|
1555
|
+
}
|
|
1556
|
+
/** Publishes the strip's height as `--tnc-strip-h`, for what sticks below it. */
|
|
1557
|
+
measure() {
|
|
1558
|
+
this.el.parentElement?.style.setProperty("--tnc-strip-h", `${this.el.offsetHeight}px`);
|
|
1559
|
+
}
|
|
1560
|
+
/** First day and end (exclusive) of the days on show. */
|
|
1561
|
+
window() {
|
|
1562
|
+
const days = this.days();
|
|
1563
|
+
return { start: days[0], end: days[days.length - 1] + 1 };
|
|
1564
|
+
}
|
|
1565
|
+
setExpanded(on) {
|
|
1566
|
+
if (on === this.expanded) return;
|
|
1567
|
+
this.expanded = on;
|
|
1568
|
+
this.shown = on ? startOfMonth(this.focus) : startOfWeek(this.focus, this.ctx.firstDay);
|
|
1569
|
+
this.draw();
|
|
1570
|
+
}
|
|
1571
|
+
/** Highlights the day a dragged item would land on. */
|
|
1572
|
+
markTarget(day) {
|
|
1573
|
+
if (day === this.target) return;
|
|
1574
|
+
this.target = day;
|
|
1575
|
+
for (const cell of this.grid.querySelectorAll(".tnc-strip-day"))
|
|
1576
|
+
cell.classList.toggle("is-drop-target", day !== null && cell.dataset.date === this.ctx.dayArg(day).dateStr);
|
|
1577
|
+
}
|
|
1578
|
+
dayAt(el) {
|
|
1579
|
+
const cell = el.closest(".tnc-strip-day");
|
|
1580
|
+
return cell && this.grid.contains(cell) ? parseDay(cell.dataset.date ?? "") : null;
|
|
1581
|
+
}
|
|
1582
|
+
step(dir) {
|
|
1583
|
+
this.shown = this.expanded ? addMonths(this.shown, dir) : this.shown + 7 * dir;
|
|
1584
|
+
this.draw();
|
|
1585
|
+
}
|
|
1586
|
+
days() {
|
|
1587
|
+
if (!this.expanded) return Array.from({ length: 7 }, (_, i) => this.shown + i);
|
|
1588
|
+
const first = startOfWeek(this.shown, this.ctx.firstDay);
|
|
1589
|
+
const end = addMonths(this.shown, 1);
|
|
1590
|
+
const weeks = Math.ceil((end - first) / 7);
|
|
1591
|
+
return Array.from({ length: weeks * 7 }, (_, i) => first + i);
|
|
1592
|
+
}
|
|
1593
|
+
/** Redraws the cells; also called on every list render to refresh the dots. */
|
|
1594
|
+
draw() {
|
|
1595
|
+
const { ctx } = this;
|
|
1596
|
+
const { doc, fmt, store } = ctx;
|
|
1597
|
+
const days = this.days();
|
|
1598
|
+
const titleDay = this.expanded ? this.shown : days.includes(this.focus) ? this.focus : this.shown + 3;
|
|
1599
|
+
this.title.replaceChildren(fmt.monthYear(titleDay), h(doc, "span", "tnc-strip-caret", { "aria-hidden": "true" }));
|
|
1600
|
+
this.title.setAttribute("aria-expanded", String(this.expanded));
|
|
1601
|
+
this.el.classList.toggle("is-expanded", this.expanded);
|
|
1602
|
+
this.todayBtn.hidden = this.focus === ctx.today;
|
|
1603
|
+
this.dows.replaceChildren(...days.slice(0, 7).map((d) => h(doc, "span", null, null, fmt.weekdayNarrow(d))));
|
|
1604
|
+
const focused = this.grid.contains(doc.activeElement) ? doc.activeElement.dataset.date : void 0;
|
|
1605
|
+
const month = ymdOfDay(this.shown).month;
|
|
1606
|
+
const tabDay = days.includes(this.focus) ? this.focus : days[0];
|
|
1607
|
+
const cells = days.map((d) => {
|
|
1608
|
+
const arg = ctx.dayArg(d);
|
|
1609
|
+
const count = store.inRange(d, d + 1).filter((n) => n.item.display !== "background" && n.item.display !== "none").length;
|
|
1610
|
+
const first = ymdOfDay(d).day === 1 && d !== days[0] && !this.expanded;
|
|
1611
|
+
const label = fmt.longDate(d);
|
|
1612
|
+
const cell = h(
|
|
1613
|
+
doc,
|
|
1614
|
+
"button",
|
|
1615
|
+
[
|
|
1616
|
+
"tnc-strip-day",
|
|
1617
|
+
...ctx.dayClasses(d),
|
|
1618
|
+
d === this.focus ? "is-focus" : "",
|
|
1619
|
+
d !== this.focus && this.range && d >= this.range.start && d < this.range.end ? "is-in-range" : "",
|
|
1620
|
+
count ? "has-items" : "",
|
|
1621
|
+
this.expanded && ymdOfDay(d).month !== month ? "is-other-month" : "",
|
|
1622
|
+
d === this.target ? "is-drop-target" : ""
|
|
1623
|
+
].filter(Boolean).join(" "),
|
|
1624
|
+
{
|
|
1625
|
+
type: "button",
|
|
1626
|
+
"data-date": arg.dateStr,
|
|
1627
|
+
"aria-label": count ? `${label}, ${ctx.text.itemCount(count)}` : label,
|
|
1628
|
+
"aria-pressed": String(d === this.focus),
|
|
1629
|
+
tabindex: d === tabDay ? "0" : "-1"
|
|
1630
|
+
}
|
|
1631
|
+
);
|
|
1632
|
+
if (first) cell.append(h(doc, "span", "tnc-strip-month", { "aria-hidden": "true" }, fmt.monthShort(d)));
|
|
1633
|
+
cell.append(h(doc, "span", "tnc-strip-num", null, fmt.dayNumber(d)), h(doc, "span", "tnc-strip-dot", { "aria-hidden": "true" }));
|
|
1634
|
+
return cell;
|
|
1635
|
+
});
|
|
1636
|
+
this.grid.replaceChildren(...cells);
|
|
1637
|
+
this.grid.setAttribute("aria-label", fmt.monthYear(titleDay));
|
|
1638
|
+
if (focused) this.grid.querySelector(`[data-date="${focused}"]`)?.focus({ preventScroll: true });
|
|
1639
|
+
const key = `${days[0]}:${days.length}`;
|
|
1640
|
+
if (key !== this.shownKey) {
|
|
1641
|
+
this.shownKey = key;
|
|
1642
|
+
this.onShown();
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
onClick = (ev) => {
|
|
1646
|
+
const day = this.dayAt(ev.target);
|
|
1647
|
+
if (day === null) return;
|
|
1648
|
+
if (this.expanded) this.setExpanded(false);
|
|
1649
|
+
this.pick(day, ev);
|
|
1650
|
+
};
|
|
1651
|
+
onKeyDown = (ev) => {
|
|
1652
|
+
const day = this.dayAt(ev.target);
|
|
1653
|
+
if (day === null) return;
|
|
1654
|
+
const move = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -7, ArrowDown: 7 }[ev.key];
|
|
1655
|
+
if (move === void 0) return;
|
|
1656
|
+
ev.preventDefault();
|
|
1657
|
+
const next = day + move;
|
|
1658
|
+
const days = this.days();
|
|
1659
|
+
if (next < days[0] || next > days[days.length - 1]) {
|
|
1660
|
+
this.shown = this.expanded ? startOfMonth(next) : startOfWeek(next, this.ctx.firstDay);
|
|
1661
|
+
this.draw();
|
|
1662
|
+
}
|
|
1663
|
+
const cell = this.grid.querySelector(`[data-date="${this.ctx.dayArg(next).dateStr}"]`);
|
|
1664
|
+
if (!cell) return;
|
|
1665
|
+
for (const c of this.grid.querySelectorAll("[tabindex='0']")) c.setAttribute("tabindex", "-1");
|
|
1666
|
+
cell.setAttribute("tabindex", "0");
|
|
1667
|
+
cell.focus();
|
|
1668
|
+
};
|
|
1669
|
+
onPointerDown = (ev) => {
|
|
1670
|
+
if (ev.button !== 0 || this.ctx.draggingId) return;
|
|
1671
|
+
this.swipe = { id: ev.pointerId, x: ev.clientX, y: ev.clientY };
|
|
1672
|
+
};
|
|
1673
|
+
onPointerUp = (ev) => {
|
|
1674
|
+
const s = this.swipe;
|
|
1675
|
+
this.swipe = null;
|
|
1676
|
+
if (!s || s.id !== ev.pointerId) return;
|
|
1677
|
+
const dx = ev.clientX - s.x;
|
|
1678
|
+
if (Math.abs(dx) < SWIPE_PX || Math.abs(dx) < Math.abs(ev.clientY - s.y)) return;
|
|
1679
|
+
this.swallowClick = true;
|
|
1680
|
+
this.step(dx < 0 ? 1 : -1);
|
|
1681
|
+
};
|
|
1682
|
+
onPointerCancel = () => {
|
|
1683
|
+
this.swipe = null;
|
|
1684
|
+
};
|
|
1685
|
+
/** A swipe that ends on a day is not a pick. */
|
|
1686
|
+
onClickCapture = (ev) => {
|
|
1687
|
+
if (!this.swallowClick) return;
|
|
1688
|
+
this.swallowClick = false;
|
|
1689
|
+
ev.stopPropagation();
|
|
1690
|
+
ev.preventDefault();
|
|
1691
|
+
};
|
|
1692
|
+
destroy() {
|
|
1693
|
+
this.ctx.opts.stripSlotWillUnmount?.({ el: this.slot });
|
|
1694
|
+
this.el.remove();
|
|
1695
|
+
}
|
|
1696
|
+
};
|
|
1697
|
+
|
|
1354
1698
|
// src/dom/views/timegrid.ts
|
|
1355
1699
|
var BAR_GAP2 = 2;
|
|
1356
1700
|
var TimeGridView = class {
|
|
@@ -2487,6 +2831,7 @@ ${this.ctx.text.more(items.length - 8)}` : ""}` : label;
|
|
|
2487
2831
|
|
|
2488
2832
|
// src/dom/calendar.ts
|
|
2489
2833
|
var EXTERNAL_ID = "__tnc_external__";
|
|
2834
|
+
var MAX_AGENDA_DAYS = 366;
|
|
2490
2835
|
function sameOption(a, b, depth = 0) {
|
|
2491
2836
|
if (a === b) return true;
|
|
2492
2837
|
if (typeof a === "function" && typeof b === "function") return true;
|
|
@@ -2555,10 +2900,15 @@ var Calendar = class {
|
|
|
2555
2900
|
slot = { min: 0, max: MINUTES_PER_DAY, duration: 30, snap: 30, scroll: 480 };
|
|
2556
2901
|
draggingId = null;
|
|
2557
2902
|
dragRow = null;
|
|
2903
|
+
strip = null;
|
|
2904
|
+
stripSize = null;
|
|
2905
|
+
rebuilding = false;
|
|
2558
2906
|
toolbarEl;
|
|
2559
2907
|
viewHost;
|
|
2560
2908
|
current = null;
|
|
2561
2909
|
anchor = 0;
|
|
2910
|
+
/** End of an agenda list grown past its `listDayCount`. */
|
|
2911
|
+
listEnd = null;
|
|
2562
2912
|
session = null;
|
|
2563
2913
|
selection = null;
|
|
2564
2914
|
suppressClick = false;
|
|
@@ -2751,6 +3101,10 @@ var Calendar = class {
|
|
|
2751
3101
|
if (!p) return;
|
|
2752
3102
|
const day = p.kind === "day" ? p.day : dayOf(p.ms, this.zone);
|
|
2753
3103
|
const ms = p.kind === "day" ? toInstant(p.day, 0, this.zone) : p.ms;
|
|
3104
|
+
if (this.agendaList()) {
|
|
3105
|
+
this.showListDay(day);
|
|
3106
|
+
return;
|
|
3107
|
+
}
|
|
2754
3108
|
const { startDay, endDay } = this.view.range;
|
|
2755
3109
|
if (day < startDay || day >= endDay) {
|
|
2756
3110
|
this.anchor = day;
|
|
@@ -2762,7 +3116,7 @@ var Calendar = class {
|
|
|
2762
3116
|
}
|
|
2763
3117
|
/** What is under a client point: a time slot, a day, or a timeline row and date. Null outside the view. */
|
|
2764
3118
|
hitTest(clientX, clientY) {
|
|
2765
|
-
return this.
|
|
3119
|
+
return this.hitAt(clientX, clientY);
|
|
2766
3120
|
}
|
|
2767
3121
|
/** Item elements currently rendered for an id. */
|
|
2768
3122
|
elementsFor(id) {
|
|
@@ -2804,6 +3158,9 @@ var Calendar = class {
|
|
|
2804
3158
|
this.unbindEvents();
|
|
2805
3159
|
this.current?.destroy();
|
|
2806
3160
|
this.current = null;
|
|
3161
|
+
this.stripSize?.disconnect();
|
|
3162
|
+
this.strip?.destroy();
|
|
3163
|
+
this.strip = null;
|
|
2807
3164
|
this.root.remove();
|
|
2808
3165
|
}
|
|
2809
3166
|
/* ========================================================== view context */
|
|
@@ -2834,7 +3191,8 @@ var Calendar = class {
|
|
|
2834
3191
|
return out;
|
|
2835
3192
|
}
|
|
2836
3193
|
isEditable(n, what) {
|
|
2837
|
-
if (n.id === EXTERNAL_ID || this.opts.view === "
|
|
3194
|
+
if (n.id === EXTERNAL_ID || this.opts.view === "year") return false;
|
|
3195
|
+
if (this.opts.view === "list" && (!this.opts.listAgenda || what === "duration")) return false;
|
|
2838
3196
|
const it = n.item;
|
|
2839
3197
|
const base = it.editable ?? this.opts.editable;
|
|
2840
3198
|
const specific = what === "start" ? it.startEditable : it.durationEditable;
|
|
@@ -3031,7 +3389,8 @@ var Calendar = class {
|
|
|
3031
3389
|
}
|
|
3032
3390
|
case "list": {
|
|
3033
3391
|
const s = this.opts.listStart === "week" ? startOfWeek(a, this.firstDay) : a;
|
|
3034
|
-
|
|
3392
|
+
const end = s + Math.max(1, listDayCount);
|
|
3393
|
+
return { startDay: s, endDay: this.opts.listAgenda && this.listEnd !== null ? Math.max(end, this.listEnd) : end };
|
|
3035
3394
|
}
|
|
3036
3395
|
case "month": {
|
|
3037
3396
|
const s = startOfWeek(startOfMonth(a), this.firstDay);
|
|
@@ -3069,6 +3428,33 @@ var Calendar = class {
|
|
|
3069
3428
|
const duration = Math.max(1, parseDurationMinutes(this.opts.slotDuration, 30));
|
|
3070
3429
|
const snap = Math.max(1, parseDurationMinutes(this.opts.snapDuration ?? null, duration));
|
|
3071
3430
|
this.slot = { min, max, duration, snap, scroll: parseDurationMinutes(this.opts.scrollTime, 480) };
|
|
3431
|
+
if (navigated) this.listEnd = null;
|
|
3432
|
+
const prev = this.view;
|
|
3433
|
+
this.applyRange();
|
|
3434
|
+
this.conflicts = dependencyConflicts(this.store);
|
|
3435
|
+
this.root.className = "tnc";
|
|
3436
|
+
this.root.classList.add(`tnc--${this.opts.view}`, `tnc--height-${this.opts.height}`);
|
|
3437
|
+
if (!this.opts.timeGrid) this.root.classList.add("tnc--no-time-grid");
|
|
3438
|
+
this.renderToolbar();
|
|
3439
|
+
this.rebuilding = true;
|
|
3440
|
+
this.syncStrip(navigated);
|
|
3441
|
+
this.rebuilding = false;
|
|
3442
|
+
this.view.dataRange = this.dataRangeFor(this.view.range);
|
|
3443
|
+
const rangeChanged = rangeMoved(prev, this.view);
|
|
3444
|
+
this.current?.destroy();
|
|
3445
|
+
this.current = this.createView();
|
|
3446
|
+
this.viewHost.replaceChildren(this.current.el);
|
|
3447
|
+
this.current.render(null);
|
|
3448
|
+
if (keep && this.current instanceof TimelineView) {
|
|
3449
|
+
this.current.setScroll({ top: keep.top, left: 0 });
|
|
3450
|
+
this.current.placeInstant(keep.ms, keep.vx);
|
|
3451
|
+
} else if (scroll) this.current.setScroll(scroll);
|
|
3452
|
+
else if (this.current instanceof TimeGridView) this.current.scrollToTime(this.slot.scroll);
|
|
3453
|
+
else if (this.current instanceof TimelineView) this.current.scrollToDay(this.anchor);
|
|
3454
|
+
if (rangeChanged) this.opts.onRangeChange?.(this.view);
|
|
3455
|
+
}
|
|
3456
|
+
/** Sets the visible days and view info from the anchor. */
|
|
3457
|
+
applyRange() {
|
|
3072
3458
|
const { startDay, endDay } = this.computeRange();
|
|
3073
3459
|
const hidden = new Set(this.opts.hiddenDays);
|
|
3074
3460
|
if (!this.opts.weekends) {
|
|
@@ -3085,26 +3471,100 @@ var Calendar = class {
|
|
|
3085
3471
|
start: new Date(toInstant(startDay, 0, this.zone)),
|
|
3086
3472
|
end: new Date(toInstant(endDay, 0, this.zone))
|
|
3087
3473
|
};
|
|
3088
|
-
|
|
3089
|
-
this.view = { type: this.opts.view, range, anchor: this.anchor, title: "" };
|
|
3474
|
+
this.view = { type: this.opts.view, range, anchor: this.anchor, title: "", dataRange: this.dataRangeFor(range) };
|
|
3090
3475
|
this.view.title = this.computeTitle(startDay, endDay);
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
this.
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3476
|
+
}
|
|
3477
|
+
/** The range, run on to the end of the strip's days so their dots are right. */
|
|
3478
|
+
dataRangeFor(range) {
|
|
3479
|
+
const end = this.strip ? Math.max(range.endDay, this.strip.window().end) : range.endDay;
|
|
3480
|
+
if (end === range.endDay) return range;
|
|
3481
|
+
return { ...range, endDay: end, end: new Date(toInstant(end, 0, this.zone)) };
|
|
3482
|
+
}
|
|
3483
|
+
/** Adds, updates or removes the date strip for the current view. */
|
|
3484
|
+
syncStrip(navigated) {
|
|
3485
|
+
const { view, listAgenda, dateStrip } = this.opts;
|
|
3486
|
+
const list = view === "list";
|
|
3487
|
+
const want = list ? listAgenda : dateStrip && (view === "day" || view === "days" || view === "week");
|
|
3488
|
+
if (!want) {
|
|
3489
|
+
if (!this.strip) return;
|
|
3490
|
+
this.stripSize?.disconnect();
|
|
3491
|
+
this.stripSize = null;
|
|
3492
|
+
this.strip.destroy();
|
|
3493
|
+
this.strip = null;
|
|
3494
|
+
this.root.style.removeProperty("--tnc-strip-h");
|
|
3495
|
+
return;
|
|
3496
|
+
}
|
|
3497
|
+
this.root.classList.add("tnc--has-strip");
|
|
3498
|
+
const { startDay, endDay } = this.view.range;
|
|
3499
|
+
if (!this.strip) {
|
|
3500
|
+
const strip = new DateStrip(this, (day, ev) => this.pickStripDay(day, ev), startDay, () => this.stripShown());
|
|
3501
|
+
this.strip = strip;
|
|
3502
|
+
this.root.insertBefore(strip.el, this.viewHost);
|
|
3503
|
+
const RO = this.win.ResizeObserver;
|
|
3504
|
+
if (RO) {
|
|
3505
|
+
this.stripSize = new RO(() => strip.measure());
|
|
3506
|
+
this.stripSize.observe(strip.el);
|
|
3507
|
+
}
|
|
3508
|
+
}
|
|
3509
|
+
if (!list) this.strip.setFocus(startDay, { start: startDay, end: endDay });
|
|
3510
|
+
else if (navigated) this.strip.setFocus(startDay, null);
|
|
3511
|
+
this.strip.draw();
|
|
3512
|
+
}
|
|
3513
|
+
pickStripDay(day, ev) {
|
|
3514
|
+
this.dateClick(day, ev);
|
|
3515
|
+
if (this.agendaList()) {
|
|
3516
|
+
this.showListDay(day);
|
|
3517
|
+
return;
|
|
3518
|
+
}
|
|
3519
|
+
const { startDay, endDay } = this.view.range;
|
|
3520
|
+
if (this.opts.view === "week" && day >= startDay && day < endDay) return;
|
|
3521
|
+
this.anchor = day;
|
|
3522
|
+
this.rebuild(true);
|
|
3523
|
+
}
|
|
3524
|
+
/** The strip moved to other days: load theirs too. */
|
|
3525
|
+
stripShown() {
|
|
3526
|
+
if (!this.strip || this.rebuilding || !this.view) return;
|
|
3527
|
+
const next = this.dataRangeFor(this.view.range);
|
|
3528
|
+
if (next.endDay === this.view.dataRange.endDay) return;
|
|
3529
|
+
this.view = { ...this.view, dataRange: next };
|
|
3530
|
+
this.opts.onRangeChange?.(this.view);
|
|
3531
|
+
}
|
|
3532
|
+
/** What is under a point, the date strip included. */
|
|
3533
|
+
hitAt(x, y) {
|
|
3534
|
+
const strip = this.strip;
|
|
3535
|
+
if (strip) {
|
|
3536
|
+
const el = this.doc.elementFromPoint(x, y);
|
|
3537
|
+
const day = el ? strip.dayAt(el) : null;
|
|
3538
|
+
strip.markTarget(day !== null && this.draggingId ? day : null);
|
|
3539
|
+
if (day !== null) return { kind: "day", day };
|
|
3540
|
+
}
|
|
3541
|
+
return this.current?.hit(x, y) ?? null;
|
|
3542
|
+
}
|
|
3543
|
+
agendaList() {
|
|
3544
|
+
return this.opts.view === "list" && this.opts.listAgenda && this.current instanceof ListView ? this.current : null;
|
|
3545
|
+
}
|
|
3546
|
+
extendList(endDay) {
|
|
3547
|
+
if (!this.agendaList()) return;
|
|
3548
|
+
const { startDay, endDay: current } = this.view.range;
|
|
3549
|
+
const end = Math.min(endDay, startDay + MAX_AGENDA_DAYS);
|
|
3550
|
+
if (end <= current) return;
|
|
3551
|
+
this.listEnd = end;
|
|
3552
|
+
this.applyRange();
|
|
3553
|
+
this.current?.render(null);
|
|
3554
|
+
this.strip?.draw();
|
|
3555
|
+
this.opts.onRangeChange?.(this.view);
|
|
3556
|
+
}
|
|
3557
|
+
showListDay(day) {
|
|
3558
|
+
if (!this.agendaList()) return;
|
|
3559
|
+
if (day < this.view.range.startDay || day - this.view.range.startDay >= MAX_AGENDA_DAYS) {
|
|
3560
|
+
this.anchor = day;
|
|
3561
|
+
this.rebuild(true);
|
|
3562
|
+
}
|
|
3563
|
+
this.extendList(day + 2 * Math.max(7, this.opts.listDayCount));
|
|
3564
|
+
this.agendaList()?.scrollToDay(day);
|
|
3565
|
+
}
|
|
3566
|
+
dateClick(day, jsEvent) {
|
|
3567
|
+
this.clickSlot({ kind: "day", day }, jsEvent, true);
|
|
3108
3568
|
}
|
|
3109
3569
|
createView() {
|
|
3110
3570
|
switch (this.opts.view) {
|
|
@@ -3407,8 +3867,9 @@ var Calendar = class {
|
|
|
3407
3867
|
};
|
|
3408
3868
|
this.session = s;
|
|
3409
3869
|
if (touch && itemEl?.classList.contains("is-draggable")) {
|
|
3410
|
-
const
|
|
3411
|
-
|
|
3870
|
+
const x = scrollerOf(itemEl, "x");
|
|
3871
|
+
const y = scrollerOf(itemEl, "y");
|
|
3872
|
+
s.pan = { x, y, left: x?.scrollLeft ?? 0, top: y?.scrollTop ?? 0, active: false };
|
|
3412
3873
|
}
|
|
3413
3874
|
if (touch) {
|
|
3414
3875
|
s.timer = this.win.setTimeout(() => {
|
|
@@ -3449,8 +3910,8 @@ var Calendar = class {
|
|
|
3449
3910
|
if (!pan.active && s.timer !== null) this.win.clearTimeout(s.timer);
|
|
3450
3911
|
s.timer = null;
|
|
3451
3912
|
pan.active = true;
|
|
3452
|
-
pan.
|
|
3453
|
-
pan.
|
|
3913
|
+
if (pan.x) pan.x.scrollLeft = pan.left - (ev.clientX - s.x0);
|
|
3914
|
+
if (pan.y) pan.y.scrollTop = pan.top - (ev.clientY - s.y0);
|
|
3454
3915
|
} else if (dist > 8) this.endSession(null, true);
|
|
3455
3916
|
return;
|
|
3456
3917
|
}
|
|
@@ -3488,7 +3949,7 @@ var Calendar = class {
|
|
|
3488
3949
|
};
|
|
3489
3950
|
/** Proposal for the current pointer position. */
|
|
3490
3951
|
track(s, ev) {
|
|
3491
|
-
const hit = this.
|
|
3952
|
+
const hit = this.hitAt(ev.clientX, ev.clientY);
|
|
3492
3953
|
if (s.mode === "external") {
|
|
3493
3954
|
this.trackExternal(s, hit);
|
|
3494
3955
|
return;
|
|
@@ -3614,7 +4075,7 @@ var Calendar = class {
|
|
|
3614
4075
|
return this.dayProposal(hit.day, hit.day + this.opts.defaultAllDayDays);
|
|
3615
4076
|
}
|
|
3616
4077
|
if (hit.kind === "day") {
|
|
3617
|
-
const dd = hit.day - (grab.kind === "
|
|
4078
|
+
const dd = hit.day - (grab.kind === "timeline" ? hit.day : grab.day);
|
|
3618
4079
|
if (o.allDay) return this.dayProposal(o.startDay + dd, o.startDay + dd + days);
|
|
3619
4080
|
const startMs2 = this.civilToMs(this.civil(o.startMs) + dd * MINUTES_PER_DAY);
|
|
3620
4081
|
const endMs = this.civilToMs(this.civil(o.endMs) + dd * MINUTES_PER_DAY);
|
|
@@ -3749,6 +4210,7 @@ var Calendar = class {
|
|
|
3749
4210
|
this.draggingId = null;
|
|
3750
4211
|
this.dragRow = null;
|
|
3751
4212
|
if (draggedId) for (const el of this.elementsFor(draggedId)) el.classList.remove("is-dragging");
|
|
4213
|
+
this.strip?.markTarget(null);
|
|
3752
4214
|
if (!s.active) return;
|
|
3753
4215
|
if (s.mode === "select") {
|
|
3754
4216
|
if (cancel) this.unselectSilently();
|
|
@@ -4035,6 +4497,19 @@ var Calendar = class {
|
|
|
4035
4497
|
});
|
|
4036
4498
|
};
|
|
4037
4499
|
};
|
|
4500
|
+
function scrollerOf(el, axis) {
|
|
4501
|
+
const doc = el.ownerDocument;
|
|
4502
|
+
const win = doc.defaultView;
|
|
4503
|
+
for (let p = el.parentElement; p && p !== doc.body && p !== doc.documentElement; p = p.parentElement) {
|
|
4504
|
+
const overflow = win?.getComputedStyle(p)[axis === "x" ? "overflowX" : "overflowY"] ?? "";
|
|
4505
|
+
const room = axis === "x" ? p.scrollWidth > p.clientWidth : p.scrollHeight > p.clientHeight;
|
|
4506
|
+
if (room && /auto|scroll|overlay/.test(overflow)) return p;
|
|
4507
|
+
}
|
|
4508
|
+
return doc.scrollingElement;
|
|
4509
|
+
}
|
|
4510
|
+
function rangeMoved(prev, next) {
|
|
4511
|
+
return !prev || prev.type !== next.type || prev.range.startDay !== next.range.startDay || prev.range.endDay !== next.range.endDay || prev.dataRange.endDay !== next.dataRange.endDay;
|
|
4512
|
+
}
|
|
4038
4513
|
|
|
4039
4514
|
// src/react/index.tsx
|
|
4040
4515
|
var import_jsx_runtime = require("react/jsx-runtime");
|