@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/react.cjs
CHANGED
|
@@ -709,6 +709,9 @@ function defaultOptions() {
|
|
|
709
709
|
listDayCount: 7,
|
|
710
710
|
listStart: "date",
|
|
711
711
|
listAgenda: false,
|
|
712
|
+
dateStrip: false,
|
|
713
|
+
listLeadTitle: null,
|
|
714
|
+
listAddText: null,
|
|
712
715
|
monthDisplay: "full",
|
|
713
716
|
timeZone: "local",
|
|
714
717
|
locale: void 0,
|
|
@@ -759,6 +762,9 @@ var LAYOUT_KEYS = /* @__PURE__ */ new Set([
|
|
|
759
762
|
"listDayCount",
|
|
760
763
|
"listStart",
|
|
761
764
|
"listAgenda",
|
|
765
|
+
"dateStrip",
|
|
766
|
+
"listLeadTitle",
|
|
767
|
+
"listAddText",
|
|
762
768
|
"monthDisplay",
|
|
763
769
|
"timeZone",
|
|
764
770
|
"locale",
|
|
@@ -1112,17 +1118,22 @@ var DayGridView = class {
|
|
|
1112
1118
|
const n = w.days.length;
|
|
1113
1119
|
const hidden = new Array(n).fill([]).map(() => []);
|
|
1114
1120
|
w.pool.begin();
|
|
1121
|
+
const previewLevels = new Array(n).fill(0);
|
|
1115
1122
|
let maxLevel = -1;
|
|
1116
1123
|
for (const p of row.placements) {
|
|
1117
1124
|
let level = p.level;
|
|
1118
1125
|
let room = Infinity;
|
|
1119
1126
|
for (let c = p.startCol; c < p.endCol; c++) room = Math.min(room, visible(c));
|
|
1120
1127
|
if (level >= room) {
|
|
1121
|
-
if (p.item.id !== ctx.draggingId) {
|
|
1128
|
+
if (!p.item.item.preview && p.item.id !== ctx.draggingId) {
|
|
1122
1129
|
for (let c = p.startCol; c < p.endCol; c++) hidden[c].push(p.item);
|
|
1123
1130
|
continue;
|
|
1124
1131
|
}
|
|
1125
|
-
|
|
1132
|
+
if (p.item.item.preview) {
|
|
1133
|
+
level = 0;
|
|
1134
|
+
for (let c = p.startCol; c < p.endCol; c++) level = Math.max(level, visible(c) + 1, previewLevels[c]);
|
|
1135
|
+
for (let c = p.startCol; c < p.endCol; c++) previewLevels[c] = level + 1;
|
|
1136
|
+
} else level = Math.max(0, room - 1);
|
|
1126
1137
|
}
|
|
1127
1138
|
maxLevel = Math.max(maxLevel, level);
|
|
1128
1139
|
const single = !p.item.allDay && p.item.endDay - p.item.startDay <= 1;
|
|
@@ -1248,12 +1259,244 @@ var DayGridView = class {
|
|
|
1248
1259
|
}
|
|
1249
1260
|
};
|
|
1250
1261
|
|
|
1262
|
+
// src/dom/views/list.ts
|
|
1263
|
+
var LOAD_AHEAD = "0px 0px 600px 0px";
|
|
1264
|
+
var ListView = class {
|
|
1265
|
+
constructor(ctx) {
|
|
1266
|
+
this.ctx = ctx;
|
|
1267
|
+
const { doc, win, opts } = ctx;
|
|
1268
|
+
this.el = h(doc, "div", "tnc-list");
|
|
1269
|
+
this.scroller = h(doc, "div", "tnc-list-scroll");
|
|
1270
|
+
this.body = h(doc, "div", "tnc-list-body", { role: "list", "aria-label": ctx.view.title });
|
|
1271
|
+
this.scroller.append(this.body);
|
|
1272
|
+
this.el.append(this.scroller);
|
|
1273
|
+
this.agenda = opts.listAgenda;
|
|
1274
|
+
if (!this.agenda) return;
|
|
1275
|
+
this.el.classList.add("tnc-list--agenda");
|
|
1276
|
+
this.sentinel = h(doc, "div", "tnc-list-more", { "aria-hidden": "true" });
|
|
1277
|
+
this.scroller.append(this.sentinel);
|
|
1278
|
+
const IO = win.IntersectionObserver;
|
|
1279
|
+
if (IO) {
|
|
1280
|
+
this.loader = new IO(
|
|
1281
|
+
(entries) => {
|
|
1282
|
+
if (entries.some((e) => e.isIntersecting)) ctx.extendList(ctx.view.range.endDay + Math.max(7, opts.listDayCount));
|
|
1283
|
+
},
|
|
1284
|
+
// Auto height scrolls the page, so watch the viewport; fixed height scrolls the list.
|
|
1285
|
+
{ root: opts.height === "auto" ? null : this.scroller, rootMargin: LOAD_AHEAD }
|
|
1286
|
+
);
|
|
1287
|
+
}
|
|
1288
|
+
win.addEventListener("scroll", this.onScroll, { capture: true, passive: true });
|
|
1289
|
+
this.scroller.addEventListener("wheel", this.fold, { passive: true });
|
|
1290
|
+
this.scroller.addEventListener("touchmove", this.fold, { passive: true });
|
|
1291
|
+
}
|
|
1292
|
+
ctx;
|
|
1293
|
+
el;
|
|
1294
|
+
scroller;
|
|
1295
|
+
body;
|
|
1296
|
+
agenda;
|
|
1297
|
+
pools = /* @__PURE__ */ new Map();
|
|
1298
|
+
sections = /* @__PURE__ */ new Map();
|
|
1299
|
+
lead = null;
|
|
1300
|
+
sentinel = null;
|
|
1301
|
+
loader = null;
|
|
1302
|
+
frame = null;
|
|
1303
|
+
/** A picked day stays marked until the list is scrolled by hand. */
|
|
1304
|
+
pinned = false;
|
|
1305
|
+
itemsOn(day) {
|
|
1306
|
+
const { store, opts } = this.ctx;
|
|
1307
|
+
const out = store.spanningIn(day, day + 1, true);
|
|
1308
|
+
const seen = new Set(out.map((n) => n.id));
|
|
1309
|
+
for (const n of store.timedOn(day)) if (!seen.has(n.id)) out.push(n);
|
|
1310
|
+
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));
|
|
1311
|
+
}
|
|
1312
|
+
render(_change) {
|
|
1313
|
+
const { ctx, agenda } = this;
|
|
1314
|
+
const { doc, days, opts } = ctx;
|
|
1315
|
+
const includes = agenda && opts.listLeadTitle !== null ? opts.listLeadIncludes : void 0;
|
|
1316
|
+
const leadItems = [];
|
|
1317
|
+
const inLead = /* @__PURE__ */ new Set();
|
|
1318
|
+
const keep = /* @__PURE__ */ new Set();
|
|
1319
|
+
let prev = null;
|
|
1320
|
+
for (const day of days) {
|
|
1321
|
+
let items = this.itemsOn(day);
|
|
1322
|
+
if (includes) {
|
|
1323
|
+
items = items.filter((n) => {
|
|
1324
|
+
if (!includes(n.item)) return true;
|
|
1325
|
+
if (!inLead.has(n.id)) {
|
|
1326
|
+
inLead.add(n.id);
|
|
1327
|
+
leadItems.push(n);
|
|
1328
|
+
}
|
|
1329
|
+
return false;
|
|
1330
|
+
});
|
|
1331
|
+
}
|
|
1332
|
+
if (!items.length && !agenda) continue;
|
|
1333
|
+
keep.add(day);
|
|
1334
|
+
let section = this.sections.get(day);
|
|
1335
|
+
let pool = this.pools.get(day);
|
|
1336
|
+
if (!section || !pool) {
|
|
1337
|
+
const arg = ctx.dayArg(day);
|
|
1338
|
+
const heading = h(doc, "div", ["tnc-list-day", ...ctx.dayClasses(day)].join(" "), { "data-date": arg.dateStr });
|
|
1339
|
+
const custom = opts.dayHeaderContent?.(arg);
|
|
1340
|
+
const label = custom ?? ctx.fmt.longDate(day);
|
|
1341
|
+
heading.append(opts.navLinks ? ctx.navLink(day, label, "tnc-list-day-link") : label);
|
|
1342
|
+
const rows = h(doc, "div", "tnc-list-rows", { role: "group" });
|
|
1343
|
+
section = h(doc, "div", "tnc-list-section", { role: "listitem", "aria-label": ctx.fmt.longDate(day), "data-date": arg.dateStr }, heading, rows);
|
|
1344
|
+
if (agenda && opts.listAddText !== null) section.append(this.addRow(day));
|
|
1345
|
+
opts.dayHeaderDidMount?.({ ...arg, el: heading });
|
|
1346
|
+
pool = new ItemPool(ctx, rows);
|
|
1347
|
+
this.sections.set(day, section);
|
|
1348
|
+
this.pools.set(day, pool);
|
|
1349
|
+
}
|
|
1350
|
+
section.classList.toggle("is-empty", !items.length);
|
|
1351
|
+
this.fill(pool, items, () => day);
|
|
1352
|
+
if (prev ? prev.nextSibling !== section : this.body.firstChild !== section) {
|
|
1353
|
+
if (prev) prev.after(section);
|
|
1354
|
+
else this.body.prepend(section);
|
|
1355
|
+
}
|
|
1356
|
+
prev = section;
|
|
1357
|
+
}
|
|
1358
|
+
for (const [day, section] of this.sections) {
|
|
1359
|
+
if (keep.has(day)) continue;
|
|
1360
|
+
this.pools.get(day)?.clear();
|
|
1361
|
+
this.pools.delete(day);
|
|
1362
|
+
section.remove();
|
|
1363
|
+
this.sections.delete(day);
|
|
1364
|
+
}
|
|
1365
|
+
this.renderLead(leadItems);
|
|
1366
|
+
let empty = this.body.querySelector(":scope > .tnc-list-empty");
|
|
1367
|
+
if (!keep.size && !leadItems.length) {
|
|
1368
|
+
if (!empty) {
|
|
1369
|
+
empty = h(doc, "div", "tnc-list-empty", { role: "listitem" }, ctx.text.noItems);
|
|
1370
|
+
this.body.append(empty);
|
|
1371
|
+
}
|
|
1372
|
+
} else empty?.remove();
|
|
1373
|
+
this.ctx.strip?.draw();
|
|
1374
|
+
if (this.loader && this.sentinel) {
|
|
1375
|
+
this.loader.unobserve(this.sentinel);
|
|
1376
|
+
this.loader.observe(this.sentinel);
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
/** Places items in a pool, keeping DOM order equal to item order. */
|
|
1380
|
+
fill(pool, items, dayFor) {
|
|
1381
|
+
pool.begin();
|
|
1382
|
+
for (const n of items) {
|
|
1383
|
+
const d = dayFor(n);
|
|
1384
|
+
pool.place(n.id, { n, day: d, isStart: n.startDay === d, isEnd: n.endDay === d + 1, variant: "list", mirror: false });
|
|
1385
|
+
}
|
|
1386
|
+
pool.end();
|
|
1387
|
+
for (const n of items) {
|
|
1388
|
+
const el = pool.elementsFor(n.id)[0];
|
|
1389
|
+
if (el) pool.container.append(el);
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
renderLead(items) {
|
|
1393
|
+
const { ctx } = this;
|
|
1394
|
+
const { doc, opts } = ctx;
|
|
1395
|
+
if (!items.length || opts.listLeadTitle === null) {
|
|
1396
|
+
if (!this.lead) return;
|
|
1397
|
+
opts.listLeadWillUnmount?.({ el: this.lead.slot });
|
|
1398
|
+
this.lead.pool.clear();
|
|
1399
|
+
this.lead.section.remove();
|
|
1400
|
+
this.lead = null;
|
|
1401
|
+
return;
|
|
1402
|
+
}
|
|
1403
|
+
if (!this.lead) {
|
|
1404
|
+
const title = opts.listLeadTitle;
|
|
1405
|
+
const slot = h(doc, "div", "tnc-list-lead-slot", { "data-tnc-no-drag": "" });
|
|
1406
|
+
const heading = h(doc, "div", "tnc-list-day tnc-list-lead-head", null, h(doc, "span", "tnc-list-lead-title", null, title), slot);
|
|
1407
|
+
const rows = h(doc, "div", "tnc-list-rows", { role: "group" });
|
|
1408
|
+
const section = h(doc, "div", "tnc-list-section tnc-list-lead", { role: "listitem", "aria-label": title }, heading, rows);
|
|
1409
|
+
this.lead = { section, slot, pool: new ItemPool(ctx, rows) };
|
|
1410
|
+
opts.listLeadDidMount?.({ el: slot });
|
|
1411
|
+
}
|
|
1412
|
+
if (this.body.firstChild !== this.lead.section) this.body.prepend(this.lead.section);
|
|
1413
|
+
const first = ctx.days[0] ?? ctx.view.range.startDay;
|
|
1414
|
+
this.fill(this.lead.pool, items, (n) => Math.max(n.startDay, first));
|
|
1415
|
+
}
|
|
1416
|
+
addRow(day) {
|
|
1417
|
+
const { ctx } = this;
|
|
1418
|
+
const label = ctx.opts.listAddText ?? "";
|
|
1419
|
+
const btn = h(ctx.doc, "button", "tnc-list-add", { type: "button", "aria-label": `${label}, ${ctx.fmt.longDate(day)}` }, label);
|
|
1420
|
+
btn.addEventListener("click", (ev) => ctx.opts.onListAdd?.({ ...ctx.dayArg(day), jsEvent: ev }));
|
|
1421
|
+
return btn;
|
|
1422
|
+
}
|
|
1423
|
+
/** Brings a day's section to the top, under the strip; the first day brings the lead section too. */
|
|
1424
|
+
scrollToDay(day) {
|
|
1425
|
+
const section = day === this.ctx.days[0] && this.lead ? this.lead.section : this.sections.get(day);
|
|
1426
|
+
if (!section) return;
|
|
1427
|
+
this.ctx.strip?.measure();
|
|
1428
|
+
section.scrollIntoView({ block: "start" });
|
|
1429
|
+
this.ctx.strip?.setFocus(day);
|
|
1430
|
+
this.pinned = true;
|
|
1431
|
+
}
|
|
1432
|
+
fold = () => {
|
|
1433
|
+
this.pinned = false;
|
|
1434
|
+
const strip = this.ctx.strip;
|
|
1435
|
+
if (strip?.expanded) strip.setExpanded(false);
|
|
1436
|
+
};
|
|
1437
|
+
onScroll = () => {
|
|
1438
|
+
if (this.frame !== null) return;
|
|
1439
|
+
this.frame = this.ctx.win.requestAnimationFrame(() => {
|
|
1440
|
+
this.frame = null;
|
|
1441
|
+
this.followTop();
|
|
1442
|
+
});
|
|
1443
|
+
};
|
|
1444
|
+
/** Marks the first day not hidden under the strip. */
|
|
1445
|
+
followTop() {
|
|
1446
|
+
const strip = this.ctx.strip;
|
|
1447
|
+
if (!strip || this.pinned) return;
|
|
1448
|
+
const edge = strip.el.getBoundingClientRect().bottom + 1;
|
|
1449
|
+
for (const day of this.ctx.days) {
|
|
1450
|
+
const section = this.sections.get(day);
|
|
1451
|
+
if (section && section.getBoundingClientRect().bottom > edge) {
|
|
1452
|
+
strip.setFocus(day);
|
|
1453
|
+
return;
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
showSelection(_sel) {
|
|
1458
|
+
}
|
|
1459
|
+
hit(x, y) {
|
|
1460
|
+
const el = this.ctx.doc.elementFromPoint(x, y);
|
|
1461
|
+
const section = el?.closest(".tnc-list-section");
|
|
1462
|
+
if (!section || !this.body.contains(section)) return null;
|
|
1463
|
+
if (section === this.lead?.section) {
|
|
1464
|
+
const item = el?.closest(".tnc-item");
|
|
1465
|
+
const meta = item ? this.ctx.meta.get(item) : void 0;
|
|
1466
|
+
return { kind: "day", day: meta?.day ?? this.ctx.days[0] ?? this.ctx.view.range.startDay };
|
|
1467
|
+
}
|
|
1468
|
+
const day = parseDay(section.dataset.date ?? "");
|
|
1469
|
+
return day === null ? null : { kind: "day", day };
|
|
1470
|
+
}
|
|
1471
|
+
getScroll() {
|
|
1472
|
+
return { top: this.scroller.scrollTop, left: 0 };
|
|
1473
|
+
}
|
|
1474
|
+
setScroll(s) {
|
|
1475
|
+
this.scroller.scrollTop = s.top;
|
|
1476
|
+
}
|
|
1477
|
+
elementsFor(id) {
|
|
1478
|
+
const pools = [...this.pools.values(), ...this.lead ? [this.lead.pool] : []];
|
|
1479
|
+
return pools.flatMap((p) => p.elementsFor(id));
|
|
1480
|
+
}
|
|
1481
|
+
destroy() {
|
|
1482
|
+
const { win, opts } = this.ctx;
|
|
1483
|
+
win.removeEventListener("scroll", this.onScroll, { capture: true });
|
|
1484
|
+
if (this.frame !== null) win.cancelAnimationFrame(this.frame);
|
|
1485
|
+
this.loader?.disconnect();
|
|
1486
|
+
if (this.lead) opts.listLeadWillUnmount?.({ el: this.lead.slot });
|
|
1487
|
+
this.lead?.pool.clear();
|
|
1488
|
+
for (const p of this.pools.values()) p.clear();
|
|
1489
|
+
this.el.remove();
|
|
1490
|
+
}
|
|
1491
|
+
};
|
|
1492
|
+
|
|
1251
1493
|
// src/dom/views/strip.ts
|
|
1252
1494
|
var SWIPE_PX = 40;
|
|
1253
1495
|
var DateStrip = class {
|
|
1254
|
-
constructor(ctx, pick, focus) {
|
|
1496
|
+
constructor(ctx, pick, focus, onShown) {
|
|
1255
1497
|
this.ctx = ctx;
|
|
1256
1498
|
this.pick = pick;
|
|
1499
|
+
this.onShown = onShown;
|
|
1257
1500
|
const { doc, text } = ctx;
|
|
1258
1501
|
this.focus = focus;
|
|
1259
1502
|
this.shown = startOfWeek(focus, ctx.firstDay);
|
|
@@ -1265,8 +1508,10 @@ var DateStrip = class {
|
|
|
1265
1508
|
const next = h(doc, "button", "tnc-strip-nav tnc-strip-next", { type: "button", "aria-label": text.next, title: text.next }, "\u203A");
|
|
1266
1509
|
prev.addEventListener("click", () => this.step(-1));
|
|
1267
1510
|
next.addEventListener("click", () => this.step(1));
|
|
1511
|
+
this.todayBtn = h(doc, "button", "tnc-strip-today", { type: "button" }, text.today);
|
|
1512
|
+
this.todayBtn.addEventListener("click", (ev) => this.pick(ctx.today, ev));
|
|
1268
1513
|
this.slot = h(doc, "div", "tnc-strip-slot", { "data-tnc-no-drag": "" });
|
|
1269
|
-
head.append(this.title, prev, next, this.slot);
|
|
1514
|
+
head.append(this.title, this.todayBtn, prev, next, this.slot);
|
|
1270
1515
|
this.dows = h(doc, "div", "tnc-strip-dows", { "aria-hidden": "true" });
|
|
1271
1516
|
this.grid = h(doc, "div", "tnc-strip-grid", { role: "group" });
|
|
1272
1517
|
const handle = h(doc, "button", "tnc-strip-handle", { type: "button", tabindex: "-1", "aria-hidden": "true" });
|
|
@@ -1283,27 +1528,45 @@ var DateStrip = class {
|
|
|
1283
1528
|
}
|
|
1284
1529
|
ctx;
|
|
1285
1530
|
pick;
|
|
1531
|
+
onShown;
|
|
1286
1532
|
el;
|
|
1287
1533
|
title;
|
|
1288
1534
|
dows;
|
|
1289
1535
|
grid;
|
|
1290
1536
|
slot;
|
|
1537
|
+
todayBtn;
|
|
1291
1538
|
/** Day at the top of the list. */
|
|
1292
1539
|
focus;
|
|
1293
1540
|
/** First day of the week shown, or the month shown when expanded. */
|
|
1294
1541
|
shown;
|
|
1295
1542
|
target = null;
|
|
1543
|
+
/** Days tinted as on screen besides the focus (a time grid's range). */
|
|
1544
|
+
range = null;
|
|
1545
|
+
shownKey = "";
|
|
1296
1546
|
swipe = null;
|
|
1297
1547
|
swallowClick = false;
|
|
1298
1548
|
expanded = false;
|
|
1299
|
-
/** The list scrolled: mark
|
|
1300
|
-
setFocus(day) {
|
|
1301
|
-
|
|
1302
|
-
this.focus
|
|
1303
|
-
|
|
1304
|
-
|
|
1549
|
+
/** The list scrolled or the grid moved: mark the day and keep it in view. */
|
|
1550
|
+
setFocus(day, range = this.range) {
|
|
1551
|
+
const sameRange = range?.start === this.range?.start && range?.end === this.range?.end;
|
|
1552
|
+
if (day === this.focus && sameRange) return;
|
|
1553
|
+
this.range = range;
|
|
1554
|
+
if (day !== this.focus) {
|
|
1555
|
+
this.focus = day;
|
|
1556
|
+
if (!this.expanded) this.shown = startOfWeek(day, this.ctx.firstDay);
|
|
1557
|
+
else if (startOfMonth(day) !== this.shown) this.shown = startOfMonth(day);
|
|
1558
|
+
}
|
|
1305
1559
|
this.draw();
|
|
1306
1560
|
}
|
|
1561
|
+
/** Publishes the strip's height as `--tnc-strip-h`, for what sticks below it. */
|
|
1562
|
+
measure() {
|
|
1563
|
+
this.el.parentElement?.style.setProperty("--tnc-strip-h", `${this.el.offsetHeight}px`);
|
|
1564
|
+
}
|
|
1565
|
+
/** First day and end (exclusive) of the days on show. */
|
|
1566
|
+
window() {
|
|
1567
|
+
const days = this.days();
|
|
1568
|
+
return { start: days[0], end: days[days.length - 1] + 1 };
|
|
1569
|
+
}
|
|
1307
1570
|
setExpanded(on) {
|
|
1308
1571
|
if (on === this.expanded) return;
|
|
1309
1572
|
this.expanded = on;
|
|
@@ -1341,6 +1604,7 @@ var DateStrip = class {
|
|
|
1341
1604
|
this.title.replaceChildren(fmt.monthYear(titleDay), h(doc, "span", "tnc-strip-caret", { "aria-hidden": "true" }));
|
|
1342
1605
|
this.title.setAttribute("aria-expanded", String(this.expanded));
|
|
1343
1606
|
this.el.classList.toggle("is-expanded", this.expanded);
|
|
1607
|
+
this.todayBtn.hidden = this.focus === ctx.today;
|
|
1344
1608
|
this.dows.replaceChildren(...days.slice(0, 7).map((d) => h(doc, "span", null, null, fmt.weekdayNarrow(d))));
|
|
1345
1609
|
const focused = this.grid.contains(doc.activeElement) ? doc.activeElement.dataset.date : void 0;
|
|
1346
1610
|
const month = ymdOfDay(this.shown).month;
|
|
@@ -1357,6 +1621,7 @@ var DateStrip = class {
|
|
|
1357
1621
|
"tnc-strip-day",
|
|
1358
1622
|
...ctx.dayClasses(d),
|
|
1359
1623
|
d === this.focus ? "is-focus" : "",
|
|
1624
|
+
d !== this.focus && this.range && d >= this.range.start && d < this.range.end ? "is-in-range" : "",
|
|
1360
1625
|
count ? "has-items" : "",
|
|
1361
1626
|
this.expanded && ymdOfDay(d).month !== month ? "is-other-month" : "",
|
|
1362
1627
|
d === this.target ? "is-drop-target" : ""
|
|
@@ -1376,6 +1641,11 @@ var DateStrip = class {
|
|
|
1376
1641
|
this.grid.replaceChildren(...cells);
|
|
1377
1642
|
this.grid.setAttribute("aria-label", fmt.monthYear(titleDay));
|
|
1378
1643
|
if (focused) this.grid.querySelector(`[data-date="${focused}"]`)?.focus({ preventScroll: true });
|
|
1644
|
+
const key = `${days[0]}:${days.length}`;
|
|
1645
|
+
if (key !== this.shownKey) {
|
|
1646
|
+
this.shownKey = key;
|
|
1647
|
+
this.onShown();
|
|
1648
|
+
}
|
|
1379
1649
|
}
|
|
1380
1650
|
onClick = (ev) => {
|
|
1381
1651
|
const day = this.dayAt(ev.target);
|
|
@@ -1430,205 +1700,6 @@ var DateStrip = class {
|
|
|
1430
1700
|
}
|
|
1431
1701
|
};
|
|
1432
1702
|
|
|
1433
|
-
// src/dom/views/list.ts
|
|
1434
|
-
var LOAD_AHEAD = "0px 0px 600px 0px";
|
|
1435
|
-
var ListView = class {
|
|
1436
|
-
constructor(ctx) {
|
|
1437
|
-
this.ctx = ctx;
|
|
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 });
|
|
1442
|
-
this.scroller.append(this.body);
|
|
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 });
|
|
1470
|
-
}
|
|
1471
|
-
ctx;
|
|
1472
|
-
el;
|
|
1473
|
-
scroller;
|
|
1474
|
-
body;
|
|
1475
|
-
pools = /* @__PURE__ */ new Map();
|
|
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;
|
|
1484
|
-
itemsOn(day) {
|
|
1485
|
-
const { store, opts } = this.ctx;
|
|
1486
|
-
const out = store.spanningIn(day, day + 1, true);
|
|
1487
|
-
const seen = new Set(out.map((n) => n.id));
|
|
1488
|
-
for (const n of store.timedOn(day)) if (!seen.has(n.id)) out.push(n);
|
|
1489
|
-
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));
|
|
1490
|
-
}
|
|
1491
|
-
render(_change) {
|
|
1492
|
-
const { ctx } = this;
|
|
1493
|
-
const { doc, days } = ctx;
|
|
1494
|
-
const agenda = !!this.strip;
|
|
1495
|
-
const keep = /* @__PURE__ */ new Set();
|
|
1496
|
-
let prev = null;
|
|
1497
|
-
for (const day of days) {
|
|
1498
|
-
const items = this.itemsOn(day);
|
|
1499
|
-
if (!items.length && !agenda) continue;
|
|
1500
|
-
keep.add(day);
|
|
1501
|
-
let section = this.sections.get(day);
|
|
1502
|
-
let pool = this.pools.get(day);
|
|
1503
|
-
if (!section || !pool) {
|
|
1504
|
-
const arg = ctx.dayArg(day);
|
|
1505
|
-
const heading = h(doc, "div", ["tnc-list-day", ...ctx.dayClasses(day)].join(" "), { "data-date": arg.dateStr });
|
|
1506
|
-
const custom = ctx.opts.dayHeaderContent?.(arg);
|
|
1507
|
-
const label = custom ?? ctx.fmt.longDate(day);
|
|
1508
|
-
heading.append(ctx.opts.navLinks ? ctx.navLink(day, label, "tnc-list-day-link") : label);
|
|
1509
|
-
const rows2 = h(doc, "div", "tnc-list-rows", { role: "group" });
|
|
1510
|
-
section = h(doc, "div", "tnc-list-section", { role: "listitem", "aria-label": ctx.fmt.longDate(day), "data-date": arg.dateStr }, heading, rows2);
|
|
1511
|
-
ctx.opts.dayHeaderDidMount?.({ ...arg, el: heading });
|
|
1512
|
-
pool = new ItemPool(ctx, rows2);
|
|
1513
|
-
this.sections.set(day, section);
|
|
1514
|
-
this.pools.set(day, pool);
|
|
1515
|
-
}
|
|
1516
|
-
section.classList.toggle("is-empty", !items.length);
|
|
1517
|
-
pool.begin();
|
|
1518
|
-
for (const n of items) {
|
|
1519
|
-
pool.place(n.id, {
|
|
1520
|
-
n,
|
|
1521
|
-
day,
|
|
1522
|
-
isStart: n.startDay === day,
|
|
1523
|
-
isEnd: n.endDay === day + 1,
|
|
1524
|
-
variant: "list",
|
|
1525
|
-
mirror: false
|
|
1526
|
-
});
|
|
1527
|
-
}
|
|
1528
|
-
pool.end();
|
|
1529
|
-
const rows = pool.container;
|
|
1530
|
-
for (const n of items) {
|
|
1531
|
-
const el = pool.elementsFor(n.id)[0];
|
|
1532
|
-
if (el) rows.append(el);
|
|
1533
|
-
}
|
|
1534
|
-
if (prev ? prev.nextSibling !== section : this.body.firstChild !== section) {
|
|
1535
|
-
if (prev) prev.after(section);
|
|
1536
|
-
else this.body.prepend(section);
|
|
1537
|
-
}
|
|
1538
|
-
prev = section;
|
|
1539
|
-
}
|
|
1540
|
-
for (const [day, section] of this.sections) {
|
|
1541
|
-
if (keep.has(day)) continue;
|
|
1542
|
-
this.pools.get(day)?.clear();
|
|
1543
|
-
this.pools.delete(day);
|
|
1544
|
-
section.remove();
|
|
1545
|
-
this.sections.delete(day);
|
|
1546
|
-
}
|
|
1547
|
-
let empty = this.body.querySelector(":scope > .tnc-list-empty");
|
|
1548
|
-
if (!keep.size) {
|
|
1549
|
-
if (!empty) {
|
|
1550
|
-
empty = h(doc, "div", "tnc-list-empty", { role: "listitem" }, ctx.text.noItems);
|
|
1551
|
-
this.body.append(empty);
|
|
1552
|
-
}
|
|
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
|
-
}
|
|
1596
|
-
}
|
|
1597
|
-
showSelection(_sel) {
|
|
1598
|
-
}
|
|
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);
|
|
1610
|
-
}
|
|
1611
|
-
getScroll() {
|
|
1612
|
-
return { top: this.scroller.scrollTop, left: 0 };
|
|
1613
|
-
}
|
|
1614
|
-
setScroll(s) {
|
|
1615
|
-
this.scroller.scrollTop = s.top;
|
|
1616
|
-
}
|
|
1617
|
-
elementsFor(id) {
|
|
1618
|
-
return [...this.pools.values()].flatMap((p) => p.elementsFor(id));
|
|
1619
|
-
}
|
|
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();
|
|
1627
|
-
for (const p of this.pools.values()) p.clear();
|
|
1628
|
-
this.el.remove();
|
|
1629
|
-
}
|
|
1630
|
-
};
|
|
1631
|
-
|
|
1632
1703
|
// src/dom/views/timegrid.ts
|
|
1633
1704
|
var BAR_GAP2 = 2;
|
|
1634
1705
|
var TimeGridView = class {
|
|
@@ -1847,17 +1918,22 @@ var TimeGridView = class {
|
|
|
1847
1918
|
const pool = this.allDayPool;
|
|
1848
1919
|
pool.begin();
|
|
1849
1920
|
const hidden = new Array(days.length).fill(0);
|
|
1921
|
+
const previewLevels = new Array(days.length).fill(0);
|
|
1850
1922
|
let maxLevel = -1;
|
|
1851
1923
|
for (const p of row.placements) {
|
|
1852
1924
|
let level = p.level;
|
|
1853
1925
|
let room = Infinity;
|
|
1854
1926
|
for (let c = p.startCol; c < p.endCol; c++) room = Math.min(room, visibleLevels(c));
|
|
1855
1927
|
if (level >= room) {
|
|
1856
|
-
if (p.item.id !== ctx.draggingId) {
|
|
1928
|
+
if (!p.item.item.preview && p.item.id !== ctx.draggingId) {
|
|
1857
1929
|
for (let c = p.startCol; c < p.endCol; c++) hidden[c]++;
|
|
1858
1930
|
continue;
|
|
1859
1931
|
}
|
|
1860
|
-
|
|
1932
|
+
if (p.item.item.preview) {
|
|
1933
|
+
level = 0;
|
|
1934
|
+
for (let c = p.startCol; c < p.endCol; c++) level = Math.max(level, visibleLevels(c) + 1, previewLevels[c]);
|
|
1935
|
+
for (let c = p.startCol; c < p.endCol; c++) previewLevels[c] = level + 1;
|
|
1936
|
+
} else level = Math.max(0, room - 1);
|
|
1861
1937
|
}
|
|
1862
1938
|
maxLevel = Math.max(maxLevel, level);
|
|
1863
1939
|
const el = pool.place(p.item.id, {
|
|
@@ -1945,7 +2021,8 @@ var TimeGridView = class {
|
|
|
1945
2021
|
const hiddenByCluster = /* @__PURE__ */ new Map();
|
|
1946
2022
|
pool.begin();
|
|
1947
2023
|
for (const p of placements) {
|
|
1948
|
-
|
|
2024
|
+
const preview = p.item.item.preview || p.item.id === ctx.draggingId;
|
|
2025
|
+
if (!preview && withMore && maxStack > 0 && p.cols > maxStack && p.col >= maxStack - 1) {
|
|
1949
2026
|
const rec = hiddenByCluster.get(p.cluster) ?? { count: 0, start: p.startMin, end: p.endMin, items: [] };
|
|
1950
2027
|
rec.count++;
|
|
1951
2028
|
rec.start = Math.min(rec.start, p.startMin);
|
|
@@ -1954,7 +2031,7 @@ var TimeGridView = class {
|
|
|
1954
2031
|
hiddenByCluster.set(p.cluster, rec);
|
|
1955
2032
|
continue;
|
|
1956
2033
|
}
|
|
1957
|
-
const cols = withMore && maxStack > 0 ? Math.min(p.cols, maxStack) : p.cols;
|
|
2034
|
+
const cols = !preview && withMore && maxStack > 0 ? Math.min(p.cols, maxStack) : p.cols;
|
|
1958
2035
|
const span = Math.min(p.span, cols - p.col);
|
|
1959
2036
|
const left = p.col / cols;
|
|
1960
2037
|
let width = span / cols;
|
|
@@ -2834,6 +2911,9 @@ var Calendar = class {
|
|
|
2834
2911
|
slot = { min: 0, max: MINUTES_PER_DAY, duration: 30, snap: 30, scroll: 480 };
|
|
2835
2912
|
draggingId = null;
|
|
2836
2913
|
dragRow = null;
|
|
2914
|
+
strip = null;
|
|
2915
|
+
stripSize = null;
|
|
2916
|
+
rebuilding = false;
|
|
2837
2917
|
toolbarEl;
|
|
2838
2918
|
viewHost;
|
|
2839
2919
|
current = null;
|
|
@@ -3047,7 +3127,7 @@ var Calendar = class {
|
|
|
3047
3127
|
}
|
|
3048
3128
|
/** What is under a client point: a time slot, a day, or a timeline row and date. Null outside the view. */
|
|
3049
3129
|
hitTest(clientX, clientY) {
|
|
3050
|
-
return this.
|
|
3130
|
+
return this.hitAt(clientX, clientY);
|
|
3051
3131
|
}
|
|
3052
3132
|
/** Item elements currently rendered for an id. */
|
|
3053
3133
|
elementsFor(id) {
|
|
@@ -3089,6 +3169,9 @@ var Calendar = class {
|
|
|
3089
3169
|
this.unbindEvents();
|
|
3090
3170
|
this.current?.destroy();
|
|
3091
3171
|
this.current = null;
|
|
3172
|
+
this.stripSize?.disconnect();
|
|
3173
|
+
this.strip?.destroy();
|
|
3174
|
+
this.strip = null;
|
|
3092
3175
|
this.root.remove();
|
|
3093
3176
|
}
|
|
3094
3177
|
/* ========================================================== view context */
|
|
@@ -3119,7 +3202,7 @@ var Calendar = class {
|
|
|
3119
3202
|
return out;
|
|
3120
3203
|
}
|
|
3121
3204
|
isEditable(n, what) {
|
|
3122
|
-
if (n.id === EXTERNAL_ID || this.opts.view === "year") return false;
|
|
3205
|
+
if (n.id === EXTERNAL_ID || n.item.preview || this.opts.view === "year") return false;
|
|
3123
3206
|
if (this.opts.view === "list" && (!this.opts.listAgenda || what === "duration")) return false;
|
|
3124
3207
|
const it = n.item;
|
|
3125
3208
|
const base = it.editable ?? this.opts.editable;
|
|
@@ -3145,6 +3228,7 @@ var Calendar = class {
|
|
|
3145
3228
|
createItemEl(meta) {
|
|
3146
3229
|
const { n, variant } = meta;
|
|
3147
3230
|
const it = n.item;
|
|
3231
|
+
const preview = meta.mirror || it.preview === true;
|
|
3148
3232
|
const el = h(this.doc, "div", "tnc-item");
|
|
3149
3233
|
el.classList.add(`tnc-item--${variant}`);
|
|
3150
3234
|
if (it.kind) el.classList.add(`tnc-k-${it.kind.replace(/[^\w-]/g, "-")}`);
|
|
@@ -3154,12 +3238,12 @@ var Calendar = class {
|
|
|
3154
3238
|
if (!meta.isStart) el.classList.add("is-cont-start");
|
|
3155
3239
|
if (!meta.isEnd) el.classList.add("is-cont-end");
|
|
3156
3240
|
if (n.allDay) el.classList.add("is-all-day");
|
|
3157
|
-
if (
|
|
3241
|
+
if (preview || n.id === EXTERNAL_ID) el.classList.add("is-mirror");
|
|
3158
3242
|
if (this.conflicts.has(n.id)) el.classList.add("is-conflict");
|
|
3159
3243
|
const indicator = variant === "indicator";
|
|
3160
|
-
if (!
|
|
3244
|
+
if (!preview && !indicator && (this.isEditable(n, "start") || this.isEditable(n, "duration"))) el.classList.add("is-draggable");
|
|
3161
3245
|
if (this.draggingId === n.id) el.classList.add("is-dragging");
|
|
3162
|
-
if (this.selectedIds.has(n.id) && !
|
|
3246
|
+
if (this.selectedIds.has(n.id) && !preview) el.classList.add("is-selected");
|
|
3163
3247
|
el.classList.add(...classList(it.className));
|
|
3164
3248
|
const arg = this.itemArg(el, meta);
|
|
3165
3249
|
el.classList.add(...classList(this.opts.itemClassNames?.(arg)));
|
|
@@ -3170,7 +3254,7 @@ var Calendar = class {
|
|
|
3170
3254
|
if (it.backgroundColor) el.style.setProperty("--tnc-bg", it.backgroundColor);
|
|
3171
3255
|
if (it.borderColor) el.style.setProperty("--tnc-border", it.borderColor);
|
|
3172
3256
|
if (it.textColor) el.style.setProperty("--tnc-fg", it.textColor);
|
|
3173
|
-
if (indicator) el.setAttribute("aria-hidden", "true");
|
|
3257
|
+
if (indicator || preview) el.setAttribute("aria-hidden", "true");
|
|
3174
3258
|
else if (it.display !== "background") {
|
|
3175
3259
|
el.tabIndex = 0;
|
|
3176
3260
|
el.setAttribute("role", "button");
|
|
@@ -3187,7 +3271,7 @@ var Calendar = class {
|
|
|
3187
3271
|
} else {
|
|
3188
3272
|
el.append(this.defaultContent(meta));
|
|
3189
3273
|
}
|
|
3190
|
-
if (variant !== "list" && !indicator && it.display !== "background") this.addHandles(el, meta);
|
|
3274
|
+
if (!preview && variant !== "list" && !indicator && it.display !== "background") this.addHandles(el, meta);
|
|
3191
3275
|
this.opts.itemDidMount?.(arg);
|
|
3192
3276
|
return el;
|
|
3193
3277
|
}
|
|
@@ -3196,7 +3280,7 @@ var Calendar = class {
|
|
|
3196
3280
|
if (time) this.fillTime(time, meta.n, meta.variant, meta.day);
|
|
3197
3281
|
el.classList.toggle("is-dragging", this.draggingId === meta.n.id);
|
|
3198
3282
|
el.classList.toggle("is-conflict", this.conflicts.has(meta.n.id));
|
|
3199
|
-
el.classList.toggle("is-selected", this.selectedIds.has(meta.n.id) && !meta.mirror);
|
|
3283
|
+
el.classList.toggle("is-selected", this.selectedIds.has(meta.n.id) && !meta.mirror && !meta.n.item.preview);
|
|
3200
3284
|
}
|
|
3201
3285
|
selectedSet() {
|
|
3202
3286
|
const s = this.opts.selected;
|
|
@@ -3206,7 +3290,7 @@ var Calendar = class {
|
|
|
3206
3290
|
this.selectedIds = this.selectedSet();
|
|
3207
3291
|
for (const el of this.viewHost.querySelectorAll(".tnc-item")) {
|
|
3208
3292
|
const meta = this.meta.get(el);
|
|
3209
|
-
if (meta) el.classList.toggle("is-selected", this.selectedIds.has(meta.n.id) && !meta.mirror);
|
|
3293
|
+
if (meta) el.classList.toggle("is-selected", this.selectedIds.has(meta.n.id) && !meta.mirror && !meta.n.item.preview);
|
|
3210
3294
|
}
|
|
3211
3295
|
}
|
|
3212
3296
|
destroyItemEl(el) {
|
|
@@ -3357,12 +3441,18 @@ var Calendar = class {
|
|
|
3357
3441
|
const snap = Math.max(1, parseDurationMinutes(this.opts.snapDuration ?? null, duration));
|
|
3358
3442
|
this.slot = { min, max, duration, snap, scroll: parseDurationMinutes(this.opts.scrollTime, 480) };
|
|
3359
3443
|
if (navigated) this.listEnd = null;
|
|
3360
|
-
const
|
|
3444
|
+
const prev = this.view;
|
|
3445
|
+
this.applyRange();
|
|
3361
3446
|
this.conflicts = dependencyConflicts(this.store);
|
|
3362
3447
|
this.root.className = "tnc";
|
|
3363
3448
|
this.root.classList.add(`tnc--${this.opts.view}`, `tnc--height-${this.opts.height}`);
|
|
3364
3449
|
if (!this.opts.timeGrid) this.root.classList.add("tnc--no-time-grid");
|
|
3365
3450
|
this.renderToolbar();
|
|
3451
|
+
this.rebuilding = true;
|
|
3452
|
+
this.syncStrip(navigated);
|
|
3453
|
+
this.rebuilding = false;
|
|
3454
|
+
this.view.dataRange = this.dataRangeFor(this.view.range);
|
|
3455
|
+
const rangeChanged = rangeMoved(prev, this.view);
|
|
3366
3456
|
this.current?.destroy();
|
|
3367
3457
|
this.current = this.createView();
|
|
3368
3458
|
this.viewHost.replaceChildren(this.current.el);
|
|
@@ -3375,7 +3465,7 @@ var Calendar = class {
|
|
|
3375
3465
|
else if (this.current instanceof TimelineView) this.current.scrollToDay(this.anchor);
|
|
3376
3466
|
if (rangeChanged) this.opts.onRangeChange?.(this.view);
|
|
3377
3467
|
}
|
|
3378
|
-
/** Sets the visible days and view info from the anchor
|
|
3468
|
+
/** Sets the visible days and view info from the anchor. */
|
|
3379
3469
|
applyRange() {
|
|
3380
3470
|
const { startDay, endDay } = this.computeRange();
|
|
3381
3471
|
const hidden = new Set(this.opts.hiddenDays);
|
|
@@ -3393,10 +3483,74 @@ var Calendar = class {
|
|
|
3393
3483
|
start: new Date(toInstant(startDay, 0, this.zone)),
|
|
3394
3484
|
end: new Date(toInstant(endDay, 0, this.zone))
|
|
3395
3485
|
};
|
|
3396
|
-
|
|
3397
|
-
this.view = { type: this.opts.view, range, anchor: this.anchor, title: "" };
|
|
3486
|
+
this.view = { type: this.opts.view, range, anchor: this.anchor, title: "", dataRange: this.dataRangeFor(range) };
|
|
3398
3487
|
this.view.title = this.computeTitle(startDay, endDay);
|
|
3399
|
-
|
|
3488
|
+
}
|
|
3489
|
+
/** The range, run on to the end of the strip's days so their dots are right. */
|
|
3490
|
+
dataRangeFor(range) {
|
|
3491
|
+
const end = this.strip ? Math.max(range.endDay, this.strip.window().end) : range.endDay;
|
|
3492
|
+
if (end === range.endDay) return range;
|
|
3493
|
+
return { ...range, endDay: end, end: new Date(toInstant(end, 0, this.zone)) };
|
|
3494
|
+
}
|
|
3495
|
+
/** Adds, updates or removes the date strip for the current view. */
|
|
3496
|
+
syncStrip(navigated) {
|
|
3497
|
+
const { view, listAgenda, dateStrip } = this.opts;
|
|
3498
|
+
const list = view === "list";
|
|
3499
|
+
const want = list ? listAgenda : dateStrip && (view === "day" || view === "days" || view === "week");
|
|
3500
|
+
if (!want) {
|
|
3501
|
+
if (!this.strip) return;
|
|
3502
|
+
this.stripSize?.disconnect();
|
|
3503
|
+
this.stripSize = null;
|
|
3504
|
+
this.strip.destroy();
|
|
3505
|
+
this.strip = null;
|
|
3506
|
+
this.root.style.removeProperty("--tnc-strip-h");
|
|
3507
|
+
return;
|
|
3508
|
+
}
|
|
3509
|
+
this.root.classList.add("tnc--has-strip");
|
|
3510
|
+
const { startDay, endDay } = this.view.range;
|
|
3511
|
+
if (!this.strip) {
|
|
3512
|
+
const strip = new DateStrip(this, (day, ev) => this.pickStripDay(day, ev), startDay, () => this.stripShown());
|
|
3513
|
+
this.strip = strip;
|
|
3514
|
+
this.root.insertBefore(strip.el, this.viewHost);
|
|
3515
|
+
const RO = this.win.ResizeObserver;
|
|
3516
|
+
if (RO) {
|
|
3517
|
+
this.stripSize = new RO(() => strip.measure());
|
|
3518
|
+
this.stripSize.observe(strip.el);
|
|
3519
|
+
}
|
|
3520
|
+
}
|
|
3521
|
+
if (!list) this.strip.setFocus(startDay, { start: startDay, end: endDay });
|
|
3522
|
+
else if (navigated) this.strip.setFocus(startDay, null);
|
|
3523
|
+
this.strip.draw();
|
|
3524
|
+
}
|
|
3525
|
+
pickStripDay(day, ev) {
|
|
3526
|
+
this.dateClick(day, ev);
|
|
3527
|
+
if (this.agendaList()) {
|
|
3528
|
+
this.showListDay(day);
|
|
3529
|
+
return;
|
|
3530
|
+
}
|
|
3531
|
+
const { startDay, endDay } = this.view.range;
|
|
3532
|
+
if (this.opts.view === "week" && day >= startDay && day < endDay) return;
|
|
3533
|
+
this.anchor = day;
|
|
3534
|
+
this.rebuild(true);
|
|
3535
|
+
}
|
|
3536
|
+
/** The strip moved to other days: load theirs too. */
|
|
3537
|
+
stripShown() {
|
|
3538
|
+
if (!this.strip || this.rebuilding || !this.view) return;
|
|
3539
|
+
const next = this.dataRangeFor(this.view.range);
|
|
3540
|
+
if (next.endDay === this.view.dataRange.endDay) return;
|
|
3541
|
+
this.view = { ...this.view, dataRange: next };
|
|
3542
|
+
this.opts.onRangeChange?.(this.view);
|
|
3543
|
+
}
|
|
3544
|
+
/** What is under a point, the date strip included. */
|
|
3545
|
+
hitAt(x, y) {
|
|
3546
|
+
const strip = this.strip;
|
|
3547
|
+
if (strip) {
|
|
3548
|
+
const el = this.doc.elementFromPoint(x, y);
|
|
3549
|
+
const day = el ? strip.dayAt(el) : null;
|
|
3550
|
+
strip.markTarget(day !== null && this.draggingId ? day : null);
|
|
3551
|
+
if (day !== null) return { kind: "day", day };
|
|
3552
|
+
}
|
|
3553
|
+
return this.current?.hit(x, y) ?? null;
|
|
3400
3554
|
}
|
|
3401
3555
|
agendaList() {
|
|
3402
3556
|
return this.opts.view === "list" && this.opts.listAgenda && this.current instanceof ListView ? this.current : null;
|
|
@@ -3409,6 +3563,7 @@ var Calendar = class {
|
|
|
3409
3563
|
this.listEnd = end;
|
|
3410
3564
|
this.applyRange();
|
|
3411
3565
|
this.current?.render(null);
|
|
3566
|
+
this.strip?.draw();
|
|
3412
3567
|
this.opts.onRangeChange?.(this.view);
|
|
3413
3568
|
}
|
|
3414
3569
|
showListDay(day) {
|
|
@@ -3530,7 +3685,7 @@ var Calendar = class {
|
|
|
3530
3685
|
day: meta.day,
|
|
3531
3686
|
isStart: meta.isStart,
|
|
3532
3687
|
isEnd: meta.isEnd,
|
|
3533
|
-
isMirror: meta.mirror,
|
|
3688
|
+
isMirror: meta.mirror || meta.n.item.preview === true,
|
|
3534
3689
|
variant: meta.variant,
|
|
3535
3690
|
timeText: meta.n.allDay ? "" : end ? `${start} \u2013 ${end}` : start
|
|
3536
3691
|
};
|
|
@@ -3675,7 +3830,7 @@ var Calendar = class {
|
|
|
3675
3830
|
}
|
|
3676
3831
|
itemElFrom(target) {
|
|
3677
3832
|
const el = target?.closest?.(".tnc-item");
|
|
3678
|
-
return el && this.root.contains(el) && this.meta.has(el) ? el : null;
|
|
3833
|
+
return el && this.root.contains(el) && this.meta.has(el) && !this.meta.get(el)?.n.item.preview ? el : null;
|
|
3679
3834
|
}
|
|
3680
3835
|
onDocPointerDown = (ev) => {
|
|
3681
3836
|
const t = ev.target;
|
|
@@ -3806,7 +3961,7 @@ var Calendar = class {
|
|
|
3806
3961
|
};
|
|
3807
3962
|
/** Proposal for the current pointer position. */
|
|
3808
3963
|
track(s, ev) {
|
|
3809
|
-
const hit = this.
|
|
3964
|
+
const hit = this.hitAt(ev.clientX, ev.clientY);
|
|
3810
3965
|
if (s.mode === "external") {
|
|
3811
3966
|
this.trackExternal(s, hit);
|
|
3812
3967
|
return;
|
|
@@ -3932,7 +4087,7 @@ var Calendar = class {
|
|
|
3932
4087
|
return this.dayProposal(hit.day, hit.day + this.opts.defaultAllDayDays);
|
|
3933
4088
|
}
|
|
3934
4089
|
if (hit.kind === "day") {
|
|
3935
|
-
const dd = hit.day - (grab.kind === "
|
|
4090
|
+
const dd = hit.day - (grab.kind === "timeline" ? hit.day : grab.day);
|
|
3936
4091
|
if (o.allDay) return this.dayProposal(o.startDay + dd, o.startDay + dd + days);
|
|
3937
4092
|
const startMs2 = this.civilToMs(this.civil(o.startMs) + dd * MINUTES_PER_DAY);
|
|
3938
4093
|
const endMs = this.civilToMs(this.civil(o.endMs) + dd * MINUTES_PER_DAY);
|
|
@@ -4067,7 +4222,7 @@ var Calendar = class {
|
|
|
4067
4222
|
this.draggingId = null;
|
|
4068
4223
|
this.dragRow = null;
|
|
4069
4224
|
if (draggedId) for (const el of this.elementsFor(draggedId)) el.classList.remove("is-dragging");
|
|
4070
|
-
this.
|
|
4225
|
+
this.strip?.markTarget(null);
|
|
4071
4226
|
if (!s.active) return;
|
|
4072
4227
|
if (s.mode === "select") {
|
|
4073
4228
|
if (cancel) this.unselectSilently();
|
|
@@ -4364,6 +4519,9 @@ function scrollerOf(el, axis) {
|
|
|
4364
4519
|
}
|
|
4365
4520
|
return doc.scrollingElement;
|
|
4366
4521
|
}
|
|
4522
|
+
function rangeMoved(prev, next) {
|
|
4523
|
+
return !prev || prev.type !== next.type || prev.range.startDay !== next.range.startDay || prev.range.endDay !== next.range.endDay || prev.dataRange.endDay !== next.dataRange.endDay;
|
|
4524
|
+
}
|
|
4367
4525
|
|
|
4368
4526
|
// src/react/index.tsx
|
|
4369
4527
|
var import_jsx_runtime = require("react/jsx-runtime");
|