@intinyagroup/calendar 0.1.1-alpha.17 → 0.1.1-alpha.19
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/Calendar.svelte +142 -30
- package/dist/DateRangePicker.svelte +123 -56
- package/dist/EventCalendar.svelte +414 -173
- package/dist/EventCalendar.svelte.d.ts +1 -1
- package/package.json +2 -2
package/dist/Calendar.svelte
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { ChevronLeft, ChevronRight } from
|
|
3
|
-
import { Button } from
|
|
4
|
-
import { cn } from
|
|
2
|
+
import { ChevronLeft, ChevronRight } from "lucide-svelte";
|
|
3
|
+
import { Button } from "@intinyagroup/ui";
|
|
4
|
+
import { cn } from "@intinyagroup/ui/utils";
|
|
5
5
|
|
|
6
6
|
export type CalendarDate = { year: number; month: number; day: number };
|
|
7
7
|
|
|
@@ -25,13 +25,27 @@
|
|
|
25
25
|
let viewYear = $state(value?.year ?? today.getFullYear());
|
|
26
26
|
let viewMonth = $state(value?.month ?? today.getMonth());
|
|
27
27
|
|
|
28
|
-
const monthNames = [
|
|
29
|
-
|
|
28
|
+
const monthNames = [
|
|
29
|
+
"January",
|
|
30
|
+
"February",
|
|
31
|
+
"March",
|
|
32
|
+
"April",
|
|
33
|
+
"May",
|
|
34
|
+
"June",
|
|
35
|
+
"July",
|
|
36
|
+
"August",
|
|
37
|
+
"September",
|
|
38
|
+
"October",
|
|
39
|
+
"November",
|
|
40
|
+
"December",
|
|
41
|
+
];
|
|
42
|
+
const dayNames = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
|
|
30
43
|
const daysInMonth = $derived(new Date(viewYear, viewMonth + 1, 0).getDate());
|
|
31
44
|
const firstDayOfMonth = $derived(new Date(viewYear, viewMonth, 1).getDay());
|
|
32
45
|
|
|
33
46
|
const calendarDays = $derived.by(() => {
|
|
34
|
-
const days: { day: number; currentMonth: boolean; date: CalendarDate }[] =
|
|
47
|
+
const days: { day: number; currentMonth: boolean; date: CalendarDate }[] =
|
|
48
|
+
[];
|
|
35
49
|
const prevMonthDays = new Date(viewYear, viewMonth, 0).getDate();
|
|
36
50
|
for (let i = firstDayOfMonth - 1; i >= 0; i--) {
|
|
37
51
|
const day = prevMonthDays - i;
|
|
@@ -40,7 +54,11 @@
|
|
|
40
54
|
days.push({ day, currentMonth: false, date: { year, month, day } });
|
|
41
55
|
}
|
|
42
56
|
for (let day = 1; day <= daysInMonth; day++) {
|
|
43
|
-
days.push({
|
|
57
|
+
days.push({
|
|
58
|
+
day,
|
|
59
|
+
currentMonth: true,
|
|
60
|
+
date: { year: viewYear, month: viewMonth, day },
|
|
61
|
+
});
|
|
44
62
|
}
|
|
45
63
|
const remaining = 42 - days.length;
|
|
46
64
|
for (let day = 1; day <= remaining; day++) {
|
|
@@ -55,48 +73,142 @@
|
|
|
55
73
|
return a?.year === b.year && a?.month === b.month && a?.day === b.day;
|
|
56
74
|
}
|
|
57
75
|
function isToday(date: CalendarDate) {
|
|
58
|
-
return
|
|
76
|
+
return (
|
|
77
|
+
date.year === today.getFullYear() &&
|
|
78
|
+
date.month === today.getMonth() &&
|
|
79
|
+
date.day === today.getDate()
|
|
80
|
+
);
|
|
59
81
|
}
|
|
60
82
|
function isDisabled(date: CalendarDate) {
|
|
61
|
-
if (
|
|
62
|
-
|
|
83
|
+
if (
|
|
84
|
+
min &&
|
|
85
|
+
(date.year < min.year ||
|
|
86
|
+
(date.year === min.year && date.month < min.month) ||
|
|
87
|
+
(date.year === min.year &&
|
|
88
|
+
date.month === min.month &&
|
|
89
|
+
date.day < min.day))
|
|
90
|
+
)
|
|
91
|
+
return true;
|
|
92
|
+
if (
|
|
93
|
+
max &&
|
|
94
|
+
(date.year > max.year ||
|
|
95
|
+
(date.year === max.year && date.month > max.month) ||
|
|
96
|
+
(date.year === max.year &&
|
|
97
|
+
date.month === max.month &&
|
|
98
|
+
date.day > max.day))
|
|
99
|
+
)
|
|
100
|
+
return true;
|
|
63
101
|
return false;
|
|
64
102
|
}
|
|
65
|
-
function prevMonth() {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
103
|
+
function prevMonth() {
|
|
104
|
+
if (viewMonth === 0) {
|
|
105
|
+
viewMonth = 11;
|
|
106
|
+
viewYear--;
|
|
107
|
+
} else {
|
|
108
|
+
viewMonth--;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function nextMonth() {
|
|
112
|
+
if (viewMonth === 11) {
|
|
113
|
+
viewMonth = 0;
|
|
114
|
+
viewYear++;
|
|
115
|
+
} else {
|
|
116
|
+
viewMonth++;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function selectDate(date: CalendarDate) {
|
|
120
|
+
if (disabled || isDisabled(date)) return;
|
|
121
|
+
value = date;
|
|
122
|
+
onSelect?.(date);
|
|
123
|
+
}
|
|
124
|
+
function goToday() {
|
|
125
|
+
viewYear = today.getFullYear();
|
|
126
|
+
viewMonth = today.getMonth();
|
|
127
|
+
selectDate({
|
|
128
|
+
year: today.getFullYear(),
|
|
129
|
+
month: today.getMonth(),
|
|
130
|
+
day: today.getDate(),
|
|
131
|
+
});
|
|
132
|
+
}
|
|
69
133
|
</script>
|
|
70
134
|
|
|
71
|
-
<div
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
135
|
+
<div
|
|
136
|
+
class={cn(
|
|
137
|
+
"inline-flex flex-col rounded-xl border border-[var(--ui-border)] bg-[var(--ui-card)] p-4 shadow-sm select-none",
|
|
138
|
+
className,
|
|
139
|
+
)}
|
|
140
|
+
style="min-width: 280px; max-width: 320px; box-sizing: border-box;"
|
|
141
|
+
>
|
|
142
|
+
<div
|
|
143
|
+
class="flex items-center justify-between mb-4"
|
|
144
|
+
style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 1rem;"
|
|
145
|
+
>
|
|
146
|
+
<Button variant="ghost" size="sm" onclick={prevMonth} {disabled}
|
|
147
|
+
><ChevronLeft class="size-4" /></Button
|
|
148
|
+
>
|
|
149
|
+
<div
|
|
150
|
+
class="flex items-center gap-2"
|
|
151
|
+
style="display: flex; align-items: center; gap: 0.5rem;"
|
|
152
|
+
>
|
|
153
|
+
<span class="text-sm font-semibold text-[var(--ui-foreground)]"
|
|
154
|
+
>{monthNames[viewMonth]} {viewYear}</span
|
|
155
|
+
>
|
|
156
|
+
<Button
|
|
157
|
+
variant="ghost"
|
|
158
|
+
size="sm"
|
|
159
|
+
onclick={goToday}
|
|
160
|
+
{disabled}
|
|
161
|
+
class="text-xs h-6 px-2">Today</Button
|
|
162
|
+
>
|
|
77
163
|
</div>
|
|
78
|
-
<Button variant="ghost" size="sm" onclick={nextMonth} {disabled}
|
|
164
|
+
<Button variant="ghost" size="sm" onclick={nextMonth} {disabled}
|
|
165
|
+
><ChevronRight class="size-4" /></Button
|
|
166
|
+
>
|
|
79
167
|
</div>
|
|
80
168
|
|
|
81
|
-
<div
|
|
82
|
-
|
|
169
|
+
<div
|
|
170
|
+
class="grid grid-cols-7 gap-1 mb-2 text-center"
|
|
171
|
+
style="display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 0.25rem; margin-bottom: 0.5rem; text-align: center;"
|
|
172
|
+
>
|
|
173
|
+
{#each dayNames as d}
|
|
174
|
+
<div
|
|
175
|
+
class="text-center text-[11px] font-medium text-[var(--ui-muted-foreground)] py-1"
|
|
176
|
+
style="text-align: center; font-size: 0.6875rem; padding: 0.25rem 0;"
|
|
177
|
+
>
|
|
178
|
+
{d}
|
|
179
|
+
</div>
|
|
180
|
+
{/each}
|
|
83
181
|
</div>
|
|
84
182
|
|
|
85
|
-
<div
|
|
86
|
-
|
|
183
|
+
<div
|
|
184
|
+
class="grid grid-cols-7 gap-1"
|
|
185
|
+
style="display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 0.25rem;"
|
|
186
|
+
>
|
|
187
|
+
{#each calendarDays as { day, currentMonth, date } (date.year + "-" + date.month + "-" + date.day)}
|
|
87
188
|
{@const isSelected = isSameDate(value, date)}
|
|
88
189
|
{@const isTodayDate = isToday(date)}
|
|
89
190
|
{@const isDisabledDate = isDisabled(date)}
|
|
90
191
|
<button
|
|
192
|
+
type="button"
|
|
91
193
|
onclick={() => selectDate(date)}
|
|
92
194
|
disabled={disabled || isDisabledDate}
|
|
195
|
+
style="width: 2.25rem; height: 2.25rem; display: inline-flex; align-items: center; justify-content: center; box-sizing: border-box;"
|
|
93
196
|
class="flex items-center justify-center size-9 rounded-lg text-sm transition-colors cursor-pointer
|
|
94
|
-
{isSelected
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
{
|
|
197
|
+
{isSelected
|
|
198
|
+
? 'bg-[var(--ui-primary)] text-[var(--ui-primary-foreground)] font-semibold'
|
|
199
|
+
: ''}
|
|
200
|
+
{!isSelected && currentMonth
|
|
201
|
+
? 'text-[var(--ui-foreground)] hover:bg-[var(--ui-secondary)]'
|
|
202
|
+
: ''}
|
|
203
|
+
{!isSelected && !currentMonth
|
|
204
|
+
? 'text-[var(--ui-muted-foreground)]/40'
|
|
205
|
+
: ''}
|
|
206
|
+
{isTodayDate && !isSelected
|
|
207
|
+
? 'ring-2 ring-[var(--ui-primary)]/30 font-medium'
|
|
208
|
+
: ''}
|
|
98
209
|
{isDisabledDate || disabled ? 'opacity-40 cursor-not-allowed' : ''}"
|
|
99
|
-
|
|
210
|
+
>{day}</button
|
|
211
|
+
>
|
|
100
212
|
{/each}
|
|
101
213
|
</div>
|
|
102
214
|
</div>
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import {
|
|
3
|
+
Calendar as CalendarIcon,
|
|
4
|
+
ChevronLeft,
|
|
5
|
+
ChevronRight,
|
|
6
|
+
X,
|
|
7
|
+
} from "lucide-svelte";
|
|
8
|
+
import { Button } from "@intinyagroup/ui";
|
|
9
|
+
import { cn } from "@intinyagroup/ui/utils";
|
|
5
10
|
import {
|
|
6
11
|
addMonths,
|
|
7
12
|
subMonths,
|
|
@@ -16,9 +21,9 @@
|
|
|
16
21
|
isSameMonth,
|
|
17
22
|
isWithinInterval,
|
|
18
23
|
isBefore,
|
|
19
|
-
format
|
|
20
|
-
} from
|
|
21
|
-
import { id as localeId, enUS as localeEn } from
|
|
24
|
+
format,
|
|
25
|
+
} from "date-fns";
|
|
26
|
+
import { id as localeId, enUS as localeEn } from "date-fns/locale";
|
|
22
27
|
|
|
23
28
|
export type CalendarDate = { year: number; month: number; day: number };
|
|
24
29
|
export type DateRange = {
|
|
@@ -36,8 +41,8 @@
|
|
|
36
41
|
min = null,
|
|
37
42
|
max = null,
|
|
38
43
|
disabled = false,
|
|
39
|
-
placeholder =
|
|
40
|
-
locale =
|
|
44
|
+
placeholder = "Select date range...",
|
|
45
|
+
locale = "en-US",
|
|
41
46
|
firstDayOfWeek = 0, // 0 = Sunday, 1 = Monday
|
|
42
47
|
presets,
|
|
43
48
|
class: className,
|
|
@@ -59,11 +64,15 @@
|
|
|
59
64
|
const today = new Date();
|
|
60
65
|
|
|
61
66
|
let viewDate = $state(
|
|
62
|
-
value?.start
|
|
67
|
+
value?.start
|
|
68
|
+
? new Date(value.start.year, value.start.month, value.start.day)
|
|
69
|
+
: today,
|
|
63
70
|
);
|
|
64
71
|
let hoverDate = $state<Date | null>(null);
|
|
65
72
|
|
|
66
|
-
const activeDateFnsLocale = $derived(
|
|
73
|
+
const activeDateFnsLocale = $derived(
|
|
74
|
+
locale.startsWith("id") ? localeId : localeEn,
|
|
75
|
+
);
|
|
67
76
|
const weekStartsOn = $derived(firstDayOfWeek as 0 | 1 | 2 | 3 | 4 | 5 | 6);
|
|
68
77
|
|
|
69
78
|
function toDate(d: CalendarDate | null): Date | null {
|
|
@@ -75,7 +84,7 @@
|
|
|
75
84
|
return {
|
|
76
85
|
year: date.getFullYear(),
|
|
77
86
|
month: date.getMonth(),
|
|
78
|
-
day: date.getDate()
|
|
87
|
+
day: date.getDate(),
|
|
79
88
|
};
|
|
80
89
|
}
|
|
81
90
|
|
|
@@ -83,7 +92,7 @@
|
|
|
83
92
|
const dayNames = $derived.by(() => {
|
|
84
93
|
const start = startOfWeek(viewDate, { weekStartsOn });
|
|
85
94
|
return Array.from({ length: 7 }, (_, i) => {
|
|
86
|
-
return format(addDays(start, i),
|
|
95
|
+
return format(addDays(start, i), "EEE", { locale: activeDateFnsLocale });
|
|
87
96
|
});
|
|
88
97
|
});
|
|
89
98
|
|
|
@@ -98,7 +107,7 @@
|
|
|
98
107
|
return days.map((d) => ({
|
|
99
108
|
date: d,
|
|
100
109
|
calendarDate: toCalendarDate(d),
|
|
101
|
-
currentMonth: isSameMonth(d, viewDate)
|
|
110
|
+
currentMonth: isSameMonth(d, viewDate),
|
|
102
111
|
}));
|
|
103
112
|
});
|
|
104
113
|
|
|
@@ -112,9 +121,15 @@
|
|
|
112
121
|
value = { start: toCalendarDate(date), end: null };
|
|
113
122
|
} else if (startDateObj && !endDateObj) {
|
|
114
123
|
if (isBefore(date, startDateObj)) {
|
|
115
|
-
value = {
|
|
124
|
+
value = {
|
|
125
|
+
start: toCalendarDate(date),
|
|
126
|
+
end: toCalendarDate(startDateObj),
|
|
127
|
+
};
|
|
116
128
|
} else {
|
|
117
|
-
value = {
|
|
129
|
+
value = {
|
|
130
|
+
start: toCalendarDate(startDateObj),
|
|
131
|
+
end: toCalendarDate(date),
|
|
132
|
+
};
|
|
118
133
|
}
|
|
119
134
|
onSelect?.(value);
|
|
120
135
|
isOpen = false;
|
|
@@ -131,8 +146,8 @@
|
|
|
131
146
|
|
|
132
147
|
function formatDisplayDate(date: CalendarDate | null): string {
|
|
133
148
|
const d = toDate(date);
|
|
134
|
-
if (!d) return
|
|
135
|
-
return format(d,
|
|
149
|
+
if (!d) return "";
|
|
150
|
+
return format(d, "dd/MM/yyyy", { locale: activeDateFnsLocale });
|
|
136
151
|
}
|
|
137
152
|
|
|
138
153
|
const label = $derived.by(() => {
|
|
@@ -159,51 +174,64 @@
|
|
|
159
174
|
|
|
160
175
|
return [
|
|
161
176
|
{
|
|
162
|
-
label:
|
|
163
|
-
range: { start: toCalendarDate(today), end: toCalendarDate(today) }
|
|
177
|
+
label: "Today",
|
|
178
|
+
range: { start: toCalendarDate(today), end: toCalendarDate(today) },
|
|
164
179
|
},
|
|
165
180
|
{
|
|
166
|
-
label:
|
|
167
|
-
range: {
|
|
181
|
+
label: "Next 7D",
|
|
182
|
+
range: {
|
|
183
|
+
start: toCalendarDate(today),
|
|
184
|
+
end: toCalendarDate(addDays(today, 7)),
|
|
185
|
+
},
|
|
168
186
|
},
|
|
169
187
|
{
|
|
170
|
-
label:
|
|
171
|
-
range: {
|
|
188
|
+
label: "This Month",
|
|
189
|
+
range: {
|
|
190
|
+
start: toCalendarDate(startOfMonth(today)),
|
|
191
|
+
end: toCalendarDate(endOfMonth(today)),
|
|
192
|
+
},
|
|
172
193
|
},
|
|
173
194
|
{
|
|
174
|
-
label:
|
|
195
|
+
label: "Last Month",
|
|
175
196
|
range: {
|
|
176
197
|
start: toCalendarDate(startOfMonth(subMonths(today, 1))),
|
|
177
|
-
end: toCalendarDate(endOfMonth(subMonths(today, 1)))
|
|
178
|
-
}
|
|
198
|
+
end: toCalendarDate(endOfMonth(subMonths(today, 1))),
|
|
199
|
+
},
|
|
179
200
|
},
|
|
180
201
|
{
|
|
181
|
-
label:
|
|
182
|
-
range: {
|
|
183
|
-
|
|
202
|
+
label: "YTD",
|
|
203
|
+
range: {
|
|
204
|
+
start: { year: y, month: 0, day: 1 },
|
|
205
|
+
end: toCalendarDate(today),
|
|
206
|
+
},
|
|
207
|
+
},
|
|
184
208
|
];
|
|
185
209
|
});
|
|
186
210
|
</script>
|
|
187
211
|
|
|
188
|
-
<div class={cn(
|
|
212
|
+
<div class={cn("relative inline-block w-full max-w-sm", className)}>
|
|
189
213
|
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
|
190
214
|
<div
|
|
191
215
|
role="button"
|
|
192
216
|
tabindex={disabled ? -1 : 0}
|
|
193
217
|
aria-disabled={disabled}
|
|
194
|
-
onclick={() => {
|
|
218
|
+
onclick={() => {
|
|
219
|
+
if (!disabled) isOpen = !isOpen;
|
|
220
|
+
}}
|
|
195
221
|
onkeydown={(e) => {
|
|
196
222
|
if (disabled) return;
|
|
197
|
-
if (e.key ===
|
|
223
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
198
224
|
e.preventDefault();
|
|
199
225
|
isOpen = !isOpen;
|
|
200
226
|
}
|
|
201
227
|
}}
|
|
202
228
|
class={cn(
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
disabled &&
|
|
206
|
-
value.start
|
|
229
|
+
"flex h-10 w-full items-center justify-between rounded-lg border border-[var(--ui-input)] bg-[var(--ui-background)] px-3 text-sm transition-colors text-left cursor-pointer select-none",
|
|
230
|
+
"hover:border-[var(--ui-border)] focus:outline-none focus:ring-2 focus:ring-[var(--ui-ring)]/20 focus:border-[var(--ui-primary)]",
|
|
231
|
+
disabled && "opacity-50 cursor-not-allowed pointer-events-none",
|
|
232
|
+
value.start
|
|
233
|
+
? "text-[var(--ui-foreground)]"
|
|
234
|
+
: "text-[var(--ui-muted-foreground)]",
|
|
207
235
|
)}
|
|
208
236
|
>
|
|
209
237
|
<div class="flex items-center gap-2 truncate">
|
|
@@ -237,37 +265,64 @@
|
|
|
237
265
|
>
|
|
238
266
|
<!-- Header controls -->
|
|
239
267
|
<div class="flex items-center justify-between mb-3">
|
|
240
|
-
<Button
|
|
268
|
+
<Button
|
|
269
|
+
variant="ghost"
|
|
270
|
+
size="sm"
|
|
271
|
+
class="size-7 p-0"
|
|
272
|
+
onclick={prevMonth}
|
|
273
|
+
>
|
|
241
274
|
<ChevronLeft class="size-4" />
|
|
242
275
|
</Button>
|
|
243
|
-
<span
|
|
244
|
-
|
|
276
|
+
<span
|
|
277
|
+
class="text-xs font-semibold text-[var(--ui-foreground)] capitalize"
|
|
278
|
+
>
|
|
279
|
+
{format(viewDate, "MMMM yyyy", { locale: activeDateFnsLocale })}
|
|
245
280
|
</span>
|
|
246
|
-
<Button
|
|
281
|
+
<Button
|
|
282
|
+
variant="ghost"
|
|
283
|
+
size="sm"
|
|
284
|
+
class="size-7 p-0"
|
|
285
|
+
onclick={nextMonth}
|
|
286
|
+
>
|
|
247
287
|
<ChevronRight class="size-4" />
|
|
248
288
|
</Button>
|
|
249
289
|
</div>
|
|
250
290
|
|
|
251
291
|
<!-- Day names -->
|
|
252
|
-
<div
|
|
292
|
+
<div
|
|
293
|
+
class="grid grid-cols-7 mb-1 text-center"
|
|
294
|
+
style="display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); text-align: center; margin-bottom: 0.25rem;"
|
|
295
|
+
>
|
|
253
296
|
{#each dayNames as day}
|
|
254
|
-
<span
|
|
297
|
+
<span
|
|
298
|
+
class="text-[11px] font-semibold text-[var(--ui-muted-foreground)] py-1 capitalize"
|
|
299
|
+
style="text-align: center; font-size: 0.6875rem; padding: 0.25rem 0;"
|
|
300
|
+
>
|
|
255
301
|
{day}
|
|
256
302
|
</span>
|
|
257
303
|
{/each}
|
|
258
304
|
</div>
|
|
259
305
|
|
|
260
306
|
<!-- Calendar grid -->
|
|
261
|
-
<div
|
|
307
|
+
<div
|
|
308
|
+
class="grid grid-cols-7 gap-y-1"
|
|
309
|
+
style="display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); row-gap: 0.25rem;"
|
|
310
|
+
>
|
|
262
311
|
{#each calendarDays as { date, calendarDate, currentMonth }}
|
|
263
|
-
{@const isStart = startDateObj
|
|
312
|
+
{@const isStart = startDateObj
|
|
313
|
+
? isSameDay(startDateObj, date)
|
|
314
|
+
: false}
|
|
264
315
|
{@const isEnd = endDateObj ? isSameDay(endDateObj, date) : false}
|
|
265
316
|
{@const activeRangeEnd = endDateObj || hoverDate}
|
|
266
317
|
{@const inRange =
|
|
267
318
|
startDateObj && activeRangeEnd
|
|
268
319
|
? isWithinInterval(date, {
|
|
269
|
-
start: isBefore(startDateObj, activeRangeEnd)
|
|
270
|
-
|
|
320
|
+
start: isBefore(startDateObj, activeRangeEnd)
|
|
321
|
+
? startDateObj
|
|
322
|
+
: activeRangeEnd,
|
|
323
|
+
end: isBefore(startDateObj, activeRangeEnd)
|
|
324
|
+
? activeRangeEnd
|
|
325
|
+
: startDateObj,
|
|
271
326
|
})
|
|
272
327
|
: false}
|
|
273
328
|
{@const isEdge = isStart || isEnd}
|
|
@@ -276,10 +331,16 @@
|
|
|
276
331
|
role="gridcell"
|
|
277
332
|
aria-selected={isEdge}
|
|
278
333
|
class={cn(
|
|
279
|
-
|
|
280
|
-
inRange && !isEdge &&
|
|
281
|
-
isStart &&
|
|
282
|
-
|
|
334
|
+
"relative py-0.5 flex items-center justify-center",
|
|
335
|
+
inRange && !isEdge && "bg-[var(--ui-primary)]/10",
|
|
336
|
+
isStart &&
|
|
337
|
+
activeRangeEnd &&
|
|
338
|
+
!isSameDay(startDateObj, activeRangeEnd) &&
|
|
339
|
+
"rounded-l-md bg-[var(--ui-primary)]/10",
|
|
340
|
+
isEnd &&
|
|
341
|
+
startDateObj &&
|
|
342
|
+
!isSameDay(startDateObj, endDateObj) &&
|
|
343
|
+
"rounded-r-md bg-[var(--ui-primary)]/10",
|
|
283
344
|
)}
|
|
284
345
|
onmouseenter={() => {
|
|
285
346
|
if (startDateObj && !endDateObj) hoverDate = date;
|
|
@@ -290,11 +351,15 @@
|
|
|
290
351
|
disabled={!currentMonth}
|
|
291
352
|
onclick={() => handleDateClick(date)}
|
|
292
353
|
class={cn(
|
|
293
|
-
|
|
294
|
-
!currentMonth &&
|
|
295
|
-
currentMonth &&
|
|
296
|
-
|
|
297
|
-
|
|
354
|
+
"size-8 rounded-md text-xs font-medium transition-colors flex items-center justify-center cursor-pointer",
|
|
355
|
+
!currentMonth && "opacity-20 cursor-default",
|
|
356
|
+
currentMonth &&
|
|
357
|
+
!isEdge &&
|
|
358
|
+
!inRange &&
|
|
359
|
+
"hover:bg-[var(--ui-secondary)] text-[var(--ui-foreground)]",
|
|
360
|
+
inRange && !isEdge && "text-[var(--ui-primary)] font-semibold",
|
|
361
|
+
isEdge &&
|
|
362
|
+
"bg-[var(--ui-primary)] text-[var(--ui-primary-foreground)] font-bold shadow-xs",
|
|
298
363
|
)}
|
|
299
364
|
>
|
|
300
365
|
{date.getDate()}
|
|
@@ -304,7 +369,9 @@
|
|
|
304
369
|
</div>
|
|
305
370
|
|
|
306
371
|
<!-- Presets quick buttons bar -->
|
|
307
|
-
<div
|
|
372
|
+
<div
|
|
373
|
+
class="flex flex-wrap items-center gap-1.5 border-t border-[var(--ui-border)] mt-3 pt-2.5"
|
|
374
|
+
>
|
|
308
375
|
{#each activePresets as preset}
|
|
309
376
|
<button
|
|
310
377
|
type="button"
|