@nomideusz/svelte-calendar 0.5.2 → 0.6.3
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 +408 -178
- package/dist/adapters/memory.d.ts +7 -9
- package/dist/adapters/memory.js +8 -23
- package/dist/adapters/recurring.d.ts +8 -6
- package/dist/adapters/recurring.js +28 -30
- package/dist/calendar/Calendar.svelte +776 -356
- package/dist/calendar/Calendar.svelte.d.ts +57 -6
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +1 -1
- package/dist/core/locale.js +2 -2
- package/dist/core/palette.d.ts +5 -0
- package/dist/core/palette.js +8 -0
- package/dist/core/types.d.ts +14 -0
- package/dist/engine/event-store.svelte.d.ts +0 -17
- package/dist/engine/event-store.svelte.js +30 -17
- package/dist/engine/index.d.ts +1 -1
- package/dist/engine/view-state.svelte.d.ts +10 -5
- package/dist/engine/view-state.svelte.js +32 -16
- package/dist/index.d.ts +7 -7
- package/dist/index.js +3 -4
- package/dist/primitives/DayHeader.svelte +103 -103
- package/dist/primitives/EmptySlot.svelte +105 -105
- package/dist/primitives/EventBlock.svelte +312 -312
- package/dist/primitives/NowIndicator.svelte +178 -178
- package/dist/primitives/TimeGutter.svelte +104 -104
- package/dist/theme/auto.d.ts +53 -0
- package/dist/theme/auto.js +534 -0
- package/dist/theme/index.d.ts +3 -1
- package/dist/theme/index.js +3 -1
- package/dist/theme/presets.d.ts +20 -6
- package/dist/theme/presets.js +55 -42
- package/dist/views/agenda/AgendaDay.svelte +1073 -976
- package/dist/views/agenda/AgendaWeek.svelte +934 -775
- package/dist/views/agenda/index.d.ts +0 -2
- package/dist/views/agenda/index.js +0 -2
- package/dist/views/index.d.ts +3 -2
- package/dist/views/index.js +3 -2
- package/dist/views/mobile/Mobile.svelte +17 -0
- package/dist/views/mobile/Mobile.svelte.d.ts +7 -0
- package/dist/views/mobile/MobileDay.svelte +702 -0
- package/dist/views/mobile/MobileDay.svelte.d.ts +20 -0
- package/dist/views/mobile/MobileWeek.svelte +461 -0
- package/dist/views/mobile/MobileWeek.svelte.d.ts +20 -0
- package/dist/views/mobile/index.d.ts +1 -0
- package/dist/views/mobile/index.js +1 -0
- package/dist/views/planner/PlannerDay.svelte +1129 -957
- package/dist/views/planner/PlannerWeek.svelte +949 -840
- package/dist/widget/CalendarWidget.svelte +142 -157
- package/package.json +2 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { TimelineEvent } from '../../core/types.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
height?: number | null;
|
|
4
|
+
events?: TimelineEvent[];
|
|
5
|
+
style?: string;
|
|
6
|
+
focusDate?: Date;
|
|
7
|
+
locale?: string;
|
|
8
|
+
oneventclick?: (event: TimelineEvent) => void;
|
|
9
|
+
oneventcreate?: (range: {
|
|
10
|
+
start: Date;
|
|
11
|
+
end: Date;
|
|
12
|
+
}) => void;
|
|
13
|
+
selectedEventId?: string | null;
|
|
14
|
+
readOnly?: boolean;
|
|
15
|
+
visibleHours?: [number, number];
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
}
|
|
18
|
+
declare const MobileDay: import("svelte").Component<Props, {}, "">;
|
|
19
|
+
type MobileDay = ReturnType<typeof MobileDay>;
|
|
20
|
+
export default MobileDay;
|
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
MobileWeek — touch-first 7-day overview.
|
|
3
|
+
|
|
4
|
+
Compact week grid with day columns. Each day shows a condensed
|
|
5
|
+
list of events. Tap a day to drill into MobileDay. Swipe
|
|
6
|
+
left/right to navigate weeks.
|
|
7
|
+
-->
|
|
8
|
+
<script lang="ts">
|
|
9
|
+
import { getContext } from 'svelte';
|
|
10
|
+
import { createClock } from '../../core/clock.svelte.js';
|
|
11
|
+
import type { TimelineEvent } from '../../core/types.js';
|
|
12
|
+
import type { ViewState } from '../../engine/view-state.svelte.js';
|
|
13
|
+
import { DAY_MS, sod, isAllDay, isMultiDay } from '../../core/time.js';
|
|
14
|
+
import { startOfWeek as sowFn } from '../../core/time.js';
|
|
15
|
+
import { fmtTime as _fmtTime, weekdayShort, getLabels } from '../../core/locale.js';
|
|
16
|
+
|
|
17
|
+
const L = $derived(getLabels());
|
|
18
|
+
|
|
19
|
+
interface Props {
|
|
20
|
+
mondayStart?: boolean;
|
|
21
|
+
locale?: string;
|
|
22
|
+
height?: number | null;
|
|
23
|
+
events?: TimelineEvent[];
|
|
24
|
+
style?: string;
|
|
25
|
+
focusDate?: Date;
|
|
26
|
+
oneventclick?: (event: TimelineEvent) => void;
|
|
27
|
+
oneventcreate?: (range: { start: Date; end: Date }) => void;
|
|
28
|
+
selectedEventId?: string | null;
|
|
29
|
+
readOnly?: boolean;
|
|
30
|
+
[key: string]: unknown;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let {
|
|
34
|
+
mondayStart = true,
|
|
35
|
+
locale,
|
|
36
|
+
height = null,
|
|
37
|
+
events = [],
|
|
38
|
+
style = '',
|
|
39
|
+
focusDate,
|
|
40
|
+
oneventclick,
|
|
41
|
+
oneventcreate,
|
|
42
|
+
selectedEventId = null,
|
|
43
|
+
readOnly = false,
|
|
44
|
+
}: Props = $props();
|
|
45
|
+
|
|
46
|
+
// ── Context ────────────────────────────────────────
|
|
47
|
+
const viewState = getContext<ViewState>('calendar:viewState') as ViewState | undefined;
|
|
48
|
+
const loadRangeCtx = getContext<{ current: { start: Date; end: Date } | null; set: (r: { start: Date; end: Date } | null) => void }>('calendar:loadRange') as { current: { start: Date; end: Date } | null; set: (r: { start: Date; end: Date } | null) => void } | undefined;
|
|
49
|
+
const equalDaysCtx = getContext<{ current: boolean }>('calendar:equalDays') as { current: boolean } | undefined;
|
|
50
|
+
const showDatesCtx = getContext<{ current: boolean }>('calendar:showDates') as { current: boolean } | undefined;
|
|
51
|
+
const hideDaysCtx = getContext<{ current: number[] | undefined }>('calendar:hideDays') as { current: number[] | undefined } | undefined;
|
|
52
|
+
const callbacksCtx = getContext<{ oneventhover?: (event: TimelineEvent) => void }>('calendar:callbacks') as { oneventhover?: (event: TimelineEvent) => void } | undefined;
|
|
53
|
+
const disabledDatesCtx = getContext<{ current: Date[] | undefined }>('calendar:disabledDates') as { current: Date[] | undefined } | undefined;
|
|
54
|
+
const autoHeightCtx = getContext<{ current: boolean }>('calendar:autoHeight') as { current: boolean } | undefined;
|
|
55
|
+
|
|
56
|
+
const equalDays = $derived(equalDaysCtx?.current ?? false);
|
|
57
|
+
const autoHeight = $derived(autoHeightCtx?.current ?? false);
|
|
58
|
+
const showDates = $derived(showDatesCtx?.current ?? true);
|
|
59
|
+
const hideDays = $derived(hideDaysCtx?.current);
|
|
60
|
+
const oneventhover = $derived(callbacksCtx?.oneventhover);
|
|
61
|
+
const disabledDates = $derived(disabledDatesCtx?.current);
|
|
62
|
+
const disabledSet = $derived(new Set(disabledDates?.map(d => sod(d.getTime())) ?? []));
|
|
63
|
+
|
|
64
|
+
const clock = createClock();
|
|
65
|
+
|
|
66
|
+
// ── Config ─────────────────────────────────────────
|
|
67
|
+
const MAX_EVENTS = 3;
|
|
68
|
+
const customDays = $derived(viewState?.dayCount ?? 7);
|
|
69
|
+
|
|
70
|
+
// ── Derived week data ──────────────────────────────
|
|
71
|
+
const todayMs = $derived(clock.today);
|
|
72
|
+
const focusMs = $derived(focusDate ? sod(focusDate.getTime()) : todayMs);
|
|
73
|
+
|
|
74
|
+
const weekStart = $derived(
|
|
75
|
+
customDays === 7
|
|
76
|
+
? sowFn(focusMs, mondayStart)
|
|
77
|
+
: sod(focusMs)
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
// ── Load range ─────────────────────────────────────
|
|
81
|
+
$effect(() => {
|
|
82
|
+
if (!loadRangeCtx) return;
|
|
83
|
+
const rangeStart = new Date(weekStart - 7 * DAY_MS);
|
|
84
|
+
const rangeEnd = new Date(weekStart + (customDays + 7) * DAY_MS);
|
|
85
|
+
loadRangeCtx.set({ start: rangeStart, end: rangeEnd });
|
|
86
|
+
return () => loadRangeCtx.set(null);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// ── Day cells ──────────────────────────────────────
|
|
90
|
+
interface DayCell {
|
|
91
|
+
ms: number;
|
|
92
|
+
dayNum: number;
|
|
93
|
+
dayName: string;
|
|
94
|
+
isToday: boolean;
|
|
95
|
+
isPast: boolean;
|
|
96
|
+
isDisabled: boolean;
|
|
97
|
+
isWeekend: boolean;
|
|
98
|
+
events: TimelineEvent[];
|
|
99
|
+
allDayCount: number;
|
|
100
|
+
totalCount: number;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const dayCells = $derived.by(() => {
|
|
104
|
+
const result: DayCell[] = [];
|
|
105
|
+
const hideSet = new Set(hideDays ?? []);
|
|
106
|
+
|
|
107
|
+
for (let i = 0; i < customDays; i++) {
|
|
108
|
+
const ms = weekStart + i * DAY_MS;
|
|
109
|
+
const d = new Date(ms);
|
|
110
|
+
const jsDay = d.getDay();
|
|
111
|
+
const isoDay = jsDay === 0 ? 7 : jsDay;
|
|
112
|
+
|
|
113
|
+
if (hideSet.has(isoDay)) continue;
|
|
114
|
+
|
|
115
|
+
const isToday = ms === todayMs;
|
|
116
|
+
const isPast = equalDays ? false : ms < todayMs;
|
|
117
|
+
const isWeekend = jsDay === 0 || jsDay === 6;
|
|
118
|
+
const isDisabled = disabledSet.has(ms);
|
|
119
|
+
const dayEnd = ms + DAY_MS;
|
|
120
|
+
|
|
121
|
+
const dayEvents = events
|
|
122
|
+
.filter(ev => ev.start.getTime() < dayEnd && ev.end.getTime() > ms)
|
|
123
|
+
.sort((a, b) => a.start.getTime() - b.start.getTime());
|
|
124
|
+
|
|
125
|
+
const allDayCount = dayEvents.filter(ev => isAllDay(ev) || isMultiDay(ev)).length;
|
|
126
|
+
|
|
127
|
+
result.push({
|
|
128
|
+
ms,
|
|
129
|
+
dayNum: d.getDate(),
|
|
130
|
+
dayName: weekdayShort(ms, locale),
|
|
131
|
+
isToday,
|
|
132
|
+
isPast,
|
|
133
|
+
isDisabled,
|
|
134
|
+
isWeekend,
|
|
135
|
+
events: dayEvents,
|
|
136
|
+
allDayCount,
|
|
137
|
+
totalCount: dayEvents.length,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return result;
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// ── Format helpers ─────────────────────────────────
|
|
144
|
+
function fmtTime(d: Date): string {
|
|
145
|
+
return _fmtTime(d, locale);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ── Touch swipe ────────────────────────────────────
|
|
149
|
+
let touchStartX = 0;
|
|
150
|
+
let touchStartY = 0;
|
|
151
|
+
let swiping = false;
|
|
152
|
+
let swipeOffset = $state(0);
|
|
153
|
+
const SWIPE_THRESHOLD = 60;
|
|
154
|
+
|
|
155
|
+
function onTouchStart(e: TouchEvent) {
|
|
156
|
+
const t = e.touches[0];
|
|
157
|
+
touchStartX = t.clientX;
|
|
158
|
+
touchStartY = t.clientY;
|
|
159
|
+
swiping = true;
|
|
160
|
+
swipeOffset = 0;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function onTouchMove(e: TouchEvent) {
|
|
164
|
+
if (!swiping) return;
|
|
165
|
+
const t = e.touches[0];
|
|
166
|
+
const dx = t.clientX - touchStartX;
|
|
167
|
+
const dy = t.clientY - touchStartY;
|
|
168
|
+
if (Math.abs(dy) > Math.abs(dx) * 0.8) { swiping = false; return; }
|
|
169
|
+
swipeOffset = dx;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function onTouchEnd() {
|
|
173
|
+
if (!swiping) { swipeOffset = 0; return; }
|
|
174
|
+
if (Math.abs(swipeOffset) > SWIPE_THRESHOLD) {
|
|
175
|
+
if (swipeOffset > 0) {
|
|
176
|
+
viewState?.prev();
|
|
177
|
+
} else {
|
|
178
|
+
viewState?.next();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
swipeOffset = 0;
|
|
182
|
+
swiping = false;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ── Day tap → switch to day mode ───────────────────
|
|
186
|
+
function handleDayTap(dayMs: number) {
|
|
187
|
+
if (viewState) {
|
|
188
|
+
viewState.setFocusDate(new Date(dayMs));
|
|
189
|
+
// Switch to day-mobile view if available
|
|
190
|
+
const currentView = viewState.view;
|
|
191
|
+
const dayView = currentView.replace('week', 'day');
|
|
192
|
+
viewState.setView(dayView);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
</script>
|
|
196
|
+
|
|
197
|
+
<div
|
|
198
|
+
class="mw"
|
|
199
|
+
class:mw--auto={autoHeight}
|
|
200
|
+
style={style || undefined}
|
|
201
|
+
style:height={autoHeight ? undefined : (height ? `${height}px` : '100%')}
|
|
202
|
+
role="region"
|
|
203
|
+
aria-label={L.weekAhead}
|
|
204
|
+
ontouchstart={onTouchStart}
|
|
205
|
+
ontouchmove={onTouchMove}
|
|
206
|
+
ontouchend={onTouchEnd}
|
|
207
|
+
>
|
|
208
|
+
<!-- Vertical day list -->
|
|
209
|
+
<div class="mw-list" role="list">
|
|
210
|
+
{#each dayCells as cell (cell.ms)}
|
|
211
|
+
<button
|
|
212
|
+
class="mw-row"
|
|
213
|
+
class:mw-row--today={cell.isToday}
|
|
214
|
+
class:mw-row--past={cell.isPast}
|
|
215
|
+
class:mw-row--weekend={cell.isWeekend}
|
|
216
|
+
class:mw-row--disabled={cell.isDisabled}
|
|
217
|
+
onclick={() => handleDayTap(cell.ms)}
|
|
218
|
+
aria-label="{cell.dayName} {cell.dayNum}"
|
|
219
|
+
>
|
|
220
|
+
<!-- Date column -->
|
|
221
|
+
<div class="mw-date">
|
|
222
|
+
<span class="mw-day-name" class:mw-day-name--today={cell.isToday}>{cell.dayName}</span>
|
|
223
|
+
{#if showDates}
|
|
224
|
+
<span class="mw-day-num" class:mw-day-num--today={cell.isToday}>{cell.dayNum}</span>
|
|
225
|
+
{/if}
|
|
226
|
+
</div>
|
|
227
|
+
|
|
228
|
+
<!-- Events column -->
|
|
229
|
+
<div class="mw-events">
|
|
230
|
+
{#if cell.events.length === 0}
|
|
231
|
+
<span class="mw-empty">{L.noEvents}</span>
|
|
232
|
+
{:else}
|
|
233
|
+
{#each cell.events.slice(0, MAX_EVENTS) as ev (ev.id)}
|
|
234
|
+
<div
|
|
235
|
+
class="mw-ev"
|
|
236
|
+
class:mw-ev--selected={selectedEventId === ev.id}
|
|
237
|
+
class:mw-ev--allday={isAllDay(ev) || isMultiDay(ev)}
|
|
238
|
+
class:mw-ev--current={!isAllDay(ev) && !isMultiDay(ev) && ev.start.getTime() <= clock.tick && ev.end.getTime() > clock.tick}
|
|
239
|
+
style:--ev-color={ev.color ?? 'var(--dt-accent)'}
|
|
240
|
+
role="button"
|
|
241
|
+
tabindex="0"
|
|
242
|
+
onclick={(e) => { e.stopPropagation(); oneventclick?.(ev); }}
|
|
243
|
+
onkeydown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); e.stopPropagation(); oneventclick?.(ev); } }}
|
|
244
|
+
onpointerenter={() => oneventhover?.(ev)}
|
|
245
|
+
>
|
|
246
|
+
<span class="mw-ev-stripe"></span>
|
|
247
|
+
<div class="mw-ev-body">
|
|
248
|
+
<span class="mw-ev-title">{ev.title}</span>
|
|
249
|
+
{#if isAllDay(ev) || isMultiDay(ev)}
|
|
250
|
+
<span class="mw-ev-time">{L.allDay}</span>
|
|
251
|
+
{:else}
|
|
252
|
+
<span class="mw-ev-time">{fmtTime(ev.start)}</span>
|
|
253
|
+
{/if}
|
|
254
|
+
</div>
|
|
255
|
+
</div>
|
|
256
|
+
{/each}
|
|
257
|
+
{#if cell.totalCount > MAX_EVENTS}
|
|
258
|
+
<span class="mw-ev-more">{L.nMore(cell.totalCount - MAX_EVENTS)}</span>
|
|
259
|
+
{/if}
|
|
260
|
+
{/if}
|
|
261
|
+
</div>
|
|
262
|
+
|
|
263
|
+
<!-- Chevron -->
|
|
264
|
+
<svg class="mw-chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="14" height="14" aria-hidden="true"><path d="M6 3l5 5-5 5"/></svg>
|
|
265
|
+
</button>
|
|
266
|
+
{/each}
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
|
|
270
|
+
<style>
|
|
271
|
+
/* ─── Container ──────────────────────────────────── */
|
|
272
|
+
.mw {
|
|
273
|
+
position: relative;
|
|
274
|
+
display: flex;
|
|
275
|
+
flex-direction: column;
|
|
276
|
+
user-select: none;
|
|
277
|
+
font-variant-numeric: tabular-nums;
|
|
278
|
+
overflow: hidden;
|
|
279
|
+
background: var(--dt-bg, #fff);
|
|
280
|
+
-webkit-tap-highlight-color: transparent;
|
|
281
|
+
}
|
|
282
|
+
.mw--auto { overflow: visible; }
|
|
283
|
+
|
|
284
|
+
/* ─── Scrollable day list ────────────────────────── */
|
|
285
|
+
.mw-list {
|
|
286
|
+
flex: 1;
|
|
287
|
+
overflow-y: auto;
|
|
288
|
+
-webkit-overflow-scrolling: touch;
|
|
289
|
+
scrollbar-width: thin;
|
|
290
|
+
scrollbar-color: var(--dt-scrollbar, rgba(0, 0, 0, 0.08)) transparent;
|
|
291
|
+
}
|
|
292
|
+
.mw--auto .mw-list { overflow-y: visible; }
|
|
293
|
+
|
|
294
|
+
/* ─── Day row ────────────────────────────────────── */
|
|
295
|
+
.mw-row {
|
|
296
|
+
display: flex;
|
|
297
|
+
align-items: center;
|
|
298
|
+
gap: 12px;
|
|
299
|
+
padding: 10px 12px;
|
|
300
|
+
border: none;
|
|
301
|
+
background: transparent;
|
|
302
|
+
cursor: pointer;
|
|
303
|
+
transition: background 120ms;
|
|
304
|
+
text-align: left;
|
|
305
|
+
width: 100%;
|
|
306
|
+
-webkit-tap-highlight-color: transparent;
|
|
307
|
+
border-bottom: 1px solid var(--dt-border, rgba(0, 0, 0, 0.06));
|
|
308
|
+
min-height: 56px;
|
|
309
|
+
}
|
|
310
|
+
.mw-row:last-child {
|
|
311
|
+
border-bottom: none;
|
|
312
|
+
}
|
|
313
|
+
.mw-row:active {
|
|
314
|
+
background: color-mix(in srgb, var(--dt-accent, #2563eb) 6%, transparent);
|
|
315
|
+
}
|
|
316
|
+
.mw-row--today {
|
|
317
|
+
background: color-mix(in srgb, var(--dt-accent, #2563eb) 4%, transparent);
|
|
318
|
+
}
|
|
319
|
+
.mw-row--past {
|
|
320
|
+
opacity: 0.5;
|
|
321
|
+
}
|
|
322
|
+
.mw-row--disabled {
|
|
323
|
+
opacity: 0.3;
|
|
324
|
+
pointer-events: none;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/* ─── Date column ────────────────────────────────── */
|
|
328
|
+
.mw-date {
|
|
329
|
+
display: flex;
|
|
330
|
+
flex-direction: column;
|
|
331
|
+
align-items: center;
|
|
332
|
+
width: 40px;
|
|
333
|
+
flex-shrink: 0;
|
|
334
|
+
gap: 2px;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.mw-day-name {
|
|
338
|
+
font: 600 10px/1 var(--dt-sans, system-ui, sans-serif);
|
|
339
|
+
letter-spacing: 0.06em;
|
|
340
|
+
text-transform: uppercase;
|
|
341
|
+
color: var(--dt-text-3, rgba(0, 0, 0, 0.38));
|
|
342
|
+
}
|
|
343
|
+
.mw-day-name--today {
|
|
344
|
+
color: var(--dt-accent, #2563eb);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.mw-day-num {
|
|
348
|
+
font: 700 18px/1 var(--dt-sans, system-ui, sans-serif);
|
|
349
|
+
color: var(--dt-text, rgba(0, 0, 0, 0.87));
|
|
350
|
+
}
|
|
351
|
+
.mw-day-num--today {
|
|
352
|
+
background: var(--dt-accent, #2563eb);
|
|
353
|
+
color: var(--dt-btn-text, #fff);
|
|
354
|
+
width: 30px;
|
|
355
|
+
height: 30px;
|
|
356
|
+
display: inline-flex;
|
|
357
|
+
align-items: center;
|
|
358
|
+
justify-content: center;
|
|
359
|
+
border-radius: 50%;
|
|
360
|
+
font-size: 15px;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/* ─── Events column ──────────────────────────────── */
|
|
364
|
+
.mw-events {
|
|
365
|
+
flex: 1;
|
|
366
|
+
min-width: 0;
|
|
367
|
+
display: flex;
|
|
368
|
+
flex-direction: column;
|
|
369
|
+
gap: 4px;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.mw-empty {
|
|
373
|
+
font: 400 12px/1 var(--dt-sans, system-ui, sans-serif);
|
|
374
|
+
color: var(--dt-text-3, rgba(0, 0, 0, 0.25));
|
|
375
|
+
font-style: italic;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/* ─── Event chip ─────────────────────────────────── */
|
|
379
|
+
.mw-ev {
|
|
380
|
+
display: flex;
|
|
381
|
+
align-items: center;
|
|
382
|
+
gap: 0;
|
|
383
|
+
border-radius: 6px;
|
|
384
|
+
background: color-mix(in srgb, var(--ev-color) 10%, var(--dt-surface, #f9fafb));
|
|
385
|
+
overflow: hidden;
|
|
386
|
+
cursor: pointer;
|
|
387
|
+
transition: background 120ms;
|
|
388
|
+
-webkit-tap-highlight-color: transparent;
|
|
389
|
+
border: none;
|
|
390
|
+
text-align: left;
|
|
391
|
+
padding: 0;
|
|
392
|
+
}
|
|
393
|
+
.mw-ev:active {
|
|
394
|
+
background: color-mix(in srgb, var(--ev-color) 20%, var(--dt-surface, #f9fafb));
|
|
395
|
+
}
|
|
396
|
+
.mw-ev--selected {
|
|
397
|
+
box-shadow: 0 0 0 1.5px var(--ev-color);
|
|
398
|
+
}
|
|
399
|
+
.mw-ev--current {
|
|
400
|
+
background: color-mix(in srgb, var(--ev-color) 16%, var(--dt-surface, #f9fafb));
|
|
401
|
+
}
|
|
402
|
+
.mw-ev--allday {
|
|
403
|
+
background: color-mix(in srgb, var(--ev-color) 14%, var(--dt-surface, #f9fafb));
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.mw-ev-stripe {
|
|
407
|
+
width: 3px;
|
|
408
|
+
align-self: stretch;
|
|
409
|
+
background: var(--ev-color, var(--dt-accent));
|
|
410
|
+
flex-shrink: 0;
|
|
411
|
+
border-radius: 6px 0 0 6px;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
.mw-ev-body {
|
|
415
|
+
flex: 1;
|
|
416
|
+
min-width: 0;
|
|
417
|
+
padding: 5px 8px;
|
|
418
|
+
display: flex;
|
|
419
|
+
align-items: baseline;
|
|
420
|
+
gap: 6px;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.mw-ev-title {
|
|
424
|
+
font: 500 12px/1.2 var(--dt-sans, system-ui, sans-serif);
|
|
425
|
+
color: var(--dt-text, rgba(0, 0, 0, 0.87));
|
|
426
|
+
white-space: nowrap;
|
|
427
|
+
overflow: hidden;
|
|
428
|
+
text-overflow: ellipsis;
|
|
429
|
+
flex: 1;
|
|
430
|
+
min-width: 0;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.mw-ev-time {
|
|
434
|
+
font: 400 11px/1 var(--dt-mono, ui-monospace, monospace);
|
|
435
|
+
color: var(--dt-text-3, rgba(0, 0, 0, 0.38));
|
|
436
|
+
white-space: nowrap;
|
|
437
|
+
flex-shrink: 0;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.mw-ev-more {
|
|
441
|
+
font: 400 11px/1 var(--dt-sans, system-ui, sans-serif);
|
|
442
|
+
color: var(--dt-text-3, rgba(0, 0, 0, 0.38));
|
|
443
|
+
padding: 2px 0;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/* ─── Chevron ────────────────────────────────────── */
|
|
447
|
+
.mw-chevron {
|
|
448
|
+
flex-shrink: 0;
|
|
449
|
+
color: var(--dt-text-3, rgba(0, 0, 0, 0.2));
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/* ─── Focus ──────────────────────────────────────── */
|
|
453
|
+
.mw-row:focus-visible {
|
|
454
|
+
outline: 2px solid var(--dt-accent, #2563eb);
|
|
455
|
+
outline-offset: -2px;
|
|
456
|
+
}
|
|
457
|
+
.mw-ev:focus-visible {
|
|
458
|
+
outline: 2px solid var(--ev-color, var(--dt-accent));
|
|
459
|
+
outline-offset: 1px;
|
|
460
|
+
}
|
|
461
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { TimelineEvent } from '../../core/types.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
mondayStart?: boolean;
|
|
4
|
+
locale?: string;
|
|
5
|
+
height?: number | null;
|
|
6
|
+
events?: TimelineEvent[];
|
|
7
|
+
style?: string;
|
|
8
|
+
focusDate?: Date;
|
|
9
|
+
oneventclick?: (event: TimelineEvent) => void;
|
|
10
|
+
oneventcreate?: (range: {
|
|
11
|
+
start: Date;
|
|
12
|
+
end: Date;
|
|
13
|
+
}) => void;
|
|
14
|
+
selectedEventId?: string | null;
|
|
15
|
+
readOnly?: boolean;
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
}
|
|
18
|
+
declare const MobileWeek: import("svelte").Component<Props, {}, "">;
|
|
19
|
+
type MobileWeek = ReturnType<typeof MobileWeek>;
|
|
20
|
+
export default MobileWeek;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Mobile } from './Mobile.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Mobile } from './Mobile.svelte';
|