@tasknotes/calendar 0.1.5 → 0.1.7
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 +31 -0
- package/dist/calendar.css +74 -19
- package/dist/cjs/core.cjs +19 -2
- package/dist/cjs/index.cjs +235 -41
- package/dist/cjs/react.cjs +235 -41
- package/dist/esm/{chunk-DVMZ5OXN.js → chunk-66A6EMCU.js} +19 -2
- package/dist/esm/{chunk-25TY2UVR.js → chunk-EDRCVOY7.js} +217 -40
- package/dist/esm/core.js +1 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/react.js +2 -2
- package/dist/types/core/layout.d.cts +5 -0
- package/dist/types/core/layout.d.ts +5 -0
- package/dist/types/dom/calendar.d.cts +5 -1
- package/dist/types/dom/calendar.d.ts +5 -1
- package/dist/types/dom/context.d.cts +1 -1
- package/dist/types/dom/context.d.ts +1 -1
- package/dist/types/dom/options.d.cts +3 -0
- package/dist/types/dom/options.d.ts +3 -0
- package/dist/types/dom/views/timegrid.d.cts +4 -0
- package/dist/types/dom/views/timegrid.d.ts +4 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.1.7
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Hosts can read collision column counts and the unobscured timed content width, choose views responsively, fill an empty list through a content hook, and translate the stack label with the `appointments` text.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Collision stacks show appointment counts and open only the matching collision group, without unrelated all-day or timed items. Narrow time grids reveal the selected day on entry and view changes, not only after Today.
|
|
14
|
+
- Crowded time-grid days use readable collision stacks without widening the entire week; stack contents retain drag-and-drop, including after moving the calendar into another window.
|
|
15
|
+
- Today reveals the current day horizontally, with persistent day navigation when the grid is wider than its pane.
|
|
16
|
+
- Overflow dialogs clamp to the visible viewport, reposition on resize and scroll, keep Close reachable, and provide touch-sized rows and keyboard navigation.
|
|
17
|
+
- Collision stacks honour `eventMaxStack`, and overflow triggers stay stable across rerenders, so Escape returns focus to them.
|
|
18
|
+
|
|
19
|
+
## 0.1.6
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- Time grids give tracked (`lane: "actual"`) items their narrow strip only where they overlap planned items. Elsewhere, planned and tracked items use the full column width, as before the side lane existed. The strip's width is the `--tnc-actual-w` custom property (default 22%).
|
|
24
|
+
- Reported by @RumiaKitinari in [TaskNotes issue 2494](https://github.com/callumalpass/tasknotes/issues/2494) and @mgreen3 in [TaskNotes issue 2498](https://github.com/callumalpass/tasknotes/issues/2498).
|
|
25
|
+
- The toolbar's previous and next buttons draw their chevrons as icons, which are larger and clearer than the ‹ › glyphs.
|
|
26
|
+
|
|
3
27
|
## 0.1.5
|
|
4
28
|
|
|
5
29
|
### Added
|
package/README.md
CHANGED
|
@@ -69,8 +69,15 @@ to 00:00–24:00 in 30-minute slots, initially scrolled to 08:00.
|
|
|
69
69
|
`text` supplies translated labels; `itemContent` and related hooks supply
|
|
70
70
|
custom DOM. The exported `CalendarOptions<D>` type documents the full API.
|
|
71
71
|
|
|
72
|
+
`toolbar.viewDisplay` defaults to `"auto"`: full view-name buttons become a
|
|
73
|
+
native select when the toolbar cannot fit them. Use `"buttons"` or `"select"`
|
|
74
|
+
to choose a fixed presentation. The select uses the same translated view names
|
|
75
|
+
and changes views through the same navigation lifecycle as the buttons.
|
|
76
|
+
|
|
72
77
|
### Month and list
|
|
73
78
|
|
|
79
|
+
- `listEmptyContent()` may return a host-owned DOM node for an empty list, such as an explanation and recovery action. The default translated empty message remains when omitted. The node is removed when the list gains items.
|
|
80
|
+
|
|
74
81
|
- `monthDisplay: "dots"` draws every item in a month cell as a small dot (`--tnc-dot-size`, `--tnc-dot-gap`) that wraps across the cell, with "+N" when they do not fit; `dayMaxEvents` caps the dots (`false` lets cells grow). The day cell is the tap target: dots ignore the pointer, are `aria-hidden`, get no content hook, and the cell is labelled "<date>, N items" (`text.itemCount`). Items in dots have `variant: "indicator"`; colour them with `color` or `itemDidMount` setting `--tnc-c`.
|
|
75
82
|
- **"+N more" height**: day rows (and the time grid's all-day row) make room for the link at its rendered height, so a host may give it a 44px touch target (`--tnc-more-h`, or any min-height) without overflowing the row.
|
|
76
83
|
- `listStart: "date" | "week"`: the list view starts at the date (default; FullCalendar `duration: { days: n }` lists, as the TaskNotes plugin and app used) or at the start of its week (FullCalendar's stock `listWeek`); week-aligned lists step by whole weeks.
|
|
@@ -105,6 +112,30 @@ custom DOM. The exported `CalendarOptions<D>` type documents the full API.
|
|
|
105
112
|
Set `--tnc-theme-*` variables on the host (they inherit into the calendar)
|
|
106
113
|
or on `.tnc`. The stylesheet is scoped to `.tnc` and does not inject CSS.
|
|
107
114
|
|
|
115
|
+
Time grids collapse crowded collision groups into labelled, expandable stacks
|
|
116
|
+
instead of widening every day. `--tnc-event-min-width` sets the readable event
|
|
117
|
+
width in pixels (default 128). Keep the day minimum independent of collisions:
|
|
118
|
+
|
|
119
|
+
```css
|
|
120
|
+
.my-calendar .tnc-tg {
|
|
121
|
+
--tnc-col-min: 128px;
|
|
122
|
+
--tnc-event-min-width: 128px;
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
When the day minimums exceed a narrow viewport, persistent day buttons reveal
|
|
127
|
+
offscreen dates, and Today reveals the current day. Headers, all-day rows and
|
|
128
|
+
timed columns stay aligned. `--tnc-collision-columns` remains available as
|
|
129
|
+
diagnostic metadata (respecting `eventMaxStack`), not a week-wide width multiplier.
|
|
130
|
+
Overflow dialogs clamp to the visible calendar and viewport, scroll vertically,
|
|
131
|
+
and support Tab, arrow keys, Home, End and Escape. Timed items dragged from
|
|
132
|
+
overflow start at their actual time rather than the slot underneath the dialog.
|
|
133
|
+
|
|
134
|
+
Timed events also expose `--tnc-event-readable-width`, the percentage of their
|
|
135
|
+
width not covered by the next collision column. Hosts can use it as the
|
|
136
|
+
`max-width` of `.tnc-item-main` to keep text readable while preserving the user's
|
|
137
|
+
overlapping event backgrounds. It is `100%` for non-overlapping placements.
|
|
138
|
+
|
|
108
139
|
```css
|
|
109
140
|
.my-calendar {
|
|
110
141
|
--tnc-theme-bg: #181818;
|
package/dist/calendar.css
CHANGED
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
--tnc-off: color-mix(in srgb, var(--tnc-text) 3.5%, transparent);
|
|
24
24
|
--tnc-shadow: 0 8px 24px rgb(0 0 0 / 0.16);
|
|
25
25
|
--tnc-radius: 6px;
|
|
26
|
+
/* Time-grid share for tracked ("actual") items where they overlap planned ones. */
|
|
27
|
+
--tnc-actual-w: 22%;
|
|
26
28
|
--tnc-font-size: 12px;
|
|
27
29
|
--tnc-small: 11px;
|
|
28
30
|
--tnc-bar-h: 20px;
|
|
@@ -131,9 +133,21 @@
|
|
|
131
133
|
}
|
|
132
134
|
.tnc-btn-prev,
|
|
133
135
|
.tnc-btn-next {
|
|
136
|
+
display: inline-flex;
|
|
137
|
+
align-items: center;
|
|
138
|
+
justify-content: center;
|
|
134
139
|
min-width: 28px;
|
|
135
140
|
padding: 3px 6px;
|
|
136
|
-
|
|
141
|
+
}
|
|
142
|
+
.tnc-btn-prev svg,
|
|
143
|
+
.tnc-btn-next svg {
|
|
144
|
+
width: 16px;
|
|
145
|
+
height: 16px;
|
|
146
|
+
fill: none;
|
|
147
|
+
stroke: currentColor;
|
|
148
|
+
stroke-width: 2;
|
|
149
|
+
stroke-linecap: round;
|
|
150
|
+
stroke-linejoin: round;
|
|
137
151
|
}
|
|
138
152
|
.tnc-btn:focus-visible,
|
|
139
153
|
.tnc-navlink:focus-visible,
|
|
@@ -453,6 +467,35 @@
|
|
|
453
467
|
overflow: auto;
|
|
454
468
|
overscroll-behavior: contain;
|
|
455
469
|
}
|
|
470
|
+
.tnc-tg-day-navigation {
|
|
471
|
+
display: flex;
|
|
472
|
+
flex-wrap: wrap;
|
|
473
|
+
gap: 2px;
|
|
474
|
+
flex: none;
|
|
475
|
+
}
|
|
476
|
+
.tnc-tg-day-navigation[hidden] {
|
|
477
|
+
display: none;
|
|
478
|
+
}
|
|
479
|
+
.tnc-tg-day-navigation .is-offscreen {
|
|
480
|
+
text-decoration: underline;
|
|
481
|
+
text-underline-offset: 3px;
|
|
482
|
+
}
|
|
483
|
+
.tnc-more--collision {
|
|
484
|
+
display: flex;
|
|
485
|
+
align-items: start;
|
|
486
|
+
gap: 4px;
|
|
487
|
+
min-height: 40px;
|
|
488
|
+
white-space: normal;
|
|
489
|
+
text-align: start;
|
|
490
|
+
}
|
|
491
|
+
.tnc-collision-title {
|
|
492
|
+
flex: 1;
|
|
493
|
+
min-width: 0;
|
|
494
|
+
display: -webkit-box;
|
|
495
|
+
-webkit-box-orient: vertical;
|
|
496
|
+
-webkit-line-clamp: 3;
|
|
497
|
+
overflow: hidden;
|
|
498
|
+
}
|
|
456
499
|
.tnc--height-auto .tnc-tg-scroll {
|
|
457
500
|
overflow: visible;
|
|
458
501
|
}
|
|
@@ -612,31 +655,19 @@
|
|
|
612
655
|
.tnc-col.is-today {
|
|
613
656
|
background-color: color-mix(in srgb, var(--tnc-accent) 5%, transparent);
|
|
614
657
|
}
|
|
658
|
+
/* Items position themselves within the column; see inLane in timegrid.ts. */
|
|
615
659
|
.tnc-col-plan,
|
|
616
660
|
.tnc-col-bg,
|
|
617
661
|
.tnc-col-actual {
|
|
618
662
|
position: absolute;
|
|
619
|
-
|
|
620
|
-
bottom: 0;
|
|
621
|
-
}
|
|
622
|
-
.tnc-col-bg {
|
|
623
|
-
left: 0;
|
|
624
|
-
right: 0;
|
|
625
|
-
}
|
|
626
|
-
.tnc-col-plan {
|
|
627
|
-
left: 1px;
|
|
628
|
-
right: 8px;
|
|
663
|
+
inset: 0;
|
|
629
664
|
}
|
|
665
|
+
/* Above the plan layer, so it must not swallow clicks meant for plan items. */
|
|
630
666
|
.tnc-col-actual {
|
|
631
|
-
|
|
632
|
-
right: 2px;
|
|
633
|
-
width: 22%;
|
|
634
|
-
}
|
|
635
|
-
.tnc-col.has-actual .tnc-col-plan {
|
|
636
|
-
right: calc(22% + 4px);
|
|
667
|
+
pointer-events: none;
|
|
637
668
|
}
|
|
638
|
-
.tnc-col
|
|
639
|
-
|
|
669
|
+
.tnc-col-actual > * {
|
|
670
|
+
pointer-events: auto;
|
|
640
671
|
}
|
|
641
672
|
.tnc-now {
|
|
642
673
|
position: absolute;
|
|
@@ -1618,6 +1649,7 @@
|
|
|
1618
1649
|
outline: none;
|
|
1619
1650
|
}
|
|
1620
1651
|
.tnc-popover-head {
|
|
1652
|
+
flex: none;
|
|
1621
1653
|
display: flex;
|
|
1622
1654
|
align-items: center;
|
|
1623
1655
|
justify-content: space-between;
|
|
@@ -1627,6 +1659,7 @@
|
|
|
1627
1659
|
font-weight: 650;
|
|
1628
1660
|
}
|
|
1629
1661
|
.tnc-popover-close {
|
|
1662
|
+
flex: none;
|
|
1630
1663
|
min-width: 28px;
|
|
1631
1664
|
min-height: 28px;
|
|
1632
1665
|
border: 0;
|
|
@@ -1640,6 +1673,7 @@
|
|
|
1640
1673
|
background: var(--tnc-surface-3);
|
|
1641
1674
|
}
|
|
1642
1675
|
.tnc-popover-body {
|
|
1676
|
+
min-height: 0;
|
|
1643
1677
|
display: flex;
|
|
1644
1678
|
flex-direction: column;
|
|
1645
1679
|
gap: 2px;
|
|
@@ -1650,6 +1684,27 @@
|
|
|
1650
1684
|
position: relative;
|
|
1651
1685
|
flex: none;
|
|
1652
1686
|
}
|
|
1687
|
+
.tnc-popover-title {
|
|
1688
|
+
min-width: 0;
|
|
1689
|
+
overflow: hidden;
|
|
1690
|
+
text-overflow: ellipsis;
|
|
1691
|
+
white-space: nowrap;
|
|
1692
|
+
}
|
|
1693
|
+
.tnc-popover-body .tnc-item {
|
|
1694
|
+
height: auto;
|
|
1695
|
+
min-height: 40px;
|
|
1696
|
+
}
|
|
1697
|
+
.tnc-popover-body .tnc-item-main {
|
|
1698
|
+
min-height: 40px;
|
|
1699
|
+
}
|
|
1700
|
+
.tnc-popover-body .tnc-item-title {
|
|
1701
|
+
white-space: normal;
|
|
1702
|
+
overflow-wrap: anywhere;
|
|
1703
|
+
}
|
|
1704
|
+
.tnc-popover-close:focus-visible {
|
|
1705
|
+
outline: 2px solid var(--tnc-accent);
|
|
1706
|
+
outline-offset: -2px;
|
|
1707
|
+
}
|
|
1653
1708
|
|
|
1654
1709
|
@media (prefers-reduced-motion: reduce) {
|
|
1655
1710
|
.tnc * {
|
package/dist/cjs/core.cjs
CHANGED
|
@@ -520,7 +520,7 @@ function packTimed(segs, minMinutes, order = defaultOrder) {
|
|
|
520
520
|
colEnds.push(0);
|
|
521
521
|
}
|
|
522
522
|
colEnds[col] = s.end;
|
|
523
|
-
const p = { ...s, col, cols: 1, span: 1, cluster: clusterIndex };
|
|
523
|
+
const p = { ...s, col, cols: 1, span: 1, cluster: clusterIndex, split: false };
|
|
524
524
|
cluster.push(p);
|
|
525
525
|
out.push(p);
|
|
526
526
|
clusterEnd = Math.max(clusterEnd, s.end);
|
|
@@ -540,7 +540,24 @@ function layoutTimedDay(items, day, zone, minMinutes, order, window = [0, MINUTE
|
|
|
540
540
|
else if (n.item.lane === "actual") actual.push(seg);
|
|
541
541
|
else plan.push(seg);
|
|
542
542
|
}
|
|
543
|
-
|
|
543
|
+
const planPlaced = packTimed(plan, minMinutes, order);
|
|
544
|
+
const actualPlaced = packTimed(actual, minMinutes, order);
|
|
545
|
+
markSplit(planPlaced, actualPlaced, minMinutes);
|
|
546
|
+
markSplit(actualPlaced, planPlaced, minMinutes);
|
|
547
|
+
return { day, plan: planPlaced, actual: actualPlaced, background };
|
|
548
|
+
}
|
|
549
|
+
function markSplit(lane, other, minMinutes) {
|
|
550
|
+
if (!lane.length || !other.length) return;
|
|
551
|
+
const end = (p) => Math.max(p.endMin, p.startMin + minMinutes);
|
|
552
|
+
const ranges = /* @__PURE__ */ new Map();
|
|
553
|
+
for (const p of lane) {
|
|
554
|
+
const r = ranges.get(p.cluster);
|
|
555
|
+
if (r) r[1] = Math.max(r[1], end(p));
|
|
556
|
+
else ranges.set(p.cluster, [p.startMin, end(p)]);
|
|
557
|
+
}
|
|
558
|
+
const split = /* @__PURE__ */ new Set();
|
|
559
|
+
for (const [cluster, [s, e]] of ranges) if (other.some((o) => o.startMin < e && s < end(o))) split.add(cluster);
|
|
560
|
+
for (const p of lane) p.split = split.has(p.cluster);
|
|
544
561
|
}
|
|
545
562
|
function layoutRow(items, days, order = defaultOrder) {
|
|
546
563
|
const first = days[0];
|