@nomideusz/svelte-calendar 1.2.0 → 1.4.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 +143 -93
- package/dist/adapters/mapped.d.ts +7 -7
- package/dist/adapters/mapped.js +5 -2
- package/dist/adapters/memory.d.ts +7 -6
- package/dist/adapters/memory.js +3 -4
- package/dist/adapters/recurring.d.ts +6 -6
- package/dist/adapters/recurring.js +12 -14
- package/dist/calendar/Calendar.svelte +226 -304
- package/dist/calendar/Calendar.svelte.d.ts +1 -1
- package/dist/primitives/DayHeader.svelte +7 -7
- package/dist/primitives/EmptySlot.svelte +6 -6
- package/dist/primitives/EventBlock.svelte +19 -19
- package/dist/primitives/FloatingPanel.svelte +4 -4
- package/dist/primitives/NowIndicator.svelte +5 -5
- package/dist/primitives/TimeGutter.svelte +4 -4
- package/dist/theme/auto.d.ts +5 -0
- package/dist/theme/auto.js +16 -3
- package/dist/theme/presets.d.ts +2 -2
- package/dist/theme/presets.js +2 -2
- package/dist/views/agenda/AgendaDay.svelte +207 -323
- package/dist/views/agenda/AgendaWeek.svelte +145 -222
- package/dist/views/mobile/MobileDay.svelte +79 -60
- package/dist/views/mobile/MobileWeek.svelte +48 -38
- package/dist/views/month/MonthGrid.svelte +92 -36
- package/dist/views/planner/PlannerScroll.svelte +78 -72
- package/dist/views/planner/PlannerWeek.svelte +145 -90
- package/dist/views/shared/context.svelte.d.ts +2 -0
- package/dist/views/shared/context.svelte.js +1 -0
- 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 +816 -725
|
@@ -376,6 +376,7 @@ setContext("calendar", {
|
|
|
376
376
|
get labels() {
|
|
377
377
|
return mergedLabels;
|
|
378
378
|
},
|
|
379
|
+
announce,
|
|
379
380
|
// Load range (read/write)
|
|
380
381
|
get loadRange() {
|
|
381
382
|
return viewLoadRange;
|
|
@@ -479,6 +480,45 @@ const dateLabel = $derived.by(() => {
|
|
|
479
480
|
year: "numeric"
|
|
480
481
|
});
|
|
481
482
|
});
|
|
483
|
+
// ── Polite announcements (views say why an action did nothing) ──
|
|
484
|
+
// Shares the header's status line; a note lasts until the range changes.
|
|
485
|
+
let note = $state(null);
|
|
486
|
+
function announce(text) {
|
|
487
|
+
// A repeated message must change the text node to be read again.
|
|
488
|
+
note = {
|
|
489
|
+
text: note?.text === text ? `${text}\u00a0` : text,
|
|
490
|
+
label: dateLabel
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
const noteText = $derived(note && note.label === dateLabel ? note.text : "");
|
|
494
|
+
// ── Period title (the built-in header's big line) ──
|
|
495
|
+
// Names the month the user is looking at: "October 2026"; a week across two
|
|
496
|
+
// months reads "Oct – Nov 2026"; a day gives its date. `dateLabel` keeps the
|
|
497
|
+
// exact range for the header snippet and the screen-reader status line.
|
|
498
|
+
const periodTitle = $derived.by(() => {
|
|
499
|
+
if (!showDates) return "";
|
|
500
|
+
const loc = locale ?? getDefaultLocale();
|
|
501
|
+
if (viewState.mode === "day") {
|
|
502
|
+
return viewState.focusDate.toLocaleDateString(loc, {
|
|
503
|
+
weekday: useMobile ? "short" : "long",
|
|
504
|
+
month: "long",
|
|
505
|
+
day: "numeric",
|
|
506
|
+
year: "numeric"
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
const s = viewState.mode === "week" ? viewState.range.start : viewState.focusDate;
|
|
510
|
+
const e = viewState.mode === "week" ? new Date(viewState.range.end.getTime() - 1) : s;
|
|
511
|
+
if (s.getMonth() === e.getMonth() && s.getFullYear() === e.getFullYear()) {
|
|
512
|
+
return s.toLocaleDateString(loc, {
|
|
513
|
+
month: "long",
|
|
514
|
+
year: "numeric"
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
return new Intl.DateTimeFormat(loc, {
|
|
518
|
+
month: "short",
|
|
519
|
+
year: "numeric"
|
|
520
|
+
}).formatRange(s, e).replace(/[ ]/g, " ");
|
|
521
|
+
});
|
|
482
522
|
// Which modes are available?
|
|
483
523
|
const modes = $derived.by(() => {
|
|
484
524
|
const g = new Set(desktopViews.map((v) => v.mode));
|
|
@@ -623,70 +663,50 @@ const navCtx = $derived({
|
|
|
623
663
|
{#if headerSnippet}
|
|
624
664
|
{@render headerSnippet(headerCtx)}
|
|
625
665
|
|
|
626
|
-
<!-- ───
|
|
627
|
-
{:else if
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
<
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
{#if !titleBelow}
|
|
650
|
-
<span class="cal-m-title" role="status" aria-live="polite" aria-atomic="true">{dateLabel}</span>
|
|
666
|
+
<!-- ─── Built-in header: the period as the title, then view switch, then Today ‹ › ─── -->
|
|
667
|
+
{:else if showNavigation || (showModePills && modes.length > 1) || dateLabel}
|
|
668
|
+
<div class="cal-hd" class:cal-hd--mobile={useMobile} class:cal-hd--stack={stackHeader}>
|
|
669
|
+
<!-- The visible title names the month; the status line announces the exact range -->
|
|
670
|
+
<span class="cal-hd-title" aria-hidden="true">{periodTitle}</span>
|
|
671
|
+
<span class="cal-sr" role="status" aria-live="polite" aria-atomic="true">{noteText || dateLabel}</span>
|
|
672
|
+
{#if showModePills && !useMobile && labelsForMode.length > 1}
|
|
673
|
+
<!-- View-type switcher (e.g. Planner ↔ Agenda) for the current mode -->
|
|
674
|
+
<div class="cal-pills cal-pills--labels" role="radiogroup" aria-label={L.viewMode}>
|
|
675
|
+
{#each labelsForMode as label (label)}
|
|
676
|
+
<button
|
|
677
|
+
type="button"
|
|
678
|
+
class="cal-pill"
|
|
679
|
+
class:cal-pill--active={activeView?.label === label}
|
|
680
|
+
role="radio"
|
|
681
|
+
aria-checked={activeView?.label === label}
|
|
682
|
+
onclick={() => switchLabel(label)}
|
|
683
|
+
>
|
|
684
|
+
{viewLabel(label)}
|
|
685
|
+
</button>
|
|
686
|
+
{/each}
|
|
687
|
+
</div>
|
|
651
688
|
{/if}
|
|
652
|
-
|
|
653
|
-
|
|
689
|
+
{#if showModePills && modes.length > 1}
|
|
690
|
+
<div class="cal-pills" role="radiogroup" aria-label={L.viewMode}>
|
|
691
|
+
{#each modes as g (g)}
|
|
692
|
+
<button
|
|
693
|
+
type="button"
|
|
694
|
+
class="cal-pill"
|
|
695
|
+
class:cal-pill--active={viewState.mode === g}
|
|
696
|
+
role="radio"
|
|
697
|
+
aria-checked={viewState.mode === g}
|
|
698
|
+
onclick={() => switchMode(g)}
|
|
699
|
+
>
|
|
700
|
+
{g === 'day' ? L.day : g === 'week' ? L.week : L.month}
|
|
701
|
+
</button>
|
|
702
|
+
{/each}
|
|
703
|
+
</div>
|
|
704
|
+
{/if}
|
|
705
|
+
<div class="cal-hd-nav">
|
|
654
706
|
{#if navigationSnippet}
|
|
655
707
|
{@render navigationSnippet(navCtx)}
|
|
656
708
|
{:else if showNavigation}
|
|
657
709
|
<!-- Always rendered (disabled on today) so navigating never shifts layout -->
|
|
658
|
-
<button
|
|
659
|
-
type="button"
|
|
660
|
-
class="cal-m-today"
|
|
661
|
-
onclick={() => viewState.goToday()}
|
|
662
|
-
disabled={viewIncludesToday}
|
|
663
|
-
title={L.goToToday}
|
|
664
|
-
>
|
|
665
|
-
{L.today}
|
|
666
|
-
</button>
|
|
667
|
-
<button type="button" class="cal-m-nav" onclick={() => viewState.prev()} aria-label={viewState.mode === 'day' ? L.previousDay : viewState.mode === 'month' ? L.previousMonth : L.previousWeek}>
|
|
668
|
-
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" width="16" height="16" aria-hidden="true"><path d="M10 3 5 8l5 5"/></svg>
|
|
669
|
-
</button>
|
|
670
|
-
<button type="button" class="cal-m-nav" onclick={() => viewState.next()} aria-label={viewState.mode === 'day' ? L.nextDay : viewState.mode === 'month' ? L.nextMonth : L.nextWeek}>
|
|
671
|
-
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" width="16" height="16" aria-hidden="true"><path d="M6 3l5 5-5 5"/></svg>
|
|
672
|
-
</button>
|
|
673
|
-
{/if}
|
|
674
|
-
</div>
|
|
675
|
-
</div>
|
|
676
|
-
{#if titleBelow}
|
|
677
|
-
<div class="cal-m-titlebar">
|
|
678
|
-
<span class="cal-m-title" role="status" aria-live="polite" aria-atomic="true">{dateLabel}</span>
|
|
679
|
-
</div>
|
|
680
|
-
{/if}
|
|
681
|
-
|
|
682
|
-
<!-- ─── Desktop header ─── -->
|
|
683
|
-
{:else if showNavigation || (showModePills && modes.length > 1) || dateLabel}
|
|
684
|
-
<div class="cal-hd">
|
|
685
|
-
<div class="cal-hd-side">
|
|
686
|
-
{#if navigationSnippet}
|
|
687
|
-
{@render navigationSnippet(navCtx)}
|
|
688
|
-
{:else if showNavigation}
|
|
689
|
-
<!-- Always rendered (disabled on today) so navigating never shifts the centered title -->
|
|
690
710
|
<button
|
|
691
711
|
type="button"
|
|
692
712
|
class="cal-hd-today"
|
|
@@ -704,45 +724,13 @@ const navCtx = $derived({
|
|
|
704
724
|
</button>
|
|
705
725
|
{/if}
|
|
706
726
|
</div>
|
|
707
|
-
<span class="cal-hd-title" role="status" aria-live="polite" aria-atomic="true">{dateLabel}</span>
|
|
708
|
-
<div class="cal-hd-side cal-hd-side--end">
|
|
709
|
-
{#if showModePills && labelsForMode.length > 1}
|
|
710
|
-
<!-- View-type switcher (e.g. Planner ↔ Agenda) for the current mode -->
|
|
711
|
-
<div class="cal-pills cal-pills--labels" role="radiogroup" aria-label={L.viewMode}>
|
|
712
|
-
{#each labelsForMode as label (label)}
|
|
713
|
-
<button
|
|
714
|
-
type="button"
|
|
715
|
-
class="cal-pill"
|
|
716
|
-
class:cal-pill--active={activeView?.label === label}
|
|
717
|
-
role="radio"
|
|
718
|
-
aria-checked={activeView?.label === label}
|
|
719
|
-
onclick={() => switchLabel(label)}
|
|
720
|
-
>
|
|
721
|
-
{viewLabel(label)}
|
|
722
|
-
</button>
|
|
723
|
-
{/each}
|
|
724
|
-
</div>
|
|
725
|
-
{/if}
|
|
726
|
-
{#if showModePills && modes.length > 1}
|
|
727
|
-
<div class="cal-pills" role="radiogroup" aria-label={L.viewMode}>
|
|
728
|
-
{#each modes as g (g)}
|
|
729
|
-
<button
|
|
730
|
-
type="button"
|
|
731
|
-
class="cal-pill"
|
|
732
|
-
class:cal-pill--active={viewState.mode === g}
|
|
733
|
-
role="radio"
|
|
734
|
-
aria-checked={viewState.mode === g}
|
|
735
|
-
onclick={() => switchMode(g)}
|
|
736
|
-
>
|
|
737
|
-
{g === 'day' ? L.day : g === 'week' ? L.week : L.month}
|
|
738
|
-
</button>
|
|
739
|
-
{/each}
|
|
740
|
-
</div>
|
|
741
|
-
{/if}
|
|
742
|
-
</div>
|
|
743
727
|
</div>
|
|
744
728
|
{/if}
|
|
745
729
|
|
|
730
|
+
{#if headerSnippet || !(showNavigation || (showModePills && modes.length > 1) || dateLabel)}
|
|
731
|
+
<span class="cal-sr" role="status" aria-live="polite" aria-atomic="true">{noteText}</span>
|
|
732
|
+
{/if}
|
|
733
|
+
|
|
746
734
|
<div class="cal-body">
|
|
747
735
|
{#if activeView}
|
|
748
736
|
{@const Comp = activeView.component}
|
|
@@ -783,7 +771,7 @@ const navCtx = $derived({
|
|
|
783
771
|
overflow: clip;
|
|
784
772
|
display: flex;
|
|
785
773
|
flex-direction: column;
|
|
786
|
-
border: 1px solid var(--dt-border, rgba(
|
|
774
|
+
border: 1px solid var(--dt-border, rgba(15, 15, 15, 0.08));
|
|
787
775
|
box-sizing: border-box;
|
|
788
776
|
}
|
|
789
777
|
.cal--auto {
|
|
@@ -792,118 +780,186 @@ const navCtx = $derived({
|
|
|
792
780
|
}
|
|
793
781
|
|
|
794
782
|
|
|
795
|
-
/* ──
|
|
783
|
+
/* ── Header: the period set large as the page's title, the view switch as
|
|
784
|
+
plain text, Today ‹ › in 2px ink frames, one rule to the grid. ── */
|
|
796
785
|
.cal-hd {
|
|
786
|
+
/* Controls take a small corner that follows the calendar's own radius */
|
|
787
|
+
--cal-ctl-r: clamp(4px, calc(var(--cal-r, 0px) / 2), 8px);
|
|
788
|
+
--cal-ctl-h: 36px;
|
|
797
789
|
display: flex;
|
|
798
790
|
flex-wrap: wrap;
|
|
799
791
|
align-items: center;
|
|
800
|
-
gap:
|
|
801
|
-
padding:
|
|
802
|
-
|
|
803
|
-
box-sizing: border-box;
|
|
804
|
-
border-bottom: 1px solid var(--dt-border, rgba(0, 0, 0, 0.08));
|
|
792
|
+
gap: 10px 24px;
|
|
793
|
+
padding: 14px 16px 12px;
|
|
794
|
+
border-bottom: 1px solid var(--dt-border, rgba(15, 15, 15, 0.08));
|
|
805
795
|
flex-shrink: 0;
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
display: flex;
|
|
810
|
-
align-items: center;
|
|
811
|
-
gap: 4px;
|
|
812
|
-
flex: 1;
|
|
813
|
-
min-width: 0;
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
.cal-hd-side--end {
|
|
817
|
-
justify-content: flex-end;
|
|
796
|
+
box-sizing: border-box;
|
|
797
|
+
font-family: var(--dt-sans, system-ui, sans-serif);
|
|
798
|
+
color: var(--dt-text, rgba(15, 15, 15, 0.87));
|
|
818
799
|
}
|
|
819
800
|
|
|
820
801
|
.cal-hd-title {
|
|
821
|
-
|
|
802
|
+
flex: 1 1 auto;
|
|
803
|
+
min-width: 0;
|
|
804
|
+
font: 400 32px/1.1 var(--dt-sans, system-ui, sans-serif);
|
|
805
|
+
font-stretch: 90%;
|
|
822
806
|
font-variant-numeric: tabular-nums;
|
|
823
|
-
letter-spacing: -0.
|
|
824
|
-
color: var(--dt-text, rgba(0, 0, 0, 0.87));
|
|
807
|
+
letter-spacing: -0.02em;
|
|
825
808
|
white-space: nowrap;
|
|
826
809
|
overflow: hidden;
|
|
827
810
|
text-overflow: ellipsis;
|
|
828
811
|
}
|
|
829
812
|
|
|
830
|
-
.cal-
|
|
813
|
+
.cal-sr {
|
|
814
|
+
position: absolute;
|
|
815
|
+
width: 1px;
|
|
816
|
+
height: 1px;
|
|
817
|
+
margin: -1px;
|
|
818
|
+
padding: 0;
|
|
819
|
+
overflow: hidden;
|
|
820
|
+
clip-path: inset(50%);
|
|
821
|
+
white-space: nowrap;
|
|
822
|
+
border: 0;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
.cal-hd-nav {
|
|
831
826
|
display: flex;
|
|
832
827
|
align-items: center;
|
|
833
|
-
|
|
834
|
-
width: 28px;
|
|
835
|
-
height: 28px;
|
|
836
|
-
border: none;
|
|
837
|
-
background: transparent;
|
|
838
|
-
color: var(--dt-text-2, rgba(0, 0, 0, 0.54));
|
|
839
|
-
border-radius: 0;
|
|
840
|
-
cursor: pointer;
|
|
841
|
-
transition: background 120ms, color 120ms;
|
|
828
|
+
gap: 8px;
|
|
842
829
|
}
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
color: var(--dt-text, rgba(0, 0, 0, 0.87));
|
|
846
|
-
background: color-mix(in srgb, var(--dt-text, rgba(0, 0, 0, 0.87)) 8%, transparent);
|
|
830
|
+
.cal-hd-nav:empty {
|
|
831
|
+
display: none;
|
|
847
832
|
}
|
|
848
833
|
|
|
849
|
-
.cal-hd-
|
|
850
|
-
.cal-hd-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
.cal-hd-today {
|
|
857
|
-
font: 500 12px/1 var(--dt-sans, system-ui, sans-serif);
|
|
858
|
-
color: var(--dt-text, rgba(0, 0, 0, 0.87));
|
|
834
|
+
.cal-hd-today,
|
|
835
|
+
.cal-hd-btn {
|
|
836
|
+
box-sizing: border-box;
|
|
837
|
+
height: var(--cal-ctl-h);
|
|
838
|
+
border: 2px solid var(--dt-text, rgba(15, 15, 15, 0.87));
|
|
839
|
+
border-radius: var(--cal-ctl-r);
|
|
859
840
|
background: transparent;
|
|
860
|
-
|
|
861
|
-
padding: 7px 10px;
|
|
862
|
-
border-radius: 0;
|
|
841
|
+
color: var(--dt-text, rgba(15, 15, 15, 0.87));
|
|
863
842
|
cursor: pointer;
|
|
843
|
+
transition: background-color 120ms, opacity 120ms;
|
|
844
|
+
-webkit-tap-highlight-color: transparent;
|
|
845
|
+
}
|
|
846
|
+
.cal-hd-today {
|
|
847
|
+
padding: 0 14px;
|
|
848
|
+
font: 400 15px/1 var(--dt-sans, system-ui, sans-serif);
|
|
864
849
|
white-space: nowrap;
|
|
865
|
-
margin-right: 2px;
|
|
866
|
-
transition: background 120ms, color 120ms, border-color 120ms;
|
|
867
850
|
}
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
851
|
+
.cal-hd-btn {
|
|
852
|
+
display: grid;
|
|
853
|
+
place-items: center;
|
|
854
|
+
width: var(--cal-ctl-h);
|
|
855
|
+
padding: 0;
|
|
856
|
+
}
|
|
857
|
+
/* The chevrons point along the reading direction */
|
|
858
|
+
.cal-hd-btn:dir(rtl) svg {
|
|
859
|
+
transform: scaleX(-1);
|
|
860
|
+
}
|
|
861
|
+
.cal-hd-today:hover:not(:disabled),
|
|
862
|
+
.cal-hd-btn:hover {
|
|
863
|
+
background: var(--dt-hover, color-mix(in srgb, currentColor 6%, transparent));
|
|
864
|
+
}
|
|
865
|
+
.cal-hd-today:active:not(:disabled),
|
|
866
|
+
.cal-hd-btn:active {
|
|
867
|
+
background: color-mix(in srgb, var(--dt-text, #0f0f0f) 12%, transparent);
|
|
872
868
|
}
|
|
869
|
+
/* Today is already in view: the frame stays, quietly */
|
|
873
870
|
.cal-hd-today:disabled {
|
|
874
|
-
opacity: 0.
|
|
871
|
+
opacity: 0.4;
|
|
875
872
|
cursor: default;
|
|
876
873
|
}
|
|
877
874
|
|
|
878
|
-
|
|
875
|
+
.cal-hd-today:focus-visible,
|
|
876
|
+
.cal-hd-btn:focus-visible,
|
|
877
|
+
.cal-pill:focus-visible {
|
|
878
|
+
outline: 2px solid var(--dt-text, rgba(15, 15, 15, 0.87));
|
|
879
|
+
outline-offset: 2px;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
/* View switch: plain text; the chosen one is ink over a 2px ink rule.
|
|
883
|
+
The accent keeps its three jobs. */
|
|
879
884
|
.cal-pills {
|
|
880
885
|
display: flex;
|
|
881
|
-
gap:
|
|
886
|
+
gap: 16px;
|
|
882
887
|
flex-shrink: 0;
|
|
883
888
|
}
|
|
884
|
-
|
|
889
|
+
/* A hairline between the view-type and the day/week/month rows */
|
|
890
|
+
.cal-pills--labels + .cal-pills {
|
|
891
|
+
align-self: stretch;
|
|
892
|
+
align-items: center;
|
|
893
|
+
padding-inline-start: 24px;
|
|
894
|
+
border-inline-start: 1px solid var(--dt-border, rgba(15, 15, 15, 0.12));
|
|
895
|
+
}
|
|
885
896
|
.cal-pill {
|
|
886
897
|
border: none;
|
|
887
898
|
background: transparent;
|
|
888
|
-
color: var(--dt-text-2, rgba(
|
|
899
|
+
color: var(--dt-text-2, rgba(15, 15, 15, 0.54));
|
|
889
900
|
cursor: pointer;
|
|
890
|
-
font:
|
|
891
|
-
|
|
901
|
+
font: 400 15px/1 var(--dt-sans, system-ui, sans-serif);
|
|
902
|
+
min-height: var(--cal-ctl-h);
|
|
903
|
+
/* 4px of air for the focus ring, given back so the row keeps its rhythm */
|
|
904
|
+
padding: 0 4px;
|
|
905
|
+
margin: 0 -4px;
|
|
892
906
|
border-radius: 0;
|
|
893
|
-
|
|
894
|
-
|
|
907
|
+
white-space: nowrap;
|
|
908
|
+
text-decoration: underline 2px transparent;
|
|
909
|
+
text-underline-offset: 7px;
|
|
910
|
+
transition: color 120ms, text-decoration-color 120ms;
|
|
911
|
+
-webkit-tap-highlight-color: transparent;
|
|
895
912
|
}
|
|
896
|
-
|
|
897
|
-
/* :not(--active) — the hover rule otherwise outranks the active color,
|
|
898
|
-
and iOS keeps :hover stuck after a tap (dark text on the accent). */
|
|
913
|
+
/* :not(--active) — iOS keeps :hover stuck after a tap */
|
|
899
914
|
.cal-pill:hover:not(.cal-pill--active) {
|
|
900
|
-
color: var(--dt-text, rgba(
|
|
915
|
+
color: var(--dt-text, rgba(15, 15, 15, 0.87));
|
|
916
|
+
text-decoration-color: var(--dt-border-day, var(--dt-border, rgba(15, 15, 15, 0.3)));
|
|
901
917
|
}
|
|
902
|
-
|
|
903
|
-
/* Selected = ink text over an ink rule; the accent keeps its three jobs. */
|
|
904
918
|
.cal-pill--active {
|
|
905
|
-
color: var(--dt-text, rgba(
|
|
906
|
-
|
|
919
|
+
color: var(--dt-text, rgba(15, 15, 15, 0.87));
|
|
920
|
+
text-decoration-color: currentColor;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
/* Compact (mobile) header: same drawing, smaller title, tighter gaps */
|
|
924
|
+
.cal-hd--mobile {
|
|
925
|
+
--cal-ctl-h: 40px;
|
|
926
|
+
gap: 8px 12px;
|
|
927
|
+
padding: 10px 8px 8px;
|
|
928
|
+
}
|
|
929
|
+
.cal-hd--mobile .cal-hd-title {
|
|
930
|
+
font-size: 22px;
|
|
931
|
+
}
|
|
932
|
+
.cal-hd--mobile .cal-pills {
|
|
933
|
+
gap: 12px;
|
|
934
|
+
}
|
|
935
|
+
.cal-hd--mobile .cal-pill {
|
|
936
|
+
font-size: 14px;
|
|
937
|
+
}
|
|
938
|
+
.cal-hd--mobile .cal-hd-today {
|
|
939
|
+
padding: 0 10px;
|
|
940
|
+
font-size: 14px;
|
|
941
|
+
}
|
|
942
|
+
.cal-hd--mobile .cal-hd-nav {
|
|
943
|
+
gap: 6px;
|
|
944
|
+
margin-inline-start: auto;
|
|
945
|
+
}
|
|
946
|
+
/* Narrow containers: the title takes its own row above the controls */
|
|
947
|
+
.cal-hd--stack .cal-hd-title:not(:empty) {
|
|
948
|
+
flex-basis: 100%;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
@media (pointer: coarse) {
|
|
952
|
+
.cal-hd {
|
|
953
|
+
--cal-ctl-h: 44px;
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
@media (prefers-reduced-motion: reduce) {
|
|
958
|
+
.cal-hd-today,
|
|
959
|
+
.cal-hd-btn,
|
|
960
|
+
.cal-pill {
|
|
961
|
+
transition: none;
|
|
962
|
+
}
|
|
907
963
|
}
|
|
908
964
|
|
|
909
965
|
.cal-body {
|
|
@@ -922,7 +978,7 @@ const navCtx = $derived({
|
|
|
922
978
|
justify-content: center;
|
|
923
979
|
height: 100%;
|
|
924
980
|
font: 400 13px / 1 var(--dt-sans, system-ui, sans-serif);
|
|
925
|
-
color: var(--dt-text-3, rgba(
|
|
981
|
+
color: var(--dt-text-3, rgba(15, 15, 15, 0.38));
|
|
926
982
|
}
|
|
927
983
|
|
|
928
984
|
.cal-loading {
|
|
@@ -934,7 +990,7 @@ const navCtx = $derived({
|
|
|
934
990
|
background: linear-gradient(
|
|
935
991
|
90deg,
|
|
936
992
|
transparent 0%,
|
|
937
|
-
var(--dt-accent, #
|
|
993
|
+
var(--dt-accent, #1d4fd7) 50%,
|
|
938
994
|
transparent 100%
|
|
939
995
|
);
|
|
940
996
|
animation: cal-slide 1.2s ease-in-out infinite;
|
|
@@ -948,142 +1004,8 @@ const navCtx = $derived({
|
|
|
948
1004
|
@media (prefers-reduced-motion: reduce) {
|
|
949
1005
|
.cal-loading {
|
|
950
1006
|
animation: none;
|
|
951
|
-
background: var(--dt-accent-dim, rgba(
|
|
1007
|
+
background: var(--dt-accent-dim, rgba(29, 79, 215, 0.12));
|
|
952
1008
|
}
|
|
953
1009
|
}
|
|
954
1010
|
|
|
955
|
-
/* ── Mobile header (flow layout) ── */
|
|
956
|
-
.cal-m-hd {
|
|
957
|
-
display: flex;
|
|
958
|
-
align-items: center;
|
|
959
|
-
gap: 4px;
|
|
960
|
-
padding: 8px 8px 6px;
|
|
961
|
-
border-bottom: 1px solid var(--dt-border, rgba(0, 0, 0, 0.08));
|
|
962
|
-
flex-shrink: 0;
|
|
963
|
-
min-height: 44px;
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
/* Narrow containers: the date label moves to its own row (.cal-m-titlebar),
|
|
967
|
-
so the controls row spreads pills and nav to the edges. */
|
|
968
|
-
.cal-m-hd--stack {
|
|
969
|
-
justify-content: space-between;
|
|
970
|
-
}
|
|
971
|
-
.cal-m-hd--titled {
|
|
972
|
-
border-bottom: none;
|
|
973
|
-
padding-bottom: 2px;
|
|
974
|
-
}
|
|
975
|
-
.cal-m-titlebar {
|
|
976
|
-
display: flex;
|
|
977
|
-
justify-content: center;
|
|
978
|
-
padding: 0 8px 8px;
|
|
979
|
-
border-bottom: 1px solid var(--dt-border, rgba(0, 0, 0, 0.08));
|
|
980
|
-
flex-shrink: 0;
|
|
981
|
-
}
|
|
982
|
-
.cal-m-titlebar .cal-m-title {
|
|
983
|
-
flex: 0 1 auto;
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
.cal-m-left,
|
|
987
|
-
.cal-m-right {
|
|
988
|
-
display: flex;
|
|
989
|
-
align-items: center;
|
|
990
|
-
gap: 2px;
|
|
991
|
-
flex-shrink: 0;
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
.cal-m-right {
|
|
995
|
-
justify-content: flex-end;
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
.cal-m-nav {
|
|
999
|
-
display: flex;
|
|
1000
|
-
align-items: center;
|
|
1001
|
-
justify-content: center;
|
|
1002
|
-
width: 40px;
|
|
1003
|
-
height: 40px;
|
|
1004
|
-
border: none;
|
|
1005
|
-
background: transparent;
|
|
1006
|
-
color: var(--dt-text-2, rgba(0, 0, 0, 0.54));
|
|
1007
|
-
border-radius: 50%;
|
|
1008
|
-
cursor: pointer;
|
|
1009
|
-
transition: background 120ms, color 120ms;
|
|
1010
|
-
-webkit-tap-highlight-color: transparent;
|
|
1011
|
-
flex-shrink: 0;
|
|
1012
|
-
}
|
|
1013
|
-
.cal-m-nav:hover {
|
|
1014
|
-
color: var(--dt-text, rgba(0, 0, 0, 0.87));
|
|
1015
|
-
background: color-mix(in srgb, var(--dt-text, rgba(0, 0, 0, 0.87)) 8%, transparent);
|
|
1016
|
-
}
|
|
1017
|
-
.cal-m-nav:active {
|
|
1018
|
-
background: var(--dt-accent-dim, rgba(37, 99, 235, 0.12));
|
|
1019
|
-
}
|
|
1020
|
-
.cal-m-nav:focus-visible {
|
|
1021
|
-
outline: 2px solid color-mix(in srgb, var(--dt-accent, #2563eb) 55%, transparent);
|
|
1022
|
-
outline-offset: 2px;
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
|
-
.cal-m-pills {
|
|
1026
|
-
display: flex;
|
|
1027
|
-
gap: 2px;
|
|
1028
|
-
flex-shrink: 0;
|
|
1029
|
-
}
|
|
1030
|
-
.cal-m-pill {
|
|
1031
|
-
border: none;
|
|
1032
|
-
background: transparent;
|
|
1033
|
-
color: var(--dt-text-2, rgba(0, 0, 0, 0.54));
|
|
1034
|
-
cursor: pointer;
|
|
1035
|
-
font: 500 13px / 1 var(--dt-sans, system-ui, sans-serif);
|
|
1036
|
-
padding: 11px 10px 9px;
|
|
1037
|
-
border-radius: 0;
|
|
1038
|
-
box-shadow: inset 0 -2px 0 transparent;
|
|
1039
|
-
transition: box-shadow 100ms, color 100ms;
|
|
1040
|
-
-webkit-tap-highlight-color: transparent;
|
|
1041
|
-
}
|
|
1042
|
-
.cal-m-pill:hover:not(.cal-m-pill--active) {
|
|
1043
|
-
color: var(--dt-text, rgba(0, 0, 0, 0.87));
|
|
1044
|
-
}
|
|
1045
|
-
.cal-m-pill--active {
|
|
1046
|
-
color: var(--dt-text, rgba(0, 0, 0, 0.87));
|
|
1047
|
-
box-shadow: inset 0 -2px 0 var(--dt-text, rgba(0, 0, 0, 0.87));
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
.cal-m-title {
|
|
1051
|
-
flex: 1;
|
|
1052
|
-
text-align: center;
|
|
1053
|
-
font: 600 14px / 1.2 var(--dt-sans, system-ui, sans-serif);
|
|
1054
|
-
color: var(--dt-text, rgba(0, 0, 0, 0.87));
|
|
1055
|
-
white-space: nowrap;
|
|
1056
|
-
overflow: hidden;
|
|
1057
|
-
text-overflow: ellipsis;
|
|
1058
|
-
min-width: 0;
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
.cal-m-today {
|
|
1062
|
-
font: 600 13px / 1 var(--dt-sans, system-ui, sans-serif);
|
|
1063
|
-
color: var(--dt-text, rgba(0, 0, 0, 0.87));
|
|
1064
|
-
background: transparent;
|
|
1065
|
-
border: 1px solid var(--dt-border-day, rgba(0, 0, 0, 0.12));
|
|
1066
|
-
min-height: 40px;
|
|
1067
|
-
padding: 5px 12px;
|
|
1068
|
-
border-radius: 0;
|
|
1069
|
-
cursor: pointer;
|
|
1070
|
-
white-space: nowrap;
|
|
1071
|
-
transition: background 120ms, color 120ms;
|
|
1072
|
-
-webkit-tap-highlight-color: transparent;
|
|
1073
|
-
flex-shrink: 0;
|
|
1074
|
-
}
|
|
1075
|
-
.cal-m-today:hover:not(:disabled) {
|
|
1076
|
-
border-color: var(--dt-text-2, rgba(0, 0, 0, 0.54));
|
|
1077
|
-
}
|
|
1078
|
-
.cal-m-today:active:not(:disabled) {
|
|
1079
|
-
background: color-mix(in srgb, var(--dt-text, #000) 8%, transparent);
|
|
1080
|
-
}
|
|
1081
|
-
.cal-m-today:disabled {
|
|
1082
|
-
opacity: 0.45;
|
|
1083
|
-
cursor: default;
|
|
1084
|
-
}
|
|
1085
|
-
.cal-m-today:focus-visible {
|
|
1086
|
-
outline: 2px solid var(--dt-text, rgba(0, 0, 0, 0.87));
|
|
1087
|
-
outline-offset: 2px;
|
|
1088
|
-
}
|
|
1089
1011
|
</style>
|
|
@@ -69,7 +69,7 @@ export interface CalendarProps {
|
|
|
69
69
|
mondayStart?: boolean;
|
|
70
70
|
/** Total height. Use `'auto'` to let content determine height (ideal for Agenda views). */
|
|
71
71
|
height?: number | 'auto';
|
|
72
|
-
/** Border radius in px (default:
|
|
72
|
+
/** Border radius in px (default: 0, square-cut). */
|
|
73
73
|
borderRadius?: number;
|
|
74
74
|
/** Text direction: 'ltr' (default), 'rtl', or 'auto' */
|
|
75
75
|
dir?: 'ltr' | 'rtl' | 'auto';
|
|
@@ -43,18 +43,18 @@ const dayNum = $derived(new Date(dayMs).getDate());
|
|
|
43
43
|
font: 500 10px / 1 var(--dt-sans, system-ui, sans-serif);
|
|
44
44
|
letter-spacing: 0.04em;
|
|
45
45
|
text-transform: uppercase;
|
|
46
|
-
color: var(--dt-text-2, rgba(
|
|
46
|
+
color: var(--dt-text-2, rgba(15, 15, 15, 0.54));
|
|
47
47
|
}
|
|
48
48
|
.dh-name-long {
|
|
49
49
|
font-size: 12px;
|
|
50
50
|
text-transform: none;
|
|
51
51
|
letter-spacing: 0.01em;
|
|
52
|
-
color: var(--dt-text, rgba(
|
|
52
|
+
color: var(--dt-text, rgba(15, 15, 15, 0.87));
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
.dh-num {
|
|
56
56
|
font: 600 14px / 1 var(--dt-sans, system-ui, sans-serif);
|
|
57
|
-
color: var(--dt-text, rgba(
|
|
57
|
+
color: var(--dt-text, rgba(15, 15, 15, 0.87));
|
|
58
58
|
width: 28px;
|
|
59
59
|
height: 28px;
|
|
60
60
|
display: flex;
|
|
@@ -63,20 +63,20 @@ const dayNum = $derived(new Date(dayMs).getDate());
|
|
|
63
63
|
border-radius: 50%;
|
|
64
64
|
}
|
|
65
65
|
.dh-num-today {
|
|
66
|
-
background: var(--dt-accent, #
|
|
66
|
+
background: var(--dt-accent, #1d4fd7);
|
|
67
67
|
color: var(--dt-btn-text, #fff);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
.dh-date {
|
|
71
71
|
font: 400 10px / 1 var(--dt-sans, system-ui, sans-serif);
|
|
72
|
-
color: var(--dt-text-2, rgba(
|
|
72
|
+
color: var(--dt-text-2, rgba(15, 15, 15, 0.54));
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
.dh-rel {
|
|
76
76
|
font: 500 12px / 1.3 var(--dt-sans, system-ui, sans-serif);
|
|
77
|
-
color: var(--dt-text, rgba(
|
|
77
|
+
color: var(--dt-text, rgba(15, 15, 15, 0.87));
|
|
78
78
|
}
|
|
79
79
|
.dh-today .dh-rel {
|
|
80
|
-
color: var(--dt-accent, #
|
|
80
|
+
color: var(--dt-accent, #1d4fd7);
|
|
81
81
|
}
|
|
82
82
|
</style>
|