@tasknotes/calendar 0.1.1 → 0.1.3
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 +21 -0
- package/README.md +2 -1
- package/dist/calendar.css +229 -0
- package/dist/cjs/index.cjs +365 -30
- package/dist/cjs/react.cjs +365 -30
- package/dist/esm/{chunk-6RRDNLZM.js → chunk-XHPZ7K4W.js} +365 -30
- package/dist/esm/index.js +1 -1
- package/dist/esm/react.js +1 -1
- package/dist/types/dom/calendar.d.cts +8 -0
- package/dist/types/dom/calendar.d.ts +8 -0
- package/dist/types/dom/context.d.cts +8 -0
- package/dist/types/dom/context.d.ts +8 -0
- package/dist/types/dom/options.d.cts +19 -0
- package/dist/types/dom/options.d.ts +19 -0
- package/dist/types/dom/views/list.d.cts +23 -2
- package/dist/types/dom/views/list.d.ts +23 -2
- package/dist/types/dom/views/strip.d.cts +43 -0
- package/dist/types/dom/views/strip.d.ts +43 -0
- package/package.json +1 -1
package/dist/cjs/react.cjs
CHANGED
|
@@ -708,6 +708,7 @@ function defaultOptions() {
|
|
|
708
708
|
dayCount: 3,
|
|
709
709
|
listDayCount: 7,
|
|
710
710
|
listStart: "date",
|
|
711
|
+
listAgenda: false,
|
|
711
712
|
monthDisplay: "full",
|
|
712
713
|
timeZone: "local",
|
|
713
714
|
locale: void 0,
|
|
@@ -757,6 +758,7 @@ var LAYOUT_KEYS = /* @__PURE__ */ new Set([
|
|
|
757
758
|
"dayCount",
|
|
758
759
|
"listDayCount",
|
|
759
760
|
"listStart",
|
|
761
|
+
"listAgenda",
|
|
760
762
|
"monthDisplay",
|
|
761
763
|
"timeZone",
|
|
762
764
|
"locale",
|
|
@@ -1246,15 +1248,225 @@ var DayGridView = class {
|
|
|
1246
1248
|
}
|
|
1247
1249
|
};
|
|
1248
1250
|
|
|
1251
|
+
// src/dom/views/strip.ts
|
|
1252
|
+
var SWIPE_PX = 40;
|
|
1253
|
+
var DateStrip = class {
|
|
1254
|
+
constructor(ctx, pick, focus) {
|
|
1255
|
+
this.ctx = ctx;
|
|
1256
|
+
this.pick = pick;
|
|
1257
|
+
const { doc, text } = ctx;
|
|
1258
|
+
this.focus = focus;
|
|
1259
|
+
this.shown = startOfWeek(focus, ctx.firstDay);
|
|
1260
|
+
this.el = h(doc, "div", "tnc-strip");
|
|
1261
|
+
const head = h(doc, "div", "tnc-strip-head");
|
|
1262
|
+
this.title = h(doc, "button", "tnc-strip-title", { type: "button", "aria-expanded": "false" });
|
|
1263
|
+
this.title.addEventListener("click", () => this.setExpanded(!this.expanded));
|
|
1264
|
+
const prev = h(doc, "button", "tnc-strip-nav tnc-strip-prev", { type: "button", "aria-label": text.prev, title: text.prev }, "\u2039");
|
|
1265
|
+
const next = h(doc, "button", "tnc-strip-nav tnc-strip-next", { type: "button", "aria-label": text.next, title: text.next }, "\u203A");
|
|
1266
|
+
prev.addEventListener("click", () => this.step(-1));
|
|
1267
|
+
next.addEventListener("click", () => this.step(1));
|
|
1268
|
+
this.slot = h(doc, "div", "tnc-strip-slot", { "data-tnc-no-drag": "" });
|
|
1269
|
+
head.append(this.title, prev, next, this.slot);
|
|
1270
|
+
this.dows = h(doc, "div", "tnc-strip-dows", { "aria-hidden": "true" });
|
|
1271
|
+
this.grid = h(doc, "div", "tnc-strip-grid", { role: "group" });
|
|
1272
|
+
const handle = h(doc, "button", "tnc-strip-handle", { type: "button", tabindex: "-1", "aria-hidden": "true" });
|
|
1273
|
+
handle.addEventListener("click", () => this.setExpanded(!this.expanded));
|
|
1274
|
+
this.el.append(head, this.dows, this.grid, handle);
|
|
1275
|
+
this.grid.addEventListener("click", this.onClick);
|
|
1276
|
+
this.grid.addEventListener("keydown", this.onKeyDown);
|
|
1277
|
+
this.el.addEventListener("pointerdown", this.onPointerDown);
|
|
1278
|
+
this.el.addEventListener("pointerup", this.onPointerUp);
|
|
1279
|
+
this.el.addEventListener("pointercancel", this.onPointerCancel);
|
|
1280
|
+
this.el.addEventListener("click", this.onClickCapture, true);
|
|
1281
|
+
this.draw();
|
|
1282
|
+
ctx.opts.stripSlotDidMount?.({ el: this.slot });
|
|
1283
|
+
}
|
|
1284
|
+
ctx;
|
|
1285
|
+
pick;
|
|
1286
|
+
el;
|
|
1287
|
+
title;
|
|
1288
|
+
dows;
|
|
1289
|
+
grid;
|
|
1290
|
+
slot;
|
|
1291
|
+
/** Day at the top of the list. */
|
|
1292
|
+
focus;
|
|
1293
|
+
/** First day of the week shown, or the month shown when expanded. */
|
|
1294
|
+
shown;
|
|
1295
|
+
target = null;
|
|
1296
|
+
swipe = null;
|
|
1297
|
+
swallowClick = false;
|
|
1298
|
+
expanded = false;
|
|
1299
|
+
/** The list scrolled: mark its top day and keep it in view. */
|
|
1300
|
+
setFocus(day) {
|
|
1301
|
+
if (day === this.focus) return;
|
|
1302
|
+
this.focus = day;
|
|
1303
|
+
if (!this.expanded) this.shown = startOfWeek(day, this.ctx.firstDay);
|
|
1304
|
+
else if (startOfMonth(day) !== this.shown) this.shown = startOfMonth(day);
|
|
1305
|
+
this.draw();
|
|
1306
|
+
}
|
|
1307
|
+
setExpanded(on) {
|
|
1308
|
+
if (on === this.expanded) return;
|
|
1309
|
+
this.expanded = on;
|
|
1310
|
+
this.shown = on ? startOfMonth(this.focus) : startOfWeek(this.focus, this.ctx.firstDay);
|
|
1311
|
+
this.draw();
|
|
1312
|
+
}
|
|
1313
|
+
/** Highlights the day a dragged item would land on. */
|
|
1314
|
+
markTarget(day) {
|
|
1315
|
+
if (day === this.target) return;
|
|
1316
|
+
this.target = day;
|
|
1317
|
+
for (const cell of this.grid.querySelectorAll(".tnc-strip-day"))
|
|
1318
|
+
cell.classList.toggle("is-drop-target", day !== null && cell.dataset.date === this.ctx.dayArg(day).dateStr);
|
|
1319
|
+
}
|
|
1320
|
+
dayAt(el) {
|
|
1321
|
+
const cell = el.closest(".tnc-strip-day");
|
|
1322
|
+
return cell && this.grid.contains(cell) ? parseDay(cell.dataset.date ?? "") : null;
|
|
1323
|
+
}
|
|
1324
|
+
step(dir) {
|
|
1325
|
+
this.shown = this.expanded ? addMonths(this.shown, dir) : this.shown + 7 * dir;
|
|
1326
|
+
this.draw();
|
|
1327
|
+
}
|
|
1328
|
+
days() {
|
|
1329
|
+
if (!this.expanded) return Array.from({ length: 7 }, (_, i) => this.shown + i);
|
|
1330
|
+
const first = startOfWeek(this.shown, this.ctx.firstDay);
|
|
1331
|
+
const end = addMonths(this.shown, 1);
|
|
1332
|
+
const weeks = Math.ceil((end - first) / 7);
|
|
1333
|
+
return Array.from({ length: weeks * 7 }, (_, i) => first + i);
|
|
1334
|
+
}
|
|
1335
|
+
/** Redraws the cells; also called on every list render to refresh the dots. */
|
|
1336
|
+
draw() {
|
|
1337
|
+
const { ctx } = this;
|
|
1338
|
+
const { doc, fmt, store } = ctx;
|
|
1339
|
+
const days = this.days();
|
|
1340
|
+
const titleDay = this.expanded ? this.shown : days.includes(this.focus) ? this.focus : this.shown + 3;
|
|
1341
|
+
this.title.replaceChildren(fmt.monthYear(titleDay), h(doc, "span", "tnc-strip-caret", { "aria-hidden": "true" }));
|
|
1342
|
+
this.title.setAttribute("aria-expanded", String(this.expanded));
|
|
1343
|
+
this.el.classList.toggle("is-expanded", this.expanded);
|
|
1344
|
+
this.dows.replaceChildren(...days.slice(0, 7).map((d) => h(doc, "span", null, null, fmt.weekdayNarrow(d))));
|
|
1345
|
+
const focused = this.grid.contains(doc.activeElement) ? doc.activeElement.dataset.date : void 0;
|
|
1346
|
+
const month = ymdOfDay(this.shown).month;
|
|
1347
|
+
const tabDay = days.includes(this.focus) ? this.focus : days[0];
|
|
1348
|
+
const cells = days.map((d) => {
|
|
1349
|
+
const arg = ctx.dayArg(d);
|
|
1350
|
+
const count = store.inRange(d, d + 1).filter((n) => n.item.display !== "background" && n.item.display !== "none").length;
|
|
1351
|
+
const first = ymdOfDay(d).day === 1 && d !== days[0] && !this.expanded;
|
|
1352
|
+
const label = fmt.longDate(d);
|
|
1353
|
+
const cell = h(
|
|
1354
|
+
doc,
|
|
1355
|
+
"button",
|
|
1356
|
+
[
|
|
1357
|
+
"tnc-strip-day",
|
|
1358
|
+
...ctx.dayClasses(d),
|
|
1359
|
+
d === this.focus ? "is-focus" : "",
|
|
1360
|
+
count ? "has-items" : "",
|
|
1361
|
+
this.expanded && ymdOfDay(d).month !== month ? "is-other-month" : "",
|
|
1362
|
+
d === this.target ? "is-drop-target" : ""
|
|
1363
|
+
].filter(Boolean).join(" "),
|
|
1364
|
+
{
|
|
1365
|
+
type: "button",
|
|
1366
|
+
"data-date": arg.dateStr,
|
|
1367
|
+
"aria-label": count ? `${label}, ${ctx.text.itemCount(count)}` : label,
|
|
1368
|
+
"aria-pressed": String(d === this.focus),
|
|
1369
|
+
tabindex: d === tabDay ? "0" : "-1"
|
|
1370
|
+
}
|
|
1371
|
+
);
|
|
1372
|
+
if (first) cell.append(h(doc, "span", "tnc-strip-month", { "aria-hidden": "true" }, fmt.monthShort(d)));
|
|
1373
|
+
cell.append(h(doc, "span", "tnc-strip-num", null, fmt.dayNumber(d)), h(doc, "span", "tnc-strip-dot", { "aria-hidden": "true" }));
|
|
1374
|
+
return cell;
|
|
1375
|
+
});
|
|
1376
|
+
this.grid.replaceChildren(...cells);
|
|
1377
|
+
this.grid.setAttribute("aria-label", fmt.monthYear(titleDay));
|
|
1378
|
+
if (focused) this.grid.querySelector(`[data-date="${focused}"]`)?.focus({ preventScroll: true });
|
|
1379
|
+
}
|
|
1380
|
+
onClick = (ev) => {
|
|
1381
|
+
const day = this.dayAt(ev.target);
|
|
1382
|
+
if (day === null) return;
|
|
1383
|
+
if (this.expanded) this.setExpanded(false);
|
|
1384
|
+
this.pick(day, ev);
|
|
1385
|
+
};
|
|
1386
|
+
onKeyDown = (ev) => {
|
|
1387
|
+
const day = this.dayAt(ev.target);
|
|
1388
|
+
if (day === null) return;
|
|
1389
|
+
const move = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -7, ArrowDown: 7 }[ev.key];
|
|
1390
|
+
if (move === void 0) return;
|
|
1391
|
+
ev.preventDefault();
|
|
1392
|
+
const next = day + move;
|
|
1393
|
+
const days = this.days();
|
|
1394
|
+
if (next < days[0] || next > days[days.length - 1]) {
|
|
1395
|
+
this.shown = this.expanded ? startOfMonth(next) : startOfWeek(next, this.ctx.firstDay);
|
|
1396
|
+
this.draw();
|
|
1397
|
+
}
|
|
1398
|
+
const cell = this.grid.querySelector(`[data-date="${this.ctx.dayArg(next).dateStr}"]`);
|
|
1399
|
+
if (!cell) return;
|
|
1400
|
+
for (const c of this.grid.querySelectorAll("[tabindex='0']")) c.setAttribute("tabindex", "-1");
|
|
1401
|
+
cell.setAttribute("tabindex", "0");
|
|
1402
|
+
cell.focus();
|
|
1403
|
+
};
|
|
1404
|
+
onPointerDown = (ev) => {
|
|
1405
|
+
if (ev.button !== 0 || this.ctx.draggingId) return;
|
|
1406
|
+
this.swipe = { id: ev.pointerId, x: ev.clientX, y: ev.clientY };
|
|
1407
|
+
};
|
|
1408
|
+
onPointerUp = (ev) => {
|
|
1409
|
+
const s = this.swipe;
|
|
1410
|
+
this.swipe = null;
|
|
1411
|
+
if (!s || s.id !== ev.pointerId) return;
|
|
1412
|
+
const dx = ev.clientX - s.x;
|
|
1413
|
+
if (Math.abs(dx) < SWIPE_PX || Math.abs(dx) < Math.abs(ev.clientY - s.y)) return;
|
|
1414
|
+
this.swallowClick = true;
|
|
1415
|
+
this.step(dx < 0 ? 1 : -1);
|
|
1416
|
+
};
|
|
1417
|
+
onPointerCancel = () => {
|
|
1418
|
+
this.swipe = null;
|
|
1419
|
+
};
|
|
1420
|
+
/** A swipe that ends on a day is not a pick. */
|
|
1421
|
+
onClickCapture = (ev) => {
|
|
1422
|
+
if (!this.swallowClick) return;
|
|
1423
|
+
this.swallowClick = false;
|
|
1424
|
+
ev.stopPropagation();
|
|
1425
|
+
ev.preventDefault();
|
|
1426
|
+
};
|
|
1427
|
+
destroy() {
|
|
1428
|
+
this.ctx.opts.stripSlotWillUnmount?.({ el: this.slot });
|
|
1429
|
+
this.el.remove();
|
|
1430
|
+
}
|
|
1431
|
+
};
|
|
1432
|
+
|
|
1249
1433
|
// src/dom/views/list.ts
|
|
1434
|
+
var LOAD_AHEAD = "0px 0px 600px 0px";
|
|
1250
1435
|
var ListView = class {
|
|
1251
1436
|
constructor(ctx) {
|
|
1252
1437
|
this.ctx = ctx;
|
|
1253
|
-
|
|
1254
|
-
this.
|
|
1255
|
-
this.
|
|
1438
|
+
const { doc, win, opts } = ctx;
|
|
1439
|
+
this.el = h(doc, "div", "tnc-list");
|
|
1440
|
+
this.scroller = h(doc, "div", "tnc-list-scroll");
|
|
1441
|
+
this.body = h(doc, "div", "tnc-list-body", { role: "list", "aria-label": ctx.view.title });
|
|
1256
1442
|
this.scroller.append(this.body);
|
|
1257
1443
|
this.el.append(this.scroller);
|
|
1444
|
+
if (!opts.listAgenda) return;
|
|
1445
|
+
this.el.classList.add("tnc-list--agenda");
|
|
1446
|
+
this.strip = new DateStrip(ctx, (day, ev) => this.pick(day, ev), ctx.view.range.startDay);
|
|
1447
|
+
this.el.prepend(this.strip.el);
|
|
1448
|
+
this.sentinel = h(doc, "div", "tnc-list-more", { "aria-hidden": "true" });
|
|
1449
|
+
this.scroller.append(this.sentinel);
|
|
1450
|
+
const globals = win;
|
|
1451
|
+
const IO = globals.IntersectionObserver;
|
|
1452
|
+
if (IO) {
|
|
1453
|
+
this.loader = new IO(
|
|
1454
|
+
(entries) => {
|
|
1455
|
+
if (entries.some((e) => e.isIntersecting)) ctx.extendList(ctx.view.range.endDay + Math.max(7, opts.listDayCount));
|
|
1456
|
+
},
|
|
1457
|
+
// Auto height scrolls the page, so watch the viewport; fixed height scrolls the list.
|
|
1458
|
+
{ root: opts.height === "auto" ? null : this.scroller, rootMargin: LOAD_AHEAD }
|
|
1459
|
+
);
|
|
1460
|
+
}
|
|
1461
|
+
const RO = globals.ResizeObserver;
|
|
1462
|
+
if (RO) {
|
|
1463
|
+
const strip = this.strip.el;
|
|
1464
|
+
this.stripSize = new RO(() => this.el.style.setProperty("--tnc-strip-h", `${strip.offsetHeight}px`));
|
|
1465
|
+
this.stripSize.observe(strip);
|
|
1466
|
+
}
|
|
1467
|
+
win.addEventListener("scroll", this.onScroll, { capture: true, passive: true });
|
|
1468
|
+
this.scroller.addEventListener("wheel", this.fold, { passive: true });
|
|
1469
|
+
this.scroller.addEventListener("touchmove", this.fold, { passive: true });
|
|
1258
1470
|
}
|
|
1259
1471
|
ctx;
|
|
1260
1472
|
el;
|
|
@@ -1262,6 +1474,13 @@ var ListView = class {
|
|
|
1262
1474
|
body;
|
|
1263
1475
|
pools = /* @__PURE__ */ new Map();
|
|
1264
1476
|
sections = /* @__PURE__ */ new Map();
|
|
1477
|
+
strip = null;
|
|
1478
|
+
sentinel = null;
|
|
1479
|
+
loader = null;
|
|
1480
|
+
stripSize = null;
|
|
1481
|
+
frame = null;
|
|
1482
|
+
/** A picked day stays marked until the list is scrolled by hand. */
|
|
1483
|
+
pinned = false;
|
|
1265
1484
|
itemsOn(day) {
|
|
1266
1485
|
const { store, opts } = this.ctx;
|
|
1267
1486
|
const out = store.spanningIn(day, day + 1, true);
|
|
@@ -1272,11 +1491,12 @@ var ListView = class {
|
|
|
1272
1491
|
render(_change) {
|
|
1273
1492
|
const { ctx } = this;
|
|
1274
1493
|
const { doc, days } = ctx;
|
|
1494
|
+
const agenda = !!this.strip;
|
|
1275
1495
|
const keep = /* @__PURE__ */ new Set();
|
|
1276
1496
|
let prev = null;
|
|
1277
1497
|
for (const day of days) {
|
|
1278
1498
|
const items = this.itemsOn(day);
|
|
1279
|
-
if (!items.length) continue;
|
|
1499
|
+
if (!items.length && !agenda) continue;
|
|
1280
1500
|
keep.add(day);
|
|
1281
1501
|
let section = this.sections.get(day);
|
|
1282
1502
|
let pool = this.pools.get(day);
|
|
@@ -1293,6 +1513,7 @@ var ListView = class {
|
|
|
1293
1513
|
this.sections.set(day, section);
|
|
1294
1514
|
this.pools.set(day, pool);
|
|
1295
1515
|
}
|
|
1516
|
+
section.classList.toggle("is-empty", !items.length);
|
|
1296
1517
|
pool.begin();
|
|
1297
1518
|
for (const n of items) {
|
|
1298
1519
|
pool.place(n.id, {
|
|
@@ -1330,11 +1551,62 @@ var ListView = class {
|
|
|
1330
1551
|
this.body.append(empty);
|
|
1331
1552
|
}
|
|
1332
1553
|
} else empty?.remove();
|
|
1554
|
+
this.strip?.draw();
|
|
1555
|
+
if (this.loader && this.sentinel) {
|
|
1556
|
+
this.loader.unobserve(this.sentinel);
|
|
1557
|
+
this.loader.observe(this.sentinel);
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
/** A strip day: the host hears of it, and the list brings it to the top. */
|
|
1561
|
+
pick(day, ev) {
|
|
1562
|
+
this.ctx.dateClick(day, ev);
|
|
1563
|
+
this.ctx.showListDay(day);
|
|
1564
|
+
}
|
|
1565
|
+
/** Brings a day's section to the top, under the strip. */
|
|
1566
|
+
scrollToDay(day) {
|
|
1567
|
+
const section = this.sections.get(day);
|
|
1568
|
+
if (!section) return;
|
|
1569
|
+
if (this.strip) this.el.style.setProperty("--tnc-strip-h", `${this.strip.el.offsetHeight}px`);
|
|
1570
|
+
section.scrollIntoView({ block: "start" });
|
|
1571
|
+
this.strip?.setFocus(day);
|
|
1572
|
+
this.pinned = true;
|
|
1573
|
+
}
|
|
1574
|
+
fold = () => {
|
|
1575
|
+
this.pinned = false;
|
|
1576
|
+
if (this.strip?.expanded) this.strip.setExpanded(false);
|
|
1577
|
+
};
|
|
1578
|
+
onScroll = () => {
|
|
1579
|
+
if (this.frame !== null) return;
|
|
1580
|
+
this.frame = this.ctx.win.requestAnimationFrame(() => {
|
|
1581
|
+
this.frame = null;
|
|
1582
|
+
this.followTop();
|
|
1583
|
+
});
|
|
1584
|
+
};
|
|
1585
|
+
/** Marks the first day not hidden under the strip. */
|
|
1586
|
+
followTop() {
|
|
1587
|
+
if (!this.strip || this.pinned) return;
|
|
1588
|
+
const edge = this.strip.el.getBoundingClientRect().bottom + 1;
|
|
1589
|
+
for (const day of this.ctx.days) {
|
|
1590
|
+
const section = this.sections.get(day);
|
|
1591
|
+
if (section && section.getBoundingClientRect().bottom > edge) {
|
|
1592
|
+
this.strip.setFocus(day);
|
|
1593
|
+
return;
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1333
1596
|
}
|
|
1334
1597
|
showSelection(_sel) {
|
|
1335
1598
|
}
|
|
1336
|
-
hit(
|
|
1337
|
-
|
|
1599
|
+
hit(x, y) {
|
|
1600
|
+
const el = this.ctx.doc.elementFromPoint(x, y);
|
|
1601
|
+
const stripDay = el && this.strip ? this.strip.dayAt(el) : null;
|
|
1602
|
+
this.strip?.markTarget(stripDay !== null && this.ctx.draggingId ? stripDay : null);
|
|
1603
|
+
if (stripDay !== null) return { kind: "day", day: stripDay };
|
|
1604
|
+
const section = el?.closest(".tnc-list-section");
|
|
1605
|
+
const day = section && this.body.contains(section) ? parseDay(section.dataset.date ?? "") : null;
|
|
1606
|
+
return day === null ? null : { kind: "day", day };
|
|
1607
|
+
}
|
|
1608
|
+
dragEnded() {
|
|
1609
|
+
this.strip?.markTarget(null);
|
|
1338
1610
|
}
|
|
1339
1611
|
getScroll() {
|
|
1340
1612
|
return { top: this.scroller.scrollTop, left: 0 };
|
|
@@ -1346,6 +1618,12 @@ var ListView = class {
|
|
|
1346
1618
|
return [...this.pools.values()].flatMap((p) => p.elementsFor(id));
|
|
1347
1619
|
}
|
|
1348
1620
|
destroy() {
|
|
1621
|
+
const { win } = this.ctx;
|
|
1622
|
+
win.removeEventListener("scroll", this.onScroll, { capture: true });
|
|
1623
|
+
if (this.frame !== null) win.cancelAnimationFrame(this.frame);
|
|
1624
|
+
this.loader?.disconnect();
|
|
1625
|
+
this.stripSize?.disconnect();
|
|
1626
|
+
this.strip?.destroy();
|
|
1349
1627
|
for (const p of this.pools.values()) p.clear();
|
|
1350
1628
|
this.el.remove();
|
|
1351
1629
|
}
|
|
@@ -2487,6 +2765,7 @@ ${this.ctx.text.more(items.length - 8)}` : ""}` : label;
|
|
|
2487
2765
|
|
|
2488
2766
|
// src/dom/calendar.ts
|
|
2489
2767
|
var EXTERNAL_ID = "__tnc_external__";
|
|
2768
|
+
var MAX_AGENDA_DAYS = 366;
|
|
2490
2769
|
function sameOption(a, b, depth = 0) {
|
|
2491
2770
|
if (a === b) return true;
|
|
2492
2771
|
if (typeof a === "function" && typeof b === "function") return true;
|
|
@@ -2559,6 +2838,8 @@ var Calendar = class {
|
|
|
2559
2838
|
viewHost;
|
|
2560
2839
|
current = null;
|
|
2561
2840
|
anchor = 0;
|
|
2841
|
+
/** End of an agenda list grown past its `listDayCount`. */
|
|
2842
|
+
listEnd = null;
|
|
2562
2843
|
session = null;
|
|
2563
2844
|
selection = null;
|
|
2564
2845
|
suppressClick = false;
|
|
@@ -2751,6 +3032,10 @@ var Calendar = class {
|
|
|
2751
3032
|
if (!p) return;
|
|
2752
3033
|
const day = p.kind === "day" ? p.day : dayOf(p.ms, this.zone);
|
|
2753
3034
|
const ms = p.kind === "day" ? toInstant(p.day, 0, this.zone) : p.ms;
|
|
3035
|
+
if (this.agendaList()) {
|
|
3036
|
+
this.showListDay(day);
|
|
3037
|
+
return;
|
|
3038
|
+
}
|
|
2754
3039
|
const { startDay, endDay } = this.view.range;
|
|
2755
3040
|
if (day < startDay || day >= endDay) {
|
|
2756
3041
|
this.anchor = day;
|
|
@@ -2834,7 +3119,8 @@ var Calendar = class {
|
|
|
2834
3119
|
return out;
|
|
2835
3120
|
}
|
|
2836
3121
|
isEditable(n, what) {
|
|
2837
|
-
if (n.id === EXTERNAL_ID || this.opts.view === "
|
|
3122
|
+
if (n.id === EXTERNAL_ID || this.opts.view === "year") return false;
|
|
3123
|
+
if (this.opts.view === "list" && (!this.opts.listAgenda || what === "duration")) return false;
|
|
2838
3124
|
const it = n.item;
|
|
2839
3125
|
const base = it.editable ?? this.opts.editable;
|
|
2840
3126
|
const specific = what === "start" ? it.startEditable : it.durationEditable;
|
|
@@ -3031,7 +3317,8 @@ var Calendar = class {
|
|
|
3031
3317
|
}
|
|
3032
3318
|
case "list": {
|
|
3033
3319
|
const s = this.opts.listStart === "week" ? startOfWeek(a, this.firstDay) : a;
|
|
3034
|
-
|
|
3320
|
+
const end = s + Math.max(1, listDayCount);
|
|
3321
|
+
return { startDay: s, endDay: this.opts.listAgenda && this.listEnd !== null ? Math.max(end, this.listEnd) : end };
|
|
3035
3322
|
}
|
|
3036
3323
|
case "month": {
|
|
3037
3324
|
const s = startOfWeek(startOfMonth(a), this.firstDay);
|
|
@@ -3069,6 +3356,27 @@ var Calendar = class {
|
|
|
3069
3356
|
const duration = Math.max(1, parseDurationMinutes(this.opts.slotDuration, 30));
|
|
3070
3357
|
const snap = Math.max(1, parseDurationMinutes(this.opts.snapDuration ?? null, duration));
|
|
3071
3358
|
this.slot = { min, max, duration, snap, scroll: parseDurationMinutes(this.opts.scrollTime, 480) };
|
|
3359
|
+
if (navigated) this.listEnd = null;
|
|
3360
|
+
const rangeChanged = this.applyRange();
|
|
3361
|
+
this.conflicts = dependencyConflicts(this.store);
|
|
3362
|
+
this.root.className = "tnc";
|
|
3363
|
+
this.root.classList.add(`tnc--${this.opts.view}`, `tnc--height-${this.opts.height}`);
|
|
3364
|
+
if (!this.opts.timeGrid) this.root.classList.add("tnc--no-time-grid");
|
|
3365
|
+
this.renderToolbar();
|
|
3366
|
+
this.current?.destroy();
|
|
3367
|
+
this.current = this.createView();
|
|
3368
|
+
this.viewHost.replaceChildren(this.current.el);
|
|
3369
|
+
this.current.render(null);
|
|
3370
|
+
if (keep && this.current instanceof TimelineView) {
|
|
3371
|
+
this.current.setScroll({ top: keep.top, left: 0 });
|
|
3372
|
+
this.current.placeInstant(keep.ms, keep.vx);
|
|
3373
|
+
} else if (scroll) this.current.setScroll(scroll);
|
|
3374
|
+
else if (this.current instanceof TimeGridView) this.current.scrollToTime(this.slot.scroll);
|
|
3375
|
+
else if (this.current instanceof TimelineView) this.current.scrollToDay(this.anchor);
|
|
3376
|
+
if (rangeChanged) this.opts.onRangeChange?.(this.view);
|
|
3377
|
+
}
|
|
3378
|
+
/** Sets the visible days and view info from the anchor; true when the range moved. */
|
|
3379
|
+
applyRange() {
|
|
3072
3380
|
const { startDay, endDay } = this.computeRange();
|
|
3073
3381
|
const hidden = new Set(this.opts.hiddenDays);
|
|
3074
3382
|
if (!this.opts.weekends) {
|
|
@@ -3088,23 +3396,32 @@ var Calendar = class {
|
|
|
3088
3396
|
const prevView = this.view;
|
|
3089
3397
|
this.view = { type: this.opts.view, range, anchor: this.anchor, title: "" };
|
|
3090
3398
|
this.view.title = this.computeTitle(startDay, endDay);
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
this.
|
|
3099
|
-
|
|
3100
|
-
if (
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
if (
|
|
3399
|
+
return !prevView || prevView.type !== this.view.type || prevView.range.startDay !== startDay || prevView.range.endDay !== endDay;
|
|
3400
|
+
}
|
|
3401
|
+
agendaList() {
|
|
3402
|
+
return this.opts.view === "list" && this.opts.listAgenda && this.current instanceof ListView ? this.current : null;
|
|
3403
|
+
}
|
|
3404
|
+
extendList(endDay) {
|
|
3405
|
+
if (!this.agendaList()) return;
|
|
3406
|
+
const { startDay, endDay: current } = this.view.range;
|
|
3407
|
+
const end = Math.min(endDay, startDay + MAX_AGENDA_DAYS);
|
|
3408
|
+
if (end <= current) return;
|
|
3409
|
+
this.listEnd = end;
|
|
3410
|
+
this.applyRange();
|
|
3411
|
+
this.current?.render(null);
|
|
3412
|
+
this.opts.onRangeChange?.(this.view);
|
|
3413
|
+
}
|
|
3414
|
+
showListDay(day) {
|
|
3415
|
+
if (!this.agendaList()) return;
|
|
3416
|
+
if (day < this.view.range.startDay || day - this.view.range.startDay >= MAX_AGENDA_DAYS) {
|
|
3417
|
+
this.anchor = day;
|
|
3418
|
+
this.rebuild(true);
|
|
3419
|
+
}
|
|
3420
|
+
this.extendList(day + 2 * Math.max(7, this.opts.listDayCount));
|
|
3421
|
+
this.agendaList()?.scrollToDay(day);
|
|
3422
|
+
}
|
|
3423
|
+
dateClick(day, jsEvent) {
|
|
3424
|
+
this.clickSlot({ kind: "day", day }, jsEvent, true);
|
|
3108
3425
|
}
|
|
3109
3426
|
createView() {
|
|
3110
3427
|
switch (this.opts.view) {
|
|
@@ -3407,8 +3724,9 @@ var Calendar = class {
|
|
|
3407
3724
|
};
|
|
3408
3725
|
this.session = s;
|
|
3409
3726
|
if (touch && itemEl?.classList.contains("is-draggable")) {
|
|
3410
|
-
const
|
|
3411
|
-
|
|
3727
|
+
const x = scrollerOf(itemEl, "x");
|
|
3728
|
+
const y = scrollerOf(itemEl, "y");
|
|
3729
|
+
s.pan = { x, y, left: x?.scrollLeft ?? 0, top: y?.scrollTop ?? 0, active: false };
|
|
3412
3730
|
}
|
|
3413
3731
|
if (touch) {
|
|
3414
3732
|
s.timer = this.win.setTimeout(() => {
|
|
@@ -3449,8 +3767,8 @@ var Calendar = class {
|
|
|
3449
3767
|
if (!pan.active && s.timer !== null) this.win.clearTimeout(s.timer);
|
|
3450
3768
|
s.timer = null;
|
|
3451
3769
|
pan.active = true;
|
|
3452
|
-
pan.
|
|
3453
|
-
pan.
|
|
3770
|
+
if (pan.x) pan.x.scrollLeft = pan.left - (ev.clientX - s.x0);
|
|
3771
|
+
if (pan.y) pan.y.scrollTop = pan.top - (ev.clientY - s.y0);
|
|
3454
3772
|
} else if (dist > 8) this.endSession(null, true);
|
|
3455
3773
|
return;
|
|
3456
3774
|
}
|
|
@@ -3749,6 +4067,7 @@ var Calendar = class {
|
|
|
3749
4067
|
this.draggingId = null;
|
|
3750
4068
|
this.dragRow = null;
|
|
3751
4069
|
if (draggedId) for (const el of this.elementsFor(draggedId)) el.classList.remove("is-dragging");
|
|
4070
|
+
this.current?.dragEnded?.();
|
|
3752
4071
|
if (!s.active) return;
|
|
3753
4072
|
if (s.mode === "select") {
|
|
3754
4073
|
if (cancel) this.unselectSilently();
|
|
@@ -3842,6 +4161,12 @@ var Calendar = class {
|
|
|
3842
4161
|
ev.preventDefault();
|
|
3843
4162
|
};
|
|
3844
4163
|
onClick = (ev) => {
|
|
4164
|
+
const yearDay = ev.target.closest?.(".tnc-ym-day");
|
|
4165
|
+
if (yearDay && this.root.contains(yearDay)) {
|
|
4166
|
+
const day = parseDay(yearDay.dataset.date ?? "");
|
|
4167
|
+
if (day !== null) this.clickSlot({ kind: "day", day }, ev, true);
|
|
4168
|
+
return;
|
|
4169
|
+
}
|
|
3845
4170
|
const el = this.itemElFrom(ev.target);
|
|
3846
4171
|
if (!el || ev.target.closest(".tnc-resize, .tnc-port")) return;
|
|
3847
4172
|
const meta = this.meta.get(el);
|
|
@@ -3901,7 +4226,7 @@ var Calendar = class {
|
|
|
3901
4226
|
return;
|
|
3902
4227
|
}
|
|
3903
4228
|
const target = ev.target;
|
|
3904
|
-
if ((ev.key === "Enter" || ev.key === " ") && target.matches(".tnc-
|
|
4229
|
+
if ((ev.key === "Enter" || ev.key === " ") && target.matches(".tnc-dg-day[tabindex]")) {
|
|
3905
4230
|
const day = parseDay(target.dataset.date ?? "");
|
|
3906
4231
|
if (day !== null) {
|
|
3907
4232
|
ev.preventDefault();
|
|
@@ -4029,6 +4354,16 @@ var Calendar = class {
|
|
|
4029
4354
|
});
|
|
4030
4355
|
};
|
|
4031
4356
|
};
|
|
4357
|
+
function scrollerOf(el, axis) {
|
|
4358
|
+
const doc = el.ownerDocument;
|
|
4359
|
+
const win = doc.defaultView;
|
|
4360
|
+
for (let p = el.parentElement; p && p !== doc.body && p !== doc.documentElement; p = p.parentElement) {
|
|
4361
|
+
const overflow = win?.getComputedStyle(p)[axis === "x" ? "overflowX" : "overflowY"] ?? "";
|
|
4362
|
+
const room = axis === "x" ? p.scrollWidth > p.clientWidth : p.scrollHeight > p.clientHeight;
|
|
4363
|
+
if (room && /auto|scroll|overlay/.test(overflow)) return p;
|
|
4364
|
+
}
|
|
4365
|
+
return doc.scrollingElement;
|
|
4366
|
+
}
|
|
4032
4367
|
|
|
4033
4368
|
// src/react/index.tsx
|
|
4034
4369
|
var import_jsx_runtime = require("react/jsx-runtime");
|