@nomideusz/svelte-calendar 1.1.1 → 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 +10 -5
- 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 +54 -21
- package/dist/views/planner/PlannerScroll.svelte +34 -28
- package/dist/views/planner/PlannerWeek.svelte +146 -30
- package/dist/views/shared/format.d.ts +3 -0
- package/dist/views/shared/format.js +10 -1
- package/package.json +2 -4
- package/widget/widget.js +750 -662
|
@@ -14,14 +14,15 @@
|
|
|
14
14
|
disabled) applies on drop.
|
|
15
15
|
• Now-line across today's column only; auto-scrolls to the current time.
|
|
16
16
|
-->
|
|
17
|
-
<script lang="ts">import { untrack } from "svelte";
|
|
17
|
+
<script lang="ts">import { tick, untrack } from "svelte";
|
|
18
18
|
import { useCalendarContext } from "../shared/context.svelte.js";
|
|
19
19
|
import EventContent from "../shared/EventContent.svelte";
|
|
20
20
|
import { createClock } from "../../core/clock.svelte.js";
|
|
21
21
|
import { fitParts } from "../../text-fit.js";
|
|
22
22
|
import { HOUR_MS, sod, addDaysMs, diffDays } from "../../core/time.js";
|
|
23
23
|
import { startOfWeek as sowFn, isAllDay, segmentForDay } from "../../core/time.js";
|
|
24
|
-
import { fmtHParts, fmtTime, fmtDuration, weekdayShort, weekdayLong } from "../../core/locale.js";
|
|
24
|
+
import { fmtHParts, fmtTime, fmtDuration, fmtWeekRange, weekdayShort, weekdayLong } from "../../core/locale.js";
|
|
25
|
+
import { dateLong } from "../shared/format.js";
|
|
25
26
|
let { mode = "week", mondayStart = true, locale, height = 520, events = [], style = "", focusDate, oneventclick, oneventcreate, onexternaldrop, selectedEventId = null, readOnly = false, visibleHours } = $props();
|
|
26
27
|
// ── Context ────────────────────────────────────────
|
|
27
28
|
const ctx = useCalendarContext();
|
|
@@ -79,6 +80,8 @@ function inAllDayStrip(ev) {
|
|
|
79
80
|
}
|
|
80
81
|
// ── Config ─────────────────────────────────────────
|
|
81
82
|
const HOUR_H = 64;
|
|
83
|
+
/** Half a gutter label plus a gap: what a scroll needs above an hour line */
|
|
84
|
+
const LABEL_ROOM = 18;
|
|
82
85
|
const GUTTER_W = 60;
|
|
83
86
|
const MIN_COL_W = $derived(ctx.minColumnWidth);
|
|
84
87
|
const ALLDAY_MAX = 3;
|
|
@@ -297,7 +300,8 @@ const nowY = $derived.by(() => {
|
|
|
297
300
|
const weekIsEmpty = $derived(!events.some((ev) => ev.start.getTime() < weekEndMs && ev.end.getTime() > weekStartMs));
|
|
298
301
|
// ── Auto-scroll (mount + week change) ──────────────
|
|
299
302
|
// Week with today → current time minus ~1h of context; otherwise the
|
|
300
|
-
// visibleHours start or 08:00, whichever is later.
|
|
303
|
+
// visibleHours start or 08:00, whichever is later. Lands on a whole hour,
|
|
304
|
+
// with room for its label (centred on the line) below the sticky top.
|
|
301
305
|
$effect(() => {
|
|
302
306
|
void weekStartMs;
|
|
303
307
|
const el = scrollEl;
|
|
@@ -310,7 +314,7 @@ $effect(() => {
|
|
|
310
314
|
} else {
|
|
311
315
|
targetHour = Math.max(startHour, Math.min(8, endHour - 1));
|
|
312
316
|
}
|
|
313
|
-
el.scrollTop = (targetHour - startHour) * HOUR_H;
|
|
317
|
+
el.scrollTop = Math.max(0, (Math.floor(targetHour) - startHour) * HOUR_H - LABEL_ROOM);
|
|
314
318
|
});
|
|
315
319
|
});
|
|
316
320
|
// ── Pointer coalescing ─────────────────────────────
|
|
@@ -623,6 +627,79 @@ function handleColsClick(e) {
|
|
|
623
627
|
end: new Date(startMs + durMin * 6e4)
|
|
624
628
|
});
|
|
625
629
|
}
|
|
630
|
+
// ── Keyboard ───────────────────────────────────────
|
|
631
|
+
// The day columns are one tab stop (roving): ←/→ walk the days, Enter or
|
|
632
|
+
// Space adds an event at the next slot (today) or 09:00. A focused event
|
|
633
|
+
// moves with Alt+↑/↓ (one snap) and Alt+←/→ (one shown day); Alt+Shift+↑/↓
|
|
634
|
+
// moves its end. Each key is a one-step drag through ctx.commitDrag, so
|
|
635
|
+
// Calendar validates it exactly as it does a pointer drag.
|
|
636
|
+
let focusedColMs = $state(null);
|
|
637
|
+
const tabbableColMs = $derived((dayCols.find((d) => d.ms === focusedColMs) ?? dayCols.find((d) => d.isToday) ?? dayCols[0])?.ms);
|
|
638
|
+
const colsCreate = $derived(!!oneventcreate && !readOnly);
|
|
639
|
+
function onColKeydown(e, i) {
|
|
640
|
+
if (e.target !== e.currentTarget) return;
|
|
641
|
+
const day = dayCols[i];
|
|
642
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
643
|
+
e.preventDefault();
|
|
644
|
+
if (!oneventcreate || readOnly || day.isDisabled) return;
|
|
645
|
+
const step = Math.max(1, ctx.snapInterval) / 60;
|
|
646
|
+
const want = day.isToday ? snapHour(nowFracHour, "floor") + step : 9;
|
|
647
|
+
const hour = Math.min(Math.max(want, startHour), endHour - step);
|
|
648
|
+
if (isBlockedAt(day.ms, hour)) return;
|
|
649
|
+
const startMs = atHour(day.ms, hour);
|
|
650
|
+
oneventcreate({
|
|
651
|
+
start: new Date(startMs),
|
|
652
|
+
end: new Date(startMs + Math.max(60, minDuration ?? 0) * 6e4)
|
|
653
|
+
});
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
const dir = e.key === "ArrowRight" ? 1 : e.key === "ArrowLeft" ? -1 : 0;
|
|
657
|
+
if (!dir) return;
|
|
658
|
+
e.preventDefault();
|
|
659
|
+
const next = dayCols[i + (isRtl() ? -dir : dir)];
|
|
660
|
+
if (!next) return;
|
|
661
|
+
focusedColMs = next.ms;
|
|
662
|
+
colsEl?.querySelector(`.tw-col[data-day="${next.ms}"]`)?.focus();
|
|
663
|
+
}
|
|
664
|
+
async function onEventKeydown(e, ev) {
|
|
665
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
666
|
+
e.preventDefault();
|
|
667
|
+
e.stopPropagation();
|
|
668
|
+
oneventclick?.(ev, e.currentTarget.getBoundingClientRect());
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
if (!e.altKey || !drag || readOnly || ev.data?.readOnly) return;
|
|
672
|
+
const vert = e.key === "ArrowUp" ? -1 : e.key === "ArrowDown" ? 1 : 0;
|
|
673
|
+
const horiz = e.key === "ArrowLeft" ? -1 : e.key === "ArrowRight" ? 1 : 0;
|
|
674
|
+
if (!vert && (!horiz || e.shiftKey)) return;
|
|
675
|
+
let start = ev.start.getTime();
|
|
676
|
+
let end = ev.end.getTime();
|
|
677
|
+
// Up lands on the snap line above (or the one before, when already on
|
|
678
|
+
// one); down on the line below.
|
|
679
|
+
const nudge = (ms) => vert < 0 ? snapMs(ms - 1, "floor") : snapMs(ms, "floor") + SNAP_MS;
|
|
680
|
+
if (e.shiftKey) end = nudge(end);
|
|
681
|
+
else if (vert) {
|
|
682
|
+
start = nudge(start);
|
|
683
|
+
end = start + (ev.end.getTime() - ev.start.getTime());
|
|
684
|
+
} else {
|
|
685
|
+
const fromDay = sod(start);
|
|
686
|
+
const i = dayCols.findIndex((d) => d.ms === fromDay);
|
|
687
|
+
const to = dayCols[i + (isRtl() ? -horiz : horiz)];
|
|
688
|
+
if (i < 0 || !to) return;
|
|
689
|
+
start = atHour(to.ms, hourOf(start, fromDay));
|
|
690
|
+
end = start + (ev.end.getTime() - ev.start.getTime());
|
|
691
|
+
}
|
|
692
|
+
e.preventDefault();
|
|
693
|
+
e.stopPropagation();
|
|
694
|
+
if (e.shiftKey) drag.beginResize(ev.id, "end", ev.start, ev.end);
|
|
695
|
+
else drag.beginMove(ev.id, ev.start, ev.end);
|
|
696
|
+
drag.updatePointer(new Date(start), new Date(end));
|
|
697
|
+
await commitDragCtx?.();
|
|
698
|
+
// A move to another day re-creates the block: keep the keyboard on it,
|
|
699
|
+
// or on its day when the host re-ids it (a recurring copy made one-off).
|
|
700
|
+
await tick();
|
|
701
|
+
(colsEl?.querySelector(`.tw-ev[data-event-id="${CSS.escape(ev.id)}"]`) ?? colsEl?.querySelector(`.tw-col[data-day="${sod(start)}"]`))?.focus();
|
|
702
|
+
}
|
|
626
703
|
// ── Drag-to-move (vertical = time, horizontal = day) ──
|
|
627
704
|
const DRAG_THRESHOLD = 5;
|
|
628
705
|
let evDragStartX = 0;
|
|
@@ -823,7 +900,9 @@ function onWindowKeydown(e) {
|
|
|
823
900
|
style:--tw-col-min="{MIN_COL_W}px"
|
|
824
901
|
style:height={autoHeight ? undefined : (height ? `${height}px` : '100%')}
|
|
825
902
|
role="region"
|
|
826
|
-
aria-label={L.weekAhead}
|
|
903
|
+
aria-label="{singleDay ? L.dayPlanner : L.weekAhead}{dayCols.length
|
|
904
|
+
? `, ${singleDay ? dateLong(dayCols[0].ms, locale) : fmtWeekRange(dayCols[0].ms, locale, dayCols[dayCols.length - 1].ms)}`
|
|
905
|
+
: ''}"
|
|
827
906
|
>
|
|
828
907
|
<div class="tw-scroll" bind:this={scrollEl}>
|
|
829
908
|
<div class="tw-inner" style:min-width="{innerMinWidth}px">
|
|
@@ -929,9 +1008,10 @@ function onWindowKeydown(e) {
|
|
|
929
1008
|
{/if}
|
|
930
1009
|
</div>
|
|
931
1010
|
|
|
932
|
-
{#each dayCols as day (day.ms)}
|
|
1011
|
+
{#each dayCols as day, di (day.ms)}
|
|
933
1012
|
{@const positioned = layoutByDay.get(day.ms) ?? []}
|
|
934
1013
|
{@const ghost = ghostForDay(day.ms)}
|
|
1014
|
+
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
935
1015
|
<div
|
|
936
1016
|
class="tw-col"
|
|
937
1017
|
class:tw-col--today={day.isToday}
|
|
@@ -939,6 +1019,12 @@ function onWindowKeydown(e) {
|
|
|
939
1019
|
class:tw-col--weekend={day.isWeekend}
|
|
940
1020
|
class:tw-col--disabled={day.isDisabled}
|
|
941
1021
|
data-day={day.ms}
|
|
1022
|
+
role={colsCreate ? 'group' : undefined}
|
|
1023
|
+
aria-label={colsCreate ? `${dateLong(day.ms, locale)}, ${L.createEvent}` : undefined}
|
|
1024
|
+
aria-keyshortcuts={colsCreate ? 'Enter' : undefined}
|
|
1025
|
+
tabindex={colsCreate ? (day.ms === tabbableColMs ? 0 : -1) : undefined}
|
|
1026
|
+
onfocusin={() => (focusedColMs = day.ms)}
|
|
1027
|
+
onkeydown={colsCreate ? (e) => onColKeydown(e, di) : undefined}
|
|
942
1028
|
>
|
|
943
1029
|
<!-- Blocked slot overlays -->
|
|
944
1030
|
{#if blockedSlots?.length}
|
|
@@ -981,6 +1067,7 @@ function onWindowKeydown(e) {
|
|
|
981
1067
|
class:tw-ev--past={p.ev.end.getTime() < clock.tick}
|
|
982
1068
|
class:tw-ev--short={p.height < 44}
|
|
983
1069
|
class:tw-ev--compact={p.height < 34}
|
|
1070
|
+
class:tw-ev--on-now={weekHasToday && nowY !== null && p.top < nowY && p.top + p.height > nowY}
|
|
984
1071
|
style:top="{p.top}px"
|
|
985
1072
|
style:height="{p.height}px"
|
|
986
1073
|
style:left="calc({(p.col / p.totalCols) * 100}% + 1px)"
|
|
@@ -988,11 +1075,13 @@ function onWindowKeydown(e) {
|
|
|
988
1075
|
style:--ev-color={p.ev.color ?? 'var(--dt-event, var(--dt-accent))'}
|
|
989
1076
|
role="button"
|
|
990
1077
|
tabindex="0"
|
|
1078
|
+
data-event-id={p.ev.id}
|
|
1079
|
+
aria-keyshortcuts={!readOnly && !p.ev.data?.readOnly ? 'Alt+ArrowUp Alt+ArrowDown Alt+ArrowLeft Alt+ArrowRight Alt+Shift+ArrowUp Alt+Shift+ArrowDown' : undefined}
|
|
991
1080
|
title={p.ev.title}
|
|
992
1081
|
aria-label="{p.ev.title}, {fmtTime(p.ev.start, locale)} – {fmtTime(p.ev.end, locale)}, {fmtDuration(p.ev.start, p.ev.end)}{statusText(p.ev)}{isCurrent ? ` (${L.inProgress})` : ''}"
|
|
993
1082
|
onpointerdown={(e) => onEventPointerDown(e, p.ev)}
|
|
994
1083
|
onpointerenter={() => oneventhover?.(p.ev)}
|
|
995
|
-
onkeydown={(e) =>
|
|
1084
|
+
onkeydown={(e) => onEventKeydown(e, p.ev)}
|
|
996
1085
|
|
|
997
1086
|
>
|
|
998
1087
|
<div class="tw-ev-stripe" aria-hidden="true"></div>
|
|
@@ -1053,9 +1142,8 @@ function onWindowKeydown(e) {
|
|
|
1053
1142
|
|
|
1054
1143
|
<!-- Now line (today's column only) -->
|
|
1055
1144
|
{#if day.isToday && nowY !== null}
|
|
1056
|
-
<div class="tw-now" style:top="{nowY}px" aria-label={L.currentTime}>
|
|
1057
|
-
|
|
1058
|
-
</div>
|
|
1145
|
+
<div class="tw-now" style:top="{nowY}px" aria-label={L.currentTime}></div>
|
|
1146
|
+
<span class="tw-now-dot" style:top="{nowY}px" aria-hidden="true"></span>
|
|
1059
1147
|
{/if}
|
|
1060
1148
|
</div>
|
|
1061
1149
|
{/each}
|
|
@@ -1198,8 +1286,9 @@ function onWindowKeydown(e) {
|
|
|
1198
1286
|
padding: 6px 8px 6px 0;
|
|
1199
1287
|
}
|
|
1200
1288
|
.tw-ad-gutter-lb {
|
|
1201
|
-
font: 500
|
|
1202
|
-
|
|
1289
|
+
font: 500 12px/1.2 var(--dt-sans, system-ui, sans-serif);
|
|
1290
|
+
letter-spacing: 0.08em;
|
|
1291
|
+
color: var(--dt-text-2, rgba(0, 0, 0, 0.66));
|
|
1203
1292
|
text-align: right;
|
|
1204
1293
|
}
|
|
1205
1294
|
|
|
@@ -1265,7 +1354,7 @@ function onWindowKeydown(e) {
|
|
|
1265
1354
|
min-width: 0;
|
|
1266
1355
|
}
|
|
1267
1356
|
.tw-ad-span {
|
|
1268
|
-
font: 400
|
|
1357
|
+
font: 400 12.5px/1 var(--dt-sans, system-ui, sans-serif);
|
|
1269
1358
|
font-variant-numeric: tabular-nums;
|
|
1270
1359
|
color: inherit;
|
|
1271
1360
|
opacity: 0.75;
|
|
@@ -1298,7 +1387,7 @@ function onWindowKeydown(e) {
|
|
|
1298
1387
|
border-radius: 3px;
|
|
1299
1388
|
text-align: left;
|
|
1300
1389
|
align-self: flex-start;
|
|
1301
|
-
font:
|
|
1390
|
+
font: 400 12.5px/1 var(--dt-sans, system-ui, sans-serif);
|
|
1302
1391
|
color: var(--dt-text-2, rgba(0, 0, 0, 0.54));
|
|
1303
1392
|
padding: 2px 6px;
|
|
1304
1393
|
cursor: pointer;
|
|
@@ -1427,20 +1516,27 @@ function onWindowKeydown(e) {
|
|
|
1427
1516
|
border-left: 1px solid var(--dt-border, rgba(0, 0, 0, 0.08));
|
|
1428
1517
|
box-sizing: border-box;
|
|
1429
1518
|
}
|
|
1519
|
+
.tw-col:focus-visible {
|
|
1520
|
+
outline: 2px solid var(--dt-accent, #2563eb);
|
|
1521
|
+
outline-offset: -2px;
|
|
1522
|
+
}
|
|
1430
1523
|
/* The gutter's right border already bounds the first column */
|
|
1431
1524
|
.tw-lines + .tw-col { border-left: none; }
|
|
1432
1525
|
|
|
1433
|
-
|
|
1526
|
+
/* --tw-col-wash: the column's ground, repeated under an event that the
|
|
1527
|
+
now line crosses (that event is opaque, so the line runs beneath it). */
|
|
1528
|
+
.tw-col { background: var(--tw-col-wash, transparent); }
|
|
1529
|
+
.tw-col--today { --tw-col-wash: var(--dt-today-bg, rgba(37, 99, 235, 0.04)); }
|
|
1434
1530
|
/* Dim past days with a wash, never a subtree opacity (event contrast) */
|
|
1435
1531
|
.tw-col--past {
|
|
1436
|
-
|
|
1532
|
+
--tw-col-wash: color-mix(in srgb, var(--dt-text, rgba(0, 0, 0, 0.87)) 4%, transparent);
|
|
1437
1533
|
}
|
|
1438
1534
|
/* What has already happened steps back; a cancelled block keeps its own look. */
|
|
1439
1535
|
.tw-ev--past:not(.tw-ev--cancelled) {
|
|
1440
1536
|
opacity: 0.78;
|
|
1441
1537
|
}
|
|
1442
1538
|
.tw-col--weekend:not(.tw-col--today):not(.tw-col--past) {
|
|
1443
|
-
|
|
1539
|
+
--tw-col-wash: var(--dt-weekend-bg, rgba(0, 0, 0, 0.012));
|
|
1444
1540
|
}
|
|
1445
1541
|
.tw-col--disabled {
|
|
1446
1542
|
background: repeating-linear-gradient(
|
|
@@ -1472,8 +1568,8 @@ function onWindowKeydown(e) {
|
|
|
1472
1568
|
overflow: hidden;
|
|
1473
1569
|
}
|
|
1474
1570
|
.tw-blocked-lb {
|
|
1475
|
-
font: 500
|
|
1476
|
-
color: var(--dt-text-
|
|
1571
|
+
font: 500 11px/1 var(--dt-sans, system-ui, sans-serif);
|
|
1572
|
+
color: var(--dt-text-2, rgba(0, 0, 0, 0.54));
|
|
1477
1573
|
text-transform: uppercase;
|
|
1478
1574
|
letter-spacing: 0.04em;
|
|
1479
1575
|
white-space: nowrap;
|
|
@@ -1481,24 +1577,28 @@ function onWindowKeydown(e) {
|
|
|
1481
1577
|
}
|
|
1482
1578
|
|
|
1483
1579
|
/* ─── Now line ───────────────────────────────────── */
|
|
1580
|
+
/* Under the events (z 6): the one it crosses is opaque (.tw-ev--on-now),
|
|
1581
|
+
so the line never strikes through a title. The dot stays on top. */
|
|
1484
1582
|
.tw-now {
|
|
1485
1583
|
position: absolute;
|
|
1486
1584
|
left: 0;
|
|
1487
1585
|
right: 0;
|
|
1488
1586
|
height: 0;
|
|
1489
1587
|
border-top: 2px solid var(--dt-accent, #2563eb);
|
|
1490
|
-
z-index:
|
|
1588
|
+
z-index: 5;
|
|
1491
1589
|
pointer-events: none;
|
|
1492
1590
|
transform: translateY(-1px);
|
|
1493
1591
|
}
|
|
1494
1592
|
.tw-now-dot {
|
|
1495
1593
|
position: absolute;
|
|
1496
1594
|
left: -4px;
|
|
1497
|
-
top: -5px;
|
|
1498
1595
|
width: 8px;
|
|
1499
1596
|
height: 8px;
|
|
1597
|
+
margin-top: -4px;
|
|
1500
1598
|
border-radius: 50%;
|
|
1501
1599
|
background: var(--dt-accent, #2563eb);
|
|
1600
|
+
z-index: 12;
|
|
1601
|
+
pointer-events: none;
|
|
1502
1602
|
}
|
|
1503
1603
|
|
|
1504
1604
|
/* ─── Events ─────────────────────────────────────── */
|
|
@@ -1541,6 +1641,17 @@ function onWindowKeydown(e) {
|
|
|
1541
1641
|
.tw-ev--current {
|
|
1542
1642
|
background: var(--dt-accent-dim, rgba(37, 99, 235, 0.12));
|
|
1543
1643
|
}
|
|
1644
|
+
/* An event the now line crosses lays its column's ground (and its own
|
|
1645
|
+
wash) over the paper, so the line passes beneath its text. */
|
|
1646
|
+
.tw-ev.tw-ev--on-now {
|
|
1647
|
+
background:
|
|
1648
|
+
linear-gradient(var(--tw-ev-wash, transparent), var(--tw-ev-wash, transparent)),
|
|
1649
|
+
linear-gradient(var(--tw-col-wash, transparent), var(--tw-col-wash, transparent)),
|
|
1650
|
+
var(--dt-paper, none),
|
|
1651
|
+
var(--dt-bg, #ffffff);
|
|
1652
|
+
}
|
|
1653
|
+
.tw-ev--on-now.tw-ev--current { --tw-ev-wash: var(--dt-accent-dim, rgba(37, 99, 235, 0.12)); }
|
|
1654
|
+
.tw-ev--on-now:hover { --tw-ev-wash: var(--dt-hover, rgba(0, 0, 0, 0.04)); }
|
|
1544
1655
|
.tw-ev--resizing {
|
|
1545
1656
|
z-index: 50;
|
|
1546
1657
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.25);
|
|
@@ -1592,14 +1703,20 @@ function onWindowKeydown(e) {
|
|
|
1592
1703
|
gap: 1px;
|
|
1593
1704
|
overflow: hidden;
|
|
1594
1705
|
}
|
|
1595
|
-
/* Compact (< ~35min at default zoom):
|
|
1706
|
+
/* Compact (< ~35min at default zoom): one line, "Title 9:00". The title
|
|
1707
|
+
wins the room: the time wraps onto a second line, which the block
|
|
1708
|
+
clips, before the title is cut; a title too long alone ellipsizes. */
|
|
1596
1709
|
.tw-ev--compact .tw-ev-body {
|
|
1597
1710
|
flex-direction: row;
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1711
|
+
flex-wrap: wrap;
|
|
1712
|
+
align-content: flex-start;
|
|
1713
|
+
align-items: baseline;
|
|
1714
|
+
gap: 24px 5px;
|
|
1715
|
+
padding-top: 2px;
|
|
1716
|
+
padding-bottom: 0;
|
|
1717
|
+
}
|
|
1718
|
+
.tw-ev--compact .tw-ev-title { flex: 0 1 auto; min-width: 0; }
|
|
1719
|
+
.tw-ev--compact .tw-ev-end { display: none; }
|
|
1603
1720
|
|
|
1604
1721
|
/* Title first, time under it; both one size, rank by weight and ink. */
|
|
1605
1722
|
.tw-ev-time {
|
|
@@ -1610,7 +1727,6 @@ function onWindowKeydown(e) {
|
|
|
1610
1727
|
white-space: nowrap;
|
|
1611
1728
|
flex-shrink: 0;
|
|
1612
1729
|
}
|
|
1613
|
-
.tw-ev--compact .tw-ev-time { order: 0; }
|
|
1614
1730
|
/* A narrow overlap column keeps the start time whole and drops the end. */
|
|
1615
1731
|
@container tw-ev (max-width: 104px) {
|
|
1616
1732
|
.tw-ev-end { display: none; }
|
|
@@ -1731,13 +1847,13 @@ function onWindowKeydown(e) {
|
|
|
1731
1847
|
box-shadow: none;
|
|
1732
1848
|
}
|
|
1733
1849
|
.tw-ghost-time {
|
|
1734
|
-
font:
|
|
1850
|
+
font: 500 12.5px/1.15 var(--dt-sans, system-ui, sans-serif);
|
|
1735
1851
|
font-variant-numeric: tabular-nums;
|
|
1736
1852
|
color: var(--ev-color, var(--dt-accent, #2563eb));
|
|
1737
1853
|
white-space: nowrap;
|
|
1738
1854
|
}
|
|
1739
1855
|
.tw-ghost-title {
|
|
1740
|
-
font:
|
|
1856
|
+
font: 500 12.5px/1.2 var(--dt-sans, system-ui, sans-serif);
|
|
1741
1857
|
color: var(--dt-text, rgba(0, 0, 0, 0.87));
|
|
1742
1858
|
white-space: nowrap;
|
|
1743
1859
|
overflow: hidden;
|
|
@@ -18,3 +18,6 @@ export interface TimeSlot {
|
|
|
18
18
|
events: TimelineEvent[];
|
|
19
19
|
}
|
|
20
20
|
export declare function groupIntoSlots(evts: TimelineEvent[]): TimeSlot[];
|
|
21
|
+
/** A day in full, in the locale's own order: "Monday, October 19" (en-US),
|
|
22
|
+
* "poniedziałek, 19 października" (pl-PL). For screen-reader names. */
|
|
23
|
+
export declare function dateLong(ms: number, locale?: string): string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { fmtTime as _fmtTime, fmtDuration, getLabels } from '../../core/locale.js';
|
|
1
|
+
import { fmtTime as _fmtTime, fmtDuration, getLabels, getDefaultLocale } from '../../core/locale.js';
|
|
2
2
|
export function fmtTime(d, locale) {
|
|
3
3
|
return _fmtTime(d, locale);
|
|
4
4
|
}
|
|
@@ -47,3 +47,12 @@ export function groupIntoSlots(evts) {
|
|
|
47
47
|
}
|
|
48
48
|
return slots;
|
|
49
49
|
}
|
|
50
|
+
/** A day in full, in the locale's own order: "Monday, October 19" (en-US),
|
|
51
|
+
* "poniedziałek, 19 października" (pl-PL). For screen-reader names. */
|
|
52
|
+
export function dateLong(ms, locale) {
|
|
53
|
+
return new Date(ms).toLocaleDateString(locale ?? getDefaultLocale(), {
|
|
54
|
+
weekday: 'long',
|
|
55
|
+
month: 'long',
|
|
56
|
+
day: 'numeric',
|
|
57
|
+
});
|
|
58
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nomideusz/svelte-calendar",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "A themeable Svelte 5 calendar: day, week, scrolling-week and month views, agenda lists, touch-first mobile layouts, drag-and-drop, timezones, a headless API and an embeddable web component.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -50,13 +50,11 @@
|
|
|
50
50
|
"svelte": "^5.29.0"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@sveltejs/adapter-
|
|
54
|
-
"@sveltejs/adapter-vercel": "^7.0.0",
|
|
53
|
+
"@sveltejs/adapter-node": "^6.0.0",
|
|
55
54
|
"@sveltejs/kit": "^3.0.0",
|
|
56
55
|
"@sveltejs/package": "^3.0.0",
|
|
57
56
|
"@sveltejs/vite-plugin-svelte": "^7.3.0",
|
|
58
57
|
"@types/node": "^22.20.0",
|
|
59
|
-
"@vercel/analytics": "^1.6.1",
|
|
60
58
|
"jsdom": "^29.1.1",
|
|
61
59
|
"marked": "^17.0.6",
|
|
62
60
|
"svelte": "^5.57.1",
|