@nomideusz/svelte-calendar 0.7.5 → 0.9.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 +33 -5
- package/dist/calendar/Calendar.svelte +379 -497
- package/dist/calendar/Calendar.svelte.d.ts +13 -2
- package/dist/core/clock.svelte.d.ts +1 -1
- package/dist/core/clock.svelte.js +20 -9
- package/dist/core/locale.d.ts +4 -0
- package/dist/core/locale.js +4 -0
- package/dist/core/timezone.d.ts +12 -0
- package/dist/core/timezone.js +31 -0
- package/dist/engine/view-state.svelte.d.ts +2 -2
- package/dist/engine/view-state.svelte.js +20 -0
- package/dist/headless/create-calendar.svelte.js +29 -7
- package/dist/headless/types.d.ts +8 -6
- package/dist/index.d.ts +3 -0
- package/dist/index.js +4 -0
- package/dist/primitives/DayHeader.svelte +9 -25
- package/dist/primitives/EmptySlot.svelte +15 -31
- package/dist/primitives/EventBlock.svelte +33 -61
- package/dist/primitives/NowIndicator.svelte +16 -42
- package/dist/primitives/TimeGutter.svelte +9 -23
- package/dist/views/agenda/Agenda.svelte +3 -10
- package/dist/views/agenda/AgendaDay.svelte +195 -167
- package/dist/views/agenda/AgendaWeek.svelte +141 -195
- package/dist/views/mobile/Mobile.svelte +3 -10
- package/dist/views/mobile/MobileDay.svelte +422 -266
- package/dist/views/mobile/MobileWeek.svelte +131 -186
- package/dist/views/month/MonthGrid.svelte +334 -0
- package/dist/views/month/MonthGrid.svelte.d.ts +15 -0
- package/dist/views/planner/Planner.svelte +3 -10
- package/dist/views/planner/PlannerDay.svelte +636 -546
- package/dist/views/planner/PlannerWeek.svelte +299 -412
- package/dist/views/shared/EventContent.svelte +16 -0
- package/dist/views/shared/EventContent.svelte.d.ts +9 -0
- package/dist/views/shared/context.svelte.d.ts +2 -0
- package/dist/views/shared/context.svelte.js +2 -0
- package/dist/widget/CalendarWidget.svelte +79 -116
- package/package.json +3 -2
- package/widget/svelte-calendar.css +321 -10
- package/widget/widget.js +2508 -1056
|
@@ -5,192 +5,135 @@
|
|
|
5
5
|
list of events. Tap a day to drill into MobileDay. Swipe
|
|
6
6
|
left/right to navigate weeks.
|
|
7
7
|
-->
|
|
8
|
-
<script lang="ts">
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
function fmtTime(d: Date): string {
|
|
138
|
-
return _fmtTime(d, locale);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// ── Touch swipe ────────────────────────────────────
|
|
142
|
-
let touchStartX = 0;
|
|
143
|
-
let touchStartY = 0;
|
|
144
|
-
let swiping = false;
|
|
145
|
-
let swipeOffset = $state(0);
|
|
146
|
-
const SWIPE_THRESHOLD = 60;
|
|
147
|
-
|
|
148
|
-
function onTouchStart(e: TouchEvent) {
|
|
149
|
-
const t = e.touches[0];
|
|
150
|
-
touchStartX = t.clientX;
|
|
151
|
-
touchStartY = t.clientY;
|
|
152
|
-
swiping = true;
|
|
153
|
-
swipeOffset = 0;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function onTouchMove(e: TouchEvent) {
|
|
157
|
-
if (!swiping) return;
|
|
158
|
-
const t = e.touches[0];
|
|
159
|
-
const dx = t.clientX - touchStartX;
|
|
160
|
-
const dy = t.clientY - touchStartY;
|
|
161
|
-
if (Math.abs(dy) > Math.abs(dx) * 0.8) { swiping = false; return; }
|
|
162
|
-
swipeOffset = dx;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function onTouchEnd() {
|
|
166
|
-
if (!swiping) { swipeOffset = 0; return; }
|
|
167
|
-
if (Math.abs(swipeOffset) > SWIPE_THRESHOLD) {
|
|
168
|
-
if (swipeOffset > 0) {
|
|
169
|
-
viewState?.prev();
|
|
170
|
-
} else {
|
|
171
|
-
viewState?.next();
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
swipeOffset = 0;
|
|
175
|
-
swiping = false;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// ── Day tap → switch to day mode ───────────────────
|
|
179
|
-
function handleDayTap(dayMs: number) {
|
|
180
|
-
if (viewState) {
|
|
181
|
-
viewState.setFocusDate(new Date(dayMs));
|
|
182
|
-
// Switch to day-mobile view if available
|
|
183
|
-
const currentView = viewState.view;
|
|
184
|
-
const dayView = currentView.replace('week', 'day');
|
|
185
|
-
viewState.setView(dayView);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function handleDayKeydown(e: KeyboardEvent, dayMs: number) {
|
|
190
|
-
if (e.key !== 'Enter' && e.key !== ' ') return;
|
|
191
|
-
e.preventDefault();
|
|
192
|
-
handleDayTap(dayMs);
|
|
193
|
-
}
|
|
8
|
+
<script lang="ts">import { useCalendarContext } from "../shared/context.svelte.js";
|
|
9
|
+
import EventContent from "../shared/EventContent.svelte";
|
|
10
|
+
import { createClock } from "../../core/clock.svelte.js";
|
|
11
|
+
import { DAY_MS, sod, isAllDay, isMultiDay } from "../../core/time.js";
|
|
12
|
+
import { startOfWeek as sowFn } from "../../core/time.js";
|
|
13
|
+
import { fmtTime as _fmtTime, weekdayShort, getLabels } from "../../core/locale.js";
|
|
14
|
+
const L = $derived(getLabels());
|
|
15
|
+
let {
|
|
16
|
+
mondayStart = true,
|
|
17
|
+
locale,
|
|
18
|
+
height = null,
|
|
19
|
+
events = [],
|
|
20
|
+
style = "",
|
|
21
|
+
focusDate,
|
|
22
|
+
oneventclick,
|
|
23
|
+
oneventcreate,
|
|
24
|
+
selectedEventId = null,
|
|
25
|
+
readOnly = false
|
|
26
|
+
} = $props();
|
|
27
|
+
const ctx = useCalendarContext();
|
|
28
|
+
const viewState = $derived(ctx.viewState);
|
|
29
|
+
const equalDays = $derived(ctx.equalDays);
|
|
30
|
+
const showDates = $derived(ctx.showDates);
|
|
31
|
+
const hideDays = $derived(ctx.hideDays);
|
|
32
|
+
const autoHeight = $derived(ctx.autoHeight);
|
|
33
|
+
const oneventhover = $derived(ctx.oneventhover);
|
|
34
|
+
const disabledSet = $derived(ctx.disabledSet);
|
|
35
|
+
const loadRangeCtx = $derived(ctx.loadRange);
|
|
36
|
+
const minDuration = $derived(ctx.minDuration);
|
|
37
|
+
const clock = createClock(ctx.timezone);
|
|
38
|
+
const MAX_EVENTS = 3;
|
|
39
|
+
const customDays = $derived(viewState?.dayCount ?? 7);
|
|
40
|
+
const todayMs = $derived(clock.today);
|
|
41
|
+
const focusMs = $derived(focusDate ? sod(focusDate.getTime()) : todayMs);
|
|
42
|
+
const weekStart = $derived(
|
|
43
|
+
customDays === 7 ? sowFn(focusMs, mondayStart) : sod(focusMs)
|
|
44
|
+
);
|
|
45
|
+
$effect(() => {
|
|
46
|
+
if (!loadRangeCtx) return;
|
|
47
|
+
const rangeStart = new Date(weekStart - 7 * DAY_MS);
|
|
48
|
+
const rangeEnd = new Date(weekStart + (customDays + 7) * DAY_MS);
|
|
49
|
+
loadRangeCtx.set({ start: rangeStart, end: rangeEnd });
|
|
50
|
+
return () => loadRangeCtx.set(null);
|
|
51
|
+
});
|
|
52
|
+
const dayCells = $derived.by(() => {
|
|
53
|
+
const result = [];
|
|
54
|
+
const hideSet = new Set(hideDays ?? []);
|
|
55
|
+
for (let i = 0; i < customDays; i++) {
|
|
56
|
+
const ms = weekStart + i * DAY_MS;
|
|
57
|
+
const d = new Date(ms);
|
|
58
|
+
const jsDay = d.getDay();
|
|
59
|
+
const isoDay = jsDay === 0 ? 7 : jsDay;
|
|
60
|
+
if (hideSet.has(isoDay)) continue;
|
|
61
|
+
const isToday = ms === todayMs;
|
|
62
|
+
const isPast = equalDays ? false : ms < todayMs;
|
|
63
|
+
const isWeekend = jsDay === 0 || jsDay === 6;
|
|
64
|
+
const isDisabled = disabledSet.has(ms);
|
|
65
|
+
const dayEnd = ms + DAY_MS;
|
|
66
|
+
const dayEvents = events.filter((ev) => ev.start.getTime() < dayEnd && ev.end.getTime() > ms).sort((a, b) => a.start.getTime() - b.start.getTime());
|
|
67
|
+
const allDayCount = dayEvents.filter((ev) => isAllDay(ev) || isMultiDay(ev)).length;
|
|
68
|
+
result.push({
|
|
69
|
+
ms,
|
|
70
|
+
dayNum: d.getDate(),
|
|
71
|
+
dayName: weekdayShort(ms, locale),
|
|
72
|
+
isToday,
|
|
73
|
+
isPast,
|
|
74
|
+
isDisabled,
|
|
75
|
+
isWeekend,
|
|
76
|
+
events: dayEvents,
|
|
77
|
+
allDayCount,
|
|
78
|
+
totalCount: dayEvents.length
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
});
|
|
83
|
+
function fmtTime(d) {
|
|
84
|
+
return _fmtTime(d, locale);
|
|
85
|
+
}
|
|
86
|
+
let touchStartX = 0;
|
|
87
|
+
let touchStartY = 0;
|
|
88
|
+
let swiping = false;
|
|
89
|
+
let swipeOffset = $state(0);
|
|
90
|
+
const SWIPE_THRESHOLD = 60;
|
|
91
|
+
function onTouchStart(e) {
|
|
92
|
+
const t = e.touches[0];
|
|
93
|
+
touchStartX = t.clientX;
|
|
94
|
+
touchStartY = t.clientY;
|
|
95
|
+
swiping = true;
|
|
96
|
+
swipeOffset = 0;
|
|
97
|
+
}
|
|
98
|
+
function onTouchMove(e) {
|
|
99
|
+
if (!swiping) return;
|
|
100
|
+
const t = e.touches[0];
|
|
101
|
+
const dx = t.clientX - touchStartX;
|
|
102
|
+
const dy = t.clientY - touchStartY;
|
|
103
|
+
if (Math.abs(dy) > Math.abs(dx) * 0.8) {
|
|
104
|
+
swiping = false;
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
swipeOffset = dx;
|
|
108
|
+
}
|
|
109
|
+
function onTouchEnd() {
|
|
110
|
+
if (!swiping) {
|
|
111
|
+
swipeOffset = 0;
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (Math.abs(swipeOffset) > SWIPE_THRESHOLD) {
|
|
115
|
+
if (swipeOffset > 0) {
|
|
116
|
+
viewState?.prev();
|
|
117
|
+
} else {
|
|
118
|
+
viewState?.next();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
swipeOffset = 0;
|
|
122
|
+
swiping = false;
|
|
123
|
+
}
|
|
124
|
+
function handleDayTap(dayMs) {
|
|
125
|
+
if (viewState) {
|
|
126
|
+
viewState.setFocusDate(new Date(dayMs));
|
|
127
|
+
const currentView = viewState.view;
|
|
128
|
+
const dayView = currentView.replace("week", "day");
|
|
129
|
+
viewState.setView(dayView);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function handleDayKeydown(e, dayMs) {
|
|
133
|
+
if (e.key !== "Enter" && e.key !== " ") return;
|
|
134
|
+
e.preventDefault();
|
|
135
|
+
handleDayTap(dayMs);
|
|
136
|
+
}
|
|
194
137
|
</script>
|
|
195
138
|
|
|
196
139
|
<div
|
|
@@ -253,12 +196,14 @@
|
|
|
253
196
|
>
|
|
254
197
|
<span class="mw-ev-stripe"></span>
|
|
255
198
|
<div class="mw-ev-body">
|
|
199
|
+
<EventContent event={ev}>
|
|
256
200
|
<span class="mw-ev-title">{ev.title}</span>
|
|
257
201
|
{#if isAllDay(ev) || isMultiDay(ev)}
|
|
258
202
|
<span class="mw-ev-time">{L.allDay}</span>
|
|
259
203
|
{:else}
|
|
260
204
|
<span class="mw-ev-time">{fmtTime(ev.start)}</span>
|
|
261
205
|
{/if}
|
|
206
|
+
</EventContent>
|
|
262
207
|
</div>
|
|
263
208
|
</button>
|
|
264
209
|
{/each}
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
MonthGrid — classic month overview.
|
|
3
|
+
|
|
4
|
+
Week-aligned grid (4–6 rows, driven by the view-state 'month' range).
|
|
5
|
+
Each cell lists up to MAX_CHIPS events plus a "+N more" overflow; clicking
|
|
6
|
+
an event fires oneventclick, clicking a day (or the overflow) fires
|
|
7
|
+
ondayclick — the natural month → day drill-down.
|
|
8
|
+
-->
|
|
9
|
+
<script lang="ts">import { useCalendarContext } from "../shared/context.svelte.js";
|
|
10
|
+
import { createClock } from "../../core/clock.svelte.js";
|
|
11
|
+
import { DAY_MS, sod, isAllDay } from "../../core/time.js";
|
|
12
|
+
import { fmtTime, weekdayShort, getLabels } from "../../core/locale.js";
|
|
13
|
+
const L = $derived(getLabels());
|
|
14
|
+
let {
|
|
15
|
+
events = [],
|
|
16
|
+
style = "",
|
|
17
|
+
height = null,
|
|
18
|
+
locale,
|
|
19
|
+
focusDate,
|
|
20
|
+
oneventclick,
|
|
21
|
+
selectedEventId = null
|
|
22
|
+
} = $props();
|
|
23
|
+
const ctx = useCalendarContext();
|
|
24
|
+
const viewState = $derived(ctx.viewState);
|
|
25
|
+
const autoHeight = $derived(ctx.autoHeight);
|
|
26
|
+
const isMobile = $derived(ctx.isMobile);
|
|
27
|
+
const disabledSet = $derived(ctx.disabledSet);
|
|
28
|
+
const oneventhover = $derived(ctx.oneventhover);
|
|
29
|
+
const ondayclick = $derived(ctx.ondayclick);
|
|
30
|
+
const eventSnippet = $derived(ctx.eventSnippet);
|
|
31
|
+
const loadRangeCtx = $derived(ctx.loadRange);
|
|
32
|
+
const clock = createClock(ctx.timezone);
|
|
33
|
+
const todayMs = $derived(clock.today);
|
|
34
|
+
const MAX_CHIPS = $derived(isMobile ? 2 : 3);
|
|
35
|
+
const range = $derived(viewState?.range);
|
|
36
|
+
const focusMonth = $derived((focusDate ?? /* @__PURE__ */ new Date()).getMonth());
|
|
37
|
+
$effect(() => {
|
|
38
|
+
if (!loadRangeCtx || !range) return;
|
|
39
|
+
loadRangeCtx.set({
|
|
40
|
+
start: new Date(range.start.getTime() - 7 * DAY_MS),
|
|
41
|
+
end: new Date(range.end.getTime() + 7 * DAY_MS)
|
|
42
|
+
});
|
|
43
|
+
return () => loadRangeCtx.set(null);
|
|
44
|
+
});
|
|
45
|
+
function eventsForDay(ms) {
|
|
46
|
+
const dayStart = ms;
|
|
47
|
+
const dayEnd = ms + DAY_MS;
|
|
48
|
+
return events.filter((e) => e.start.getTime() < dayEnd && e.end.getTime() > dayStart).sort((a, b) => {
|
|
49
|
+
const aAll = isAllDay(a) ? 0 : 1;
|
|
50
|
+
const bAll = isAllDay(b) ? 0 : 1;
|
|
51
|
+
return aAll - bAll || a.start.getTime() - b.start.getTime();
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
const weeks = $derived.by(() => {
|
|
55
|
+
if (!range) return [];
|
|
56
|
+
const rows = [];
|
|
57
|
+
for (let ms = sod(range.start.getTime()); ms < range.end.getTime(); ms += 7 * DAY_MS) {
|
|
58
|
+
const row = [];
|
|
59
|
+
for (let i = 0; i < 7; i++) {
|
|
60
|
+
const cellMs = ms + i * DAY_MS;
|
|
61
|
+
const date = new Date(cellMs);
|
|
62
|
+
const jsDay = date.getDay();
|
|
63
|
+
const dayEvents = eventsForDay(cellMs);
|
|
64
|
+
row.push({
|
|
65
|
+
ms: cellMs,
|
|
66
|
+
date,
|
|
67
|
+
dayNum: date.getDate(),
|
|
68
|
+
inMonth: date.getMonth() === focusMonth,
|
|
69
|
+
isToday: cellMs === todayMs,
|
|
70
|
+
isWeekend: jsDay === 0 || jsDay === 6,
|
|
71
|
+
isDisabled: disabledSet.has(cellMs),
|
|
72
|
+
chips: dayEvents.slice(0, MAX_CHIPS),
|
|
73
|
+
overflow: Math.max(0, dayEvents.length - MAX_CHIPS)
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
rows.push(row);
|
|
77
|
+
}
|
|
78
|
+
return rows;
|
|
79
|
+
});
|
|
80
|
+
const weekdayLabels = $derived.by(() => {
|
|
81
|
+
const first = weeks[0];
|
|
82
|
+
if (!first) return [];
|
|
83
|
+
return first.map((c) => weekdayShort(c.ms, locale));
|
|
84
|
+
});
|
|
85
|
+
function chipTime(e) {
|
|
86
|
+
return isAllDay(e) ? "" : fmtTime(e.start, locale);
|
|
87
|
+
}
|
|
88
|
+
</script>
|
|
89
|
+
|
|
90
|
+
<div
|
|
91
|
+
class="mg"
|
|
92
|
+
class:mg--auto={autoHeight}
|
|
93
|
+
style={style || undefined}
|
|
94
|
+
style:height={autoHeight ? undefined : height ? `${height}px` : '100%'}
|
|
95
|
+
role="grid"
|
|
96
|
+
aria-label={L.month}
|
|
97
|
+
>
|
|
98
|
+
<div class="mg-head" role="row">
|
|
99
|
+
{#each weekdayLabels as name (name)}
|
|
100
|
+
<div class="mg-head-cell" role="columnheader">{name}</div>
|
|
101
|
+
{/each}
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
<div class="mg-body" style:--mg-rows={weeks.length}>
|
|
105
|
+
{#each weeks as row, ri (ri)}
|
|
106
|
+
{#each row as cell (cell.ms)}
|
|
107
|
+
<div
|
|
108
|
+
class="mg-cell"
|
|
109
|
+
class:mg-cell--out={!cell.inMonth}
|
|
110
|
+
class:mg-cell--today={cell.isToday}
|
|
111
|
+
class:mg-cell--weekend={cell.isWeekend}
|
|
112
|
+
class:mg-cell--disabled={cell.isDisabled}
|
|
113
|
+
class:mg-cell--clickable={!!ondayclick && !cell.isDisabled}
|
|
114
|
+
role="gridcell"
|
|
115
|
+
tabindex={ondayclick && !cell.isDisabled ? 0 : undefined}
|
|
116
|
+
onclick={() => {
|
|
117
|
+
if (!cell.isDisabled) ondayclick?.(cell.date);
|
|
118
|
+
}}
|
|
119
|
+
onkeydown={(e) => {
|
|
120
|
+
if ((e.key === 'Enter' || e.key === ' ') && !cell.isDisabled) {
|
|
121
|
+
e.preventDefault();
|
|
122
|
+
ondayclick?.(cell.date);
|
|
123
|
+
}
|
|
124
|
+
}}
|
|
125
|
+
>
|
|
126
|
+
<span class="mg-daynum" class:mg-daynum--today={cell.isToday}>
|
|
127
|
+
{cell.dayNum}
|
|
128
|
+
</span>
|
|
129
|
+
<div class="mg-chips">
|
|
130
|
+
{#each cell.chips as ev (ev.id + cell.ms)}
|
|
131
|
+
{#if eventSnippet}
|
|
132
|
+
<button
|
|
133
|
+
class="mg-chip mg-chip--custom"
|
|
134
|
+
class:mg-chip--selected={ev.id === selectedEventId}
|
|
135
|
+
onclick={(e) => {
|
|
136
|
+
e.stopPropagation();
|
|
137
|
+
oneventclick?.(ev);
|
|
138
|
+
}}
|
|
139
|
+
onmouseenter={() => oneventhover?.(ev)}
|
|
140
|
+
>
|
|
141
|
+
{@render eventSnippet(ev)}
|
|
142
|
+
</button>
|
|
143
|
+
{:else}
|
|
144
|
+
<button
|
|
145
|
+
class="mg-chip"
|
|
146
|
+
class:mg-chip--selected={ev.id === selectedEventId}
|
|
147
|
+
class:mg-chip--cancelled={ev.status === 'cancelled'}
|
|
148
|
+
style:--mg-chip-color={ev.color ?? 'var(--dt-accent)'}
|
|
149
|
+
title={ev.title}
|
|
150
|
+
onclick={(e) => {
|
|
151
|
+
e.stopPropagation();
|
|
152
|
+
oneventclick?.(ev);
|
|
153
|
+
}}
|
|
154
|
+
onmouseenter={() => oneventhover?.(ev)}
|
|
155
|
+
>
|
|
156
|
+
<span class="mg-chip-dot" aria-hidden="true"></span>
|
|
157
|
+
{#if chipTime(ev)}<span class="mg-chip-time">{chipTime(ev)}</span>{/if}
|
|
158
|
+
<span class="mg-chip-title">{ev.title}</span>
|
|
159
|
+
</button>
|
|
160
|
+
{/if}
|
|
161
|
+
{/each}
|
|
162
|
+
{#if cell.overflow > 0}
|
|
163
|
+
<span class="mg-more">{L.nMore(cell.overflow)}</span>
|
|
164
|
+
{/if}
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
{/each}
|
|
168
|
+
{/each}
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
|
|
172
|
+
<style>
|
|
173
|
+
.mg {
|
|
174
|
+
display: flex;
|
|
175
|
+
flex-direction: column;
|
|
176
|
+
background: var(--dt-bg);
|
|
177
|
+
color: var(--dt-text);
|
|
178
|
+
font-family: var(--dt-sans);
|
|
179
|
+
overflow: hidden;
|
|
180
|
+
}
|
|
181
|
+
.mg--auto {
|
|
182
|
+
height: auto;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.mg-head {
|
|
186
|
+
display: grid;
|
|
187
|
+
grid-template-columns: repeat(7, 1fr);
|
|
188
|
+
border-bottom: 1px solid var(--dt-border);
|
|
189
|
+
flex: none;
|
|
190
|
+
}
|
|
191
|
+
.mg-head-cell {
|
|
192
|
+
padding: 6px 8px;
|
|
193
|
+
font-family: var(--dt-mono);
|
|
194
|
+
font-size: 11px;
|
|
195
|
+
font-weight: 600;
|
|
196
|
+
text-transform: uppercase;
|
|
197
|
+
letter-spacing: 0.06em;
|
|
198
|
+
color: var(--dt-text-3);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.mg-body {
|
|
202
|
+
flex: 1;
|
|
203
|
+
display: grid;
|
|
204
|
+
grid-template-columns: repeat(7, 1fr);
|
|
205
|
+
grid-template-rows: repeat(var(--mg-rows, 5), minmax(88px, 1fr));
|
|
206
|
+
min-height: 0;
|
|
207
|
+
}
|
|
208
|
+
.mg--auto .mg-body {
|
|
209
|
+
grid-template-rows: repeat(var(--mg-rows, 5), minmax(88px, auto));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.mg-cell {
|
|
213
|
+
display: flex;
|
|
214
|
+
flex-direction: column;
|
|
215
|
+
gap: 3px;
|
|
216
|
+
padding: 6px;
|
|
217
|
+
border-right: 1px solid var(--dt-border-day);
|
|
218
|
+
border-bottom: 1px solid var(--dt-border-day);
|
|
219
|
+
min-width: 0;
|
|
220
|
+
overflow: hidden;
|
|
221
|
+
text-align: left;
|
|
222
|
+
}
|
|
223
|
+
.mg-cell:nth-child(7n) {
|
|
224
|
+
border-right: none;
|
|
225
|
+
}
|
|
226
|
+
.mg-cell--weekend {
|
|
227
|
+
background: var(--dt-weekend-bg);
|
|
228
|
+
}
|
|
229
|
+
.mg-cell--out {
|
|
230
|
+
opacity: 0.45;
|
|
231
|
+
}
|
|
232
|
+
.mg-cell--today {
|
|
233
|
+
background: var(--dt-today-bg);
|
|
234
|
+
}
|
|
235
|
+
.mg-cell--disabled {
|
|
236
|
+
opacity: 0.35;
|
|
237
|
+
pointer-events: none;
|
|
238
|
+
}
|
|
239
|
+
.mg-cell--clickable {
|
|
240
|
+
cursor: pointer;
|
|
241
|
+
}
|
|
242
|
+
.mg-cell--clickable:hover {
|
|
243
|
+
background: var(--dt-hover);
|
|
244
|
+
}
|
|
245
|
+
.mg-cell--clickable:focus-visible {
|
|
246
|
+
outline: 2px solid var(--dt-accent);
|
|
247
|
+
outline-offset: -2px;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.mg-daynum {
|
|
251
|
+
flex: none;
|
|
252
|
+
font-family: var(--dt-mono);
|
|
253
|
+
font-size: 12px;
|
|
254
|
+
font-weight: 600;
|
|
255
|
+
color: var(--dt-text-2);
|
|
256
|
+
width: 22px;
|
|
257
|
+
height: 22px;
|
|
258
|
+
display: inline-flex;
|
|
259
|
+
align-items: center;
|
|
260
|
+
justify-content: center;
|
|
261
|
+
border-radius: 999px;
|
|
262
|
+
}
|
|
263
|
+
.mg-daynum--today {
|
|
264
|
+
background: var(--dt-accent);
|
|
265
|
+
color: var(--dt-btn-text);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.mg-chips {
|
|
269
|
+
display: flex;
|
|
270
|
+
flex-direction: column;
|
|
271
|
+
gap: 2px;
|
|
272
|
+
min-height: 0;
|
|
273
|
+
overflow: hidden;
|
|
274
|
+
}
|
|
275
|
+
.mg-chip {
|
|
276
|
+
display: flex;
|
|
277
|
+
align-items: center;
|
|
278
|
+
gap: 5px;
|
|
279
|
+
border: none;
|
|
280
|
+
background: none;
|
|
281
|
+
padding: 2px 4px;
|
|
282
|
+
border-radius: 5px;
|
|
283
|
+
font-family: var(--dt-sans);
|
|
284
|
+
font-size: 12px;
|
|
285
|
+
line-height: 1.3;
|
|
286
|
+
color: var(--dt-text);
|
|
287
|
+
cursor: pointer;
|
|
288
|
+
min-width: 0;
|
|
289
|
+
text-align: left;
|
|
290
|
+
}
|
|
291
|
+
.mg-chip:hover {
|
|
292
|
+
background: var(--dt-hover);
|
|
293
|
+
}
|
|
294
|
+
.mg-chip--selected {
|
|
295
|
+
background: var(--dt-accent-dim);
|
|
296
|
+
}
|
|
297
|
+
.mg-chip--cancelled {
|
|
298
|
+
text-decoration: line-through;
|
|
299
|
+
opacity: 0.55;
|
|
300
|
+
}
|
|
301
|
+
.mg-chip-dot {
|
|
302
|
+
flex: none;
|
|
303
|
+
width: 7px;
|
|
304
|
+
height: 7px;
|
|
305
|
+
border-radius: 999px;
|
|
306
|
+
background: var(--mg-chip-color);
|
|
307
|
+
}
|
|
308
|
+
.mg-chip-time {
|
|
309
|
+
flex: none;
|
|
310
|
+
font-family: var(--dt-mono);
|
|
311
|
+
font-size: 11px;
|
|
312
|
+
color: var(--dt-text-2);
|
|
313
|
+
}
|
|
314
|
+
.mg-chip-title {
|
|
315
|
+
overflow: hidden;
|
|
316
|
+
text-overflow: ellipsis;
|
|
317
|
+
white-space: nowrap;
|
|
318
|
+
}
|
|
319
|
+
.mg-more {
|
|
320
|
+
padding: 1px 4px;
|
|
321
|
+
font-family: var(--dt-mono);
|
|
322
|
+
font-size: 11px;
|
|
323
|
+
color: var(--dt-text-3);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
@media (max-width: 640px) {
|
|
327
|
+
.mg-body {
|
|
328
|
+
grid-template-rows: repeat(var(--mg-rows, 5), minmax(64px, 1fr));
|
|
329
|
+
}
|
|
330
|
+
.mg-chip-time {
|
|
331
|
+
display: none;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
</style>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { TimelineEvent } from '../../core/types.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
events?: TimelineEvent[];
|
|
4
|
+
style?: string;
|
|
5
|
+
height?: number | null;
|
|
6
|
+
locale?: string;
|
|
7
|
+
focusDate?: Date;
|
|
8
|
+
oneventclick?: (event: TimelineEvent) => void;
|
|
9
|
+
selectedEventId?: string | null;
|
|
10
|
+
readOnly?: boolean;
|
|
11
|
+
[key: string]: unknown;
|
|
12
|
+
}
|
|
13
|
+
declare const MonthGrid: import("svelte").Component<Props, {}, "">;
|
|
14
|
+
type MonthGrid = ReturnType<typeof MonthGrid>;
|
|
15
|
+
export default MonthGrid;
|