@nomideusz/svelte-calendar 1.2.0 → 1.3.0
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/README.md +3 -3
- package/dist/calendar/Calendar.svelte +202 -296
- package/dist/theme/presets.d.ts +2 -2
- package/dist/theme/presets.js +2 -2
- package/dist/views/agenda/AgendaDay.svelte +162 -278
- package/dist/views/agenda/AgendaWeek.svelte +113 -191
- package/dist/views/mobile/MobileDay.svelte +51 -32
- package/dist/views/mobile/MobileWeek.svelte +17 -10
- package/dist/views/month/MonthGrid.svelte +50 -19
- package/dist/views/planner/PlannerScroll.svelte +33 -27
- package/dist/views/planner/PlannerWeek.svelte +52 -25
- package/dist/views/shared/format.d.ts +3 -0
- package/dist/views/shared/format.js +10 -1
- package/package.json +1 -1
- package/widget/widget.js +671 -653
|
@@ -12,6 +12,7 @@ import { createClock } from "../../core/clock.svelte.js";
|
|
|
12
12
|
import { HOUR_MS, sod, addDaysMs, diffDays, isAllDay, segmentForDay } from "../../core/time.js";
|
|
13
13
|
import { fmtHParts, fmtTime } from "../../core/locale.js";
|
|
14
14
|
import { createSwipe } from "./swipe.js";
|
|
15
|
+
import { dateLong } from "../shared/format.js";
|
|
15
16
|
let { height = null, events = [], style = "", locale, focusDate, oneventclick, oneventcreate, selectedEventId = null, readOnly = false, visibleHours } = $props();
|
|
16
17
|
// ── Context ────────────────────────────────────────
|
|
17
18
|
const ctx = useCalendarContext();
|
|
@@ -475,7 +476,7 @@ $effect(() => {
|
|
|
475
476
|
style={style || undefined}
|
|
476
477
|
style:height={autoHeight ? undefined : (height ? `${height}px` : '100%')}
|
|
477
478
|
role="region"
|
|
478
|
-
aria-label={L.dayPlanner}
|
|
479
|
+
aria-label="{L.dayPlanner}, {dateLong(dayMs, locale)}"
|
|
479
480
|
ontouchstart={swipe.ontouchstart}
|
|
480
481
|
ontouchmove={swipe.ontouchmove}
|
|
481
482
|
ontouchend={swipe.ontouchend}
|
|
@@ -588,11 +589,11 @@ $effect(() => {
|
|
|
588
589
|
<div class="mb-ev-stripe"></div>
|
|
589
590
|
<div class="mb-ev-body">
|
|
590
591
|
<EventContent event={p.ev}>
|
|
591
|
-
<!-- Line thresholds must track real content height (title
|
|
592
|
-
time
|
|
592
|
+
<!-- Line thresholds must track real content height (title 19 +
|
|
593
|
+
time 14 + sub 14 + loc 13 + tags 19 + padding 8) — showing
|
|
593
594
|
a line the block can't hold clips text at both ends. -->
|
|
594
595
|
<span class="mb-ev-title">{p.ev.title}</span>
|
|
595
|
-
{#if p.height >
|
|
596
|
+
{#if p.height > 36}
|
|
596
597
|
<span class="mb-ev-time">{fmtTime(p.ev.start, locale)} – {fmtTime(p.ev.end, locale)}</span>
|
|
597
598
|
{/if}
|
|
598
599
|
{#if p.ev.subtitle && p.height > 56}
|
|
@@ -753,7 +754,7 @@ $effect(() => {
|
|
|
753
754
|
}
|
|
754
755
|
|
|
755
756
|
.mb-allday-title {
|
|
756
|
-
font: 500
|
|
757
|
+
font: 500 13.5px/1 var(--dt-sans, system-ui, sans-serif);
|
|
757
758
|
color: inherit;
|
|
758
759
|
white-space: nowrap;
|
|
759
760
|
max-width: 100px;
|
|
@@ -762,14 +763,14 @@ $effect(() => {
|
|
|
762
763
|
}
|
|
763
764
|
|
|
764
765
|
.mb-allday-span {
|
|
765
|
-
font: 400
|
|
766
|
+
font: 400 12.5px/1 var(--dt-sans, system-ui, sans-serif);
|
|
766
767
|
font-variant-numeric: tabular-nums;
|
|
767
768
|
color: inherit;
|
|
768
769
|
opacity: 0.75;
|
|
769
770
|
}
|
|
770
771
|
|
|
771
772
|
.mb-allday-more {
|
|
772
|
-
font:
|
|
773
|
+
font: 400 12.5px/1 var(--dt-sans, system-ui, sans-serif);
|
|
773
774
|
color: var(--dt-text-2, rgba(0, 0, 0, 0.54));
|
|
774
775
|
white-space: nowrap;
|
|
775
776
|
flex-shrink: 0;
|
|
@@ -846,8 +847,8 @@ $effect(() => {
|
|
|
846
847
|
visibility: hidden;
|
|
847
848
|
}
|
|
848
849
|
.mb-hour-label small {
|
|
849
|
-
font-size:
|
|
850
|
-
font-weight:
|
|
850
|
+
font-size: 10px;
|
|
851
|
+
font-weight: 500;
|
|
851
852
|
vertical-align: top;
|
|
852
853
|
margin-left: 1px;
|
|
853
854
|
letter-spacing: 0;
|
|
@@ -859,7 +860,7 @@ $effect(() => {
|
|
|
859
860
|
start at — content-box pushed digits flush under the event edge */
|
|
860
861
|
box-sizing: border-box;
|
|
861
862
|
flex-shrink: 0;
|
|
862
|
-
font:
|
|
863
|
+
font: 400 15px/1 var(--dt-sans, system-ui, sans-serif);
|
|
863
864
|
font-stretch: 90%;
|
|
864
865
|
font-variant-numeric: tabular-nums;
|
|
865
866
|
letter-spacing: -0.02em;
|
|
@@ -892,18 +893,20 @@ $effect(() => {
|
|
|
892
893
|
left: 44px;
|
|
893
894
|
top: 50%;
|
|
894
895
|
transform: translateY(-50%);
|
|
895
|
-
font: 500
|
|
896
|
+
font: 500 11px/1 var(--dt-sans, system-ui, sans-serif);
|
|
896
897
|
color: var(--dt-text-2, rgba(0, 0, 0, 0.54));
|
|
897
898
|
text-transform: uppercase;
|
|
898
899
|
letter-spacing: 0.04em;
|
|
899
900
|
}
|
|
900
901
|
|
|
901
902
|
/* ─── Now line ───────────────────────────────────── */
|
|
903
|
+
/* No stacking context of its own: the plate and the dot sit above the
|
|
904
|
+
events, the line under them (the event it crosses is opaque), so the
|
|
905
|
+
line never strikes through a title. */
|
|
902
906
|
.mb-now {
|
|
903
907
|
position: absolute;
|
|
904
908
|
left: 0;
|
|
905
909
|
right: 0;
|
|
906
|
-
z-index: 10;
|
|
907
910
|
display: flex;
|
|
908
911
|
align-items: center;
|
|
909
912
|
pointer-events: none;
|
|
@@ -911,18 +914,32 @@ $effect(() => {
|
|
|
911
914
|
|
|
912
915
|
/* The clock is a reversed plate on the accent, as in the desktop gutter. */
|
|
913
916
|
.mb-now-label {
|
|
917
|
+
position: relative;
|
|
918
|
+
z-index: 10;
|
|
914
919
|
width: 40px;
|
|
915
920
|
box-sizing: border-box;
|
|
916
921
|
flex-shrink: 0;
|
|
917
922
|
padding: 0 4px 0 0;
|
|
918
923
|
text-align: right;
|
|
924
|
+
white-space: nowrap;
|
|
925
|
+
}
|
|
926
|
+
.mb-now-label::after {
|
|
927
|
+
content: '';
|
|
928
|
+
position: absolute;
|
|
929
|
+
right: -4px;
|
|
930
|
+
top: 50%;
|
|
931
|
+
width: 8px;
|
|
932
|
+
height: 8px;
|
|
933
|
+
margin-top: -4px;
|
|
934
|
+
border-radius: 50%;
|
|
935
|
+
background: var(--dt-accent, #2563eb);
|
|
919
936
|
}
|
|
920
937
|
.mb-now-label span {
|
|
921
938
|
display: inline-block;
|
|
922
939
|
padding: 2px 3px;
|
|
923
940
|
border-radius: 0;
|
|
924
941
|
background: var(--dt-accent, #2563eb);
|
|
925
|
-
font:
|
|
942
|
+
font: 500 11px/1.2 var(--dt-sans, system-ui, sans-serif);
|
|
926
943
|
font-variant-numeric: tabular-nums;
|
|
927
944
|
color: var(--dt-btn-text, #ffffff);
|
|
928
945
|
}
|
|
@@ -933,17 +950,7 @@ $effect(() => {
|
|
|
933
950
|
border-top: 2px solid var(--dt-accent, #2563eb);
|
|
934
951
|
box-shadow: none;
|
|
935
952
|
position: relative;
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
.mb-now-line::before {
|
|
939
|
-
content: '';
|
|
940
|
-
position: absolute;
|
|
941
|
-
left: -4px;
|
|
942
|
-
top: -4px;
|
|
943
|
-
width: 10px;
|
|
944
|
-
height: 10px;
|
|
945
|
-
border-radius: 50%;
|
|
946
|
-
background: var(--dt-accent, #2563eb);
|
|
953
|
+
z-index: 4;
|
|
947
954
|
}
|
|
948
955
|
|
|
949
956
|
/* ─── Events ─────────────────────────────────────── */
|
|
@@ -990,8 +997,20 @@ $effect(() => {
|
|
|
990
997
|
.mb-event--selected {
|
|
991
998
|
box-shadow: inset 0 0 0 1px var(--ev-color);
|
|
992
999
|
}
|
|
993
|
-
|
|
994
|
-
|
|
1000
|
+
/* The event in progress is the one the now line crosses: its wash is
|
|
1001
|
+
laid over the paper, opaque, so the line runs beneath its text. */
|
|
1002
|
+
.mb-event.mb-event--current {
|
|
1003
|
+
background:
|
|
1004
|
+
linear-gradient(var(--dt-accent-dim, rgba(37, 99, 235, 0.12)), var(--dt-accent-dim, rgba(37, 99, 235, 0.12))),
|
|
1005
|
+
var(--dt-paper, none),
|
|
1006
|
+
var(--dt-bg, #ffffff);
|
|
1007
|
+
}
|
|
1008
|
+
.mb-event.mb-event--current:hover,
|
|
1009
|
+
.mb-event.mb-event--current:active {
|
|
1010
|
+
background:
|
|
1011
|
+
linear-gradient(var(--dt-hover, rgba(0, 0, 0, 0.04)), var(--dt-hover, rgba(0, 0, 0, 0.04))),
|
|
1012
|
+
var(--dt-paper, none),
|
|
1013
|
+
var(--dt-bg, #ffffff);
|
|
995
1014
|
}
|
|
996
1015
|
/* Printed states: a cancelled class is struck through; a tentative or
|
|
997
1016
|
limited one is set in grey with a dashed bar — never a bare opacity. */
|
|
@@ -1092,7 +1111,7 @@ $effect(() => {
|
|
|
1092
1111
|
pointer-events: none;
|
|
1093
1112
|
}
|
|
1094
1113
|
.mb-create-ghost-time {
|
|
1095
|
-
font:
|
|
1114
|
+
font: 500 11px/1 var(--dt-sans, system-ui, sans-serif);
|
|
1096
1115
|
font-variant-numeric: tabular-nums;
|
|
1097
1116
|
color: var(--dt-accent, #2563eb);
|
|
1098
1117
|
padding: 4px 8px;
|
|
@@ -1125,13 +1144,13 @@ $effect(() => {
|
|
|
1125
1144
|
}
|
|
1126
1145
|
|
|
1127
1146
|
.mb-ev-time {
|
|
1128
|
-
font: 400
|
|
1147
|
+
font: 400 14px/1 var(--dt-sans, system-ui, sans-serif);
|
|
1129
1148
|
font-variant-numeric: tabular-nums;
|
|
1130
|
-
color: var(--dt-text
|
|
1149
|
+
color: var(--dt-text, rgba(0, 0, 0, 0.87));
|
|
1131
1150
|
}
|
|
1132
1151
|
|
|
1133
1152
|
.mb-ev-sub {
|
|
1134
|
-
font: 400
|
|
1153
|
+
font: 400 12.5px/1.1 var(--dt-sans, system-ui, sans-serif);
|
|
1135
1154
|
color: var(--dt-text-2, rgba(0, 0, 0, 0.54));
|
|
1136
1155
|
white-space: nowrap;
|
|
1137
1156
|
overflow: hidden;
|
|
@@ -1139,7 +1158,7 @@ $effect(() => {
|
|
|
1139
1158
|
}
|
|
1140
1159
|
|
|
1141
1160
|
.mb-ev-loc {
|
|
1142
|
-
font: 400
|
|
1161
|
+
font: 400 12.5px/1 var(--dt-sans, system-ui, sans-serif);
|
|
1143
1162
|
color: var(--dt-text-2, rgba(0, 0, 0, 0.54));
|
|
1144
1163
|
white-space: nowrap;
|
|
1145
1164
|
overflow: hidden;
|
|
@@ -1178,7 +1197,7 @@ $effect(() => {
|
|
|
1178
1197
|
position: absolute;
|
|
1179
1198
|
top: 4px;
|
|
1180
1199
|
right: 4px;
|
|
1181
|
-
font:
|
|
1200
|
+
font: 500 11px/1 var(--dt-sans, system-ui, sans-serif);
|
|
1182
1201
|
color: var(--ev-color, var(--dt-accent));
|
|
1183
1202
|
background: color-mix(in srgb, var(--ev-color, var(--dt-accent)) 15%, transparent);
|
|
1184
1203
|
padding: 2px 5px;
|
|
@@ -10,7 +10,8 @@ import EventContent from "../shared/EventContent.svelte";
|
|
|
10
10
|
import { createClock } from "../../core/clock.svelte.js";
|
|
11
11
|
import { sod, addDaysMs, isAllDay } from "../../core/time.js";
|
|
12
12
|
import { startOfWeek as sowFn } from "../../core/time.js";
|
|
13
|
-
import { fmtTime as _fmtTime, weekdayShort } from "../../core/locale.js";
|
|
13
|
+
import { fmtTime as _fmtTime, fmtWeekRange, weekdayShort } from "../../core/locale.js";
|
|
14
|
+
import { dateLong } from "../shared/format.js";
|
|
14
15
|
import { createSwipe } from "./swipe.js";
|
|
15
16
|
let { mondayStart = true, locale, height = null, events = [], style = "", focusDate, oneventclick, selectedEventId = null } = $props();
|
|
16
17
|
// ── Context ────────────────────────────────────────
|
|
@@ -165,7 +166,9 @@ function handleDayKeydown(e, dayMs) {
|
|
|
165
166
|
style={style || undefined}
|
|
166
167
|
style:height={autoHeight ? undefined : (height ? `${height}px` : '100%')}
|
|
167
168
|
role="region"
|
|
168
|
-
aria-label={L.weekAhead}
|
|
169
|
+
aria-label="{L.weekAhead}{dayCells.length
|
|
170
|
+
? `, ${fmtWeekRange(dayCells[0].ms, locale, dayCells[dayCells.length - 1].ms)}`
|
|
171
|
+
: ''}"
|
|
169
172
|
ontouchstart={swipe.ontouchstart}
|
|
170
173
|
ontouchmove={swipe.ontouchmove}
|
|
171
174
|
ontouchend={swipe.ontouchend}
|
|
@@ -195,7 +198,7 @@ function handleDayKeydown(e, dayMs) {
|
|
|
195
198
|
disabled={cell.isDisabled}
|
|
196
199
|
onclick={() => handleDayTap(cell.ms)}
|
|
197
200
|
onkeydown={(e) => handleDayKeydown(e, cell.ms)}
|
|
198
|
-
aria-label="{cell.
|
|
201
|
+
aria-label="{dateLong(cell.ms, locale)}{cell.isToday ? `, ${L.today}` : ''}"
|
|
199
202
|
></button>
|
|
200
203
|
<!-- Date column -->
|
|
201
204
|
<div class="mw-date">
|
|
@@ -322,6 +325,7 @@ function handleDayKeydown(e, dayMs) {
|
|
|
322
325
|
background: color-mix(in srgb, var(--dt-text, rgba(0, 0, 0, 0.87)) 3%, transparent);
|
|
323
326
|
}
|
|
324
327
|
.mw-row--past :global(.mw-ev-title),
|
|
328
|
+
.mw-row--past :global(.mw-ev-time),
|
|
325
329
|
.mw-row--past .mw-day-num {
|
|
326
330
|
color: var(--dt-text-2, rgba(0, 0, 0, 0.54));
|
|
327
331
|
}
|
|
@@ -366,8 +370,8 @@ function handleDayKeydown(e, dayMs) {
|
|
|
366
370
|
}
|
|
367
371
|
|
|
368
372
|
.mw-day-name {
|
|
369
|
-
font:
|
|
370
|
-
letter-spacing: 0.
|
|
373
|
+
font: 500 12px/1 var(--dt-sans, system-ui, sans-serif);
|
|
374
|
+
letter-spacing: 0.08em;
|
|
371
375
|
text-transform: uppercase;
|
|
372
376
|
color: var(--dt-text-2, rgba(0, 0, 0, 0.54));
|
|
373
377
|
}
|
|
@@ -375,13 +379,16 @@ function handleDayKeydown(e, dayMs) {
|
|
|
375
379
|
color: var(--dt-accent, #2563eb);
|
|
376
380
|
}
|
|
377
381
|
|
|
382
|
+
/* The date is the typography: large and regular, narrowed like the planner's */
|
|
378
383
|
.mw-day-num {
|
|
379
|
-
font:
|
|
384
|
+
font: 400 22px/1 var(--dt-sans, system-ui, sans-serif);
|
|
385
|
+
font-stretch: 90%;
|
|
386
|
+
font-variant-numeric: tabular-nums;
|
|
387
|
+
letter-spacing: -0.02em;
|
|
380
388
|
color: var(--dt-text, rgba(0, 0, 0, 0.87));
|
|
381
389
|
}
|
|
382
390
|
.mw-day-num--today {
|
|
383
391
|
color: var(--dt-accent, #2563eb);
|
|
384
|
-
font-weight: 500;
|
|
385
392
|
}
|
|
386
393
|
|
|
387
394
|
/* ─── Events column ──────────────────────────────── */
|
|
@@ -502,15 +509,15 @@ function handleDayKeydown(e, dayMs) {
|
|
|
502
509
|
}
|
|
503
510
|
|
|
504
511
|
.mw-ev-time {
|
|
505
|
-
font: 400
|
|
512
|
+
font: 400 14px/1 var(--dt-sans, system-ui, sans-serif);
|
|
506
513
|
font-variant-numeric: tabular-nums;
|
|
507
|
-
color: var(--dt-text
|
|
514
|
+
color: var(--dt-text, rgba(0, 0, 0, 0.87));
|
|
508
515
|
white-space: nowrap;
|
|
509
516
|
flex-shrink: 0;
|
|
510
517
|
}
|
|
511
518
|
|
|
512
519
|
.mw-ev-more {
|
|
513
|
-
font:
|
|
520
|
+
font: 400 12.5px/1 var(--dt-sans, system-ui, sans-serif);
|
|
514
521
|
color: var(--dt-text-2, rgba(0, 0, 0, 0.54));
|
|
515
522
|
padding: 2px 4px;
|
|
516
523
|
min-height: 32px;
|
|
@@ -28,6 +28,39 @@ const todayMs = $derived(clock.today);
|
|
|
28
28
|
// cell tap (day drill-down) carry the interaction.
|
|
29
29
|
const dotsMode = $derived(isMobile);
|
|
30
30
|
const MAX_CHIPS = 3;
|
|
31
|
+
// How many chips a cell has room for, measured, never more than MAX_CHIPS.
|
|
32
|
+
// A cell that overflows must also show its "+N more", so it gets one row
|
|
33
|
+
// fewer when that row would not fit beside its chips: a hidden event is
|
|
34
|
+
// always counted, never clipped away. Auto height and dots grow instead.
|
|
35
|
+
let fitAll = $state(MAX_CHIPS);
|
|
36
|
+
let fitMore = $state(MAX_CHIPS);
|
|
37
|
+
function measureFit() {
|
|
38
|
+
const cell = bodyEl?.querySelector(".mg-cell");
|
|
39
|
+
const chip = bodyEl?.querySelector(".mg-chip");
|
|
40
|
+
if (!cell || !chip) return;
|
|
41
|
+
const num = cell.querySelector(".mg-daynum");
|
|
42
|
+
const chips = cell.querySelector(".mg-chips");
|
|
43
|
+
const cs = getComputedStyle(cell);
|
|
44
|
+
const gap = chips ? parseFloat(getComputedStyle(chips).rowGap) || 0 : 0;
|
|
45
|
+
const room = cell.clientHeight - parseFloat(cs.paddingTop) - parseFloat(cs.paddingBottom) - (num?.offsetHeight ?? 0) - (parseFloat(cs.rowGap) || 0);
|
|
46
|
+
const chipH = chip.offsetHeight;
|
|
47
|
+
const moreH = bodyEl?.querySelector(".mg-more")?.offsetHeight ?? chipH;
|
|
48
|
+
if (!chipH) return;
|
|
49
|
+
fitAll = Math.min(MAX_CHIPS, Math.floor((room + gap) / (chipH + gap)));
|
|
50
|
+
fitMore = Math.max(0, Math.min(MAX_CHIPS, Math.floor((room - moreH) / (chipH + gap))));
|
|
51
|
+
}
|
|
52
|
+
$effect(() => {
|
|
53
|
+
if (!bodyEl || autoHeight || dotsMode) {
|
|
54
|
+
fitAll = fitMore = MAX_CHIPS;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
void events;
|
|
58
|
+
measureFit();
|
|
59
|
+
const ro = new ResizeObserver(measureFit);
|
|
60
|
+
ro.observe(bodyEl);
|
|
61
|
+
document.fonts?.ready.then(measureFit);
|
|
62
|
+
return () => ro.disconnect();
|
|
63
|
+
});
|
|
31
64
|
const range = $derived(viewState?.range);
|
|
32
65
|
const focusMonth = $derived((focusDate ?? new Date()).getMonth());
|
|
33
66
|
$effect(() => {
|
|
@@ -143,6 +176,7 @@ const weeks = $derived.by(() => {
|
|
|
143
176
|
const date = new Date(cellMs);
|
|
144
177
|
const jsDay = date.getDay();
|
|
145
178
|
const dayEvents = eventsForDay(cellMs);
|
|
179
|
+
const shown = dayEvents.length <= fitAll ? dayEvents.length : fitMore;
|
|
146
180
|
row.push({
|
|
147
181
|
ms: cellMs,
|
|
148
182
|
date,
|
|
@@ -151,9 +185,9 @@ const weeks = $derived.by(() => {
|
|
|
151
185
|
isToday: cellMs === todayMs,
|
|
152
186
|
isWeekend: jsDay === 0 || jsDay === 6,
|
|
153
187
|
isDisabled: disabledSet.has(cellMs),
|
|
154
|
-
chips: dayEvents.slice(0,
|
|
188
|
+
chips: dayEvents.slice(0, shown),
|
|
155
189
|
all: dayEvents,
|
|
156
|
-
overflow:
|
|
190
|
+
overflow: dayEvents.length - shown
|
|
157
191
|
});
|
|
158
192
|
}
|
|
159
193
|
rows.push(row);
|
|
@@ -300,13 +334,11 @@ function chipTime(e) {
|
|
|
300
334
|
}
|
|
301
335
|
.mg-head-cell {
|
|
302
336
|
padding: 6px 8px;
|
|
303
|
-
font
|
|
337
|
+
font: 500 12px/1.2 var(--dt-sans, system-ui, sans-serif);
|
|
304
338
|
font-variant-numeric: tabular-nums;
|
|
305
|
-
font-size: 11px;
|
|
306
|
-
font-weight: 600;
|
|
307
339
|
text-transform: uppercase;
|
|
308
|
-
letter-spacing: 0.
|
|
309
|
-
color: var(--dt-text-
|
|
340
|
+
letter-spacing: 0.08em;
|
|
341
|
+
color: var(--dt-text-2, rgba(0, 0, 0, 0.66));
|
|
310
342
|
}
|
|
311
343
|
|
|
312
344
|
.mg-body {
|
|
@@ -342,12 +374,13 @@ function chipTime(e) {
|
|
|
342
374
|
.mg-cell--weekend {
|
|
343
375
|
background: var(--dt-weekend-bg, rgba(0, 0, 0, 0.02));
|
|
344
376
|
}
|
|
345
|
-
/* Dim only the day number for adjacent-month cells — their events stay
|
|
377
|
+
/* Dim only the day number for adjacent-month cells — their events stay
|
|
378
|
+
legible. text-2, not text-3: a 22px numeral still needs 4.5:1. */
|
|
346
379
|
.mg-cell--out {
|
|
347
380
|
background: var(--dt-surface, transparent);
|
|
348
381
|
}
|
|
349
382
|
.mg-cell--out .mg-daynum {
|
|
350
|
-
color: var(--dt-text-
|
|
383
|
+
color: var(--dt-text-2, rgba(0, 0, 0, 0.66));
|
|
351
384
|
}
|
|
352
385
|
.mg-cell--today {
|
|
353
386
|
background: var(--dt-today-bg);
|
|
@@ -406,9 +439,7 @@ function chipTime(e) {
|
|
|
406
439
|
background: none;
|
|
407
440
|
padding: 2px 4px;
|
|
408
441
|
border-radius: 0;
|
|
409
|
-
font
|
|
410
|
-
font-size: 12px;
|
|
411
|
-
line-height: 1.3;
|
|
442
|
+
font: 400 12.5px/1.3 var(--dt-sans, system-ui, sans-serif);
|
|
412
443
|
color: var(--dt-text);
|
|
413
444
|
cursor: pointer;
|
|
414
445
|
min-width: 0;
|
|
@@ -442,9 +473,7 @@ function chipTime(e) {
|
|
|
442
473
|
}
|
|
443
474
|
.mg-chip-time {
|
|
444
475
|
flex: none;
|
|
445
|
-
font-family: var(--dt-sans, system-ui, sans-serif);
|
|
446
476
|
font-variant-numeric: tabular-nums;
|
|
447
|
-
font-size: 11px;
|
|
448
477
|
color: var(--dt-text-2);
|
|
449
478
|
}
|
|
450
479
|
.mg-chip-title {
|
|
@@ -458,9 +487,8 @@ function chipTime(e) {
|
|
|
458
487
|
background: none;
|
|
459
488
|
padding: 1px 4px;
|
|
460
489
|
border-radius: 0;
|
|
461
|
-
font
|
|
490
|
+
font: 400 12.5px/1.3 var(--dt-sans, system-ui, sans-serif);
|
|
462
491
|
font-variant-numeric: tabular-nums;
|
|
463
|
-
font-size: 11px;
|
|
464
492
|
color: var(--dt-text-2);
|
|
465
493
|
cursor: pointer;
|
|
466
494
|
}
|
|
@@ -505,14 +533,17 @@ function chipTime(e) {
|
|
|
505
533
|
.mg--dots .mg-chip-time {
|
|
506
534
|
display: none;
|
|
507
535
|
}
|
|
536
|
+
/* Marks are circles, like every dot in the calendar */
|
|
508
537
|
.mg--dots .mg-chip-dot {
|
|
509
|
-
|
|
510
|
-
|
|
538
|
+
align-self: center;
|
|
539
|
+
width: 7px;
|
|
540
|
+
height: 7px;
|
|
541
|
+
border-radius: 50%;
|
|
511
542
|
}
|
|
512
543
|
.mg--dots .mg-more {
|
|
513
544
|
padding: 0 3px;
|
|
514
545
|
min-height: 0;
|
|
515
546
|
align-self: center;
|
|
516
|
-
font-size:
|
|
547
|
+
font-size: 11px;
|
|
517
548
|
}
|
|
518
549
|
</style>
|
|
@@ -64,6 +64,7 @@ const MAX_EVENTS_SHOWN = 5;
|
|
|
64
64
|
// mount re-centres once the events have landed.
|
|
65
65
|
const CHIP_H = 22;
|
|
66
66
|
const CHIP_GAP = 3;
|
|
67
|
+
const NOW_H = 18;
|
|
67
68
|
/** The day's hours at their floor height — a quiet week is short, a busy
|
|
68
69
|
* day grows its row. Chips sit on this axis by start time. */
|
|
69
70
|
const TIME_H = 120;
|
|
@@ -122,26 +123,31 @@ function timeTop(hoursIntoDay) {
|
|
|
122
123
|
return Math.min(Math.max(f, 0), 1) * (TIME_H - CHIP_H);
|
|
123
124
|
}
|
|
124
125
|
/** Each chip at its start time, or just under the chip before it when that
|
|
125
|
-
* one is in the way: the order is time, the height is time, nothing overlaps.
|
|
126
|
-
|
|
126
|
+
* one is in the way: the order is time, the height is time, nothing overlaps.
|
|
127
|
+
* On today the now line is one more item on the axis: at the clock's
|
|
128
|
+
* height, under every chip that has started (pushed-down chips would read
|
|
129
|
+
* as still to come), and it pushes the chips to come down by its plate,
|
|
130
|
+
* so the plate never covers a chip's text. `end` is where the last item ends. */
|
|
131
|
+
function chipTops(list, dayMs, withNow = false) {
|
|
127
132
|
const tops = [];
|
|
128
133
|
let min = 0;
|
|
134
|
+
let nowY = null;
|
|
135
|
+
const placeNow = () => {
|
|
136
|
+
nowY = Math.max(timeTop(hourOf(clock.tick, dayMs)), min - CHIP_GAP + NOW_H / 2);
|
|
137
|
+
min = nowY + NOW_H / 2 + CHIP_GAP;
|
|
138
|
+
};
|
|
129
139
|
for (const ev of list) {
|
|
140
|
+
if (withNow && nowY === null && ev.start.getTime() > clock.tick) placeNow();
|
|
130
141
|
const top = Math.max(timeTop(hourOf(ev.start.getTime(), dayMs)), min);
|
|
131
142
|
tops.push(top);
|
|
132
143
|
min = top + CHIP_H + CHIP_GAP;
|
|
133
144
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
let y = timeTop(hourOf(clock.tick, dayMs));
|
|
141
|
-
list.forEach((ev, i) => {
|
|
142
|
-
if (ev.start.getTime() <= clock.tick) y = Math.max(y, tops[i] + CHIP_H + CHIP_GAP / 2);
|
|
143
|
-
});
|
|
144
|
-
return y;
|
|
145
|
+
if (withNow && nowY === null) placeNow();
|
|
146
|
+
return {
|
|
147
|
+
tops,
|
|
148
|
+
nowY,
|
|
149
|
+
end: Math.max(0, min - CHIP_GAP)
|
|
150
|
+
};
|
|
145
151
|
}
|
|
146
152
|
const _initMs = untrack(() => sod(focusDate?.getTime() ?? Date.now()));
|
|
147
153
|
let internalFocusMs = $state(_initMs);
|
|
@@ -836,8 +842,9 @@ function onCellKeydown(e, ms) {
|
|
|
836
842
|
{@const previewTimedEvent = dragPreviewTimedForDay(day.ms)}
|
|
837
843
|
{@const previewSegment = dragPreviewSegmentForDay(day.ms)}
|
|
838
844
|
{@const shownTimed = visibleTimedEvents.slice(0, isExpanded ? visibleTimedEvents.length : timedCap)}
|
|
839
|
-
{@const
|
|
840
|
-
{@const
|
|
845
|
+
{@const axis = chipTops(shownTimed, day.ms, day.isToday)}
|
|
846
|
+
{@const tops = axis.tops}
|
|
847
|
+
{@const nowY = axis.nowY}
|
|
841
848
|
{@const inset = day.monthLabel ? MONTH_INSET : 0}
|
|
842
849
|
<div
|
|
843
850
|
class="wg-cell"
|
|
@@ -935,6 +942,7 @@ function onCellKeydown(e, ms) {
|
|
|
935
942
|
<button
|
|
936
943
|
type="button"
|
|
937
944
|
class="wg-ev-more"
|
|
945
|
+
style:margin-top="{axis.end - (tops.length ? tops[tops.length - 1] + CHIP_H : 0) + 2}px"
|
|
938
946
|
aria-expanded={isExpanded}
|
|
939
947
|
onclick={(e) => { e.stopPropagation(); expandedCells[day.ms] = !isExpanded; }}
|
|
940
948
|
>{isExpanded ? L.showLess : L.nMore(hiddenCount)}</button>
|
|
@@ -1112,7 +1120,7 @@ function onCellKeydown(e, ms) {
|
|
|
1112
1120
|
transform: rotate(180deg);
|
|
1113
1121
|
text-align: end; /* the rotation puts the end at the top */
|
|
1114
1122
|
padding: 6px 0;
|
|
1115
|
-
font:
|
|
1123
|
+
font: 500 12px / 22px var(--dt-sans, system-ui, sans-serif);
|
|
1116
1124
|
letter-spacing: 0.08em;
|
|
1117
1125
|
white-space: nowrap;
|
|
1118
1126
|
overflow: hidden;
|
|
@@ -1184,7 +1192,7 @@ function onCellKeydown(e, ms) {
|
|
|
1184
1192
|
}
|
|
1185
1193
|
|
|
1186
1194
|
.wg-blocked-label {
|
|
1187
|
-
font: 500
|
|
1195
|
+
font: 500 11px/1 var(--dt-sans, system-ui, sans-serif);
|
|
1188
1196
|
color: var(--dt-text-2, rgba(0, 0, 0, 0.54));
|
|
1189
1197
|
text-transform: uppercase;
|
|
1190
1198
|
letter-spacing: 0.04em;
|
|
@@ -1208,8 +1216,8 @@ function onCellKeydown(e, ms) {
|
|
|
1208
1216
|
}
|
|
1209
1217
|
|
|
1210
1218
|
.wg-day-wd {
|
|
1211
|
-
font:
|
|
1212
|
-
letter-spacing: 0.
|
|
1219
|
+
font: 500 12px / 1 var(--dt-sans, system-ui, sans-serif);
|
|
1220
|
+
letter-spacing: 0.08em;
|
|
1213
1221
|
text-transform: uppercase;
|
|
1214
1222
|
color: var(--dt-text-3, rgba(0, 0, 0, 0.38));
|
|
1215
1223
|
}
|
|
@@ -1219,7 +1227,8 @@ function onCellKeydown(e, ms) {
|
|
|
1219
1227
|
}
|
|
1220
1228
|
|
|
1221
1229
|
.wg-day-num {
|
|
1222
|
-
font:
|
|
1230
|
+
font: 400 15px / 1 var(--dt-sans, system-ui, sans-serif);
|
|
1231
|
+
font-variant-numeric: tabular-nums;
|
|
1223
1232
|
color: var(--dt-text, rgba(0, 0, 0, 0.87));
|
|
1224
1233
|
}
|
|
1225
1234
|
|
|
@@ -1286,7 +1295,7 @@ function onCellKeydown(e, ms) {
|
|
|
1286
1295
|
}
|
|
1287
1296
|
|
|
1288
1297
|
.wg-ad-title {
|
|
1289
|
-
font: 500
|
|
1298
|
+
font: 500 13.5px / 1.1 var(--dt-sans, system-ui, sans-serif);
|
|
1290
1299
|
color: inherit;
|
|
1291
1300
|
white-space: nowrap;
|
|
1292
1301
|
overflow: hidden;
|
|
@@ -1335,7 +1344,8 @@ function onCellKeydown(e, ms) {
|
|
|
1335
1344
|
border-top: 2px solid var(--dt-accent, #2563eb);
|
|
1336
1345
|
}
|
|
1337
1346
|
.wg-now-time {
|
|
1338
|
-
font: 500
|
|
1347
|
+
font: 500 11px / 1.2 var(--dt-sans, system-ui, sans-serif);
|
|
1348
|
+
font-variant-numeric: tabular-nums;
|
|
1339
1349
|
padding: 2px 4px;
|
|
1340
1350
|
border-radius: 0;
|
|
1341
1351
|
background: var(--dt-accent, #2563eb);
|
|
@@ -1348,9 +1358,6 @@ function onCellKeydown(e, ms) {
|
|
|
1348
1358
|
.wg-drop::after {
|
|
1349
1359
|
border-top-style: dashed;
|
|
1350
1360
|
}
|
|
1351
|
-
.wg-drop .wg-now-time {
|
|
1352
|
-
font-weight: 600;
|
|
1353
|
-
}
|
|
1354
1361
|
|
|
1355
1362
|
.wg-ev {
|
|
1356
1363
|
position: relative; /* above the blocked bands */
|
|
@@ -1456,12 +1463,11 @@ function onCellKeydown(e, ms) {
|
|
|
1456
1463
|
border-radius: 0;
|
|
1457
1464
|
text-align: left;
|
|
1458
1465
|
align-self: flex-start;
|
|
1459
|
-
font:
|
|
1466
|
+
font: 400 12.5px / 1 var(--dt-sans, system-ui, sans-serif);
|
|
1460
1467
|
color: var(--dt-text-2, rgba(0, 0, 0, 0.54));
|
|
1461
1468
|
padding: 2px 8px;
|
|
1462
1469
|
cursor: pointer;
|
|
1463
1470
|
flex-shrink: 0;
|
|
1464
|
-
margin-top: 2px;
|
|
1465
1471
|
}
|
|
1466
1472
|
|
|
1467
1473
|
.wg-ev-more:hover {
|