@getmicdrop/svelte-components 2.0.13 → 2.1.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/dist/__LIB_STORES__.js +30 -2
- package/dist/components/AboutShow/AboutShow.svelte +278 -0
- package/dist/components/AboutShow/AboutShow.svelte.d.ts +43 -0
- package/dist/components/AboutShow/AboutShow.svelte.d.ts.map +1 -0
- package/dist/components/Calendar/MiniMonthCalendar.svelte +1446 -0
- package/dist/components/Calendar/{Calendar.svelte.d.ts → MiniMonthCalendar.svelte.d.ts} +20 -21
- package/dist/components/Calendar/MiniMonthCalendar.svelte.d.ts.map +1 -0
- package/dist/components/DarkModeToggle.svelte +3 -1
- package/dist/components/DarkModeToggle.svelte.d.ts.map +1 -1
- package/dist/components/FAQs/FAQs.svelte +49 -0
- package/dist/components/{Calendar/QuarterView.svelte.d.ts → FAQs/FAQs.svelte.d.ts} +10 -10
- package/dist/components/FAQs/FAQs.svelte.d.ts.map +1 -0
- package/dist/components/Input/Input.svelte +100 -12
- package/dist/components/Input/Input.svelte.d.ts +12 -0
- package/dist/components/Input/Input.svelte.d.ts.map +1 -1
- package/dist/components/Input/OTPInput.svelte +1 -1
- package/dist/components/MonthSwitcher/MonthSwitcher.svelte +206 -0
- package/dist/components/MonthSwitcher/MonthSwitcher.svelte.d.ts +37 -0
- package/dist/components/MonthSwitcher/MonthSwitcher.svelte.d.ts.map +1 -0
- package/dist/components/OrderSummary/OrderSummary.svelte +553 -0
- package/dist/components/OrderSummary/OrderSummary.svelte.d.ts +65 -0
- package/dist/components/OrderSummary/OrderSummary.svelte.d.ts.map +1 -0
- package/dist/components/PublicCard/PublicCard.svelte +267 -0
- package/dist/components/{pages/performers/AvailabilityCalendarModal.svelte.d.ts → PublicCard/PublicCard.svelte.d.ts} +12 -14
- package/dist/components/PublicCard/PublicCard.svelte.d.ts.map +1 -0
- package/dist/components/ShowCard/ShowCard.svelte +240 -0
- package/dist/components/ShowCard/ShowCard.svelte.d.ts +39 -0
- package/dist/components/ShowCard/ShowCard.svelte.d.ts.map +1 -0
- package/dist/components/ShowTimeCard/ShowTimeCard.svelte +92 -0
- package/dist/components/{Calendar/QuarterView.stories.svelte.d.ts → ShowTimeCard/ShowTimeCard.svelte.d.ts} +17 -21
- package/dist/components/ShowTimeCard/ShowTimeCard.svelte.d.ts.map +1 -0
- package/dist/components/Spinner/Spinner.svelte +73 -17
- package/dist/components/Spinner/Spinner.svelte.d.ts +5 -3
- package/dist/components/Spinner/Spinner.svelte.d.ts.map +1 -1
- package/dist/components/pages/performers/ShowDetails.svelte.d.ts +2 -2
- package/dist/components/pages/performers/ShowItemCard.svelte.d.ts +6 -6
- package/dist/components/pages/performers/VenueItemCard.svelte.d.ts +2 -2
- package/dist/components/pages/shows/TabNavigation.svelte +7 -8
- package/dist/index.d.ts +8 -3
- package/dist/index.js +12 -3
- package/dist/services/EventService.js +75 -75
- package/dist/services/EventService.spec.js +217 -217
- package/dist/services/ShowService.spec.js +342 -342
- package/package.json +160 -160
- package/dist/components/Calendar/Calendar.spec.d.ts +0 -2
- package/dist/components/Calendar/Calendar.spec.d.ts.map +0 -1
- package/dist/components/Calendar/Calendar.spec.js +0 -131
- package/dist/components/Calendar/Calendar.svelte +0 -1115
- package/dist/components/Calendar/Calendar.svelte.d.ts.map +0 -1
- package/dist/components/Calendar/QuarterView.spec.d.ts +0 -2
- package/dist/components/Calendar/QuarterView.spec.d.ts.map +0 -1
- package/dist/components/Calendar/QuarterView.spec.js +0 -394
- package/dist/components/Calendar/QuarterView.stories.svelte +0 -134
- package/dist/components/Calendar/QuarterView.stories.svelte.d.ts.map +0 -1
- package/dist/components/Calendar/QuarterView.svelte +0 -736
- package/dist/components/Calendar/QuarterView.svelte.d.ts.map +0 -1
- package/dist/components/pages/performers/AvailabilityCalendarModal.svelte +0 -632
- package/dist/components/pages/performers/AvailabilityCalendarModal.svelte.d.ts.map +0 -1
|
@@ -1,736 +0,0 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import Icon from "../Icons/Icon.svelte";
|
|
3
|
-
import { getDay, getDaysInMonth, startOfMonth } from "date-fns";
|
|
4
|
-
import { writable } from "svelte/store";
|
|
5
|
-
import { fly } from "svelte/transition";
|
|
6
|
-
import { cubicOut } from "svelte/easing";
|
|
7
|
-
import Button from "../Button/Button.svelte";
|
|
8
|
-
|
|
9
|
-
export let currentevents = [];
|
|
10
|
-
export let selectedDates;
|
|
11
|
-
export let saveStatus = "";
|
|
12
|
-
|
|
13
|
-
let currentDate = new Date();
|
|
14
|
-
let currentYear = writable(currentDate.getUTCFullYear());
|
|
15
|
-
let currentMonth = writable(currentDate.getUTCMonth());
|
|
16
|
-
|
|
17
|
-
// Track slide direction for animations
|
|
18
|
-
let slideDirection = 0; // -1 for left (prev), 1 for right (next), 0 for no animation
|
|
19
|
-
|
|
20
|
-
let events = {};
|
|
21
|
-
|
|
22
|
-
// Process `currentevents` into a dictionary
|
|
23
|
-
$: {
|
|
24
|
-
const newEvents = {};
|
|
25
|
-
currentevents.forEach((event) => {
|
|
26
|
-
// event.date is already in YYYY-MM-DD format from normalizeDate
|
|
27
|
-
newEvents[event.date] = true;
|
|
28
|
-
});
|
|
29
|
-
events = newEvents;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Check if a day is an event
|
|
33
|
-
function isEvent(year, month, day) {
|
|
34
|
-
const dateKey = `${year}-${String(month + 1).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
|
35
|
-
return events[dateKey];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Haptic feedback helper - iOS Safari compatible
|
|
39
|
-
function triggerHaptic(style = 'light') {
|
|
40
|
-
// Try iOS Haptic Engine first (iOS 13+)
|
|
41
|
-
if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.haptic) {
|
|
42
|
-
window.webkit.messageHandlers.haptic.postMessage(style);
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Try Taptic Engine (older method)
|
|
47
|
-
if (window.TapticEngine) {
|
|
48
|
-
if (style === 'light') {
|
|
49
|
-
window.TapticEngine.impact({ style: 'light' });
|
|
50
|
-
} else if (style === 'medium') {
|
|
51
|
-
window.TapticEngine.impact({ style: 'medium' });
|
|
52
|
-
} else if (style === 'success') {
|
|
53
|
-
window.TapticEngine.notification({ type: 'success' });
|
|
54
|
-
}
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Fallback to Vibration API (Android)
|
|
59
|
-
if (navigator.vibrate) {
|
|
60
|
-
const duration = style === 'light' ? 10 : style === 'medium' ? 20 : [10, 50, 10];
|
|
61
|
-
navigator.vibrate(duration);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Toggle selection of an event day with optimistic update
|
|
66
|
-
function toggleDateSelection(year, month, day) {
|
|
67
|
-
if (isSwiping) return;
|
|
68
|
-
|
|
69
|
-
const dateKey = `${year}-${String(month + 1).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
|
70
|
-
|
|
71
|
-
// Determine if selecting or deselecting for haptic feedback
|
|
72
|
-
const isCurrentlySelected = $selectedDates.includes(dateKey);
|
|
73
|
-
|
|
74
|
-
selectedDates.update((dates) => {
|
|
75
|
-
if (dates.includes(dateKey)) {
|
|
76
|
-
// Deselecting - medium haptic
|
|
77
|
-
triggerHaptic('medium');
|
|
78
|
-
return dates.filter((date) => date !== dateKey);
|
|
79
|
-
} else {
|
|
80
|
-
// Selecting - light haptic
|
|
81
|
-
triggerHaptic('light');
|
|
82
|
-
return [...dates, dateKey];
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Check if a day is in the past
|
|
88
|
-
function isPastDate(year, month, day) {
|
|
89
|
-
const today = new Date();
|
|
90
|
-
const date = new Date(year, month, day);
|
|
91
|
-
return date < today.setHours(0, 0, 0, 0);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
let prevAnimating = false;
|
|
95
|
-
let nextAnimating = false;
|
|
96
|
-
let todayButtonPressed = false;
|
|
97
|
-
|
|
98
|
-
// Touch swipe detection
|
|
99
|
-
let touchStartX = 0;
|
|
100
|
-
let touchStartY = 0;
|
|
101
|
-
let touchStartTime = 0;
|
|
102
|
-
let isSwiping = false;
|
|
103
|
-
|
|
104
|
-
function handleTouchStart(e) {
|
|
105
|
-
touchStartX = e.touches[0].clientX;
|
|
106
|
-
touchStartY = e.touches[0].clientY;
|
|
107
|
-
touchStartTime = Date.now();
|
|
108
|
-
isSwiping = false;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function handleTouchMove(e) {
|
|
112
|
-
if (!touchStartX) return;
|
|
113
|
-
|
|
114
|
-
const touchCurrentX = e.touches[0].clientX;
|
|
115
|
-
const touchCurrentY = e.touches[0].clientY;
|
|
116
|
-
const diffX = touchStartX - touchCurrentX;
|
|
117
|
-
const diffY = touchStartY - touchCurrentY;
|
|
118
|
-
|
|
119
|
-
// If horizontal movement is greater than vertical, it's a swipe
|
|
120
|
-
if (Math.abs(diffX) > Math.abs(diffY) && Math.abs(diffX) > 10) {
|
|
121
|
-
isSwiping = true;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function handleTouchEnd(e) {
|
|
126
|
-
if (!touchStartX || !isSwiping) {
|
|
127
|
-
touchStartX = 0;
|
|
128
|
-
touchStartY = 0;
|
|
129
|
-
isSwiping = false;
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const touchEndX = e.changedTouches[0].clientX;
|
|
134
|
-
const touchEndY = e.changedTouches[0].clientY;
|
|
135
|
-
const diffX = touchStartX - touchEndX;
|
|
136
|
-
const diffY = touchStartY - touchEndY;
|
|
137
|
-
const diffTime = Date.now() - touchStartTime;
|
|
138
|
-
|
|
139
|
-
// Swipe detection: horizontal movement > 50px, mostly horizontal, and quick (< 300ms)
|
|
140
|
-
if (Math.abs(diffX) > 50 && Math.abs(diffX) > Math.abs(diffY) * 2 && diffTime < 300) {
|
|
141
|
-
if (diffX > 0) {
|
|
142
|
-
// Swiped left -> go to next month
|
|
143
|
-
handleNext(true); // Pass true to indicate swipe
|
|
144
|
-
} else {
|
|
145
|
-
// Swiped right -> go to previous month
|
|
146
|
-
handlePrev(true); // Pass true to indicate swipe
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
touchStartX = 0;
|
|
151
|
-
touchStartY = 0;
|
|
152
|
-
isSwiping = false;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function handleNext(isSwipe = false) {
|
|
156
|
-
if (nextAnimating) return;
|
|
157
|
-
if (!isSwipe) nextAnimating = true; // Only animate chevron on button click
|
|
158
|
-
|
|
159
|
-
// Haptic feedback for month navigation
|
|
160
|
-
triggerHaptic('medium');
|
|
161
|
-
|
|
162
|
-
slideDirection = 1; // Slide from right
|
|
163
|
-
currentMonth.update((month) => {
|
|
164
|
-
if (month === 11) {
|
|
165
|
-
currentYear.update((year) => year + 1);
|
|
166
|
-
return 0;
|
|
167
|
-
}
|
|
168
|
-
return month + 1;
|
|
169
|
-
});
|
|
170
|
-
setTimeout(() => {
|
|
171
|
-
nextAnimating = false;
|
|
172
|
-
slideDirection = 0;
|
|
173
|
-
}, 300);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function handlePrev(isSwipe = false) {
|
|
177
|
-
if (prevAnimating) return;
|
|
178
|
-
if (!isSwipe) prevAnimating = true; // Only animate chevron on button click
|
|
179
|
-
|
|
180
|
-
// Haptic feedback for month navigation
|
|
181
|
-
triggerHaptic('medium');
|
|
182
|
-
|
|
183
|
-
slideDirection = -1; // Slide from left
|
|
184
|
-
currentMonth.update((month) => {
|
|
185
|
-
if (month === 0) {
|
|
186
|
-
currentYear.update((year) => year - 1);
|
|
187
|
-
return 11;
|
|
188
|
-
}
|
|
189
|
-
return month - 1;
|
|
190
|
-
});
|
|
191
|
-
setTimeout(() => {
|
|
192
|
-
prevAnimating = false;
|
|
193
|
-
slideDirection = 0;
|
|
194
|
-
}, 300);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Add all available event dates to the selection
|
|
198
|
-
function setAllAvailable() {
|
|
199
|
-
const year = $currentYear;
|
|
200
|
-
const month = $currentMonth;
|
|
201
|
-
|
|
202
|
-
selectedDates.update((dates) => {
|
|
203
|
-
const newDates = [...dates];
|
|
204
|
-
for (let day = 1; day <= getDaysInMonth(new Date(year, month)); day++) {
|
|
205
|
-
const dateKey = `${year}-${String(month + 1).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
|
206
|
-
if (isEvent(year, month, day) && !newDates.includes(dateKey)) {
|
|
207
|
-
newDates.push(dateKey);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
return newDates;
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Remove all event dates from the selection
|
|
215
|
-
function setAllUnavailable() {
|
|
216
|
-
const year = $currentYear;
|
|
217
|
-
const month = $currentMonth;
|
|
218
|
-
|
|
219
|
-
selectedDates.update((dates) =>
|
|
220
|
-
dates.filter((dateKey) => {
|
|
221
|
-
const [y, m, d] = dateKey.split("-").map(Number);
|
|
222
|
-
return !(y === year && m === month + 1 && isEvent(year, month, d));
|
|
223
|
-
}),
|
|
224
|
-
);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Handle Today button touch start
|
|
228
|
-
function handleTodayTouchStart() {
|
|
229
|
-
if (isSwiping) return;
|
|
230
|
-
todayButtonPressed = true;
|
|
231
|
-
triggerHaptic('light');
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// Handle Today button touch end
|
|
235
|
-
function handleTodayTouchEnd() {
|
|
236
|
-
todayButtonPressed = false;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Navigate to today's month with animation
|
|
240
|
-
function goToToday(event) {
|
|
241
|
-
// Prevent action if user is swiping
|
|
242
|
-
if (isSwiping) return;
|
|
243
|
-
|
|
244
|
-
// Remove focus/hover state from button immediately on mobile
|
|
245
|
-
if (event?.target) {
|
|
246
|
-
event.target.blur();
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
const today = new Date();
|
|
250
|
-
const todayYear = today.getUTCFullYear();
|
|
251
|
-
const todayMonth = today.getUTCMonth();
|
|
252
|
-
|
|
253
|
-
const currentYearVal = $currentYear;
|
|
254
|
-
const currentMonthVal = $currentMonth;
|
|
255
|
-
|
|
256
|
-
// Calculate if we're going forward or backward in time
|
|
257
|
-
const currentDate = new Date(currentYearVal, currentMonthVal, 1);
|
|
258
|
-
const targetDate = new Date(todayYear, todayMonth, 1);
|
|
259
|
-
|
|
260
|
-
if (targetDate > currentDate) {
|
|
261
|
-
// Going forward in time - slide from right
|
|
262
|
-
slideDirection = 1;
|
|
263
|
-
} else if (targetDate < currentDate) {
|
|
264
|
-
// Going backward in time - slide from left
|
|
265
|
-
slideDirection = -1;
|
|
266
|
-
} else {
|
|
267
|
-
// Already on today's month - no animation needed
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
currentYear.set(todayYear);
|
|
272
|
-
currentMonth.set(todayMonth);
|
|
273
|
-
|
|
274
|
-
// Reset slide direction after animation
|
|
275
|
-
setTimeout(() => {
|
|
276
|
-
slideDirection = 0;
|
|
277
|
-
}, 300);
|
|
278
|
-
}
|
|
279
|
-
</script>
|
|
280
|
-
|
|
281
|
-
<div class="calendar-container">
|
|
282
|
-
<!-- Month Navigation -->
|
|
283
|
-
<header class="calendar-header sticky top-0 bg-bg-primary z-10">
|
|
284
|
-
<button
|
|
285
|
-
on:click={handlePrev}
|
|
286
|
-
class="nav-btn"
|
|
287
|
-
class:animating={prevAnimating}
|
|
288
|
-
aria-label="Previous month"
|
|
289
|
-
>
|
|
290
|
-
<Icon name="ChevronLeft" size="16" />
|
|
291
|
-
</button>
|
|
292
|
-
|
|
293
|
-
<div class="month-display">
|
|
294
|
-
{#key `${$currentYear}-${$currentMonth}`}
|
|
295
|
-
<div class="month-content-wrapper" in:fly={{ x: slideDirection * 100, duration: 250, easing: cubicOut, opacity: 0 }} out:fly={{ x: slideDirection * -100, duration: 250, easing: cubicOut, opacity: 0 }}>
|
|
296
|
-
<div class="month-content">
|
|
297
|
-
<h2 class="month-title">
|
|
298
|
-
{new Intl.DateTimeFormat("en", { month: "long" }).format(
|
|
299
|
-
new Date($currentYear, $currentMonth),
|
|
300
|
-
)}
|
|
301
|
-
</h2>
|
|
302
|
-
<span class="year-text">{$currentYear}</span>
|
|
303
|
-
</div>
|
|
304
|
-
{#if saveStatus}
|
|
305
|
-
<div class="save-indicator-overlay">
|
|
306
|
-
{#if saveStatus === "saving"}
|
|
307
|
-
<span class="animate-spin text-green-500">
|
|
308
|
-
<Icon name="Reload" size="20" />
|
|
309
|
-
</span>
|
|
310
|
-
{:else if saveStatus === "saved"}
|
|
311
|
-
<span class="text-green-500">
|
|
312
|
-
<Icon name="CheckCircle" size="20" />
|
|
313
|
-
</span>
|
|
314
|
-
{/if}
|
|
315
|
-
</div>
|
|
316
|
-
{/if}
|
|
317
|
-
</div>
|
|
318
|
-
{/key}
|
|
319
|
-
</div>
|
|
320
|
-
|
|
321
|
-
<button
|
|
322
|
-
on:click={handleNext}
|
|
323
|
-
class="nav-btn"
|
|
324
|
-
class:animating={nextAnimating}
|
|
325
|
-
aria-label="Next month"
|
|
326
|
-
>
|
|
327
|
-
<Icon name="ChevronRight" size="16" />
|
|
328
|
-
</button>
|
|
329
|
-
</header>
|
|
330
|
-
|
|
331
|
-
<!-- Calendar Grid -->
|
|
332
|
-
<div class="calendar-grid-wrapper">
|
|
333
|
-
{#key `${$currentYear}-${$currentMonth}-${Object.keys(events).length}`}
|
|
334
|
-
<div
|
|
335
|
-
class="calendar-grid"
|
|
336
|
-
on:touchstart={handleTouchStart}
|
|
337
|
-
on:touchmove={handleTouchMove}
|
|
338
|
-
on:touchend={handleTouchEnd}
|
|
339
|
-
in:fly={{ x: slideDirection * 100, duration: 250, easing: cubicOut }}
|
|
340
|
-
out:fly={{ x: slideDirection * -100, duration: 250, easing: cubicOut }}
|
|
341
|
-
>
|
|
342
|
-
{#each ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] as dayName}
|
|
343
|
-
<div class="day-header">{dayName}</div>
|
|
344
|
-
{/each}
|
|
345
|
-
|
|
346
|
-
{#each Array(getDay(startOfMonth(new Date($currentYear, $currentMonth)))) as _}
|
|
347
|
-
<div class="empty-day"></div>
|
|
348
|
-
{/each}
|
|
349
|
-
|
|
350
|
-
{#each Array(getDaysInMonth(new Date($currentYear, $currentMonth)))
|
|
351
|
-
.fill()
|
|
352
|
-
.map((_, i) => i + 1) as day}
|
|
353
|
-
{@const isPast = isPastDate($currentYear, $currentMonth, day)}
|
|
354
|
-
{@const isDisabled = !isEvent($currentYear, $currentMonth, day) || isPast}
|
|
355
|
-
{@const isSelected = $selectedDates.includes(
|
|
356
|
-
`${$currentYear}-${String($currentMonth + 1).padStart(2, "0")}-${String(day).padStart(2, "0")}`
|
|
357
|
-
)}
|
|
358
|
-
{@const isPastSelected = isPast && isSelected}
|
|
359
|
-
|
|
360
|
-
<button
|
|
361
|
-
class="day-cell"
|
|
362
|
-
class:disabled={isDisabled}
|
|
363
|
-
class:selected={isSelected && !isPast}
|
|
364
|
-
class:past-selected={isPastSelected}
|
|
365
|
-
on:click={() => !isDisabled && toggleDateSelection($currentYear, $currentMonth, day)}
|
|
366
|
-
disabled={isDisabled}
|
|
367
|
-
aria-label="Day {day}"
|
|
368
|
-
aria-pressed={isSelected}
|
|
369
|
-
>
|
|
370
|
-
<span class="day-number">
|
|
371
|
-
{day}
|
|
372
|
-
</span>
|
|
373
|
-
</button>
|
|
374
|
-
{/each}
|
|
375
|
-
|
|
376
|
-
<!-- Fill remaining cells in the last row to maintain grid structure -->
|
|
377
|
-
{#each Array((7 - ((getDay(startOfMonth(new Date($currentYear, $currentMonth))) + getDaysInMonth(new Date($currentYear, $currentMonth))) % 7)) % 7) as _}
|
|
378
|
-
<div class="empty-day"></div>
|
|
379
|
-
{/each}
|
|
380
|
-
</div>
|
|
381
|
-
{/key}
|
|
382
|
-
</div>
|
|
383
|
-
|
|
384
|
-
<!-- Quick Actions -->
|
|
385
|
-
<div class="action-buttons" class:swiping={isSwiping}>
|
|
386
|
-
<div
|
|
387
|
-
class="today-button-wrapper"
|
|
388
|
-
class:pressed={todayButtonPressed}
|
|
389
|
-
on:touchstart={handleTodayTouchStart}
|
|
390
|
-
on:touchend={handleTodayTouchEnd}
|
|
391
|
-
on:touchcancel={handleTodayTouchEnd}
|
|
392
|
-
>
|
|
393
|
-
<Button
|
|
394
|
-
variant="blue-outline"
|
|
395
|
-
type="button"
|
|
396
|
-
size="lg"
|
|
397
|
-
on:click={goToToday}
|
|
398
|
-
>
|
|
399
|
-
Today
|
|
400
|
-
</Button>
|
|
401
|
-
</div>
|
|
402
|
-
</div>
|
|
403
|
-
</div>
|
|
404
|
-
|
|
405
|
-
<style>
|
|
406
|
-
.calendar-container {
|
|
407
|
-
margin-left: auto;
|
|
408
|
-
margin-right: auto;
|
|
409
|
-
width: 100%;
|
|
410
|
-
max-width: 28rem;
|
|
411
|
-
text-align: center;
|
|
412
|
-
touch-action: manipulation;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
.calendar-header {
|
|
416
|
-
margin-bottom: 1.5rem;
|
|
417
|
-
padding-left: 0.5rem;
|
|
418
|
-
padding-right: 0.5rem;
|
|
419
|
-
display: grid;
|
|
420
|
-
grid-template-columns: 40px minmax(0, 1fr) 40px;
|
|
421
|
-
align-items: center;
|
|
422
|
-
gap: 8px;
|
|
423
|
-
position: relative;
|
|
424
|
-
isolation: isolate;
|
|
425
|
-
-moz-user-select: none;
|
|
426
|
-
user-select: none;
|
|
427
|
-
-webkit-user-select: none;
|
|
428
|
-
-webkit-touch-callout: none;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
.nav-btn {
|
|
432
|
-
display: flex;
|
|
433
|
-
height: 2.5rem;
|
|
434
|
-
width: 2.5rem;
|
|
435
|
-
align-items: center;
|
|
436
|
-
justify-content: center;
|
|
437
|
-
border-radius: 0.5rem;
|
|
438
|
-
border-width: 1px;
|
|
439
|
-
--tw-border-opacity: 1;
|
|
440
|
-
border-color: rgb(29 78 216 / var(--tw-border-opacity, 1));
|
|
441
|
-
background-color: hsl(var(--BG-Primary));
|
|
442
|
-
padding: 0.75rem;
|
|
443
|
-
--tw-text-opacity: 1;
|
|
444
|
-
color: rgb(29 78 216 / var(--tw-text-opacity, 1));
|
|
445
|
-
transition: opacity 0.15s ease;
|
|
446
|
-
-webkit-tap-highlight-color: transparent;
|
|
447
|
-
touch-action: manipulation;
|
|
448
|
-
-moz-user-select: none;
|
|
449
|
-
user-select: none;
|
|
450
|
-
-webkit-user-select: none;
|
|
451
|
-
-webkit-touch-callout: none;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
.nav-btn.animating {
|
|
455
|
-
opacity: 0.4;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
.month-display {
|
|
459
|
-
position: relative;
|
|
460
|
-
min-height: 48px;
|
|
461
|
-
overflow: hidden;
|
|
462
|
-
display: flex;
|
|
463
|
-
align-items: center;
|
|
464
|
-
justify-content: center;
|
|
465
|
-
min-width: 0;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
.month-content-wrapper {
|
|
469
|
-
position: absolute;
|
|
470
|
-
top: 0;
|
|
471
|
-
bottom: 0;
|
|
472
|
-
left: 0;
|
|
473
|
-
right: 0;
|
|
474
|
-
display: flex;
|
|
475
|
-
align-items: center;
|
|
476
|
-
justify-content: center;
|
|
477
|
-
/* GPU acceleration */
|
|
478
|
-
transform: translateZ(0);
|
|
479
|
-
backface-visibility: hidden;
|
|
480
|
-
will-change: transform;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
.month-content {
|
|
484
|
-
display: flex;
|
|
485
|
-
flex-direction: column;
|
|
486
|
-
align-items: center;
|
|
487
|
-
white-space: nowrap;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
.month-title {
|
|
491
|
-
font-size: 1.125rem;
|
|
492
|
-
line-height: 1.75rem;
|
|
493
|
-
font-weight: 500;
|
|
494
|
-
color: hsl(var(--Text-Primary));
|
|
495
|
-
line-height: 1.2;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
.year-text {
|
|
499
|
-
font-size: 0.75rem;
|
|
500
|
-
line-height: 1rem;
|
|
501
|
-
font-weight: 400;
|
|
502
|
-
color: hsl(var(--Text-Tartiary));
|
|
503
|
-
line-height: 1;
|
|
504
|
-
margin-top: 2px;
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
.save-indicator-overlay {
|
|
508
|
-
position: absolute;
|
|
509
|
-
left: 50%;
|
|
510
|
-
top: 50%;
|
|
511
|
-
transform: translate(50px, -50%);
|
|
512
|
-
display: flex;
|
|
513
|
-
align-items: center;
|
|
514
|
-
justify-content: center;
|
|
515
|
-
width: 24px;
|
|
516
|
-
height: 24px;
|
|
517
|
-
z-index: 1;
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
.calendar-grid-wrapper {
|
|
521
|
-
position: relative;
|
|
522
|
-
overflow: hidden;
|
|
523
|
-
/* Set min-height for typical calendar (most months are 5-6 weeks) */
|
|
524
|
-
min-height: 410px;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
.calendar-grid {
|
|
528
|
-
display: grid;
|
|
529
|
-
grid-template-columns: repeat(7, minmax(0, 1fr));
|
|
530
|
-
grid-auto-rows: minmax(56px, auto);
|
|
531
|
-
/* GPU acceleration for smooth grid sliding */
|
|
532
|
-
transform: translateZ(0);
|
|
533
|
-
backface-visibility: hidden;
|
|
534
|
-
will-change: transform;
|
|
535
|
-
position: absolute;
|
|
536
|
-
width: 100%;
|
|
537
|
-
top: 0;
|
|
538
|
-
left: 0;
|
|
539
|
-
border-width: 1px;
|
|
540
|
-
border-color: hsl(var(--Stroke-Secondary));
|
|
541
|
-
border-radius: 8px;
|
|
542
|
-
overflow: hidden;
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
.day-header {
|
|
546
|
-
border-bottom-width: 1px;
|
|
547
|
-
border-right-width: 1px;
|
|
548
|
-
border-color: hsl(var(--Stroke-Secondary));
|
|
549
|
-
background-color: hsl(var(--BG-Primary));
|
|
550
|
-
padding-top: 0.75rem;
|
|
551
|
-
padding-bottom: 0.75rem;
|
|
552
|
-
font-size: 0.875rem;
|
|
553
|
-
line-height: 1.25rem;
|
|
554
|
-
font-weight: 500;
|
|
555
|
-
color: hsl(var(--Text-Tartiary));
|
|
556
|
-
-moz-user-select: none;
|
|
557
|
-
user-select: none;
|
|
558
|
-
-webkit-user-select: none;
|
|
559
|
-
-webkit-touch-callout: none;
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
/* Top-left corner */
|
|
563
|
-
.day-header:nth-child(1) {
|
|
564
|
-
border-top-left-radius: 8px;
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
/* Top-right corner */
|
|
568
|
-
.day-header:nth-child(7) {
|
|
569
|
-
border-top-right-radius: 8px;
|
|
570
|
-
border-right: none;
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
.day-header:nth-child(7n) {
|
|
574
|
-
border-right: none;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
.empty-day {
|
|
578
|
-
height: 3.5rem;
|
|
579
|
-
border-bottom-width: 1px;
|
|
580
|
-
border-right-width: 1px;
|
|
581
|
-
border-color: hsl(var(--Stroke-Secondary));
|
|
582
|
-
background-color: hsl(var(--BG-Primary));
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
.empty-day:nth-child(7n) {
|
|
586
|
-
border-right: none;
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
/* Bottom-left corner - target last row, first column */
|
|
590
|
-
.empty-day:last-child:nth-child(7n+1) {
|
|
591
|
-
border-bottom-left-radius: 8px;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
/* Bottom-right corner - target last row, last column */
|
|
595
|
-
.empty-day:last-child:nth-child(7n) {
|
|
596
|
-
border-bottom-right-radius: 8px;
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
.day-cell {
|
|
600
|
-
display: flex;
|
|
601
|
-
height: 3.5rem;
|
|
602
|
-
cursor: pointer;
|
|
603
|
-
align-items: center;
|
|
604
|
-
justify-content: center;
|
|
605
|
-
border-bottom-width: 1px;
|
|
606
|
-
border-right-width: 1px;
|
|
607
|
-
border-color: hsl(var(--Stroke-Secondary));
|
|
608
|
-
background-color: hsl(var(--BG-Primary));
|
|
609
|
-
padding: 0.25rem;
|
|
610
|
-
transition: background-color 0.1s ease;
|
|
611
|
-
-webkit-tap-highlight-color: transparent;
|
|
612
|
-
touch-action: manipulation;
|
|
613
|
-
-moz-user-select: none;
|
|
614
|
-
user-select: none;
|
|
615
|
-
-webkit-user-select: none;
|
|
616
|
-
-webkit-touch-callout: none;
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
.day-cell:nth-child(7n) {
|
|
620
|
-
border-right: none;
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
/* Bottom-left corner - target last row, first column for day cells */
|
|
624
|
-
.day-cell:last-child:nth-child(7n+1) {
|
|
625
|
-
border-bottom-left-radius: 8px;
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
/* Bottom-right corner - target last row, last column for day cells */
|
|
629
|
-
.day-cell:last-child:nth-child(7n) {
|
|
630
|
-
border-bottom-right-radius: 8px;
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
.day-number {
|
|
634
|
-
display: flex;
|
|
635
|
-
height: 2rem;
|
|
636
|
-
width: 2rem;
|
|
637
|
-
align-items: center;
|
|
638
|
-
justify-content: center;
|
|
639
|
-
border-radius: 0.375rem;
|
|
640
|
-
font-size: 0.875rem;
|
|
641
|
-
line-height: 1.25rem;
|
|
642
|
-
font-weight: 500;
|
|
643
|
-
transition: all 0.15s ease;
|
|
644
|
-
-moz-user-select: none;
|
|
645
|
-
user-select: none;
|
|
646
|
-
-webkit-user-select: none;
|
|
647
|
-
-webkit-touch-callout: none;
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
.day-cell:not(.disabled):not(.selected) .day-number {
|
|
651
|
-
color: hsl(var(--Text-Primary));
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
.day-cell:not(.disabled):not(.selected):active .day-number {
|
|
655
|
-
--tw-bg-opacity: 1;
|
|
656
|
-
background-color: rgb(239 246 255 / var(--tw-bg-opacity, 1));
|
|
657
|
-
transform: scale(0.95);
|
|
658
|
-
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.2);
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
.day-cell.selected .day-number {
|
|
662
|
-
--tw-bg-opacity: 1;
|
|
663
|
-
background-color: rgb(29 78 216 / var(--tw-bg-opacity, 1));
|
|
664
|
-
--tw-text-opacity: 1;
|
|
665
|
-
color: rgb(255 255 255 / var(--tw-text-opacity, 1));
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
.day-cell.selected:active .day-number {
|
|
669
|
-
transform: scale(0.95);
|
|
670
|
-
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.3);
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
.day-cell.past-selected .day-number {
|
|
674
|
-
background-color: hsl(var(--BG-Quaternary));
|
|
675
|
-
color: hsl(var(--Text-Tartiary));
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
.day-cell.disabled {
|
|
679
|
-
background-color: hsl(var(--BG-Secondary) / 0.5);
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
.day-cell.disabled .day-number {
|
|
683
|
-
pointer-events: none;
|
|
684
|
-
color: hsl(var(--Text-Tartiary) / 0.5);
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
.action-buttons {
|
|
688
|
-
display: flex;
|
|
689
|
-
justify-content: center;
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
.action-buttons.swiping {
|
|
693
|
-
pointer-events: none;
|
|
694
|
-
touch-action: none;
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
.today-button-wrapper {
|
|
698
|
-
transition: all 0.15s ease;
|
|
699
|
-
-webkit-tap-highlight-color: transparent;
|
|
700
|
-
-moz-user-select: none;
|
|
701
|
-
user-select: none;
|
|
702
|
-
-webkit-user-select: none;
|
|
703
|
-
-webkit-touch-callout: none;
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
.today-button-wrapper.pressed :global(button) {
|
|
707
|
-
background-color: rgba(99, 102, 241, 0.1) !important;
|
|
708
|
-
transform: scale(0.95);
|
|
709
|
-
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.2);
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
/* Prevent hover state from sticking on mobile */
|
|
713
|
-
.action-buttons :global(button) {
|
|
714
|
-
-webkit-tap-highlight-color: transparent;
|
|
715
|
-
transition: all 0.15s ease;
|
|
716
|
-
-moz-user-select: none;
|
|
717
|
-
user-select: none;
|
|
718
|
-
-webkit-user-select: none;
|
|
719
|
-
-webkit-touch-callout: none;
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
@media (hover: none) {
|
|
723
|
-
.action-buttons :global(button:hover) {
|
|
724
|
-
/* Disable hover styles on touch devices */
|
|
725
|
-
background-color: transparent !important;
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
@media (prefers-reduced-motion: reduce) {
|
|
730
|
-
.nav-btn,
|
|
731
|
-
.day-number,
|
|
732
|
-
.day-cell {
|
|
733
|
-
transition: none;
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
</style>
|