@nomideusz/svelte-calendar 0.11.0 → 0.13.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 +2 -16
- package/dist/calendar/Calendar.svelte +4 -0
- package/dist/calendar/Calendar.svelte.d.ts +8 -0
- package/dist/core/index.d.ts +0 -2
- package/dist/core/index.js +0 -2
- package/dist/engine/view-state.svelte.d.ts +5 -4
- package/dist/headless/create-range-agenda.svelte.d.ts +62 -0
- package/dist/headless/create-range-agenda.svelte.js +110 -0
- package/dist/headless/index.d.ts +2 -0
- package/dist/headless/index.js +1 -0
- package/dist/index.d.ts +6 -6
- package/dist/index.js +2 -2
- package/dist/theme/auto.js +56 -21
- package/dist/theme/presets.d.ts +6 -5
- package/dist/theme/presets.js +2 -3
- package/dist/views/agenda/AgendaDay.svelte +15 -30
- package/dist/views/agenda/AgendaWeek.svelte +68 -30
- package/dist/views/planner/Planner.svelte +3 -7
- package/dist/views/planner/PlannerWeek.svelte +16 -7
- package/dist/views/planner/PlannerWeek.svelte.d.ts +2 -0
- package/dist/views/shared/context.svelte.d.ts +1 -0
- package/dist/views/shared/context.svelte.js +1 -0
- package/package.json +1 -8
- package/widget/widget.js +634 -1726
- package/dist/core/measure.d.ts +0 -104
- package/dist/core/measure.js +0 -195
- package/dist/views/planner/PlannerDay.svelte +0 -1279
- package/dist/views/planner/PlannerDay.svelte.d.ts +0 -30
|
@@ -24,6 +24,7 @@ const hideDays = $derived(ctx.hideDays);
|
|
|
24
24
|
const isMobile = $derived(ctx.isMobile);
|
|
25
25
|
const autoHeight = $derived(ctx.autoHeight);
|
|
26
26
|
const compact = $derived(ctx.compact);
|
|
27
|
+
const cols = $derived(ctx.columns && !isMobile);
|
|
27
28
|
const dayHeaderSnippet = $derived(ctx.dayHeaderSnippet);
|
|
28
29
|
const oneventhover = $derived(ctx.oneventhover);
|
|
29
30
|
const disabledSet = $derived(ctx.disabledSet);
|
|
@@ -199,7 +200,6 @@ const weekDays = $derived.by(() => {
|
|
|
199
200
|
onpointerenter={() => oneventhover?.(ev)}
|
|
200
201
|
>
|
|
201
202
|
<EventContent event={ev}>
|
|
202
|
-
<span class="ag-compact-dot"></span>
|
|
203
203
|
<span class="ag-compact-time">{fmt(ev.start)}</span>
|
|
204
204
|
<div class="ag-compact-main">
|
|
205
205
|
<span class="ag-compact-title">{ev.title}</span>
|
|
@@ -240,13 +240,14 @@ const weekDays = $derived.by(() => {
|
|
|
240
240
|
class="ag ag--week"
|
|
241
241
|
class:ag--mobile={isMobile}
|
|
242
242
|
class:ag--auto={autoHeight}
|
|
243
|
+
class:ag--cols={cols}
|
|
243
244
|
style={style || undefined}
|
|
244
245
|
style:height={height ? `${height}px` : undefined}
|
|
245
246
|
onpointerdown={onPointerDown}
|
|
246
247
|
onpointerup={onPointerUp}
|
|
247
248
|
onpointercancel={onPointerCancel}
|
|
248
249
|
>
|
|
249
|
-
<div class="ag-body" role="list" aria-label={L.weekAhead}>
|
|
250
|
+
<div class="ag-body" role="list" aria-label={L.weekAhead} style:--ag-cols={weekDays.length}>
|
|
250
251
|
{#each weekDays as day (day.ms)}
|
|
251
252
|
{@const expanded = day.tier === 'today' || day.tier === 'tomorrow'}
|
|
252
253
|
{#if day.tier === 'past'}
|
|
@@ -329,15 +330,19 @@ const weekDays = $derived.by(() => {
|
|
|
329
330
|
|
|
330
331
|
{#if day.events.length === 0}
|
|
331
332
|
<div class="ag-wday-empty">{L.noEvents}</div>
|
|
332
|
-
{:else if compact}
|
|
333
|
-
<!-- Compact: minimal dot + time + title rows for all days
|
|
333
|
+
{:else if compact && !cols}
|
|
334
|
+
<!-- Compact: minimal dot + time + title rows for all days.
|
|
335
|
+
Ignored in columns mode — single-line rows truncate to
|
|
336
|
+
nothing at column width; cards wrap instead. -->
|
|
334
337
|
<div class="ag-wday-compact">
|
|
335
338
|
{#each day.timedEvents as ev (ev.id)}
|
|
336
339
|
{@render compactRow(ev, false, false)}
|
|
337
340
|
{/each}
|
|
338
341
|
</div>
|
|
339
|
-
{:else if equalDays}
|
|
340
|
-
<!-- Equal days: card layout
|
|
342
|
+
{:else if equalDays || (cols && !expanded)}
|
|
343
|
+
<!-- Equal days (or timetable columns): card layout, no time-relative
|
|
344
|
+
badges. In columns, today/tomorrow still fall through to the
|
|
345
|
+
expanded branch below for now/ETA treatment. -->
|
|
341
346
|
<div class="ag-wday-expanded">
|
|
342
347
|
{#each groupIntoSlots(day.timedEvents) as slot (slot.startMs)}
|
|
343
348
|
<div class="ag-wslot">
|
|
@@ -814,8 +819,9 @@ const weekDays = $derived.by(() => {
|
|
|
814
819
|
text-decoration: line-through;
|
|
815
820
|
text-decoration-color: var(--dt-text-3, rgba(0, 0, 0, 0.38));
|
|
816
821
|
}
|
|
817
|
-
.ag-compact--done .ag-compact-
|
|
818
|
-
|
|
822
|
+
.ag-compact--done .ag-compact-time {
|
|
823
|
+
color: var(--dt-text-3, rgba(0, 0, 0, 0.38));
|
|
824
|
+
font-weight: 400;
|
|
819
825
|
}
|
|
820
826
|
|
|
821
827
|
/* Compact day events */
|
|
@@ -834,13 +840,10 @@ const weekDays = $derived.by(() => {
|
|
|
834
840
|
.ag-compact--selected {
|
|
835
841
|
background: color-mix(in srgb, var(--ev-color) 10%, transparent);
|
|
836
842
|
border-radius: 4px;
|
|
837
|
-
/*
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
margin-left: -6px;
|
|
842
|
-
margin-right: -6px;
|
|
843
|
-
width: calc(100% + 12px);
|
|
843
|
+
/* The highlight bleeds into the gutter via a spread shadow — zero
|
|
844
|
+
layout impact, so nothing shifts or clips even when the host
|
|
845
|
+
reduces the gutters below the bleed width. */
|
|
846
|
+
box-shadow: 0 0 0 6px color-mix(in srgb, var(--ev-color) 10%, transparent);
|
|
844
847
|
}
|
|
845
848
|
.ag-compact:hover .ag-compact-title,
|
|
846
849
|
.ag-compact:active .ag-compact-title {
|
|
@@ -855,18 +858,14 @@ const weekDays = $derived.by(() => {
|
|
|
855
858
|
box-shadow: 0 0 0 2px var(--dt-accent, #2563eb);
|
|
856
859
|
border-radius: 4px;
|
|
857
860
|
}
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
border-radius: 50%;
|
|
862
|
-
background: var(--ev-color, var(--dt-accent));
|
|
863
|
-
flex-shrink: 0;
|
|
864
|
-
align-self: center;
|
|
865
|
-
}
|
|
861
|
+
/* The time label doubles as the class-color signal (replaces the old
|
|
862
|
+
dot): the event color mixed toward the text color, so it stays
|
|
863
|
+
legible on any palette and costs zero horizontal space. */
|
|
866
864
|
.ag-compact-time {
|
|
867
865
|
font-size: 11px;
|
|
868
866
|
font-family: var(--dt-mono, monospace);
|
|
869
|
-
|
|
867
|
+
font-weight: 500;
|
|
868
|
+
color: color-mix(in srgb, var(--ev-color, var(--dt-accent)) 60%, var(--dt-text, rgba(0, 0, 0, 0.87)));
|
|
870
869
|
min-width: 40px;
|
|
871
870
|
flex-shrink: 0;
|
|
872
871
|
white-space: nowrap;
|
|
@@ -897,12 +896,6 @@ const weekDays = $derived.by(() => {
|
|
|
897
896
|
.ag--mobile .ag-compact-sub {
|
|
898
897
|
max-width: 100%;
|
|
899
898
|
}
|
|
900
|
-
/* Wrapped rows are two/three lines tall — center-aligning the dot floats
|
|
901
|
-
it between lines; pin it optically to the first (title) line instead. */
|
|
902
|
-
.ag--mobile .ag-compact-dot {
|
|
903
|
-
align-self: flex-start;
|
|
904
|
-
margin-top: 8px;
|
|
905
|
-
}
|
|
906
899
|
.ag-compact-title {
|
|
907
900
|
font-size: 12px;
|
|
908
901
|
font-weight: 500;
|
|
@@ -991,6 +984,51 @@ const weekDays = $derived.by(() => {
|
|
|
991
984
|
border-radius: 4px;
|
|
992
985
|
}
|
|
993
986
|
|
|
987
|
+
/* ═══ Timetable columns (desktop) ═══ */
|
|
988
|
+
.ag--cols .ag-body {
|
|
989
|
+
display: grid;
|
|
990
|
+
grid-template-columns: repeat(var(--ag-cols, 7), minmax(0, 1fr));
|
|
991
|
+
}
|
|
992
|
+
/* Columns stretch to the tallest day, so the separator runs full height */
|
|
993
|
+
.ag--cols .ag-wday {
|
|
994
|
+
border-bottom: none;
|
|
995
|
+
border-inline-start: 1px solid var(--dt-border, rgba(0, 0, 0, 0.08));
|
|
996
|
+
min-width: 0;
|
|
997
|
+
}
|
|
998
|
+
.ag--cols .ag-wday:first-child {
|
|
999
|
+
border-inline-start: none;
|
|
1000
|
+
}
|
|
1001
|
+
/* Uniform head padding — the :first-child top bump would misalign columns.
|
|
1002
|
+
Also overrides the past-day head variant (extra class = higher specificity). */
|
|
1003
|
+
.ag--cols .ag-wday .ag-wday-head {
|
|
1004
|
+
padding: 12px 10px 8px;
|
|
1005
|
+
}
|
|
1006
|
+
/* Badge + name + date won't fit one line in a ~160px column */
|
|
1007
|
+
.ag--cols .ag-wday-head-left {
|
|
1008
|
+
flex-wrap: wrap;
|
|
1009
|
+
row-gap: 2px;
|
|
1010
|
+
}
|
|
1011
|
+
.ag--cols .ag-wday-expanded,
|
|
1012
|
+
.ag--cols .ag-wday-compact,
|
|
1013
|
+
.ag--cols .ag-wday-empty,
|
|
1014
|
+
.ag--cols .ag-wday-past-line--summary {
|
|
1015
|
+
padding-left: 10px;
|
|
1016
|
+
padding-right: 10px;
|
|
1017
|
+
}
|
|
1018
|
+
.ag--cols .ag-allday {
|
|
1019
|
+
padding-left: 10px;
|
|
1020
|
+
padding-right: 10px;
|
|
1021
|
+
}
|
|
1022
|
+
/* Narrow cards: long titles get a third line, meta wraps instead of clipping */
|
|
1023
|
+
.ag--cols .ag-card-title {
|
|
1024
|
+
-webkit-line-clamp: 3;
|
|
1025
|
+
line-clamp: 3;
|
|
1026
|
+
}
|
|
1027
|
+
.ag--cols .ag-card-meta {
|
|
1028
|
+
flex-wrap: wrap;
|
|
1029
|
+
row-gap: 3px;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
994
1032
|
/* ═══ Mobile adaptations ═══ */
|
|
995
1033
|
.ag--mobile .ag-wday-head {
|
|
996
1034
|
padding: 12px 16px;
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
<script lang="ts">import
|
|
2
|
-
import PlannerWeek from "./PlannerWeek.svelte";
|
|
1
|
+
<script lang="ts">import PlannerWeek from "./PlannerWeek.svelte";
|
|
3
2
|
let { mode = "week", ...rest } = $props();
|
|
4
3
|
</script>
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
{:else}
|
|
9
|
-
<PlannerWeek {...rest} />
|
|
10
|
-
{/if}
|
|
5
|
+
<!-- Both planner modes are the same vertical time grid; day is one column. -->
|
|
6
|
+
<PlannerWeek {mode} {...rest} />
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
<!--
|
|
2
|
-
Planner
|
|
2
|
+
Planner time grid — vertical, Google-Calendar-style. Renders both planner
|
|
3
|
+
modes: week (N columns from viewState.dayCount) and day (a single column),
|
|
4
|
+
so the two share one geometry, one drag mapping, and one overlap engine.
|
|
3
5
|
|
|
4
6
|
• Left time gutter with localized hour labels; N day columns (viewState.dayCount).
|
|
5
7
|
• Sticky day-header row + sticky all-day strip; the grid scrolls vertically.
|
|
@@ -17,8 +19,9 @@ import EventContent from "../shared/EventContent.svelte";
|
|
|
17
19
|
import { createClock } from "../../core/clock.svelte.js";
|
|
18
20
|
import { DAY_MS, HOUR_MS, sod, addDaysMs } from "../../core/time.js";
|
|
19
21
|
import { startOfWeek as sowFn, isAllDay, isMultiDay, segmentForDay } from "../../core/time.js";
|
|
20
|
-
import { fmtH, fmtTime, fmtDuration, weekdayShort } from "../../core/locale.js";
|
|
22
|
+
import { fmtH, fmtTime, fmtDuration, weekdayShort, weekdayLong } from "../../core/locale.js";
|
|
21
23
|
let {
|
|
24
|
+
mode = "week",
|
|
22
25
|
mondayStart = true,
|
|
23
26
|
locale,
|
|
24
27
|
height = 520,
|
|
@@ -59,7 +62,9 @@ const gridHeight = $derived(hourCount * HOUR_H);
|
|
|
59
62
|
let scrollEl;
|
|
60
63
|
let colsEl;
|
|
61
64
|
const todayMs = $derived(clock.today);
|
|
62
|
-
const
|
|
65
|
+
const singleDay = $derived(mode === "day" || viewState?.mode === "day");
|
|
66
|
+
const customDays = $derived(singleDay ? 1 : viewState?.dayCount ?? 7);
|
|
67
|
+
const hideDayHead = $derived(singleDay && showDates && !!viewState && !dayHeaderSnippet);
|
|
63
68
|
const weekStartMs = $derived.by(() => {
|
|
64
69
|
const r = viewState?.range;
|
|
65
70
|
if (r) return sod(r.start.getTime());
|
|
@@ -259,8 +264,8 @@ function statusText(ev) {
|
|
|
259
264
|
}
|
|
260
265
|
function ghostForDay(dayMs) {
|
|
261
266
|
if (!drag?.active || !drag.payload) return null;
|
|
262
|
-
const
|
|
263
|
-
if (
|
|
267
|
+
const mode2 = drag.mode;
|
|
268
|
+
if (mode2 !== "move" && mode2 !== "create") return null;
|
|
264
269
|
const s = drag.payload.start.getTime();
|
|
265
270
|
const e = drag.payload.end.getTime();
|
|
266
271
|
const bandS = dayMs + startHour * HOUR_MS;
|
|
@@ -273,7 +278,7 @@ function ghostForDay(dayMs) {
|
|
|
273
278
|
height: Math.max(12, (ce - cs) / HOUR_MS * HOUR_H),
|
|
274
279
|
start: drag.payload.start,
|
|
275
280
|
end: drag.payload.end,
|
|
276
|
-
create:
|
|
281
|
+
create: mode2 === "create",
|
|
277
282
|
// Only the segment containing the start shows the readout
|
|
278
283
|
showTime: cs === Math.max(s, dayMs)
|
|
279
284
|
};
|
|
@@ -570,7 +575,9 @@ function onWindowKeydown(e) {
|
|
|
570
575
|
<div class="tw-scroll" bind:this={scrollEl}>
|
|
571
576
|
<div class="tw-inner" style:min-width="{innerMinWidth}px">
|
|
572
577
|
<!-- Sticky top: day headers + all-day strip -->
|
|
578
|
+
{#if !hideDayHead || hasAllDayRow}
|
|
573
579
|
<div class="tw-top">
|
|
580
|
+
{#if !hideDayHead}
|
|
574
581
|
<div class="tw-head">
|
|
575
582
|
<div class="tw-corner" style:width="{GUTTER_W}px" aria-hidden="true"></div>
|
|
576
583
|
{#each dayCols as day (day.ms)}
|
|
@@ -579,7 +586,7 @@ function onWindowKeydown(e) {
|
|
|
579
586
|
class:tw-hd--today={day.isToday}
|
|
580
587
|
aria-current={day.isToday ? 'date' : undefined}
|
|
581
588
|
>
|
|
582
|
-
<span class="tw-hd-wd">{weekdayShort(day.ms, locale)}</span>
|
|
589
|
+
<span class="tw-hd-wd">{singleDay ? weekdayLong(day.ms, locale) : weekdayShort(day.ms, locale)}</span>
|
|
583
590
|
{#if showDates}
|
|
584
591
|
<span class="tw-hd-num" class:tw-hd-num--today={day.isToday}>{day.dayNum}</span>
|
|
585
592
|
{/if}
|
|
@@ -591,6 +598,7 @@ function onWindowKeydown(e) {
|
|
|
591
598
|
</div>
|
|
592
599
|
{/each}
|
|
593
600
|
</div>
|
|
601
|
+
{/if}
|
|
594
602
|
|
|
595
603
|
{#if hasAllDayRow}
|
|
596
604
|
<div class="tw-allday">
|
|
@@ -618,6 +626,7 @@ function onWindowKeydown(e) {
|
|
|
618
626
|
</div>
|
|
619
627
|
{/if}
|
|
620
628
|
</div>
|
|
629
|
+
{/if}
|
|
621
630
|
|
|
622
631
|
<!-- Grid body -->
|
|
623
632
|
<div class="tw-body" style:height="{gridHeight}px">
|
|
@@ -14,6 +14,7 @@ export interface CalendarContext {
|
|
|
14
14
|
readonly isMobile: boolean;
|
|
15
15
|
readonly autoHeight: boolean;
|
|
16
16
|
readonly compact: boolean;
|
|
17
|
+
readonly columns: boolean;
|
|
17
18
|
readonly readOnly: boolean;
|
|
18
19
|
readonly blockedSlots: BlockedSlot[] | undefined;
|
|
19
20
|
readonly dayHeaderSnippet: Snippet<[{
|
|
@@ -34,6 +34,7 @@ export function useCalendarContext() {
|
|
|
34
34
|
get isMobile() { return raw?.mobile ?? false; },
|
|
35
35
|
get autoHeight() { return raw?.autoHeight ?? false; },
|
|
36
36
|
get compact() { return raw?.compact ?? false; },
|
|
37
|
+
get columns() { return raw?.columns ?? false; },
|
|
37
38
|
get readOnly() { return raw?.readOnly ?? false; },
|
|
38
39
|
get blockedSlots() { return raw?.blockedSlots; },
|
|
39
40
|
get dayHeaderSnippet() { return raw?.dayHeaderSnippet; },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nomideusz/svelte-calendar",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "A themeable Svelte 5 calendar with Day and Week views — Planner and Agenda.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -35,16 +35,9 @@
|
|
|
35
35
|
"test:watch": "vitest"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"@chenglou/pretext": "^0.0.5",
|
|
39
38
|
"svelte": "^5.0.0"
|
|
40
39
|
},
|
|
41
|
-
"peerDependenciesMeta": {
|
|
42
|
-
"@chenglou/pretext": {
|
|
43
|
-
"optional": true
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
40
|
"devDependencies": {
|
|
47
|
-
"@chenglou/pretext": "^0.0.5",
|
|
48
41
|
"@sveltejs/adapter-auto": "^7.0.1",
|
|
49
42
|
"@sveltejs/kit": "^2.69.1",
|
|
50
43
|
"@sveltejs/package": "^2.5.8",
|